<commit_before>/*global System*/
// import config from "../config.js";
import { obj, arr, num } from "lively.lang";
import { rect, Rectangle, Color, pt } from "lively.graphics";
import { connect, signal, disconnect } from "lively.bindings"; // for makeInputLine
import { Text } from "./morph.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, 'input', input, 'remove');
    connect(input, 'input', 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;
  }

  constructor(props = {}) {
    var {label, placeholder} = props;
    super({
      extent: pt(100, 20), padding: Rectangle.inset(1),
      clipMode: "auto", lineWrapping: false,
      historyId: null, clearOnInput: false,
      ...obj.dissoc(props, ["label", "placeholder"])
    })
    if (label) this.label = label || "";
    if (placeholder) this.placeholder = placeholder;
    this.height = this.defaultLineHeight + this.padding.top() + this.padding.bottom();
    connect(this, 'textChange', this, 'onInputChanged');
    connect(this, 'selectionChange', this, 'fixCursor');
  }

  get label() { return this.getProperty("label") || ""; }
  set label(value) {
    disconnect(this, 'textChange', this, 'onInputChanged');
    this.textString = value + this.input;
    connect(this, 'textChange', this, 'onInputChanged');
    this.addValueChange("label", value);
  }

  get allowDuplicatesInHistory() { return false }

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

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

  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);
  }

  get input() {
    var input = this.textString;
    if (this.label && input.startsWith(this.label))
      input = input.slice(this.label.length);
    return input;
  }
  set input(val) { this.textString = this.label + val; }

  get placeholder() {
    return this._placeHolder ? this._placeHolder.value : null;
  }
  set placeholder(val) {
    if (!val) {
      if (this._placeHolder) {
        this._placeHolder.remove();
        this._placeHolder = null;
      }
    } else {
      if (!this._placeHolder) {
        this._placeHolder = this.addMorph(Text.makeLabel(val, {
          ...this.defaultTextStyle,
          name: "placeholder",
          reactsToPointer: false,
          fontColor: Color.gray
        }));
        this._placeHolder.onInputChange = () => {
          this._placeHolder.visible = !this.input.length;
        }
        this._placeHolder.onLabelChange = () => {
          let fm = this.env.fontMetric,
              style = this.defaultTextStyle,
              w = fm.sizeFor(style, this.label).width;
          this._placeHolder.leftCenter = this.leftCenter.addXY(w + this.padding.left(), 0);
        }
        connect(this, "inputChanged", this._placeHolder, "onInputChange");
        connect(this, "label", this._placeHolder, "onLabelChange")
        this._placeHolder.onInputChange();
        this._placeHolder.onLabelChange();
      }
    }
  }

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

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

  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, "input", input);
  }
  onInputChanged(change) { signal(this, "inputChanged", change); }

  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  // 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"}
    ]);
  }
}

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

export class PasswordInputLine extends HTMLMorph {
  constructor(opts = {}) {
    super(opts);
    this.html = `<input style="height: calc(100% - 6px); width: calc(100% - 6px);" type="password" value="">`;
    // hmm key events aren't dispatched by default...
    this.ensureInputNode().then(node =>
      node.onkeydown = evt => this.env.eventDispatcher.dispatchDOMEvent(evt, this, "onKeyDown"));
  }

  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, "input", i);; return i; }
  onInputChanged(change) { signal(this, "inputChanged", change); }

  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>don't show scroll for input line text, fixes issues on linux + windows<commit_after>/*global System*/
// import config from "../config.js";
import { obj, arr, num } from "lively.lang";
import { rect, Rectangle, Color, pt } from "lively.graphics";
import { connect, signal, disconnect } from "lively.bindings"; // for makeInputLine
import { Text } from "./morph.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, 'input', input, 'remove');
    connect(input, 'input', 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;
  }

  constructor(props = {}) {
    var {label, placeholder} = props;
    super({
      extent: pt(100, 20), padding: Rectangle.inset(1),
      clipMode: "hidden", lineWrapping: false,
      historyId: null, clearOnInput: false,
      ...obj.dissoc(props, ["label", "placeholder"])
    })
    if (label) this.label = label || "";
    if (placeholder) this.placeholder = placeholder;
    this.height = this.defaultLineHeight + this.padding.top() + this.padding.bottom();
    connect(this, 'textChange', this, 'onInputChanged');
    connect(this, 'selectionChange', this, 'fixCursor');
  }

  get label() { return this.getProperty("label") || ""; }
  set label(value) {
    disconnect(this, 'textChange', this, 'onInputChanged');
    this.textString = value + this.input;
    connect(this, 'textChange', this, 'onInputChanged');
    this.addValueChange("label", value);
  }

  get allowDuplicatesInHistory() { return false }

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

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

  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);
  }

  get input() {
    var input = this.textString;
    if (this.label && input.startsWith(this.label))
      input = input.slice(this.label.length);
    return input;
  }
  set input(val) { this.textString = this.label + val; }

  get placeholder() {
    return this._placeHolder ? this._placeHolder.value : null;
  }
  set placeholder(val) {
    if (!val) {
      if (this._placeHolder) {
        this._placeHolder.remove();
        this._placeHolder = null;
      }
    } else {
      if (!this._placeHolder) {
        this._placeHolder = this.addMorph(Text.makeLabel(val, {
          ...this.defaultTextStyle,
          name: "placeholder",
          reactsToPointer: false,
          fontColor: Color.gray
        }));
        this._placeHolder.onInputChange = () => {
          this._placeHolder.visible = !this.input.length;
        }
        this._placeHolder.onLabelChange = () => {
          let fm = this.env.fontMetric,
              style = this.defaultTextStyle,
              w = fm.sizeFor(style, this.label).width;
          this._placeHolder.leftCenter = this.leftCenter.addXY(w + this.padding.left(), 0);
        }
        connect(this, "inputChanged", this._placeHolder, "onInputChange");
        connect(this, "label", this._placeHolder, "onLabelChange")
        this._placeHolder.onInputChange();
        this._placeHolder.onLabelChange();
      }
    }
  }

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

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

  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, "input", input);
  }
  onInputChanged(change) { signal(this, "inputChanged", change); }

  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  // 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"}
    ]);
  }
}

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

export class PasswordInputLine extends HTMLMorph {
  constructor(opts = {}) {
    super(opts);
    this.html = `<input style="height: calc(100% - 6px); width: calc(100% - 6px);" type="password" value="">`;
    // hmm key events aren't dispatched by default...
    this.ensureInputNode().then(node =>
      node.onkeydown = evt => this.env.eventDispatcher.dispatchDOMEvent(evt, this, "onKeyDown"));
  }

  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, "input", i);; return i; }
  onInputChanged(change) { signal(this, "inputChanged", change); }

  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>// ==UserScript==
// @name         Namuru-button
// @namespace    https://github.com/yuzulabo
// @version      1.4.3
// @description  南無りまくれぇ！
// @author       neziri_wasabi (yuzu_1203)
// @match        https://knzk.me/*
// @license       MIT License
// @require      https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.slim.min.js
// ==/UserScript==

/*
 「ゴーン」効果音について
フリー素材を使用しています。魔王魂: http://maoudamashii.jokersounds.com/

「チーン」効果音について
フリー素材を使用しています。http://nico.ms/nc42872

 木魚　効果音について
 フリー素材を使用しています。http://nico.ms/nc92438

 レア音声：神崎おにいさんについて
 1: 可視光線様のKNZK_ZONEの一部を使用しています。: http://nico.ms/sm31146004
 2: 「溺れ死ぬボーちゃんの深海少女。を歌ってみた」の一部を使用しています。：http://nico.ms/sm31734029

 これらの音声はスクリプト作者の個人サーバにアップロードされていますが、問題があればお問い合わせください。
 連絡先: https://knzk.me/@y
*/
(function() {
    var css = ".star-icon, .icon-button {filter: grayscale(100%);}.fa-star:before{content: \"\\1F64F\";white-space: nowrap;} .star-icon.active, .icon-button.active {filter: none;}";

    // 旧版「ﾅﾑ」ボタンを有効化するには次の行のコメントアウトを削除
    // css = ".fa-star:before {content: \"ﾅﾑ\";white-space: nowrap;}";

    var node = document.createElement("style");
    node.type = "text/css";
    node.appendChild(document.createTextNode(css));

    var heads = document.getElementsByTagName("head");
    heads[0].appendChild(node);

    var audio_element  = Array();
    var audio_src  = Array();
    var audio_d = Array();
    var rand_sp = Array();
    var rand_def = Array();

    rand_def[0] = 0;
    audio_src[0] = "https://yuzu.tk/files/yuzu_1203/gooooon.wav";

    rand_sp[0] = 1;
    audio_src[1] = "https://yuzu.tk/files/yuzu_1203/knzk.wav";

    rand_def[1] = 2;
    audio_src[2] = "https://yuzu.tk/files/yuzu_1203/nc42872.wav";

    rand_sp[1] = 3;
    audio_src[3] = "https://yuzu.tk/files/yuzu_1203/knzk2.wav";

    rand_def[2] = 4;
    audio_src[4] = "https://yuzu.tk/files/yuzu_1203/nc92438.wav";

    rand_sp[2] = 5;
    audio_src[5] = "https://yuzu.tk/files/yuzu_1203/knzk3.wav";

    rand_sp[3] = 6;
    audio_src[6] = "https://yuzu.tk/files/yuzu_1203/knzk4.wav";

    var i = 0;
    while (audio_src[i]) {
        audio_element[i] = document.createElement("audio");
        audio_element[i].src = audio_src[i];
        audio_element[i].setAttribute('controls', '0');
        audio_element[i].setAttribute('id', 'namuru'+i+'_audio');
        audio_element[i].setAttribute('preload', 'auto');

        heads[0].appendChild(audio_element[i]);
        audio_d[i] = document.getElementById('namuru'+i+'_audio');
        i++;
    }

    var rand_def_mode = 0;
    var rand_sp_mode = 0;
    var audio_rand = 0;
    var audio_mode = 9999;
    $(document).on('click', 'button.star-icon', function(event) {
        audio_mode = 9999;
        audio_rand = Math.floor( Math.random() * 10000 );
        rand_def_mode = Math.floor(Math.random()*rand_def.length);
        rand_sp_mode = Math.floor(Math.random()*rand_sp.length);

        if (audio_rand < 334) {
            audio_mode = rand_sp[rand_sp_mode];
        } else {
            audio_mode = rand_def[rand_def_mode];
        }

        audio_d[audio_mode].currentTime = 0;
        audio_d[audio_mode].play();

        console.log("南無！");
    });
})();
<commit_msg>v1.4.4<commit_after>// ==UserScript==
// @name         Namuru-button
// @namespace    https://github.com/yuzulabo
// @version      1.4.4
// @description  南無りまくれぇ！
// @author       neziri_wasabi (yuzu_1203)
// @match        https://knzk.me/*
// @license       MIT License
// @require      https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.slim.min.js
// ==/UserScript==

/*
 「ゴーン」効果音について
フリー素材を使用しています。魔王魂: http://maoudamashii.jokersounds.com/

「チーン」効果音について
フリー素材を使用しています。http://nico.ms/nc42872

 木魚　効果音について
 フリー素材を使用しています。http://nico.ms/nc92438

 レア音声：神崎おにいさんについて
 1: 可視光線様のKNZK_ZONEの一部を使用しています。: http://nico.ms/sm31146004
 2: 「溺れ死ぬボーちゃんの深海少女。を歌ってみた」の一部を使用しています。：http://nico.ms/sm31734029

 これらの音声はスクリプト作者の個人サーバにアップロードされていますが、問題があればお問い合わせください。
 連絡先: https://knzk.me/@y
*/
(function() {
    var css = ".star-icon, .icon-button {filter: grayscale(100%);}.fa-star:before{content: \"\\1F64F\" !important;white-space: nowrap;} .star-icon.active, .icon-button.active {filter: none;}";

    // 旧版「ﾅﾑ」ボタンを有効化するには次の行のコメントアウトを削除
    // css = ".fa-star:before {content: \"ﾅﾑ\";white-space: nowrap;}";

    var node = document.createElement("style");
    node.type = "text/css";
    node.appendChild(document.createTextNode(css));

    var heads = document.getElementsByTagName("head");
    heads[0].appendChild(node);

    var audio_element  = Array();
    var audio_src  = Array();
    var audio_d = Array();
    var rand_sp = Array();
    var rand_def = Array();

    rand_def[0] = 0;
    audio_src[0] = "https://yuzu.tk/files/yuzu_1203/gooooon.wav";

    rand_sp[0] = 1;
    audio_src[1] = "https://yuzu.tk/files/yuzu_1203/knzk.wav";

    rand_def[1] = 2;
    audio_src[2] = "https://yuzu.tk/files/yuzu_1203/nc42872.wav";

    rand_sp[1] = 3;
    audio_src[3] = "https://yuzu.tk/files/yuzu_1203/knzk2.wav";

    rand_def[2] = 4;
    audio_src[4] = "https://yuzu.tk/files/yuzu_1203/nc92438.wav";

    rand_sp[2] = 5;
    audio_src[5] = "https://yuzu.tk/files/yuzu_1203/knzk3.wav";

    rand_sp[3] = 6;
    audio_src[6] = "https://yuzu.tk/files/yuzu_1203/knzk4.wav";

    var i = 0;
    while (audio_src[i]) {
        audio_element[i] = document.createElement("audio");
        audio_element[i].src = audio_src[i];
        audio_element[i].setAttribute('controls', '0');
        audio_element[i].setAttribute('id', 'namuru'+i+'_audio');
        audio_element[i].setAttribute('preload', 'auto');

        heads[0].appendChild(audio_element[i]);
        audio_d[i] = document.getElementById('namuru'+i+'_audio');
        i++;
    }

    var rand_def_mode = 0;
    var rand_sp_mode = 0;
    var audio_rand = 0;
    var audio_mode = 9999;
    $(document).on('click', 'button.star-icon', function(event) {
        audio_mode = 9999;
        audio_rand = Math.floor( Math.random() * 10000 );
        rand_def_mode = Math.floor(Math.random()*rand_def.length);
        rand_sp_mode = Math.floor(Math.random()*rand_sp.length);

        if (audio_rand < 334) {
            audio_mode = rand_sp[rand_sp_mode];
        } else {
            audio_mode = rand_def[rand_def_mode];
        }

        audio_d[audio_mode].currentTime = 0;
        audio_d[audio_mode].play();

        console.log("南無！");
    });
})();
<|endoftext|>


<commit_before>const _ = require('lodash');
const getLocation = require('./lib/getLocation');
const weatherFor = require('./lib/weatherFor');
const postInSlack = require('./lib/postInSlack');
const RtmClient = require('@slack/client').RtmClient;
const WebClient = require('@slack/client').WebClient;
const RTM_EVENTS = require('@slack/client').RTM_EVENTS;
const CLIENT_EVENTS = require('@slack/client').CLIENT_EVENTS;
const postMessage = require('./lib/slackWebClient').postMessage;

function respondWithWeather(web, location, channel, units) {
  getLocation(location)
    .then((coords) => {
      return weatherFor(coords.lat, coords.lng, coords.location, units);
    })
    .then((weather) => {
      postInSlack(web, channel, weather);
    })
    .catch(() => {
      postMessage(web, channel, `Sorry, I could not find any location called ${location}. Can you be more specific?\n\n Just type "@forecast help" if you need help!`, { as_user: true })
    });
}

module.exports = function(bot) {
  const token = bot.accessToken;
  const rtm = new RtmClient(token);
  const web = new WebClient(token);

  rtm.on(CLIENT_EVENTS.RTM.UNABLE_TO_RTM_START, (message) => {
    if (message === 'invalid_auth' || message == 'account_inactive') {
      console.log(`Removing bot ${bot.slackID} with invalid or inactive token`);
      bot.remove();
    }
  });

  rtm.on(RTM_EVENTS.MESSAGE, (message) => {
    const location = message.text.replace(`<@${rtm.activeUserId}>`, '').replace(/![\w]*/, '');
    if (message.user != rtm.activeUserId && message.text && message.text.match(RegExp(rtm.activeUserId))) {
      if (message.text.match(/help\s*$/) || location.match(/^(?![\s\S])/)) {
        const exampleLocation = _.sample([
          'Paris, France',
          'Tokyo',
          'Durham',
          'Antarctica',
          '90210'
        ]);
        rtm.sendMessage(`
*Get Weather*
> \`@forecast ${exampleLocation}\` to get weather for location (try any place in the world)
> \`@forecast ${exampleLocation} !us\` to get weather for location in specific units (us or si)
*Settings*
> \`@forecast set si\` to change units (\`si\` for metric, \`us\` for imperial)
`, message.channel);
      } else if (message.text.match(/set\s*/)) {
        unit = message.text.match(/set\s*([\w]*)$/)[1];
        if(_.includes(['si', 'us'], unit.toLowerCase())) {
          bot.unit = unit;
          console.log(`Setting bot unit to ${unit}`);
          bot.save();
          rtm.sendMessage(`Your preferred units have been set to ${unit}`, message.channel);
        }
      } else {
        console.log(`🤖  Weather Requested for ${location}`);
        let units;
        if (message.text.match(/!([\w]*)/)) {
          units = message.text.match(/!([\w]*)/)[1];
        } else {
          units = bot.unit || 'us';
        }
        respondWithWeather(web, location, message.channel, units.toLowerCase());
      }
    }
  });

  rtm.start();
}
<commit_msg>Added comments<commit_after>const _ = require('lodash');
const getLocation = require('./lib/getLocation');
const weatherFor = require('./lib/weatherFor');
const postInSlack = require('./lib/postInSlack');
const RtmClient = require('@slack/client').RtmClient;
const WebClient = require('@slack/client').WebClient;
const RTM_EVENTS = require('@slack/client').RTM_EVENTS;
const CLIENT_EVENTS = require('@slack/client').CLIENT_EVENTS;
const postMessage = require('./lib/slackWebClient').postMessage;

function respondWithWeather(web, location, channel, units) {
  getLocation(location)
    .then((coords) => {
      return weatherFor(coords.lat, coords.lng, coords.location, units);
    })
    .then((weather) => {
      postInSlack(web, channel, weather);
    })
    .catch(() => {
      postMessage(web, channel, `Sorry, I could not find any location called ${location}. Can you be more specific?\n\n Just type "@forecast help" if you need help!`, { as_user: true })
    });
}

module.exports = function(bot) {
  const token = bot.accessToken;
  const rtm = new RtmClient(token);
  const web = new WebClient(token);

  rtm.on(CLIENT_EVENTS.RTM.UNABLE_TO_RTM_START, (message) => {
    if (message === 'invalid_auth' || message == 'account_inactive') {
      console.log(`Removing bot ${bot.slackID} with invalid or inactive token`);
      bot.remove();
    }
  });

  rtm.on(RTM_EVENTS.MESSAGE, (message) => {
    const location = message.text.replace(`<@${rtm.activeUserId}>`, '').replace(/![\w]*/, '');
    if (message.user != rtm.activeUserId && message.text && message.text.match(RegExp(rtm.activeUserId))) {
      //
      // @forecast help
      //
      if (message.text.match(/help\s*$/) || location.match(/^(?![\s\S])/)) {
        const exampleLocation = _.sample([
          'Paris, France',
          'Tokyo',
          'Durham',
          'Antarctica',
          '90210'
        ]);
        rtm.sendMessage(`
*Get Weather*
> \`@forecast ${exampleLocation}\` to get weather for location (try any place in the world)
> \`@forecast ${exampleLocation} !us\` to get weather for location in specific units (us or si)
*Settings*
> \`@forecast set si\` to change units (\`si\` for metric, \`us\` for imperial)
`, message.channel);
      //
      // @forecast set <unit>
      //
      } else if (message.text.match(/set\s*/)) {
        unit = message.text.match(/set\s*([\w]*)$/)[1];
        if(_.includes(['si', 'us'], unit.toLowerCase())) {
          bot.unit = unit;
          console.log(`Setting bot unit to ${unit}`);
          bot.save();
          rtm.sendMessage(`Your preferred units have been set to ${unit}`, message.channel);
        }
      //
      // @forecast <Location>
      //
      } else {
        console.log(`🤖  Weather Requested for ${location}`);
        let units;
        if (message.text.match(/!([\w]*)/)) {
          units = message.text.match(/!([\w]*)/)[1];
        } else {
          units = bot.unit || 'us';
        }
        respondWithWeather(web, location, message.channel, units.toLowerCase());
      }
    }
  });

  rtm.start();
}
<|endoftext|>


<commit_before>/**
 * Main JS file for Casper behaviours
 */

/*globals jQuery, document */
(function ($) {
    "use strict";

    $(document).ready(function(){

        // On the home page, move the blog icon inside the header
        // for better relative/absolute positioning.

        //$("#blog-logo").prependTo("#site-head-content");

    });

}(jQuery));<commit_msg>analytics added<commit_after>/**
 * Main JS file for Casper behaviours
 */

/*globals jQuery, document */
(function ($) {
    "use strict";

    $(document).ready(function(){

        // On the home page, move the blog icon inside the header
        // for better relative/absolute positioning.

        //$("#blog-logo").prependTo("#site-head-content");

    });

}(jQuery));

// Google analytics
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');

ga('create', 'UA-9850851-4', 'lingliang.me');
ga('send', 'pageview');

<|endoftext|>


<commit_before>function PaprColumnizer($elem, settings) {

   this.$elem = $elem;
   this.settings = settings;

}

PaprColumnizer.prototype.run = function() {
   if (this.$elem.data('PaprColumnizer.COLUMNIZING')) {
      debug('Columnizing is already happening');
      return;
   }
   debug('starting columnization');
   this.$elem.data('PaprColumnizer.COLUMNIZING', true);
   this.prepareForColumnization();

   // columnize is implemented by subclasses
   this.columnize();

   this.addFirstLastColClasses();
   this.afterColumnization();
   this.$elem.data('PaprColumnizer.COLUMNIZING', false);

   this.bindEventHandlers();
   debug('columnization done');
};

PaprColumnizer.prototype.bindEventHandlers = function() {
   if (this.eventHandlersBound) {
      return;
   }
   this.eventHandlersBound = true;
   var paprcolumns = this;

   $(window).resize(function() {
      paprcolumns.onWindowResize();
   });
   $(window).load(function() {
      paprcolumns.$dest.find('img').each(function(ind, img) {
         var $img = $(img), $ci = paprcolumns.$contents.find('img#' + $img.attr('id'));
         $ci.height($img.height()).width($img.width());
      });
      debug('recolumnizing because window has finished loading');
      paprcolumns.run();
   });
};

PaprColumnizer.prototype.addFirstLastColClasses = function() {
   this.$holder.find('.col').last().addClass('lastcol').end().first().addClass('firstcol');
};

PaprColumnizer.prototype.prepareForColumnization = function() {
   if (!this.$contents) {
      this.$contents = this.$elem.contents().clone(true);
      this.$contents.find('img').each(function(ind, img) {
         var $img = $(img);
         $img.addClass('dontsplit');
         if ($.trim($img.attr('id')) === '') {
            // we only want to force an ID on those images that do not already have one
            // so that we do not break users' selectors that may be based on hard-coded
            // or otherwise-assigned IDs
            $img.attr('id', 'img-' + ind);
         }
      });
   }
   this.$elem.empty();
   // TODO: actual destination needs to be configurable
   // destination is where the column holder will go
   this.$dest = this.$elem.addClass('columnized');
   // all columnizers should put their generated content into this.$holder
   this.$holder = $('
').addClass('colHolder').appendTo(this.$dest);
};

PaprColumnizer.prototype.prepareSplitElement = function($orig, $newEl) {
   $orig.addClass('split');
   $newEl.addClass('split');
   if ($orig.attr('id') !== undefined && $.trim($orig.attr('id')) !== '') {
      var origID = $orig.attr('id'),
          cls = origID + '-splitID';
      $orig.removeAttr('id').addClass(cls).data('undosplit', function() {
         this.attr('id', origID).removeClass(cls).removeClass('split');
      });
   } else {
      $orig.data('undosplit', function() {
         this.removeClass('split');
      });
   }
};

PaprColumnizer.prototype.onWindowResize = function() {
   this.equalizeColumnHeights();
};

PaprColumnizer.prototype.afterColumnization = function() {
   this.$holder.find('.col').css('height', '');
   this.equalizeColumnHeights();
};

PaprColumnizer.prototype.equalizeColumnHeights = function() {
   var $cols = this.$holder.find('.col'),
       max = Math.max.apply(null, $.makeArray($cols.map(function() { return $(this).height(); })));
   $cols.height(max);
   debug('set all columns to max height: ' + max);
};

PaprColumnizer.prototype.createColumn = function(i) {
   return $('
').attr('id', 'col-' + i).addClass('col');
};

PaprColumnizer.prototype.splitInto = function($contents, $dest, acceptanceTest) {
   var paprcolumns = this, lastIndex = 0, $leftover = false;
   $contents.each(function(index, el) {
      var $el = $(el);

      $el.detach().appendTo($dest);
      lastIndex = index;

      if (!acceptanceTest()) {
         // take $el back out of the destination
         $el.detach();

         if ($el.hasClass('dontsplit')) {
            lastIndex--;
            return false;
         }

         if ($el.get(0).nodeType === NODE_TYPES.TEXT) {
            var $textDest = $('').appendTo($dest),
                leftoverText = paprcolumns.splitTextInto($el.text(), $textDest, acceptanceTest);

            if (leftoverText === false) {
               // there was no point at which you could split the text and still meet the acceptance
               // test, so basically we have ended up with an empty splitTextNode.
               $textDest.remove();
               lastIndex--;
               return false;
            }

            $el.text('');
            $leftover = $('').text(leftoverText);
         } else {
            var $newDest = $el.clone(true),
                $newCont = $newDest.contents();

            $newDest.empty().appendTo($dest);

            paprcolumns.prepareSplitElement($el, $newDest);

            $leftover = $el.empty().append(paprcolumns.splitInto($newCont, $newDest, acceptanceTest));
            if (paprcolumns.isEmptyOrEmptyTextNode($newDest.contents())) {
               // we basically split an element and stuck it into the current column but then were not
               // able to put anything into it that was accepted by the acceptance test, so we need to
               // get our empty node back out of the current column since all of its contents are really
               // going to end up in the next column anyway
               $newDest.remove();
               // and we want to undo the changes we made to the original element when we prepared it to
               // be split by calling the undo function that was saved in the node by prepareSplitElement
               $el.data('undosplit').call($el);
            }
            $el.removeData('undosplit');
         }
         return false;
      }
   });

   return $('
')
      .append($leftover)
      .append($contents.slice(lastIndex + 1))
      .contents();
};

PaprColumnizer.prototype.splitTextInto = function(text, $dest, acceptanceTest) {
   var splitLoc = this.findBestSplitLocation(text, $dest, acceptanceTest),
       destText = splitLoc === false ? false : text.substr(0, splitLoc);

   if (destText === false || $.trim(destText) === '') {
      return false;
   }

   $dest.text(text.substr(0, splitLoc));
   return text.substr(splitLoc);
};

PaprColumnizer.prototype.findBestSplitLocation = function(text, $dest, acceptanceTest) {
   var range = { min: 0, max: text.length },
       desiredSplit = Math.floor(text.length / 2),
       best = false;
   for (var i = 0; i < this.settings.maxTextIterations; i++) {
      var wordBreak = this.findWordBreakNear(text, desiredSplit);
      $dest.text(text.substr(0, wordBreak));
      if (acceptanceTest()) {
         best = range.min = wordBreak;
      } else {
         range.max = wordBreak;
      }

      // make our new split halfway between min and max
      desiredSplit = (range.max - Math.floor((range.max - range.min) / 2));
      if ((range.max - range.min) < this.settings.minTextIncrement) {
         break;
      }
   }

   return best;
};

PaprColumnizer.prototype.findWordBreakNear = function(text, desiredSplitLoc) {
   // TODO: this is an extremely naive implementation ... it needs to look backward and
   // forward as well as recognize word break characters and markup (i.e. <wbr />), not
   // just relying on spaces
   return text.indexOf(' ', desiredSplitLoc);
};

PaprColumnizer.prototype.isEmptyOrEmptyTextNode = function($node) {
   return $node.size() === 0 ||
          ($node.size() === 1 && $node.get(0).nodeType === NODE_TYPES.TEXT && ($.trim($($node.get(0)).text()) === ''));
};
<commit_msg>improve search for word break by looking both directions<commit_after>function PaprColumnizer($elem, settings) {

   this.$elem = $elem;
   this.settings = settings;

}

PaprColumnizer.prototype.run = function() {
   if (this.$elem.data('PaprColumnizer.COLUMNIZING')) {
      debug('Columnizing is already happening');
      return;
   }
   debug('starting columnization');
   this.$elem.data('PaprColumnizer.COLUMNIZING', true);
   this.prepareForColumnization();

   // columnize is implemented by subclasses
   this.columnize();

   this.addFirstLastColClasses();
   this.afterColumnization();
   this.$elem.data('PaprColumnizer.COLUMNIZING', false);

   this.bindEventHandlers();
   debug('columnization done');
};

PaprColumnizer.prototype.bindEventHandlers = function() {
   if (this.eventHandlersBound) {
      return;
   }
   this.eventHandlersBound = true;
   var paprcolumns = this;

   $(window).resize(function() {
      paprcolumns.onWindowResize();
   });
   $(window).load(function() {
      paprcolumns.$dest.find('img').each(function(ind, img) {
         var $img = $(img), $ci = paprcolumns.$contents.find('img#' + $img.attr('id'));
         $ci.height($img.height()).width($img.width());
      });
      debug('recolumnizing because window has finished loading');
      paprcolumns.run();
   });
};

PaprColumnizer.prototype.addFirstLastColClasses = function() {
   this.$holder.find('.col').last().addClass('lastcol').end().first().addClass('firstcol');
};

PaprColumnizer.prototype.prepareForColumnization = function() {
   if (!this.$contents) {
      this.$contents = this.$elem.contents().clone(true);
      this.$contents.find('img').each(function(ind, img) {
         var $img = $(img);
         $img.addClass('dontsplit');
         if ($.trim($img.attr('id')) === '') {
            // we only want to force an ID on those images that do not already have one
            // so that we do not break users' selectors that may be based on hard-coded
            // or otherwise-assigned IDs
            $img.attr('id', 'img-' + ind);
         }
      });
   }
   this.$elem.empty();
   // TODO: actual destination needs to be configurable
   // destination is where the column holder will go
   this.$dest = this.$elem.addClass('columnized');
   // all columnizers should put their generated content into this.$holder
   this.$holder = $('
').addClass('colHolder').appendTo(this.$dest);
};

PaprColumnizer.prototype.prepareSplitElement = function($orig, $newEl) {
   $orig.addClass('split');
   $newEl.addClass('split');
   if ($orig.attr('id') !== undefined && $.trim($orig.attr('id')) !== '') {
      var origID = $orig.attr('id'),
          cls = origID + '-splitID';
      $orig.removeAttr('id').addClass(cls).data('undosplit', function() {
         this.attr('id', origID).removeClass(cls).removeClass('split');
      });
   } else {
      $orig.data('undosplit', function() {
         this.removeClass('split');
      });
   }
};

PaprColumnizer.prototype.onWindowResize = function() {
   this.equalizeColumnHeights();
};

PaprColumnizer.prototype.afterColumnization = function() {
   this.$holder.find('.col').css('height', '');
   this.equalizeColumnHeights();
};

PaprColumnizer.prototype.equalizeColumnHeights = function() {
   var $cols = this.$holder.find('.col'),
       max = Math.max.apply(null, $.makeArray($cols.map(function() { return $(this).height(); })));
   $cols.height(max);
   debug('set all columns to max height: ' + max);
};

PaprColumnizer.prototype.createColumn = function(i) {
   return $('
').attr('id', 'col-' + i).addClass('col');
};

PaprColumnizer.prototype.splitInto = function($contents, $dest, acceptanceTest) {
   var paprcolumns = this, lastIndex = 0, $leftover = false;
   $contents.each(function(index, el) {
      var $el = $(el);

      $el.detach().appendTo($dest);
      lastIndex = index;

      if (!acceptanceTest()) {
         // take $el back out of the destination
         $el.detach();

         if ($el.hasClass('dontsplit')) {
            lastIndex--;
            return false;
         }

         if ($el.get(0).nodeType === NODE_TYPES.TEXT) {
            var $textDest = $('').appendTo($dest),
                leftoverText = paprcolumns.splitTextInto($el.text(), $textDest, acceptanceTest);

            if (leftoverText === false) {
               // there was no point at which you could split the text and still meet the acceptance
               // test, so basically we have ended up with an empty splitTextNode.
               $textDest.remove();
               lastIndex--;
               return false;
            }

            $el.text('');
            $leftover = $('').text(leftoverText);
         } else {
            var $newDest = $el.clone(true),
                $newCont = $newDest.contents();

            $newDest.empty().appendTo($dest);

            paprcolumns.prepareSplitElement($el, $newDest);

            $leftover = $el.empty().append(paprcolumns.splitInto($newCont, $newDest, acceptanceTest));
            if (paprcolumns.isEmptyOrEmptyTextNode($newDest.contents())) {
               // we basically split an element and stuck it into the current column but then were not
               // able to put anything into it that was accepted by the acceptance test, so we need to
               // get our empty node back out of the current column since all of its contents are really
               // going to end up in the next column anyway
               $newDest.remove();
               // and we want to undo the changes we made to the original element when we prepared it to
               // be split by calling the undo function that was saved in the node by prepareSplitElement
               $el.data('undosplit').call($el);
            }
            $el.removeData('undosplit');
         }
         return false;
      }
   });

   return $('
')
      .append($leftover)
      .append($contents.slice(lastIndex + 1))
      .contents();
};

PaprColumnizer.prototype.splitTextInto = function(text, $dest, acceptanceTest) {
   var splitLoc = this.findBestSplitLocation(text, $dest, acceptanceTest),
       destText = splitLoc === false ? false : text.substr(0, splitLoc);

   if (destText === false || $.trim(destText) === '') {
      return false;
   }

   $dest.text(text.substr(0, splitLoc));
   return text.substr(splitLoc);
};

PaprColumnizer.prototype.findBestSplitLocation = function(text, $dest, acceptanceTest) {
   var range = { min: 0, max: text.length },
       desiredSplit = Math.floor(text.length / 2),
       best = false;
   for (var i = 0; i < this.settings.maxTextIterations; i++) {
      var wordBreak = this.findWordBreakNear(text, desiredSplit);
      if (wordBreak !== false) {
         $dest.text(text.substr(0, wordBreak));
         if (acceptanceTest()) {
            best = range.min = wordBreak;
         } else {
            range.max = wordBreak;
         }
      }

      // make our new split halfway between min and max
      desiredSplit = (range.max - Math.floor((range.max - range.min) / 2));
      if ((range.max - range.min) < this.settings.minTextIncrement) {
         break;
      }
   }

   return best;
};

PaprColumnizer.prototype.findWordBreakNear = function(text, desiredSplitLoc) {
   // TODO: this is an extremely naive implementation ... it should look for more
   // than just space characters
   var down = text.lastIndexOf(' ', desiredSplitLoc),
       up   = text.indexOf(' ', desiredSplitLoc);
   if (down !== -1 && ((desiredSplitLoc - down) < (up - desiredSplitLoc))) {
      return down;
   }
   return up === -1 ? false : up;
};

PaprColumnizer.prototype.isEmptyOrEmptyTextNode = function($node) {
   return $node.size() === 0 ||
          ($node.size() === 1 && $node.get(0).nodeType === NODE_TYPES.TEXT && ($.trim($($node.get(0)).text()) === ''));
};
<|endoftext|>


<commit_before>// Enabled draw zone bounding box and stack to visualize the stack of zone components
Render.getConfig("RenderMainView.DrawZoneStack").enabled = true
Render.getConfig("RenderMainView.DrawZones").enabled = true

// Create the zone centered at the avatar position
var pos = MyAvatar.position;

// As a 5 meters cube box
var dim = { x: 5.0, y: 5.0, z: 5.0};

// Define zone properties
var properties = {
  lifetime: 60,
  type: "Zone",
  name: "test create zone",
  position: pos,
  dimensions: dim,
  keyLight:{"color": {"red":0,"green":255,"blue":0}},
  backgroundMode:"skybox",
  skybox:{"color":{"red":0,"green":0,"blue":255}}
};

// Add the sphere and check its properties
var zone = Entities.addEntity(properties);
properties = Entities.getEntityProperties(zone);
print("Zone added :" + zone);
print(JSON.stringify(properties));

// clean up after test
Script.scriptEnding.connect(function () {
  Entities.deleteEntity(zone)
});
<commit_msg>Delete create.js<commit_after><|endoftext|>


<commit_before>import Ember from 'ember';
import DS from 'ember-data';
import moment from 'moment';

const { RSVP, Controller, computed, A} = Ember;
const { PromiseArray } = DS;

export default Controller.extend({
  selectedDate: new Date(),
  selectedView: 'week',
  fakeSingleEvent: false,
  icsFeedUrl: 'http://example.com/icsfeedurlsareusuallyprettylong',
  fromTimeStamp: computed('selectedDate', 'selectedView', function(){
    return moment(this.get('selectedDate')).startOf(this.get('selectedView')).unix();
  }),
  toTimeStamp: computed('selectedDate', 'selectedView', function(){
    return moment(this.get('selectedDate')).endOf(this.get('selectedView')).unix();
  }),
  calendarEventsPromise: computed('fromTimeStamp', 'toTimeStamp', function(){
    let events = new A();
    let today = moment().hour(8);
    let todaysEvent = this.createUserEventObject();
    todaysEvent.name = 'Some new thing';
    todaysEvent.startDate = today.clone();
    todaysEvent.endDate = today.clone().add(1, 'hour');

    events.pushObject(todaysEvent);

    let todaysDoubleEvent = this.createUserEventObject();
    todaysDoubleEvent.name = 'Some new thing again';
    todaysDoubleEvent.startDate = today.clone();
    todaysDoubleEvent.endDate = today.clone().add(1, 'hour');

    events.pushObject(todaysDoubleEvent);

    let anotherEvent = this.createUserEventObject();
    anotherEvent.name = 'Sweet Lecture';
    anotherEvent.startDate = today.clone().add(2, 'hour');
    anotherEvent.endDate = today.clone().add(3, 'hour');
    events.pushObject(anotherEvent);

    let multiDayEvent = this.createUserEventObject();
    multiDayEvent.name = 'Multiday Event';
    multiDayEvent.startDate = today.clone();
    multiDayEvent.endDate = today.clone().add(3, 'day');
    events.pushObject(multiDayEvent);

    let tomorrow = moment().add(1, 'day').hour(8);
    let tomorrowsEvents = this.createUserEventObject();
    tomorrowsEvents.name = 'Tomorrows Lecture';
    tomorrowsEvents.startDate = tomorrow.clone();
    tomorrowsEvents.endDate = tomorrow.clone().add(2, 'hour');

    events.pushObject(tomorrowsEvents);

    let promise = RSVP.resolve(events);
    return PromiseArray.create({
      promise: promise
    });
  }),
  createUserEventObject(){
    return {
      user: 1,
      name: '',
      offering: 1,
      startDate: null,
      endDate: null,
      eventClass: 'peer-teaching',
      location: 'Rm. 160',
      lastModified: new Date(),
      isPublished: true,
      isScheduled: false
    };
  },
  actions: {
    changeDate(newDate){
      this.set('selectedDate', newDate);
    },
    changeView(newView){
      this.set('selectedView', newView);
    },
    selectEvent(event){
      let fakeEvent = {
        courseTitle: 'Fake Event',
        sessionTitle: 'Fake Event',
        offeredAtPhrase: 'On ' + moment(event.startDate).format('dddd, MMMM Do YYYY, h:mm a'),
        taughtByPhrase: 'Taught By Barry Bods, Neil deGrasse Tyson',
        sessionIs: 'Session is "lecture"',
        courseObjectives: [
          {domain: 'Medical (With People)', title: 'Help People'},
          {domain: 'Medical (With Machines)', title: 'Use Computers'},
        ],
        courseLearningMaterials: [
          {title: 'syllabus', url: 'http://#', type: 'citation'},
        ],
        sessionObjectives: [
          {domain: 'Medical (With People)', title: 'Help People'},
          {domain: 'Medical (With Machines)', title: 'Use Computers'},
          {domain: 'Medical (With Machines)', title: 'Help People'},
        ],
        sessionLearningMaterials: [
          {title: 'slides', url: 'http://#', type: 'citation'},
        ]
      };

      this.set('fakeSingleEvent', fakeEvent);
    },
    refreshIcsFeed(){
      this.set('icsFeedUrl', 'http://example.com/a_new_ics_feed_value');
    }
  }
});
<commit_msg>added course title and instructors to dummy data.<commit_after>import Ember from 'ember';
import DS from 'ember-data';
import moment from 'moment';

const { RSVP, Controller, computed, A} = Ember;
const { PromiseArray } = DS;

export default Controller.extend({
  selectedDate: new Date(),
  selectedView: 'week',
  fakeSingleEvent: false,
  icsFeedUrl: 'http://example.com/icsfeedurlsareusuallyprettylong',
  fromTimeStamp: computed('selectedDate', 'selectedView', function(){
    return moment(this.get('selectedDate')).startOf(this.get('selectedView')).unix();
  }),
  toTimeStamp: computed('selectedDate', 'selectedView', function(){
    return moment(this.get('selectedDate')).endOf(this.get('selectedView')).unix();
  }),
  calendarEventsPromise: computed('fromTimeStamp', 'toTimeStamp', function(){
    let events = new A();
    let today = moment().hour(8);
    let todaysEvent = this.createUserEventObject();
    todaysEvent.name = 'Some new thing';
    todaysEvent.startDate = today.clone();
    todaysEvent.endDate = today.clone().add(1, 'hour');
    todaysEvent.instructors = ['Tweedledee', 'Tweedledum'];
    todaysEvent.courseTitle = 'Lorem Ipsum';
    events.pushObject(todaysEvent);

    let todaysDoubleEvent = this.createUserEventObject();
    todaysDoubleEvent.name = 'Some new thing again';
    todaysDoubleEvent.startDate = today.clone();
    todaysDoubleEvent.endDate = today.clone().add(1, 'hour');

    events.pushObject(todaysDoubleEvent);

    let anotherEvent = this.createUserEventObject();
    anotherEvent.name = 'Sweet Lecture';
    anotherEvent.startDate = today.clone().add(2, 'hour');
    anotherEvent.endDate = today.clone().add(3, 'hour');
    events.pushObject(anotherEvent);

    let multiDayEvent = this.createUserEventObject();
    multiDayEvent.name = 'Multiday Event';
    multiDayEvent.startDate = today.clone();
    multiDayEvent.endDate = today.clone().add(3, 'day');
    events.pushObject(multiDayEvent);

    let tomorrow = moment().add(1, 'day').hour(8);
    let tomorrowsEvents = this.createUserEventObject();
    tomorrowsEvents.name = 'Tomorrows Lecture';
    tomorrowsEvents.startDate = tomorrow.clone();
    tomorrowsEvents.endDate = tomorrow.clone().add(2, 'hour');

    events.pushObject(tomorrowsEvents);

    let promise = RSVP.resolve(events);
    return PromiseArray.create({
      promise: promise
    });
  }),
  createUserEventObject(){
    return {
      user: 1,
      name: '',
      offering: 1,
      startDate: null,
      endDate: null,
      eventClass: 'peer-teaching',
      location: 'Rm. 160',
      lastModified: new Date(),
      isPublished: true,
      isScheduled: false
    };
  },
  actions: {
    changeDate(newDate){
      this.set('selectedDate', newDate);
    },
    changeView(newView){
      this.set('selectedView', newView);
    },
    selectEvent(event){
      let fakeEvent = {
        courseTitle: 'Fake Event',
        sessionTitle: 'Fake Event',
        offeredAtPhrase: 'On ' + moment(event.startDate).format('dddd, MMMM Do YYYY, h:mm a'),
        taughtByPhrase: 'Taught By Barry Bods, Neil deGrasse Tyson',
        sessionIs: 'Session is "lecture"',
        courseObjectives: [
          {domain: 'Medical (With People)', title: 'Help People'},
          {domain: 'Medical (With Machines)', title: 'Use Computers'},
        ],
        courseLearningMaterials: [
          {title: 'syllabus', url: 'http://#', type: 'citation'},
        ],
        sessionObjectives: [
          {domain: 'Medical (With People)', title: 'Help People'},
          {domain: 'Medical (With Machines)', title: 'Use Computers'},
          {domain: 'Medical (With Machines)', title: 'Help People'},
        ],
        sessionLearningMaterials: [
          {title: 'slides', url: 'http://#', type: 'citation'},
        ]
      };

      this.set('fakeSingleEvent', fakeEvent);
    },
    refreshIcsFeed(){
      this.set('icsFeedUrl', 'http://example.com/a_new_ics_feed_value');
    }
  }
});
<|endoftext|>


<commit_before>import marked from 'marked';
import hljs from 'highlight.js';
import { helper } from '@ember/component/helper';
import { htmlSafe } from '@ember/string';

export function formatMarkdown([value]) {
  if (!value) {
    return;
  }

  // Set options
  // `highlight` example uses https://highlightjs.org
  marked.setOptions({
    highlight: function (code, lang) {
      const language = hljs.getLanguage(lang) ? lang : 'plaintext';
      return hljs.highlight(code, { language }).value;
    },
    langPrefix: 'hljs language-', // highlight.js css expects a top-level 'hljs' class.
  });

  let parsedMarkdown = marked.parse(value);

  return new htmlSafe(parsedMarkdown);
}

export default helper(formatMarkdown);
<commit_msg>Fix marked import in format-markdown helper<commit_after>import { marked } from 'marked';
import hljs from 'highlight.js';
import { helper } from '@ember/component/helper';
import { htmlSafe } from '@ember/string';

export function formatMarkdown([value]) {
  if (!value) {
    return;
  }

  // Set options
  // `highlight` example uses https://highlightjs.org
  marked.setOptions({
    highlight: function (code, lang) {
      const language = hljs.getLanguage(lang) ? lang : 'plaintext';
      return hljs.highlight(code, { language }).value;
    },
    langPrefix: 'hljs language-', // highlight.js css expects a top-level 'hljs' class.
  });

  let parsedMarkdown = marked.parse(value);

  return htmlSafe(parsedMarkdown);
}

export default helper(formatMarkdown);
<|endoftext|>


<commit_before>const weakMap = new WeakMap()

const createAssertionError = (actual, expected) =>
  Error(`Expected ${expected}. Found ${actual}.`)

export const tokenTypes = {
  variable: 0,
  tag: 1,
  endtag: 2,
  key: 3,
  value: 4,
  node: 5,
  text: 6,
  constant: 7
}

const valueTrue = {
  type: tokenTypes.value,
  value: true
}

const END = Symbol('end')

const isSpaceChar = (char) => !char.trim()
const isOfTag = (char) => char !== '/' && char !== '>' && !isSpaceChar(char)
const isOfKey = (char) => char !== '=' && isOfTag(char)
const isQuoteChar = (char) => char === '"' || char === "'"

const tokenizer = {
  *get(acc, strs, vlength) {
    let afterVar = false

    for (let index = 0, length = strs.length; index < length; index++) {
      const str = strs[index]

      let tag = acc.tag
      let i = 0

      const current = () => str.charAt(i)
      const next = () => str.charAt(i + 1)

      while (current()) {
        if (!tag && current() === '<') {
          let value = ''
          let end = false

          if (next() === '/') {
            end = true

            i++
          }

          while (next() && isOfTag(next())) {
            i++

            value += current()
          }

          afterVar = false

          yield {
            type: !end ? tokenTypes.tag : tokenTypes.endtag,
            value
          }

          tag = value

          i++
        } else if (tag && isSpaceChar(current())) {
          i++
        } else if (tag && current() === '/' && next() === '>') {
          yield* [
            END,
            {
              type: tokenTypes.endtag,
              value: tag
            },
            END
          ]

          tag = false

          i += 2
        } else if (tag && current() === '>') {
          yield END

          tag = false

          i++
        } else if (tag && isOfKey(current())) {
          let value = ''

          i--

          while (next() && isOfKey(next())) {
            i++

            value += current()
          }

          yield {
            type: tokenTypes.key,
            value
          }

          if (next() === '=') {
            i++

            let quote = ''
            let value = ''

            if (next() && isQuoteChar(next())) {
              i++

              quote = current()

              while (next() !== quote) {
                if (next()) {
                  i++

                  value += current()
                } else {
                  throw createAssertionError('', quote)
                }
              }

              i++

              yield {
                type: tokenTypes.value,
                value
              }
            } else if (next()) {
              throw createAssertionError(next(), '"')
            }
          } else {
            yield valueTrue
          }

          i++
        } else if (!tag) {
          let value = ''

          while (current() && current() !== '<') {
            value += current()

            i++
          }

          if (value.trim() || (afterVar && current() !== '<')) {
            yield {
              type: tokenTypes.text,
              value
            }
          }
        }
      }

      acc.tag = tag

      if (index < vlength) {
        afterVar = true

        yield {
          type: tokenTypes.variable,
          value: index
        }
      }
    }
  }
}

const parse = (tokens, parent, tag, variables) => {
  const child = {
    tag,
    dynamic: false,
    type: tokenTypes.node,
    attributes: [],
    children: []
  }

  let current

  for (;;) {
    current = tokens.next()

    if (current.done) break

    const token = current.value

    if (token === END) {
      break
    } else if (token.type === tokenTypes.key) {
      const next = tokens.next()?.value
      let key = token.value

      const firstChar = key.charAt(0)
      const hasColon = ':' === firstChar
      const hasAtSign = '@' === firstChar

      if (hasColon) {
        key = token.value.substring(1)
      }

      if (next.type === tokenTypes.value) {
        if (child.attributes.inset != null) child.attributes.inset++

        child.attributes.unshift({
          type: tokenTypes.constant,
          key,
          value: next.value
        })
      } else if (!hasColon && !hasAtSign) {
        if (child.attributes.inset != null) child.attributes.inset++

        child.attributes.unshift({
          type: tokenTypes.constant,
          key,
          value: variables[next.value]
        })
      } else {
        child.dynamic = true

        child.attributes.inset =
          child.attributes.inset ?? child.attributes.length

        child.attributes.push({
          type: tokenTypes.variable,
          key,
          value: next.value
        })
      }
    } else if (token.type === tokenTypes.variable) {
      child.dynamic = true

      child.attributes.inset = child.attributes.inset ?? child.attributes.length

      child.attributes.push({
        type: tokenTypes.variable,
        key: false,
        value: token.value
      })
    }
  }

  for (;;) {
    current = tokens.next()

    if (current.done) break

    const token = current.value

    if (token.type === tokenTypes.endtag && token.value === child.tag) {
      break
    } else if (token.type === tokenTypes.tag) {
      const dynamic = parse(tokens, child, token.value, variables)

      child.dynamic = child.dynamic || dynamic
    } else if (token.type === tokenTypes.text) {
      child.children.push({
        type: tokenTypes.text,
        value: token.value
      })
    } else if (token.type === tokenTypes.variable) {
      child.dynamic = true

      child.children.inset = child.children.inset ?? child.children.length

      child.children.push({
        type: tokenTypes.variable,
        value: token.value
      })
    }
  }

  if (child.dynamic) {
    parent.children.inset = parent.children.inset ?? parent.children.length
  }

  parent.children.push(child)

  return child.dynamic
}

let view = 1

const toTemplate = (strs, variables) => {
  const acc = {
    tag: false
  }

  const tokens = tokenizer.get(acc, strs, variables.length)

  const children = []

  for (;;) {
    const current = tokens.next()

    if (current.done) break

    const token = current.value

    if (token.type === tokenTypes.tag) {
      parse(tokens, {children}, token.value, variables)
    } else if (token.type === tokenTypes.text && token.value.trim()) {
      throw createAssertionError(token.type, "'node'")
    }
  }

  if (children.length !== 1) {
    throw createAssertionError(children.length, 1)
  }

  children[0].view = view++

  return children[0]
}

export const html = (strs, ...variables) => {
  let result = weakMap.get(strs)

  if (!result) {
    result = toTemplate(strs, variables)

    weakMap.set(strs, result)
  }

  return Object.assign({variables}, result)
}
<commit_msg>Clean up attributes a bit<commit_after>const weakMap = new WeakMap()

const createAssertionError = (actual, expected) =>
  Error(`Expected ${expected}. Found ${actual}.`)

export const tokenTypes = {
  variable: 0,
  tag: 1,
  endtag: 2,
  key: 3,
  value: 4,
  node: 5,
  text: 6,
  constant: 7
}

const valueTrue = {
  type: tokenTypes.value,
  value: true
}

const END = Symbol('end')

const isSpaceChar = (char) => !char.trim()
const isOfTag = (char) => char !== '/' && char !== '>' && !isSpaceChar(char)
const isOfKey = (char) => char !== '=' && isOfTag(char)
const isQuoteChar = (char) => char === '"' || char === "'"

const tokenizer = {
  *get(acc, strs, vlength) {
    let afterVar = false

    for (let index = 0, length = strs.length; index < length; index++) {
      const str = strs[index]

      let tag = acc.tag
      let i = 0

      const current = () => str.charAt(i)
      const next = () => str.charAt(i + 1)

      while (current()) {
        if (!tag && current() === '<') {
          let value = ''
          let end = false

          if (next() === '/') {
            end = true

            i++
          }

          while (next() && isOfTag(next())) {
            i++

            value += current()
          }

          afterVar = false

          yield {
            type: !end ? tokenTypes.tag : tokenTypes.endtag,
            value
          }

          tag = value

          i++
        } else if (tag && isSpaceChar(current())) {
          i++
        } else if (tag && current() === '/' && next() === '>') {
          yield* [
            END,
            {
              type: tokenTypes.endtag,
              value: tag
            },
            END
          ]

          tag = false

          i += 2
        } else if (tag && current() === '>') {
          yield END

          tag = false

          i++
        } else if (tag && isOfKey(current())) {
          let value = ''

          i--

          while (next() && isOfKey(next())) {
            i++

            value += current()
          }

          yield {
            type: tokenTypes.key,
            value
          }

          if (next() === '=') {
            i++

            let quote = ''
            let value = ''

            if (next() && isQuoteChar(next())) {
              i++

              quote = current()

              while (next() !== quote) {
                if (next()) {
                  i++

                  value += current()
                } else {
                  throw createAssertionError('', quote)
                }
              }

              i++

              yield {
                type: tokenTypes.value,
                value
              }
            } else if (next()) {
              throw createAssertionError(next(), '"')
            }
          } else {
            yield valueTrue
          }

          i++
        } else if (!tag) {
          let value = ''

          while (current() && current() !== '<') {
            value += current()

            i++
          }

          if (value.trim() || (afterVar && current() !== '<')) {
            yield {
              type: tokenTypes.text,
              value
            }
          }
        }
      }

      acc.tag = tag

      if (index < vlength) {
        afterVar = true

        yield {
          type: tokenTypes.variable,
          value: index
        }
      }
    }
  }
}

const parse = (tokens, parent, tag, variables) => {
  const child = {
    tag,
    dynamic: false,
    type: tokenTypes.node,
    attributes: [],
    children: []
  }

  let current

  for (;;) {
    current = tokens.next()

    if (current.done) break

    const token = current.value

    if (token === END) {
      break
    }

    let next = token
    let key = false
    let firstChar
    let hasColon
    let hasAtSign
    let constant = false
    let value = token.value

    if (next.type === tokenTypes.key) {
      next = tokens.next()?.value
      key = token.value

      firstChar = key.charAt(0)
      hasColon = ':' === firstChar
      hasAtSign = '@' === firstChar

      if (hasColon) {
        key = token.value.substring(1)
      }

      constant = next.type === tokenTypes.value
      value = next.value

      if (next.type === tokenTypes.variable && !hasColon && !hasAtSign) {
        value = variables[value]
        constant = true
      }
    }

    if (constant) {
      if (child.attributes.inset != null) child.attributes.inset++

      child.attributes.unshift({
        type: tokenTypes.constant,
        key,
        value
      })
    } else {
      child.dynamic = true

      child.attributes.inset = child.attributes.inset ?? child.attributes.length

      child.attributes.push({
        type: tokenTypes.variable,
        key,
        value
      })
    }
  }

  for (;;) {
    current = tokens.next()

    if (current.done) break

    const token = current.value

    if (token.type === tokenTypes.endtag && token.value === child.tag) {
      break
    } else if (token.type === tokenTypes.tag) {
      const dynamic = parse(tokens, child, token.value, variables)

      child.dynamic = child.dynamic || dynamic
    } else if (token.type === tokenTypes.text) {
      child.children.push({
        type: tokenTypes.text,
        value: token.value
      })
    } else if (token.type === tokenTypes.variable) {
      child.dynamic = true

      child.children.inset = child.children.inset ?? child.children.length

      child.children.push({
        type: tokenTypes.variable,
        value: token.value
      })
    }
  }

  if (child.dynamic) {
    parent.children.inset = parent.children.inset ?? parent.children.length
  }

  parent.children.push(child)

  return child.dynamic
}

let view = 1

const toTemplate = (strs, variables) => {
  const acc = {
    tag: false
  }

  const tokens = tokenizer.get(acc, strs, variables.length)

  const children = []

  for (;;) {
    const current = tokens.next()

    if (current.done) break

    const token = current.value

    if (token.type === tokenTypes.tag) {
      parse(tokens, {children}, token.value, variables)
    } else if (token.type === tokenTypes.text && token.value.trim()) {
      throw createAssertionError(token.type, "'node'")
    }
  }

  if (children.length !== 1) {
    throw createAssertionError(children.length, 1)
  }

  children[0].view = view++

  return children[0]
}

export const html = (strs, ...variables) => {
  let result = weakMap.get(strs)

  if (!result) {
    result = toTemplate(strs, variables)

    weakMap.set(strs, result)
  }

  return Object.assign({variables}, result)
}
<|endoftext|>


<commit_before>const slug = require('slug')
const ift = require('@erickmerchant/ift')('')
const moment = require('moment-timezone')
const groupby = require('lodash.groupby')
const icons = require('geomicons-open')
const host = 'http://erickmerchant.com/'

module.exports = ({collection}) => {
  collection('post', ({on, save, remove, read}) => {
    on('draft', ({parameter, option}) => {
      option('title', {
        description: 'the title',
        required: true
      })

      option('summary', {
        description: 'the summary',
        default: ''
      })

      return (args) => {
        save('posts/:slug', {
          title: args.title,
          summary: args.summary,
          slug: slug(args.title.toLowerCase()),
          timeZone: moment.tz.guess()
        })
      }
    })

    on('publish', ({parameter, option}) => {
      parameter('post', {
        description: 'the post to publish',
        required: true
      })

      return (args) => {
        read(args.post, 'posts/:slug', (post) => {
          save(
            'posts/:time.:slug',
            Object.assign(post, {
              time: Date.now()
            }),
            () => {
              remove(args.post)
            }
          )
        })
      }
    })
  })

  return ({get, html, save, safe, link, dev}) => {
    save('404', layout('404 Not Found', '404.html', ({title, url}) => html`
      <form role="search" action="http://google.com/search" class="clearfix">

${title}

That page doesn't exist. It was either moved, removed, or never existed.

          <input type="hidden" name="q" value="site:${host}">
          <input class="field col-12 sm-col-6" type="text" placeholder="Search..." name="q">

          <button class="rounded btn bold background-blue white p2 m1 h4" type="submit">
            ${icon('search')}
            Submit
          </button>

      </form>`
    ))

    const routes = ['posts/:time.:slug']

    if (dev) {
      routes.push('posts/:slug')
    }

    get(routes, (posts) => {
      posts = posts.reverse().map((post) => {
        post.date = moment(new Date(Number(post.time))).tz(post.timeZone)

        return post
      })

      const grouped = groupby(posts, (post) => post.date.format('MMMM YYYY'))

      save('posts/', layout('Posts', 'posts/', ({title, url}) => html`

${title}

        ${safe(Object.keys(grouped).map((monthYear) => html`

              ${icon('calendar')}
              ${monthYear}

              ${safe(grouped[monthYear].map((post) => html`

${post.title}

${post.summary}
`
              ))}

`
        ))}`
      ))

      if (posts.length > 0) {
        posts.forEach((post, index) => {
          let url = link('/posts/:slug/', post)

          if (index === 0) {
            url = ['/', url]
          }

          save(url, layout(`${post.title} | Posts`, link('/posts/:slug/', post), ({title, url}) => html`

${post.title}

                  <time class="h3 bold" datetime="${post.date.format('YYYY-MM-DD')}">
                    ${icon('calendar')}
                    ${post.date.format('MMMM D, YYYY')}
                  </time>

${safe(post.content)}

              ${safe(ift(
              posts[index + 1],
              (previous) => html`

                  ${icon('chevronLeft')}
                  Older
                `,
              () => html`

                  ${icon('chevronLeft')}
                  Older
                `
              ))}
              ${safe(ift(
              posts[index - 1],
              (next) => html`

                  Newer
                  ${icon('chevronRight')}
                `,
              () => html`

                  Newer
                  ${icon('chevronRight')}
                `
              ))}

            `
          ))
        })
      }
    })

    function layout (title, url, main) {
      return html`
      <!DOCTYPE html>

          <meta charset="utf-8">
          <meta http-equiv="X-UA-Compatible" content="IE=edge">
          <meta name="viewport" content="width=device-width, initial-scale=1">
          <title>${title}</title>
          <link href="/favicon.png" rel="shortcut icon" type="image/png">
          <link href="/app.css" rel="stylesheet" type="text/css">
          <link rel="canonical" href="${host}${url}">

                Erick Merchant

                      ${icon('calendar')}
                      Posts

                      ${icon('github')}
                      GitHub

                      ${icon('twitter')}
                      Twitter

                  ${safe(main({title, url}))}

                    ${icon('github')}
                    View Source

                    ${icon('twitter')}
                    Tweet

                  © Erick Merchant, ${(new Date()).getFullYear()}

      `
    }

    function icon (name) {
      return safe(html`
      <svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
        <path d="${icons.paths[name]}" />
      </svg>`)
    }
  }
}
<commit_msg>Have a default date for drafts<commit_after>const slug = require('slug')
const ift = require('@erickmerchant/ift')('')
const moment = require('moment-timezone')
const groupby = require('lodash.groupby')
const icons = require('geomicons-open')
const host = 'http://erickmerchant.com/'

module.exports = ({collection}) => {
  collection('post', ({on, save, remove, read}) => {
    on('draft', ({parameter, option}) => {
      option('title', {
        description: 'the title',
        required: true
      })

      option('summary', {
        description: 'the summary',
        default: ''
      })

      return (args) => {
        save('posts/:slug', {
          title: args.title,
          summary: args.summary,
          slug: slug(args.title.toLowerCase()),
          timeZone: moment.tz.guess()
        })
      }
    })

    on('publish', ({parameter, option}) => {
      parameter('post', {
        description: 'the post to publish',
        required: true
      })

      return (args) => {
        read(args.post, 'posts/:slug', (post) => {
          save(
            'posts/:time.:slug',
            Object.assign(post, {
              time: Date.now()
            }),
            () => {
              remove(args.post)
            }
          )
        })
      }
    })
  })

  return ({get, html, save, safe, link, dev}) => {
    save('404', layout('404 Not Found', '404.html', ({title, url}) => html`
      <form role="search" action="http://google.com/search" class="clearfix">

${title}

That page doesn't exist. It was either moved, removed, or never existed.

          <input type="hidden" name="q" value="site:${host}">
          <input class="field col-12 sm-col-6" type="text" placeholder="Search..." name="q">

          <button class="rounded btn bold background-blue white p2 m1 h4" type="submit">
            ${icon('search')}
            Submit
          </button>

      </form>`
    ))

    const routes = ['posts/:time.:slug']

    if (dev) {
      routes.push('posts/:slug')
    }

    get(routes, (posts) => {
      posts = posts.reverse().map((post) => {
        if (post.time) {
          post.date = moment(new Date(Number(post.time))).tz(post.timeZone)
        } else {
          post.date = moment()
        }

        return post
      })

      const grouped = groupby(posts, (post) => post.date.format('MMMM YYYY'))

      save('posts/', layout('Posts', 'posts/', ({title, url}) => html`

${title}

        ${safe(Object.keys(grouped).map((monthYear) => html`

              ${icon('calendar')}
              ${monthYear}

              ${safe(grouped[monthYear].map((post) => html`

${post.title}

${post.summary}
`
              ))}

`
        ))}`
      ))

      if (posts.length > 0) {
        posts.forEach((post, index) => {
          let url = link('/posts/:slug/', post)

          if (index === 0) {
            url = ['/', url]
          }

          save(url, layout(`${post.title} | Posts`, link('/posts/:slug/', post), ({title, url}) => html`

${post.title}

                  <time class="h3 bold" datetime="${post.date.format('YYYY-MM-DD')}">
                    ${icon('calendar')}
                    ${post.date.format('MMMM D, YYYY')}
                  </time>

${safe(post.content)}

              ${safe(ift(
              posts[index + 1],
              (previous) => html`

                  ${icon('chevronLeft')}
                  Older
                `,
              () => html`

                  ${icon('chevronLeft')}
                  Older
                `
              ))}
              ${safe(ift(
              posts[index - 1],
              (next) => html`

                  Newer
                  ${icon('chevronRight')}
                `,
              () => html`

                  Newer
                  ${icon('chevronRight')}
                `
              ))}

            `
          ))
        })
      }
    })

    function layout (title, url, main) {
      return html`
      <!DOCTYPE html>

          <meta charset="utf-8">
          <meta http-equiv="X-UA-Compatible" content="IE=edge">
          <meta name="viewport" content="width=device-width, initial-scale=1">
          <title>${title}</title>
          <link href="/favicon.png" rel="shortcut icon" type="image/png">
          <link href="/app.css" rel="stylesheet" type="text/css">
          <link rel="canonical" href="${host}${url}">

                Erick Merchant

                      ${icon('calendar')}
                      Posts

                      ${icon('github')}
                      GitHub

                      ${icon('twitter')}
                      Twitter

                  ${safe(main({title, url}))}

                    ${icon('github')}
                    View Source

                    ${icon('twitter')}
                    Tweet

                  © Erick Merchant, ${(new Date()).getFullYear()}

      `
    }

    function icon (name) {
      return safe(html`
      <svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
        <path d="${icons.paths[name]}" />
      </svg>`)
    }
  }
}
<|endoftext|>