<commit_before>'use strict';

//
//  PencilTool
//

const textPencilTool = '鉛筆';

/// 新しいインスタンスを初期化する。
function PencilTool(rect)
{
  this.m_iconBounds = rect;

  // this.m_drawOp = new NullDrawOp();
  this.m_drawOp = new DrawOp_FreeHand();
  // this.m_effect = new NullEffect();
  this.m_effect = new Effect_Pencil();
  this.m_cursor = new Cursor_Circle();
  this.m_drawerCore = new DrawerBase(this.m_drawOp, this.m_effect, this.m_cursor);

  this.m_setting = null;
}

/// 選択時呼ばれる。
PencilTool.prototype.OnSelected = function(e)
{
  // console.log("PencilTool::OnSelected() called. (" + e.m_point.x + ", " + e.m_point.y + ")");

  // 選択時アイコン描画
  draw_icon_wrp(this.m_iconBounds, textPencilTool, null, true, e);

  // 共通設定オブジェクト記憶
  this.m_setting = e.m_sender.getCommonSetting();

  // 描画ツール設定
  e.m_sender.addDrawer(this);
  e.m_sender.addDrawer(this.m_drawerCore);
}

/// 選択解除時呼ばれる。
PencilTool.prototype.OnDiselected = function(e)
{
  // console.log("PencilTool::OnDiselected() called. ");

  // 描画ツール解除
  e.m_sender.removeDrawer(this.m_drawerCore);
  e.m_sender.removeDrawer(this);

  // 非選択時アイコン描画
  draw_icon_wrp(this.m_iconBounds, textPencilTool, null, false, e);
}

/// 再ポイントされたとき呼ばれる。
PencilTool.prototype.OnPicked = function(e)
{
  // console.log("PencilTool::OnPicked() called. (" + e.m_point.x + ", " + e.m_point.y + ")");
  /*NOP*/
}

/// 描画ストローク開始時に呼ばれる。
PencilTool.prototype.OnDrawStart = function(e)
{
  // console.log("PencilTool.OnDrawStart() called.");

  // 最新の描画設定を反映
  let thickness = this.m_setting.getThickness();
  let color = this.m_setting.getColor();
  this.m_effect.setParam(thickness, color);
  this.m_cursor.setParam(thickness);
}

//
//  線幅ツール
//

/// 新しいインスタンスを初期化する。
function ThicknessTool(iconBounds)
{
  this.m_slideBar = new MicroSlideBar(
    iconBounds, true,
    'rgb(0,0,0)',
    1, 30, 1,
    "", "px",
    0, 30
  );
}

/// 最初の表示を行う。
ThicknessTool.prototype.show = function(setting, toolCanvas)
{
  let val = setting.getThickness();
  let context = toolCanvas.getContext('2d');
  this.m_slideBar.show(val, context);
}

/// 選択時呼ばれる。
ThicknessTool.prototype.OnSelected = function(e)
{
  // console.log("ThicknessTool::OnSelected() called. (" + e.m_point.x + ", " + e.m_point.y + ")");
  let val = e.m_sender.getCommonSetting().getThickness();
  this.m_slideBar.OnSelected(e, val);
}

/// 選択解除時呼ばれる。
ThicknessTool.prototype.OnDiselected = function(e)
{
  // console.log("ThicknessTool::OnDiselected() called. ");
  /*NOP*/
}

/// 再ポイントされたとき呼ばれる。
ThicknessTool.prototype.OnPicked = function(e)
{
  // console.log("ThicknessTool::OnPicked() called. (" + e.m_point.x + ", " + e.m_point.y + ")");
  let val = this.m_slideBar.OnPicked(e);

  // 共通設定変更
  let setting = e.m_sender.getCommonSetting();  // Alias
  setting.setThickness(val);
}

//
//  カラーパレット
//

/// 新しいインスタンスを初期化する。
function ColorPalette(iconBounds)
{
  this.m_iconBounds = iconBounds;
  this.m_color = null;

  this.m_setting = null;
  this.m_toolCanvas = null;

  // 画像合成先キャンバス
  // 暫定的に、index.html内のものを使う。
	this.m_joint_canvas = document.getElementById("joint_canvas");
}

/// 最初の表示を行う。
ColorPalette.prototype.show = function(color, bActive, toolCanvas)
{
  let context = toolCanvas.getContext('2d');

  this.m_color = color;
  draw_color_palette(this.m_iconBounds, this.m_color, bActive, context);
}

/// 選択時呼ばれる。
ColorPalette.prototype.OnSelected = function(e)
{
  // console.log("ColorPalette::OnSelected() called. (" + e.m_point.x + ", " + e.m_point.y + ")");

  // アイコン描画(選択状態)
  let context = e.m_sender.getToolPaletteCanvas().getContext('2d');
  draw_color_palette(this.m_iconBounds, this.m_color, true, context);

  // 設定更新
  this.m_setting = e.m_sender.getCommonSetting();
  this.m_setting.setColor(this.m_color);

  // キャンバス記憶
  this.m_toolCanvas = e.m_sender.getToolPaletteCanvas();

  // スポイト操作のための登録
  e.m_sender.addDrawer(this);

  // 設定変更追跡のための登録
  this.m_setting.addListener(this);
}

/// 選択解除時呼ばれる。
ColorPalette.prototype.OnDiselected = function(e)
{
  // console.log("ColorPalette::OnDiselected() called. ");
  let setting = e.m_sender.getCommonSetting();

  // 設定変更追跡のための登録を解除
  setting.removeListener(this);

  // 登録解除
  e.m_sender.removeDrawer(this);

  // アイコン描画(非選択状態)
  let context = e.m_sender.getToolPaletteCanvas().getContext('2d');
  draw_color_palette(this.m_iconBounds, this.m_color, false, context);
}

/// 再ポイントされたとき呼ばれる。
ColorPalette.prototype.OnPicked = function(e)
{
  // console.log("ColorPalette::OnPicked() called. (" + e.m_point.x + ", " + e.m_point.y + ")");
  /*NOP*/
}

/// 描画ストローク開始時に呼ばれる。
ColorPalette.prototype.OnDrawStart = function(e)
{
  // スポイト操作
  if ((e.m_spKey & SpKey.KY_CTRL) != 0) {
    // 画像合成
    e.m_sender.getJointImage(this.m_joint_canvas);

    // 画素データ取得
    let jointCtx = this.m_joint_canvas.getContext('2d');
    let px_data = jointCtx.getImageData(e.m_point.x, e.m_point.y, 1, 1);
    assert(px_data.data.length == 4);

    // 設定更新
    let color = get_color_as_RGB(px_data.data);
    eval(dbgv([ 'color' ]));
    this.m_color = color;
    this.m_setting.setColor(this.m_color);

    // 表示更新
    let context = this.m_toolCanvas.getContext('2d');
    draw_color_palette(this.m_iconBounds, this.m_color, true, context);
  }
}
/// 設定が変化したとき呼ばれる。
ColorPalette.prototype.OnSettingChanged = function(setting)
{
  if (this.m_toolCanvas != null) {
    this.m_color = setting.getColor();
    let context = this.m_toolCanvas.getContext('2d');
    draw_color_palette(this.m_iconBounds, this.m_color, true, context);
  }
}

//
//  色ツール
//

/// 新しいインスタンスを初期化する。
function ColorCompoTool(iconBounds)
{
  this.m_iconBounds = iconBounds;
  this.m_slideBar = null;
  this.m_colorCompoIdx = null;
  this.m_alphaIdx = null;
  this.m_toolCanvas = null;
}

/// 最初の表示を行う。
/// ここで与える引数により、RGBAのどのツールなのかが決まる。
ColorCompoTool.prototype.show = function(setting, colorCompoIdx, alphaIdx, toolCanvas)
{
  this.m_colorCompoIdx = colorCompoIdx;
  this.m_alphaIdx = alphaIdx;
  this.m_toolCanvas = null;

  // 現在の色を取得する。
  let color = setting.getColor();
  let colors = color.match(/\d+/g);
  assert(colors.length >= 3);

  // 色成分別処理
  let viewColor, pfx, iniVal;
  switch (colorCompoIdx) {
  case 0:
    viewColor = 'rgb(255,150,150)';
    pfx = 'R';
    iniVal = colors[0];
    break;
  case 1:
    viewColor = 'rgb(130,242,56)';
    pfx = 'G';
    iniVal = colors[1];
    break;
  case 2:
    viewColor = 'rgb(128,128,255)';
    pfx = 'B';
    iniVal = colors[2];
    break;
  case 3:
    viewColor = 'rgb(170,170,170)';
    pfx = 'A';
    iniVal = setting.getAlpha(alphaIdx);
    break;
  default:
    assert(false);
    return;
  }
  eval(dbgv([ 'iniVal' ]));

  // スライドバー構成
  this.m_slideBar = new MicroSlideBar(
    this.m_iconBounds, false,
    viewColor,
    0, 255, iniVal,
    pfx, "",
    -1, 255     // 値0でも色つきの線を表示させるため、exValMin=-1とする。
  );

  // 最初の表示
  let context = toolCanvas.getContext('2d');
  this.m_slideBar.show(iniVal, context);

  // イベントハンドラ登録
  this.m_toolCanvas = toolCanvas;
  setting.addListener(this);
}

/// 共通設定から必要な値を取得する。
ColorCompoTool.prototype.getValue = function(setting)
{
  let val = null;

  switch (this.m_colorCompoIdx) {
  case 0:
  case 1:
  case 2:
    {
      let color = setting.getColor();
      let colors = get_components_from_RGBx(color);
      val = colors[this.m_colorCompoIdx];
    }
    break;
  case 3:
    val = setting.getAlpha(this.m_alphaIdx);
    break;
  default:
    assert(false);
    break;
  }

  return val;
}

/// 共通設定を変更する。
ColorCompoTool.prototype.setValue = function(val, setting)
{
  switch (this.m_colorCompoIdx) {
  case 0:
  case 1:
  case 2:
    {
      let color = setting.getColor();
      let colors = get_components_from_RGBx(color);
      colors[this.m_colorCompoIdx] = val;
      color = get_color_as_RGB(colors);
      setting.setColor(color);
    }
    break;
  case 3:
    setting.setColor(this.m_alphaIdx, val);
    break;
  default:
    assert(false);
    break;
  }
}

/// 選択時呼ばれる。
ColorCompoTool.prototype.OnSelected = function(e)
{
  // console.log("ColorCompoTool::OnSelected() called. (" + e.m_point.x + ", " + e.m_point.y + ")");
  let setting = e.m_sender.getCommonSetting();
  let val = this.getValue(setting);
  this.m_slideBar.OnSelected(e, val);
}

/// 選択解除時呼ばれる。
ColorCompoTool.prototype.OnDiselected = function(e)
{
  // console.log("ColorCompoTool::OnDiselected() called. ");
  /*NOP*/
}

/// 再ポイントされたとき呼ばれる。
ColorCompoTool.prototype.OnPicked = function(e)
{
  // console.log("ColorCompoTool::OnPicked() called. (" + e.m_point.x + ", " + e.m_point.y + ")");
  let val = this.m_slideBar.OnPicked(e);
  let setting = e.m_sender.getCommonSetting();
  this.setValue(val, setting);
}

/// 設定が変更されたとき呼ばれる。
ColorCompoTool.prototype.OnSettingChanged = function(setting)
{
  if (this.m_toolCanvas != null) {
    let val = this.getValue(setting);
    let context = this.m_toolCanvas.getContext('2d');
    this.m_slideBar.drawValue(val, context);
  }
}
<commit_msg>呼び出す関数に誤りがあったので修正した。<commit_after>'use strict';

//
//  PencilTool
//

const textPencilTool = '鉛筆';

/// 新しいインスタンスを初期化する。
function PencilTool(rect)
{
  this.m_iconBounds = rect;

  // this.m_drawOp = new NullDrawOp();
  this.m_drawOp = new DrawOp_FreeHand();
  // this.m_effect = new NullEffect();
  this.m_effect = new Effect_Pencil();
  this.m_cursor = new Cursor_Circle();
  this.m_drawerCore = new DrawerBase(this.m_drawOp, this.m_effect, this.m_cursor);

  this.m_setting = null;
}

/// 選択時呼ばれる。
PencilTool.prototype.OnSelected = function(e)
{
  // console.log("PencilTool::OnSelected() called. (" + e.m_point.x + ", " + e.m_point.y + ")");

  // 選択時アイコン描画
  draw_icon_wrp(this.m_iconBounds, textPencilTool, null, true, e);

  // 共通設定オブジェクト記憶
  this.m_setting = e.m_sender.getCommonSetting();

  // 描画ツール設定
  e.m_sender.addDrawer(this);
  e.m_sender.addDrawer(this.m_drawerCore);
}

/// 選択解除時呼ばれる。
PencilTool.prototype.OnDiselected = function(e)
{
  // console.log("PencilTool::OnDiselected() called. ");

  // 描画ツール解除
  e.m_sender.removeDrawer(this.m_drawerCore);
  e.m_sender.removeDrawer(this);

  // 非選択時アイコン描画
  draw_icon_wrp(this.m_iconBounds, textPencilTool, null, false, e);
}

/// 再ポイントされたとき呼ばれる。
PencilTool.prototype.OnPicked = function(e)
{
  // console.log("PencilTool::OnPicked() called. (" + e.m_point.x + ", " + e.m_point.y + ")");
  /*NOP*/
}

/// 描画ストローク開始時に呼ばれる。
PencilTool.prototype.OnDrawStart = function(e)
{
  // console.log("PencilTool.OnDrawStart() called.");

  // 最新の描画設定を反映
  let thickness = this.m_setting.getThickness();
  let color = this.m_setting.getColor();
  this.m_effect.setParam(thickness, color);
  this.m_cursor.setParam(thickness);
}

//
//  線幅ツール
//

/// 新しいインスタンスを初期化する。
function ThicknessTool(iconBounds)
{
  this.m_slideBar = new MicroSlideBar(
    iconBounds, true,
    'rgb(0,0,0)',
    1, 30, 1,
    "", "px",
    0, 30
  );
}

/// 最初の表示を行う。
ThicknessTool.prototype.show = function(setting, toolCanvas)
{
  let val = setting.getThickness();
  let context = toolCanvas.getContext('2d');
  this.m_slideBar.show(val, context);
}

/// 選択時呼ばれる。
ThicknessTool.prototype.OnSelected = function(e)
{
  // console.log("ThicknessTool::OnSelected() called. (" + e.m_point.x + ", " + e.m_point.y + ")");
  let val = e.m_sender.getCommonSetting().getThickness();
  this.m_slideBar.OnSelected(e, val);
}

/// 選択解除時呼ばれる。
ThicknessTool.prototype.OnDiselected = function(e)
{
  // console.log("ThicknessTool::OnDiselected() called. ");
  /*NOP*/
}

/// 再ポイントされたとき呼ばれる。
ThicknessTool.prototype.OnPicked = function(e)
{
  // console.log("ThicknessTool::OnPicked() called. (" + e.m_point.x + ", " + e.m_point.y + ")");
  let val = this.m_slideBar.OnPicked(e);

  // 共通設定変更
  let setting = e.m_sender.getCommonSetting();  // Alias
  setting.setThickness(val);
}

//
//  カラーパレット
//

/// 新しいインスタンスを初期化する。
function ColorPalette(iconBounds)
{
  this.m_iconBounds = iconBounds;
  this.m_color = null;

  this.m_setting = null;
  this.m_toolCanvas = null;

  // 画像合成先キャンバス
  // 暫定的に、index.html内のものを使う。
	this.m_joint_canvas = document.getElementById("joint_canvas");
}

/// 最初の表示を行う。
ColorPalette.prototype.show = function(color, bActive, toolCanvas)
{
  let context = toolCanvas.getContext('2d');

  this.m_color = color;
  draw_color_palette(this.m_iconBounds, this.m_color, bActive, context);
}

/// 選択時呼ばれる。
ColorPalette.prototype.OnSelected = function(e)
{
  // console.log("ColorPalette::OnSelected() called. (" + e.m_point.x + ", " + e.m_point.y + ")");

  // アイコン描画(選択状態)
  let context = e.m_sender.getToolPaletteCanvas().getContext('2d');
  draw_color_palette(this.m_iconBounds, this.m_color, true, context);

  // 設定更新
  this.m_setting = e.m_sender.getCommonSetting();
  this.m_setting.setColor(this.m_color);

  // キャンバス記憶
  this.m_toolCanvas = e.m_sender.getToolPaletteCanvas();

  // スポイト操作のための登録
  e.m_sender.addDrawer(this);

  // 設定変更追跡のための登録
  this.m_setting.addListener(this);
}

/// 選択解除時呼ばれる。
ColorPalette.prototype.OnDiselected = function(e)
{
  // console.log("ColorPalette::OnDiselected() called. ");
  let setting = e.m_sender.getCommonSetting();

  // 設定変更追跡のための登録を解除
  setting.removeListener(this);

  // 登録解除
  e.m_sender.removeDrawer(this);

  // アイコン描画(非選択状態)
  let context = e.m_sender.getToolPaletteCanvas().getContext('2d');
  draw_color_palette(this.m_iconBounds, this.m_color, false, context);
}

/// 再ポイントされたとき呼ばれる。
ColorPalette.prototype.OnPicked = function(e)
{
  // console.log("ColorPalette::OnPicked() called. (" + e.m_point.x + ", " + e.m_point.y + ")");
  /*NOP*/
}

/// 描画ストローク開始時に呼ばれる。
ColorPalette.prototype.OnDrawStart = function(e)
{
  // スポイト操作
  if ((e.m_spKey & SpKey.KY_CTRL) != 0) {
    // 画像合成
    e.m_sender.getJointImage(this.m_joint_canvas);

    // 画素データ取得
    let jointCtx = this.m_joint_canvas.getContext('2d');
    let px_data = jointCtx.getImageData(e.m_point.x, e.m_point.y, 1, 1);
    assert(px_data.data.length == 4);

    // 設定更新
    let color = get_color_as_RGB(px_data.data);
    eval(dbgv([ 'color' ]));
    this.m_color = color;
    this.m_setting.setColor(this.m_color);

    // 表示更新
    let context = this.m_toolCanvas.getContext('2d');
    draw_color_palette(this.m_iconBounds, this.m_color, true, context);
  }
}
/// 設定が変化したとき呼ばれる。
ColorPalette.prototype.OnSettingChanged = function(setting)
{
  if (this.m_toolCanvas != null) {
    this.m_color = setting.getColor();
    let context = this.m_toolCanvas.getContext('2d');
    draw_color_palette(this.m_iconBounds, this.m_color, true, context);
  }
}

//
//  色ツール
//

/// 新しいインスタンスを初期化する。
function ColorCompoTool(iconBounds)
{
  this.m_iconBounds = iconBounds;
  this.m_slideBar = null;
  this.m_colorCompoIdx = null;
  this.m_alphaIdx = null;
  this.m_toolCanvas = null;
}

/// 最初の表示を行う。
/// ここで与える引数により、RGBAのどのツールなのかが決まる。
ColorCompoTool.prototype.show = function(setting, colorCompoIdx, alphaIdx, toolCanvas)
{
  this.m_colorCompoIdx = colorCompoIdx;
  this.m_alphaIdx = alphaIdx;
  this.m_toolCanvas = null;

  // 現在の色を取得する。
  let color = setting.getColor();
  let colors = color.match(/\d+/g);
  assert(colors.length >= 3);

  // 色成分別処理
  let viewColor, pfx, iniVal;
  switch (colorCompoIdx) {
  case 0:
    viewColor = 'rgb(255,150,150)';
    pfx = 'R';
    iniVal = colors[0];
    break;
  case 1:
    viewColor = 'rgb(130,242,56)';
    pfx = 'G';
    iniVal = colors[1];
    break;
  case 2:
    viewColor = 'rgb(128,128,255)';
    pfx = 'B';
    iniVal = colors[2];
    break;
  case 3:
    viewColor = 'rgb(170,170,170)';
    pfx = 'A';
    iniVal = setting.getAlpha(alphaIdx);
    break;
  default:
    assert(false);
    return;
  }
  eval(dbgv([ 'iniVal' ]));

  // スライドバー構成
  this.m_slideBar = new MicroSlideBar(
    this.m_iconBounds, false,
    viewColor,
    0, 255, iniVal,
    pfx, "",
    -1, 255     // 値0でも色つきの線を表示させるため、exValMin=-1とする。
  );

  // 最初の表示
  let context = toolCanvas.getContext('2d');
  this.m_slideBar.show(iniVal, context);

  // イベントハンドラ登録
  this.m_toolCanvas = toolCanvas;
  setting.addListener(this);
}

/// 共通設定から必要な値を取得する。
ColorCompoTool.prototype.getValue = function(setting)
{
  let val = null;

  switch (this.m_colorCompoIdx) {
  case 0:
  case 1:
  case 2:
    {
      let color = setting.getColor();
      let colors = get_components_from_RGBx(color);
      val = colors[this.m_colorCompoIdx];
    }
    break;
  case 3:
    val = setting.getAlpha(this.m_alphaIdx);
    break;
  default:
    assert(false);
    break;
  }

  return val;
}

/// 共通設定を変更する。
ColorCompoTool.prototype.setValue = function(val, setting)
{
  switch (this.m_colorCompoIdx) {
  case 0:
  case 1:
  case 2:
    {
      let color = setting.getColor();
      let colors = get_components_from_RGBx(color);
      colors[this.m_colorCompoIdx] = val;
      color = get_color_as_RGB(colors);
      setting.setColor(color);
    }
    break;
  case 3:
    setting.setAlpha(this.m_alphaIdx, val);
    break;
  default:
    assert(false);
    break;
  }
}

/// 選択時呼ばれる。
ColorCompoTool.prototype.OnSelected = function(e)
{
  // console.log("ColorCompoTool::OnSelected() called. (" + e.m_point.x + ", " + e.m_point.y + ")");
  let setting = e.m_sender.getCommonSetting();
  let val = this.getValue(setting);
  this.m_slideBar.OnSelected(e, val);
}

/// 選択解除時呼ばれる。
ColorCompoTool.prototype.OnDiselected = function(e)
{
  // console.log("ColorCompoTool::OnDiselected() called. ");
  /*NOP*/
}

/// 再ポイントされたとき呼ばれる。
ColorCompoTool.prototype.OnPicked = function(e)
{
  // console.log("ColorCompoTool::OnPicked() called. (" + e.m_point.x + ", " + e.m_point.y + ")");
  let val = this.m_slideBar.OnPicked(e);
  let setting = e.m_sender.getCommonSetting();
  this.setValue(val, setting);
}

/// 設定が変更されたとき呼ばれる。
ColorCompoTool.prototype.OnSettingChanged = function(setting)
{
  if (this.m_toolCanvas != null) {
    let val = this.getValue(setting);
    let context = this.m_toolCanvas.getContext('2d');
    this.m_slideBar.drawValue(val, context);
  }
}
<|endoftext|>


<commit_before>/* eslint no-bitwise: off */
export function lighter (color, percent = 10) {
  const rgb = parseInt ((color[0] === '#') ? color.slice (1) : color, 16);
  const adjustment = (percent > 0) ? 255 / percent : 0;
  let red = (rgb >> 16) + adjustment;
  let green = ((rgb & 0x00FF00) >> 8) + adjustment;
  let blue = (rgb & 0x0000FF) + adjustment;
  red = (red > 255) ? 255 : red;
  green = (green) > 255 ? 255 : green;
  blue = (blue > 255) ? 255 : blue;
  red = ((red < 16) ? '0' : '') + red.toString (16);
  green = ((green < 16) ? '0' : '') + green.toString (16);
  blue = ((blue < 16) ? '0' : '') + blue.toString (16);
  return `#${red}${green}${blue}`;
}
<commit_msg>Adjustment to integer value<commit_after>/* eslint no-bitwise: off */
export function lighter (color, percent = 10) {
  const rgb = parseInt ((color[0] === '#') ? color.slice (1) : color, 16);
  const adjustment = (percent > 0) ? Math.floor (255 / percent) : 0;
  let red = (rgb >> 16) + adjustment;
  let green = ((rgb & 0x00FF00) >> 8) + adjustment;
  let blue = (rgb & 0x0000FF) + adjustment;
  red = (red > 255) ? 255 : red;
  green = (green) > 255 ? 255 : green;
  blue = (blue > 255) ? 255 : blue;
  red = ((red < 16) ? '0' : '') + red.toString (16);
  green = ((green < 16) ? '0' : '') + green.toString (16);
  blue = ((blue < 16) ? '0' : '') + blue.toString (16);
  return `#${red}${green}${blue}`;
}
<|endoftext|>


<commit_before>import focalStorage from 'src/external/focalStorage.js';
import uuid from '../uuid.js';

const STORAGE_PREFIX = 'triple-notes:';
const STORAGE_PREFIX_ITEMS = STORAGE_PREFIX + 'items:';

export async function cachedFetch(url, options) {
  const key = STORAGE_PREFIX_ITEMS + url.toString();
  if(null === await focalStorage.getItem(key)) {
    let text = await fetch(url, options).then(r => r.text())
    focalStorage.setItem(key, text);
    return text;
  } else {
    return focalStorage.getItem(key);
  }
}

export async function invalidateFetchCache(url) {
  const key = STORAGE_PREFIX_ITEMS + url.toString();
  await focalStorage.removeItem(key);
}

class Knot {
  constructor(fileName, content) {
    this.fileName = fileName;
    this.content = content;
  }
  get url() {
    return this.fileName;
  }

  label() {
    if(this.fileName.endsWith('.md')) {
      return this.content.split('\n')[0];
    }
    return this.fileName;
  }
  isTriple() { return false; }
  async save(newContent) {
    invalidateFetchCache(this.url);
    this.content = newContent;
    await lively.files.saveFile(this.url, newContent);
  }
}

class Triple extends Knot {
  constructor(fileName, content) {
    super(fileName, JSON.parse(content));
  }

  label() {
    if(this.predicate) {
      return 't:' + this.predicate.label();
    }
    return this.filename.replace('.triple.json', '');
  }
  isTriple() { return true; }
}

export class Graph {
  constructor() {
    this.knots = [];
    this.loadedDirectories = [];
    // url string -> Promise for Knot
    this.requestedKnots = new Map();
  }

  getKnots() {
    return this.knots;
  }
  get triples() {
    return this.knots.filter(knot => knot.isTriple());
  }

  async deserializeKnot(fileName, text) {
    function isTriple(fileName, text) {
      return fileName.endsWith(".triple.json");
    }

    let knot;
    if(isTriple(fileName, text)) {
      knot = new Triple(fileName, text);
      await this.linkUpTriple(knot);
    } else {
      knot = new Knot(fileName, text);
    }

    this.knots.push(knot);

    return knot;
  }

  async createReferree(triple, propName) {
    let subjectUrlString = triple.content[propName];
    let subjectUrl = new URL(subjectUrlString, triple.fileName);
    let searchString = subjectUrl.toString();

    let subject = await this.requestKnot(subjectUrl);
    //let subject = this.knots.find(knot => knot.fileName === searchString);
    if(subject) {
      triple[propName] = subject;
    } else {
      throw new Error(searchString +' '+ triple.fileName+ 'external referrees not yet implemented!');
    }
  }
  async linkUpTriple(triple) {
    return Promise.all([
      this.createReferree(triple, 'subject'),
      this.createReferree(triple, 'predicate'),
      this.createReferree(triple, 'object')
    ]);
  }

  static getInstance() {
    if(!this.instance) {
      this.instance = new Graph();
    }
    return this.instance;
  }
  static clearInstance() {
    this.instance = undefined;
  }

  query(s, p, o) {
    let matchingTriples = [];
    this.triples.forEach(triple => {
      if(s === _ || triple.subject === s) {
        if(p === _ || triple.predicate === p) {
          if(o === _ || triple.object === o) {
            matchingTriples.push(triple);
          }
        }
      }
    });

    return matchingTriples;
  }

  getUrlsByKnot(knot) {
    // TODO: we simply return the single reference url for now
    return [knot.fileName];
  }

  // returns a promise for the Knot
  async requestKnot(url) {
    const filePath = url.toString();
    if(!this.requestedKnots.has(filePath)) {
      this.requestedKnots.set(filePath, this.loadSingleKnot(url));
    }
    return this.requestedKnots.get(filePath);
  }

  async loadSingleKnot(url) {
    return cachedFetch(url)
      .then(text => {
        const fileName = url.toString();
        return this.deserializeKnot(fileName, text);
      });
  }

  async loadFromDir(directory) {
    if(this.loadedDirectories.includes(directory)) {
      return;
    }
    this.loadedDirectories.push(directory);
    let directoryURL = new URL(directory);
    let text = await cachedFetch(directory, { method: 'OPTIONS' });
    let json = JSON.parse(text);
    let fileDescriptors = json.contents;
    fileDescriptors = fileDescriptors.filter(desc => desc.type === "file");
    let fileNames = fileDescriptors.map(desc => desc.name);

    await Promise.all(fileNames.map(fileName => {
      let knotURL = new URL(fileName, directoryURL);
      return this.requestKnot(knotURL);
    }));

    //await this.linkUpTriples();
  }

  async getNonCollidableURL(directory, name, fileEnding) {
    const maxTries = 10;
    let fileName = name.replace(/\s/g, '_');
    let offset = 0;

    for(let i = 0; i < maxTries; i++) {
      let bust = offset === 0 ? '' : offset;
      let url = new URL(`${fileName}${bust}.${fileEnding}`, directory);
      let fileExists = (await fetch(url)).status === 200;
      if(fileExists) {
        offset++;
      } else {
        return url;
      }
    }
    throw new Error('too many tries for '); // TODO: improve error message
  }
  async createKnot(directory, name, fileEnding) {
    if(fileEnding !== 'md') { throw new Error('only .md files supported by now, instead found ', + fileEnding); }

    let url = await this.getNonCollidableURL(directory, name, fileEnding);
    let content = `# ${name}`;
    await lively.files.saveFile(url, content);

    await invalidateFetchCache(directory);

    let knot = await this.requestKnot(url);
    let knotView = await lively.openComponentInWindow("knot-view");
    knotView.loadKnotForURL(knot.url);
  }
  async createTriple(subjectURL, predicateURL, objectURL) {
    const directory = 'https://lively4/dropbox/';
    let url = await this.getNonCollidableURL(directory, 'triple', 'triple.json');
    let content = JSON.stringify({
      subject: subjectURL,
      predicate: predicateURL,
      object: objectURL
    });
    await lively.files.saveFile(url, content);

    await invalidateFetchCache(directory);

    let triple = await this.requestKnot(url);
    let knotView = await lively.openComponentInWindow("knot-view");
    knotView.loadKnotForURL(triple.url);
  }
}

// wild card for querying
export const _ = {};
<commit_msg>AUTO-COMMIT src/client/triples/triples.js<commit_after>import focalStorage from 'src/external/focalStorage.js';
//import uuid from '../uuid.js';

const STORAGE_PREFIX = 'triple-notes:';
const STORAGE_PREFIX_ITEMS = STORAGE_PREFIX + 'items:';

export async function cachedFetch(url, options) {
  const key = STORAGE_PREFIX_ITEMS + url.toString();
  if(null === await focalStorage.getItem(key)) {
    let text = await fetch(url, options).then(r => r.text())
    focalStorage.setItem(key, text);
    return text;
  } else {
    return focalStorage.getItem(key);
  }
}

export async function invalidateFetchCache(url) {
  const key = STORAGE_PREFIX_ITEMS + url.toString();
  await focalStorage.removeItem(key);
}

class Knot {
  constructor(fileName, content) {
    this.fileName = fileName;
    this.content = content;
  }
  get url() {
    return this.fileName;
  }

  label() {
    if(this.fileName.endsWith('.md')) {
      return this.content.split('\n')[0];
    }
    return this.fileName;
  }
  isTriple() { return false; }
  async save(newContent) {
    invalidateFetchCache(this.url);
    this.content = newContent;
    await lively.files.saveFile(this.url, newContent);
  }
}

class Triple extends Knot {
  constructor(fileName, content) {
    super(fileName, JSON.parse(content));
  }

  label() {
    if(this.predicate) {
      return 't:' + this.predicate.label();
    }
    return this.filename.replace('.triple.json', '');
  }
  isTriple() { return true; }
}

export class Graph {
  constructor() {
    this.knots = [];
    this.loadedDirectories = [];
    // url string -> Promise for Knot
    this.requestedKnots = new Map();
  }

  getKnots() {
    return this.knots;
  }
  get triples() {
    return this.knots.filter(knot => knot.isTriple());
  }

  async deserializeKnot(fileName, text) {
    function isTriple(fileName, text) {
      return fileName.endsWith(".triple.json");
    }

    let knot;
    if(isTriple(fileName, text)) {
      knot = new Triple(fileName, text);
      await this.linkUpTriple(knot);
    } else {
      knot = new Knot(fileName, text);
    }

    this.knots.push(knot);

    return knot;
  }

  async createReferree(triple, propName) {
    let subjectUrlString = triple.content[propName];
    let subjectUrl = new URL(subjectUrlString, triple.fileName);
    let searchString = subjectUrl.toString();

    let subject = await this.requestKnot(subjectUrl);
    //let subject = this.knots.find(knot => knot.fileName === searchString);
    if(subject) {
      triple[propName] = subject;
    } else {
      throw new Error(searchString +' '+ triple.fileName+ 'external referrees not yet implemented!');
    }
  }
  async linkUpTriple(triple) {
    return Promise.all([
      this.createReferree(triple, 'subject'),
      this.createReferree(triple, 'predicate'),
      this.createReferree(triple, 'object')
    ]);
  }

  static getInstance() {
    if(!this.instance) {
      this.instance = new Graph();
    }
    return this.instance;
  }
  static clearInstance() {
    this.instance = undefined;
  }

  query(s, p, o) {
    let matchingTriples = [];
    this.triples.forEach(triple => {
      if(s === _ || triple.subject === s) {
        if(p === _ || triple.predicate === p) {
          if(o === _ || triple.object === o) {
            matchingTriples.push(triple);
          }
        }
      }
    });

    return matchingTriples;
  }

  getUrlsByKnot(knot) {
    // TODO: we simply return the single reference url for now
    return [knot.fileName];
  }

  // returns a promise for the Knot
  async requestKnot(url) {
    const filePath = url.toString();
    if(!this.requestedKnots.has(filePath)) {
      this.requestedKnots.set(filePath, this.loadSingleKnot(url));
    }
    return this.requestedKnots.get(filePath);
  }

  async loadSingleKnot(url) {
    return cachedFetch(url)
      .then(text => {
        const fileName = url.toString();
        return this.deserializeKnot(fileName, text);
      });
  }

  async loadFromDir(directory) {
    if(this.loadedDirectories.includes(directory)) {
      return;
    }
    this.loadedDirectories.push(directory);
    let directoryURL = new URL(directory);
    let text = await cachedFetch(directory, { method: 'OPTIONS' });
    let json = JSON.parse(text);
    let fileDescriptors = json.contents;
    fileDescriptors = fileDescriptors.filter(desc => desc.type === "file");
    let fileNames = fileDescriptors.map(desc => desc.name);

    await Promise.all(fileNames.map(fileName => {
      let knotURL = new URL(fileName, directoryURL);
      return this.requestKnot(knotURL);
    }));

    //await this.linkUpTriples();
  }

  async getNonCollidableURL(directory, name, fileEnding) {
    const maxTries = 10;
    let fileName = name.replace(/\s/g, '_');
    let offset = 0;

    for(let i = 0; i < maxTries; i++) {
      let bust = offset === 0 ? '' : offset;
      let url = new URL(`${fileName}${bust}.${fileEnding}`, directory);
      let fileExists = (await fetch(url)).status === 200;
      if(fileExists) {
        offset++;
      } else {
        return url;
      }
    }
    throw new Error('too many tries for '); // TODO: improve error message
  }
  async createKnot(directory, name, fileEnding) {
    if(fileEnding !== 'md') { throw new Error('only .md files supported by now, instead found ', + fileEnding); }

    let url = await this.getNonCollidableURL(directory, name, fileEnding);
    let content = `# ${name}`;
    await lively.files.saveFile(url, content);

    await invalidateFetchCache(directory);

    let knot = await this.requestKnot(url);
    let knotView = await lively.openComponentInWindow("knot-view");
    knotView.loadKnotForURL(knot.url);
  }
  async createTriple(subjectURL, predicateURL, objectURL) {
    const directory = 'https://lively4/dropbox/';
    let url = await this.getNonCollidableURL(directory, 'triple', 'triple.json');
    let content = JSON.stringify({
      subject: subjectURL,
      predicate: predicateURL,
      object: objectURL
    });
    await lively.files.saveFile(url, content);

    await invalidateFetchCache(directory);

    let triple = await this.requestKnot(url);
    let knotView = await lively.openComponentInWindow("knot-view");
    knotView.loadKnotForURL(triple.url);
  }
}

// wild card for querying
export const _ = {};
<|endoftext|>


<commit_before>var isInteger = require('../lang/isInteger')

function makeCollectionMethod (arr, obj, str, rex, def) {
  if (arguments.length === 2) {
    return function (list) {
      return (isInteger(list.length))
      ? arr.apply(null, arguments)
      : obj.apply(null, arguments)
    }
  }

  return function (list) {
    // this will be an excellent use of the spread operator.
    // will be able to get rid of apply
    if (list == null) return def
    if (typeof list === 'string') return str.apply(null, arguments)
    if (list instanceof RegExp) return rex.apply(null, arguments)

    return (isInteger(list.length))
      ? arr.apply(null, arguments)
      : obj.apply(null, arguments)
  }
}

module.exports = makeCollectionMethod
<commit_msg>update collection/_make -- use function/apply<commit_after>var isInteger = require('../lang/isInteger')
var apply = require('../function/apply')

function makeCollectionMethod (arr, obj, str, rex, def) {
  if (arguments.length === 2) {
    return function (list) {
      return (isInteger(list.length))
      ? apply(arr, null, arguments)
      : apply(obj, null, arguments)
    }
  }

  return function (list) {
    // this will be an excellent use of the spread operator.
    // will be able to get rid of apply
    if (list == null) return def
    if (typeof list === 'string') return apply(str, null, arguments)
    if (list instanceof RegExp) return apply(rex, null, arguments)

    return (isInteger(list.length))
      ? apply(arr, null, arguments)
      : apply(obj, null, arguments)
  }
}

module.exports = makeCollectionMethod
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import '../styles/TodoRegister.css';

const ENTER = 13;

class TodoRegister extends Component {
  constructor(){
    super();
    this.state = {
      newTodo: ""
    };
  }

  onChange = (e) => {
    this.setState({
      newTodo: e.target.value
    });
  }

  onClickButton = () => {
    this._registerTodo();
  }

  onEnter = (e) => {
    if (e.which === ENTER){
      this._registerTodo();
    }
  }

  _registerTodo = () => {
    let newTodoData = {
      completed: false,
      registeredDate: new Date(),
      completedDate: null,
      content: this.state.newTodo
    };
    this.props.onRegister(newTodoData);
  }

  render(){
    return (

        <input placeholder="오늘의 할 일을 추가해주세요" type="text" value={this.state.newTodo}
          onChange={this.onChange} onKeyUp={this.onEnter}/>
        <button type="button" name="add-todo-button" onClick={this.onClickButton}>+</button>

    );
  }
}

export default TodoRegister;
<commit_msg>[04] todo를 등록한 후에는 입력창을 비웁니다<commit_after>import React, { Component } from 'react';
import '../styles/TodoRegister.css';

const ENTER = 13;

class TodoRegister extends Component {
  constructor(){
    super();
    this.state = {
      newTodo: ""
    };
  }

  onChange = (e) => {
    this.setState({
      newTodo: e.target.value
    });
  }

  onClickButton = () => {
    this._registerTodo();
  }

  onEnter = (e) => {
    if (e.which === ENTER){
      this._registerTodo();
    }
  }

  _registerTodo = () => {
    let newTodoData = {
      completed: false,
      registeredDate: new Date(),
      completedDate: null,
      content: this.state.newTodo
    };
    this.props.onRegister(newTodoData);
    this._resetRegister();
  }

  _resetRegister = () => {
    this.setState({
      newTodo: ""
    });
  }

  render(){
    return (

        <input placeholder="오늘의 할 일을 추가해주세요" type="text" value={this.state.newTodo}
          onChange={this.onChange} onKeyUp={this.onEnter}/>
        <button type="button" name="add-todo-button" onClick={this.onClickButton}>+</button>

    );
  }
}

export default TodoRegister;
<|endoftext|>


<commit_before>/*
Copyright 2011 Justin Obara

Licensed under the MIT License. You may not use this file except
in compliance with this License.
*/

// Declare Dependencies
/*global setTimeout, clearTimout, tt, jQuery*/

var tt = tt || {};

(function ($) {

    var startTimer = function (that) {
        return setTimeout(that.events.afterTimeFinished.fire, 60000);
    };

    var calculateWPMStats = function (that) {
        var sampleText = that.toArray(that.sampleText);
        var typedText = that.toArray(that.locate("input").val());
        var errors = that.compare(sampleText, typedText);
        var adjustedWPM = that.calculateWPM(typedText.length, errors.length, 1);

        return {
            WPM: typedText.length,
            errors: errors.length,
            adjustedWPM: adjustedWPM
        };
    };

    var displayWPM = function (that, WPM) {
        that.locate("WPMScore").text(WPM);
    };

    var bindStartEvent = function (that) {
        that.locate("input").bind("keyup.tt-start", function (event) {
            that.events.afterStarted.fire(event.which);
        });
    };

    var bindCancelEvent = function (that) {
        that.locate("input").bind("blur.tt-cancel", function (event) {
            that.cancel();
        });
    };

    var bindEvents = function (that) {
        bindStartEvent(that);
    };

    var setup = function (that) {
        bindEvents(that);
        that.fetchText(that.options.texts[0].url);
    };

    fluid.registerNamespace("tt.typingTest");

    tt.typingTest.stringToArray = function (str) {
        return str.split(/\s/);
    };

    tt.typingTest.compareStringArrays = function (baseArray, comparisonArray) {
        var differences = [];

        for (var i = 0; i < comparisonArray.length; i++) {
            var baseString = baseArray[i];
            var comparisonString = comparisonArray[i];

            if (baseString !== comparisonString) {
                differences.push({
                    position: i,
                    expected: baseString,
                    actual: comparisonString
                });
            }
        }

        return differences;
    };

    tt.typingTest.wordsPerMinute = function (numWords, numErrors, numMinutes) {
        return Math.max((numWords - numErrors) / numMinutes, 0);
    };

    tt.typingTest.defaultNotification = function (WPMStats) {
        alert("Your WPM is " + WPMStats.adjustedWPM + "\n\n" + "WPM: " + WPMStats.WPM + "\nErrors: " + WPMStats.errors + "\nAdjusted WPM: " + WPMStats.adjustedWPM);
    };

    tt.typingTest.getText = function (pathToFile, success, error) {
        $.ajax({
            url: pathToFile,
            dataType: "text",
            success: success,
            error: error
        });
    };

    tt.typingTest.preInit = function (that) {
        that.resetTest = function () {
            var textInput = that.locate("input");
            textInput.val("");
            textInput.removeAttr("disabled");
            textInput.unbind("blur.tt-cancel");
            bindStartEvent(that);
        };

        that.cancel = function () {
            clearTimeout(that.timerID);
            that.resetTest();
        };

        that.fetchText = function (pathToFile) {
            that.getText(pathToFile, that.events.afterTextFetched.fire);
        };

        that.renderText = function (text) {
            that.sampleText = text || "";
            that.locate("sampleText").text(that.sampleText);
        };

        that.start = function () {
            that.locate("input").unbind("keyup.tt-start");
            bindCancelEvent(that);
            that.timerID = startTimer(that);
        };

        that.finish = function () {
            that.locate("input").attr("disabled", true);
            var WPMStats = calculateWPMStats(that);
            displayWPM(that, WPMStats.adjustedWPM);
            that.notify(WPMStats);
            that.cancel();
        };
    };

    tt.typingTest.finalInit = function (that) {
        setup(that);
    };

    fluid.defaults("tt.typingTest", {
        gradeNames: ["fluid.viewComponent", "autoInit"],
        preInitFunction: "tt.typingTest.preInit",
        finalInitFunction: "tt.typingTest.finalInit",

        invokers: {
            notify: "tt.typingTest.defaultNotification",
            toArray: "tt.typingTest.stringToArray",
            compare: "tt.typingTest.compareStringArrays",
            calculateWPM: "tt.typingTest.wordsPerMinute",
            getText: "tt.typingTest.getText"
        },

        selectors: {
            sampleText: ".tt-typingTest-sampleText",
            input: ".tt-typingTest-input",
            WPMScore: ".tt-typingTest-WMPScore"
        },

        listeners: {
            afterTextFetched: "{tt.typingTest}.renderText",
            afterStarted: "{tt.typingTest}.start",
            afterTimeFinished: "{tt.typingTest}.finish"
        },

        events: {
            afterTextFetched: null,
            afterStarted: null,
            afterTimeFinished: null
        },

        texts: [
            {
                name: "Macbeth",
                url: "../text/Macbeth.txt"
            },
            {
                name: "A Tale of Two Cities",
                url: "../text/A_Tale_of_Two_Cities.txt"
            },
            {
                name: "The Wonderful Wizard of Oz",
                url: "../text/The_Wonderful_Wizard_of_Oz.txt"
            }
        ]
    });
})(jQuery);
<commit_msg>GH-14 : Dropped the bindEvents function<commit_after>/*
Copyright 2011 Justin Obara

Licensed under the MIT License. You may not use this file except
in compliance with this License.
*/

// Declare Dependencies
/*global setTimeout, clearTimout, tt, jQuery*/

var tt = tt || {};

(function ($) {

    var startTimer = function (that) {
        return setTimeout(that.events.afterTimeFinished.fire, 60000);
    };

    var calculateWPMStats = function (that) {
        var sampleText = that.toArray(that.sampleText);
        var typedText = that.toArray(that.locate("input").val());
        var errors = that.compare(sampleText, typedText);
        var adjustedWPM = that.calculateWPM(typedText.length, errors.length, 1);

        return {
            WPM: typedText.length,
            errors: errors.length,
            adjustedWPM: adjustedWPM
        };
    };

    var displayWPM = function (that, WPM) {
        that.locate("WPMScore").text(WPM);
    };

    var bindStartEvent = function (that) {
        that.locate("input").bind("keyup.tt-start", function (event) {
            that.events.afterStarted.fire(event.which);
        });
    };

    var bindCancelEvent = function (that) {
        that.locate("input").bind("blur.tt-cancel", function (event) {
            that.cancel();
        });
    };

    var setup = function (that) {
        bindStartEvent(that);
        that.fetchText(that.options.texts[0].url);
    };

    fluid.registerNamespace("tt.typingTest");

    tt.typingTest.stringToArray = function (str) {
        return str.split(/\s/);
    };

    tt.typingTest.compareStringArrays = function (baseArray, comparisonArray) {
        var differences = [];

        for (var i = 0; i < comparisonArray.length; i++) {
            var baseString = baseArray[i];
            var comparisonString = comparisonArray[i];

            if (baseString !== comparisonString) {
                differences.push({
                    position: i,
                    expected: baseString,
                    actual: comparisonString
                });
            }
        }

        return differences;
    };

    tt.typingTest.wordsPerMinute = function (numWords, numErrors, numMinutes) {
        return Math.max((numWords - numErrors) / numMinutes, 0);
    };

    tt.typingTest.defaultNotification = function (WPMStats) {
        alert("Your WPM is " + WPMStats.adjustedWPM + "\n\n" + "WPM: " + WPMStats.WPM + "\nErrors: " + WPMStats.errors + "\nAdjusted WPM: " + WPMStats.adjustedWPM);
    };

    tt.typingTest.getText = function (pathToFile, success, error) {
        $.ajax({
            url: pathToFile,
            dataType: "text",
            success: success,
            error: error
        });
    };

    tt.typingTest.preInit = function (that) {
        that.resetTest = function () {
            var textInput = that.locate("input");
            textInput.val("");
            textInput.removeAttr("disabled");
            textInput.unbind("blur.tt-cancel");
            bindStartEvent(that);
        };

        that.cancel = function () {
            clearTimeout(that.timerID);
            that.resetTest();
        };

        that.fetchText = function (pathToFile) {
            that.getText(pathToFile, that.events.afterTextFetched.fire);
        };

        that.renderText = function (text) {
            that.sampleText = text || "";
            that.locate("sampleText").text(that.sampleText);
        };

        that.start = function () {
            that.locate("input").unbind("keyup.tt-start");
            bindCancelEvent(that);
            that.timerID = startTimer(that);
        };

        that.finish = function () {
            that.locate("input").attr("disabled", true);
            var WPMStats = calculateWPMStats(that);
            displayWPM(that, WPMStats.adjustedWPM);
            that.notify(WPMStats);
            that.cancel();
        };
    };

    tt.typingTest.finalInit = function (that) {
        setup(that);
    };

    fluid.defaults("tt.typingTest", {
        gradeNames: ["fluid.viewComponent", "autoInit"],
        preInitFunction: "tt.typingTest.preInit",
        finalInitFunction: "tt.typingTest.finalInit",

        invokers: {
            notify: "tt.typingTest.defaultNotification",
            toArray: "tt.typingTest.stringToArray",
            compare: "tt.typingTest.compareStringArrays",
            calculateWPM: "tt.typingTest.wordsPerMinute",
            getText: "tt.typingTest.getText"
        },

        selectors: {
            sampleText: ".tt-typingTest-sampleText",
            input: ".tt-typingTest-input",
            WPMScore: ".tt-typingTest-WMPScore"
        },

        listeners: {
            afterTextFetched: "{tt.typingTest}.renderText",
            afterStarted: "{tt.typingTest}.start",
            afterTimeFinished: "{tt.typingTest}.finish"
        },

        events: {
            afterTextFetched: null,
            afterStarted: null,
            afterTimeFinished: null
        },

        texts: [
            {
                name: "Macbeth",
                url: "../text/Macbeth.txt"
            },
            {
                name: "A Tale of Two Cities",
                url: "../text/A_Tale_of_Two_Cities.txt"
            },
            {
                name: "The Wonderful Wizard of Oz",
                url: "../text/The_Wonderful_Wizard_of_Oz.txt"
            }
        ]
    });
})(jQuery);
<|endoftext|>


<commit_before>import React, {PureComponent} from 'react'
import {connect} from 'react-redux'
import CSSModules from 'react-css-modules'
import PropTypes from 'prop-types'

import Selector from '../selector'
import Action from '../action'
import style from './player-audio.css'

function mapStateToProps(state) {
  return {
    id: state.song.playing.id,
    file: Selector.playingFile(state).url,
    time: state.song.playing.time,
    running: state.player.running,
    volume: state.player.volume,
    reload: state.player.reload
  }
}

function mapDispatchToProps(dispatch) {
  return {
    dispatchRunning(running) {
      dispatch(Action.Player.running.create(running))
    },
    dispatchElapsed(elapsed) {
      dispatch(Action.Player.elapsed.create(elapsed))
    },
    dispatchDuration(duration) {
      dispatch(Action.Player.duration.create(duration))
    },
    dispatchEnded() {
      dispatch(Action.Player.ended.create())
    },
    dispatchReloaded() {
      dispatch(Action.Player.reload.create(false))
    }
  }
}

@connect(mapStateToProps, mapDispatchToProps)
@CSSModules(style)
export default class PlayerAudio extends PureComponent {
  static propTypes = {
    id: PropTypes.string,
    file: PropTypes.string,
    time: PropTypes.number,
    running: PropTypes.bool,
    volume: PropTypes.number,
    reload: PropTypes.bool,
    dispatchRunning: PropTypes.func,
    dispatchElapsed: PropTypes.func,
    dispatchDuration: PropTypes.func,
    dispatchEnded: PropTypes.func,
    dispatchReloaded: PropTypes.func
  }

  setAudioState(url, time) {
    const {audio} = this.refs
    if (url) {
      audio.src = url
      if (time) audio.currentTime = (Date.now() / 1000) - time
      audio.play()
    } else {
      audio.removeAttribute('src')
    }
  }

  onAudioEvent = (event) => {
    const {audio} = this.refs
    switch (event.type) {
      case 'playing':
        this.props.dispatchRunning(true)
        break
      case 'pause':
        this.props.dispatchRunning(false)
        break
      case 'timeupdate':
        this.props.dispatchElapsed(audio.currentTime)
        this.props.dispatchDuration(audio.duration)
        break
      case 'ended':
        this.setAudioState(null)
        this.props.dispatchEnded()
        break
      case 'error':
        this.setAudioState(null)
        this.setAudioState(this.props.file, this.props.time)
        break
    }
  }

  componentDidMount() {
    const {audio} = this.refs
    for (const type of [
      'playing', 'pause', 'timeupdate', 'ended', 'error'
    ]) audio.addEventListener(type, this.onAudioEvent)
    audio.volume = this.props.volume
    this.setAudioState(this.props.file, this.props.time)
  }

  componentWillUnmount() {
    const {audio} = this.refs
    for (const type of [
      'playing', 'pause', 'timeupdate', 'ended', 'error'
    ]) audio.removeEventListener(type, this.onAudioEvent)
  }

  shouldComponentUpdate(nextProps, nextState) {
    const {audio} = this.refs
    if (this.props.volume != nextProps.volume) {
      audio.volume = nextProps.volume
    }
    if (nextProps.reload) {
      this.setAudioState(null)
      this.setAudioState(nextProps.file, nextProps.time)
      nextProps.dispatchReloaded()
    } else if (this.props.id != nextProps.id
      || Math.abs(this.props.time - nextProps.time) > 10) {
      this.setAudioState(nextProps.file, nextProps.time)
    }
    return false
  }

  render() {
    return (

        <audio ref='audio' autoPlay={true}/>

    )
  }
}
<commit_msg>remove unused prop<commit_after>import React, {PureComponent} from 'react'
import {connect} from 'react-redux'
import CSSModules from 'react-css-modules'
import PropTypes from 'prop-types'

import Selector from '../selector'
import Action from '../action'
import style from './player-audio.css'

function mapStateToProps(state) {
  return {
    id: state.song.playing.id,
    time: state.song.playing.time,
    file: Selector.playingFile(state).url,
    volume: state.player.volume,
    reload: state.player.reload
  }
}

function mapDispatchToProps(dispatch) {
  return {
    dispatchRunning(running) {
      dispatch(Action.Player.running.create(running))
    },
    dispatchElapsed(elapsed) {
      dispatch(Action.Player.elapsed.create(elapsed))
    },
    dispatchDuration(duration) {
      dispatch(Action.Player.duration.create(duration))
    },
    dispatchEnded() {
      dispatch(Action.Player.ended.create())
    },
    dispatchReloaded() {
      dispatch(Action.Player.reload.create(false))
    }
  }
}

@connect(mapStateToProps, mapDispatchToProps)
@CSSModules(style)
export default class PlayerAudio extends PureComponent {
  static propTypes = {
    id: PropTypes.string,
    time: PropTypes.number,
    file: PropTypes.string,
    volume: PropTypes.number,
    reload: PropTypes.bool,
    dispatchRunning: PropTypes.func,
    dispatchElapsed: PropTypes.func,
    dispatchDuration: PropTypes.func,
    dispatchEnded: PropTypes.func,
    dispatchReloaded: PropTypes.func
  }

  setAudioState(url, time) {
    const {audio} = this.refs
    if (url) {
      audio.src = url
      if (time) audio.currentTime = (Date.now() / 1000) - time
      audio.play()
    } else {
      audio.removeAttribute('src')
    }
  }

  onAudioEvent = (event) => {
    const {audio} = this.refs
    switch (event.type) {
      case 'playing':
        this.props.dispatchRunning(true)
        break
      case 'pause':
        this.props.dispatchRunning(false)
        break
      case 'timeupdate':
        this.props.dispatchElapsed(audio.currentTime)
        this.props.dispatchDuration(audio.duration)
        break
      case 'ended':
        this.setAudioState(null)
        this.props.dispatchEnded()
        break
      case 'error':
        this.setAudioState(null)
        this.setAudioState(this.props.file, this.props.time)
        break
    }
  }

  componentDidMount() {
    const {audio} = this.refs
    for (const type of [
      'playing', 'pause', 'timeupdate', 'ended', 'error'
    ]) audio.addEventListener(type, this.onAudioEvent)
    audio.volume = this.props.volume
    this.setAudioState(this.props.file, this.props.time)
  }

  componentWillUnmount() {
    const {audio} = this.refs
    for (const type of [
      'playing', 'pause', 'timeupdate', 'ended', 'error'
    ]) audio.removeEventListener(type, this.onAudioEvent)
  }

  shouldComponentUpdate(nextProps, nextState) {
    const {audio} = this.refs
    if (this.props.volume != nextProps.volume) {
      audio.volume = nextProps.volume
    }
    if (nextProps.reload) {
      this.setAudioState(null)
      this.setAudioState(nextProps.file, nextProps.time)
      nextProps.dispatchReloaded()
    } else if (this.props.id != nextProps.id
      || Math.abs(this.props.time - nextProps.time) > 10) {
      this.setAudioState(nextProps.file, nextProps.time)
    }
    return false
  }

  render() {
    return (

        <audio ref='audio' autoPlay={true}/>

    )
  }
}
<|endoftext|>


<commit_before>const React = require('react');
const Radium = require('radium');

const Button = require('./Button');

const StyleConstants = require('../constants/Style');

const { buttonTypes } = require('../constants/App');

const ButtonGroup = React.createClass({
  propTypes: {
    buttons: React.PropTypes.arrayOf(React.PropTypes.shape({
      icon: React.PropTypes.string,
      onClick: React.PropTypes.func,
      style: React.PropTypes.object,
      text: React.PropTypes.string,
      type: React.PropTypes.oneOf(buttonTypes)
    }).isRequired),
    primaryColor: React.PropTypes.string,
    type: React.PropTypes.oneOf(buttonTypes)
  },

  getDefaultProps () {
    return {
      buttons: [],
      primaryColor: StyleConstants.Colors.PRIMARY,
      type: 'primaryOutline'
    };
  },

  render () {
    const styles = this.styles();

    return (

        {this.props.buttons.map((button, i) => {
          const isFirstChild = i === 0;
          const isLastChild = i === this.props.buttons.length - 1;
          const isOnlyChild = isFirstChild && isLastChild;
          const isDisabled = button.type === 'disabled';

          return (
            <Button
              icon={button.icon}
              key={i}
              onClick={button.onClick}
              primaryColor={this.props.primaryColor}
              style={Object.assign({},
                styles.component,
                isFirstChild && styles.firstChild,
                isLastChild && styles.lastChild,
                isOnlyChild && styles.onlyChild,
                isDisabled && styles.disabled,
                button.style)}
              type={this.props.type}
            >
              {button.text}
            </Button>
          );
        })}

    );
  },

  styles () {
    return {
      component: Object.assign({
        boxSizing: 'border-box',
        borderRadius: 0,
        borderWidth: 1,
        borderRightWidth: this.props.type === 'base' ? 1 : 0,
        verticalAlign: 'middle'
      }, this.props.style),
      firstChild: {
        borderRadius: '2px 0 0 2px'
      },
      lastChild: {
        borderRadius: '0 2px 2px 0',
        borderRightWidth: 1
      },
      onlyChild: {
        borderRadius: 2,
        borderWidth: 1
      },
      disabled: {
        backgroundColor: 'transparent',
        color: StyleConstants.Colors.FOG,
        cursor: 'default',
        fill: StyleConstants.Colors.FOG,
        ':hover': {
          backgroundColor: 'transparent'
        },
        ':active': {
          backgroundColor: 'transparent'
        }
      }
    };
  }
});

module.exports = Radium(ButtonGroup);
<commit_msg>disables onClick for disabled buttons<commit_after>const React = require('react');
const Radium = require('radium');

const Button = require('./Button');

const StyleConstants = require('../constants/Style');

const { buttonTypes } = require('../constants/App');

const ButtonGroup = React.createClass({
  propTypes: {
    buttons: React.PropTypes.arrayOf(React.PropTypes.shape({
      icon: React.PropTypes.string,
      onClick: React.PropTypes.func,
      style: React.PropTypes.object,
      text: React.PropTypes.string,
      type: React.PropTypes.oneOf(buttonTypes)
    }).isRequired),
    primaryColor: React.PropTypes.string,
    type: React.PropTypes.oneOf(buttonTypes)
  },

  getDefaultProps () {
    return {
      buttons: [],
      primaryColor: StyleConstants.Colors.PRIMARY,
      type: 'primaryOutline'
    };
  },

  render () {
    const styles = this.styles();

    return (

        {this.props.buttons.map((button, i) => {
          const isFirstChild = i === 0;
          const isLastChild = i === this.props.buttons.length - 1;
          const isOnlyChild = isFirstChild && isLastChild;
          const isDisabled = button.type === 'disabled';

          return (
            <Button
              icon={button.icon}
              key={i}
              onClick={!isDisabled && button.onClick}
              primaryColor={this.props.primaryColor}
              style={Object.assign({},
                styles.component,
                isFirstChild && styles.firstChild,
                isLastChild && styles.lastChild,
                isOnlyChild && styles.onlyChild,
                isDisabled && styles.disabled,
                button.style)}
              type={this.props.type}
            >
              {button.text}
            </Button>
          );
        })}

    );
  },

  styles () {
    return {
      component: Object.assign({
        boxSizing: 'border-box',
        borderRadius: 0,
        borderWidth: 1,
        borderRightWidth: this.props.type === 'base' ? 1 : 0,
        verticalAlign: 'middle'
      }, this.props.style),
      firstChild: {
        borderRadius: '2px 0 0 2px'
      },
      lastChild: {
        borderRadius: '0 2px 2px 0',
        borderRightWidth: 1
      },
      onlyChild: {
        borderRadius: 2,
        borderWidth: 1
      },
      disabled: {
        backgroundColor: 'transparent',
        color: StyleConstants.Colors.FOG,
        cursor: 'default',
        fill: StyleConstants.Colors.FOG,
        ':hover': {
          backgroundColor: 'transparent'
        },
        ':active': {
          backgroundColor: 'transparent'
        }
      }
    };
  }
});

module.exports = Radium(ButtonGroup);
<|endoftext|>


<commit_before>enyo.kind({
	name: "analyzer.Analyzer",
	kind: "Component",
	debug: false,
	events: {
		onIndexReady: ""
	},
	create: function() {
		this.index = new analyzer.Indexer();
		this.inherited(arguments);
	},
	/**
	 * Analyze all the items of inPaths and add the result to the
	 * index database.
	 * @param inPaths: is either an array of strings or an array of
	 * objects with path and label fields.  If labels are
	 * provided, the objects from the analysis for that path
	 * are tagged with a label property
	 * @param inPathResolver: optional parameter similar to enyo.path
	 * needed only if enyo, onyx, ... to analyze are not the one used
	 * by the current application (e.g: Ares case).
	 * @protected
	 */
	analyze: function(inPaths, inPathResolver) {
		this.walk(inPaths, inPathResolver);
	},
	/**
	 * Walk over the inPaths items and analyze them
	 * @param inPaths: is either an array of strings or an array of
	 * objects with path and label fields.  If labels are
	 * provided, the objects from the analysis for that path
	 * are tagged with a label property
	 * @param inPathResolver: optional parameter similar to enyo.path
	 * needed only if enyo, onyx, ... to analyze are not the one used
	 * by the current application (e.g: Ares case).
	 * @protected
	 */
	walk: function(inPaths, inPathResolver) {
		var modules = [];
		var designs = [];
		var currentLabel;
		var next = function(inSender, inData) {
			if (inData) {
				if (this.debug) {
					enyo.log("analyzer.Analyzer.walk.next() - inData: ", inData);
				}
				for (var i = 0; i < inData.modules.length; ++i) {
					inData.modules[i].label = currentLabel;
				}
				modules = modules.concat(inData.modules);
				designs = designs.concat(inData.designs);
			}
			var path = inPaths.shift();
			if (path) {
				if (this.debug) {
					enyo.log("analyzer.Analyzer.walk.next() - path: " + path);
				}
				if (!enyo.isString(path)) {
					currentLabel = path.label;
					path = path.path;
				}
				new analyzer.Walker().walk(path, inPathResolver).response(this, next);
			} else {
				this.walkFinished(modules, designs);
			}
		};
		next.call(this);
	},
	//* @protected
	walkFinished: function(inModules, inDesigns) {
		this.read(inModules, inDesigns);
	},
	//* @protected
	read: function(inModules, inDesigns) {
		new analyzer.Reader()
			.go({modules: inModules, designs: inDesigns})
			.response(this, function(inSender, inData) {
				this.indexModules(inData.modules);
				this.indexDesigns(inData.designs);
				this.doIndexReady();
			})
		;
	},
	//* @protected
	indexModules: function(inModules) {
		this.index.addModules(inModules);
	},
	//* @protected
	indexDesigns: function(inDesigns) {
		this.index.addDesigns(inDesigns);
	}
});
<commit_msg>ENYO-2470: transform exception coming from indexer into Error events<commit_after>enyo.kind({
	name: "analyzer.Analyzer",
	kind: "Component",
	debug: false,
	events: {
		onIndexReady: "",
		onError: ""
	},
	create: function() {
		this.index = new analyzer.Indexer();
		this.inherited(arguments);
	},
	/**
	 * Analyze all the items of inPaths and add the result to the
	 * index database.
	 * @param inPaths: is either an array of strings or an array of
	 * objects with path and label fields.  If labels are
	 * provided, the objects from the analysis for that path
	 * are tagged with a label property
	 * @param inPathResolver: optional parameter similar to enyo.path
	 * needed only if enyo, onyx, ... to analyze are not the one used
	 * by the current application (e.g: Ares case).
	 * @protected
	 */
	analyze: function(inPaths, inPathResolver) {
		this.walk(inPaths, inPathResolver);
	},
	/**
	 * Walk over the inPaths items and analyze them
	 * @param inPaths: is either an array of strings or an array of
	 * objects with path and label fields.  If labels are
	 * provided, the objects from the analysis for that path
	 * are tagged with a label property
	 * @param inPathResolver: optional parameter similar to enyo.path
	 * needed only if enyo, onyx, ... to analyze are not the one used
	 * by the current application (e.g: Ares case).
	 * @protected
	 */
	walk: function(inPaths, inPathResolver) {
		var modules = [];
		var designs = [];
		var currentLabel;
		var next = function(inSender, inData) {
			if (inData) {
				if (this.debug) {
					enyo.log("analyzer.Analyzer.walk.next() - inData: ", inData);
				}
				for (var i = 0; i < inData.modules.length; ++i) {
					inData.modules[i].label = currentLabel;
				}
				modules = modules.concat(inData.modules);
				designs = designs.concat(inData.designs);
			}
			var path = inPaths.shift();
			if (path) {
				if (this.debug) {
					enyo.log("analyzer.Analyzer.walk.next() - path: " + path);
				}
				if (!enyo.isString(path)) {
					currentLabel = path.label;
					path = path.path;
				}
				new analyzer.Walker().walk(path, inPathResolver).response(this, next);
			} else {
				this.walkFinished(modules, designs);
			}
		};
		next.call(this);
	},
	//* @protected
	walkFinished: function(inModules, inDesigns) {
		if (this.debug) {
			enyo.log("analyzer.Analyzer.walkFinished called");
		}
		this.read(inModules, inDesigns);
	},
	//* @protected
	read: function(inModules, inDesigns) {
		new analyzer.Reader()
			.go({modules: inModules, designs: inDesigns})
			.response(this, function(inSender, inData) {
				try {
					this.indexModules(inData.modules); // this may throw an error
					this.indexDesigns(inData.designs);
					this.doIndexReady();
				}
				catch (error) {
					this.log("Analysis failed with: ",error.toString() );
					this.doError(error) ;
				}
			})
		;
	},
	//* @protected
	indexModules: function(inModules) {
		this.index.addModules(inModules);
	},
	//* @protected
	indexDesigns: function(inDesigns) {
		this.index.addDesigns(inDesigns);
	}
});
<|endoftext|>


<commit_before>/*
	angular-photoswipe v0.0.4
	(c) 2015 Massimiliano Sartoretto <massimilianosartoretto@gmail.com>
	License: MIT
*/

'format amd';
/* global define */

(function () {
  'use strict';

  function ngPhotoswipe(angular, Photoswipe) {

    return angular
      .module('ngPhotoswipe', [])
      .directive('ngPhotoswipe', ngPhotoswipeDirective);

    function ngPhotoswipeDirective($compile, $http, $templateCache) {
      return {
        restrict: 'AE',
        replace: true,
        scope: {
          slides: '=',
          options: '=',
          template: '@',
          open: '=',//lets us put a watch on when the open changes'
          onClose: '&',
          extensions:'=?'
        },
        link: linkFn
      };

      function linkFn(scope, iElement, iAttrs) {
        scope.template = scope.template || 'views/ng-photoswipe.html';
        $http
          .get(scope.template, { cache: $templateCache })
          .success(function(html) {
            var template = angular.element(html);
            iElement.append($compile(template)(scope));
          });

        //var gallery;
        scope.start = function () {
            scope.open = true;
            startGallery();
        };
        var startGallery = function () {
            var pswpElement = document.querySelectorAll('.pswp')[0];
            scope.gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default || false, scope.slides, scope.options);
            scope.gallery.init();
            scope.item = scope.gallery.currItem;
            scope.gallery.listen('destroy', function () {
                if (typeof scope.onClose === 'function') {
                    scope.onClose();
                }

            });
            scope.gallery.listen('afterChange', function () {

                scope.item = scope.gallery.currItem;
                if (!scope.$$phase) {
                    scope.$apply();
                }

            });
        }
        scope.$watch('open', function (nval, oval) {
            if (nval != oval) {
                if (nval) {
                    startGallery();
                }
            } else if (!nval && scope.gallery) {
                //close it
                scope.gallery.close();
                scope.gallery = null;
            }
        });
        scope.$on('destroy', function () {
            scope.gallery = null;
        });

      }
    }
  }

  if (typeof define === 'function' && define.amd) {
		define(['angular', 'Photoswipe'], ngPhotoswipe);
	} else if (typeof module !== 'undefined' && module && module.exports) {
		ngPhotoswipe(angular, require('Photoswipe'));
		module.exports = 'ngPhotoswipe';
	} else {
		ngPhotoswipe(angular, (typeof global !== 'undefined' ? global : window).Photoswipe);
	}
})();
<commit_msg>(chore) Code cleanup<commit_after>/*
	angular-photoswipe v0.0.4
	(c) 2015 Massimiliano Sartoretto <massimilianosartoretto@gmail.com>
	License: MIT
*/

'format amd';
/* global define */

(function () {
  'use strict';

  function ngPhotoswipe(angular, Photoswipe) {

    return angular
      .module('ngPhotoswipe', [])
      .directive('ngPhotoswipe', ngPhotoswipeDirective);

    function ngPhotoswipeDirective($compile, $http, $templateCache) {
      return {
        restrict: 'AE',
        replace: true,
        scope: {
          slides: '=',
          options: '=',
          template: '@',
          open: '=',
          onClose: '&',
          extensions:'=?'
        },
        link: linkFn
      };

      function linkFn(scope, iElement, iAttrs) {
        scope.template = scope.template || 'views/ng-photoswipe.html';

        $http
          .get(scope.template, { cache: $templateCache })
          .success(function(html) {
            var template = angular.element(html);
            iElement.append($compile(template)(scope));
          });

        scope.start = function () {
          scope.open = true;
          startGallery();
        };

        var startGallery = function () {
          var pswpElement = document.querySelectorAll('.pswp')[0];
          scope.gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default || false, scope.slides, scope.options);
          scope.gallery.init();
          scope.item = scope.gallery.currItem;

          scope.gallery.listen('destroy', function () {
            if (typeof scope.onClose === 'function') {
              scope.onClose();
            }
          });

          scope.gallery.listen('afterChange', function () {
            scope.item = scope.gallery.currItem;
            if (!scope.$$phase) {
              scope.$apply();
            }
          });
        }

        scope.$watch('open', function (nVal, oVal) {
          if (nVal != oVal) {
            if (nVal) {
              startGallery();
            }
          } else if (!nVal && scope.gallery) {
            scope.gallery.close();
            scope.gallery = null;
          }
        });

        scope.safeApply = function(fn) {
          var phase = this.$root.$$phase;
          if(phase == '$apply' || phase == '$digest') {
            if(fn && (typeof(fn) === 'function')) {
              fn();
            }
          } else {
            this.$apply(fn);
          }
        };

        scope.$on('destroy', function () {
            scope.gallery = null;
        });
      }
    }
  }

  if (typeof define === 'function' && define.amd) {
		define(['angular', 'Photoswipe'], ngPhotoswipe);
	} else if (typeof module !== 'undefined' && module && module.exports) {
		ngPhotoswipe(angular, require('Photoswipe'));
		module.exports = 'ngPhotoswipe';
	} else {
		ngPhotoswipe(angular, (typeof global !== 'undefined' ? global : window).Photoswipe);
	}
})();
<|endoftext|>


<commit_before>import './Image.scss';
import React, { Component } from 'react';
import AssetSchema from '../schemas/asset';
import classNames from 'classnames';

class Image extends Component {
  constructor(props, context) {
    super(props, context);
    this.state = {
      removed: false,
    };
  }

  handleImageRemoved() {
    this.setState({ removed: true });
  }

  wrapIcon(icon) {
    return (

        {icon}

    );
  }

  renderServiceIcon() {
    if (['instagram', 'twitter'].indexOf(this.props.service) >= 0) {
      const className = classNames('icon', `icon-${this.props.service}`);
      const icon = ;
      return this.props.importUrl ? this.wrapIcon(icon) : icon;
    }
  }

  render() {
    const className = classNames('image', {
      '-removed': this.state.removed,
    });
    return (

            <img src={`${this.props.url}/311x313`} onError={() => this.handleImageRemoved()}/>

          {this.props.author.username}
          {this.renderServiceIcon()}

{this.props.caption}

    );
  }
}

Image.propTypes = AssetSchema;

export default Image;
<commit_msg>Rename wrapIcon to wrapServiceIcon.<commit_after>import './Image.scss';
import React, { Component } from 'react';
import AssetSchema from '../schemas/asset';
import classNames from 'classnames';

class Image extends Component {
  constructor(props, context) {
    super(props, context);
    this.state = {
      removed: false,
    };
  }

  handleImageRemoved() {
    this.setState({ removed: true });
  }

  wrapServiceIcon(icon) {
    return (

        {icon}

    );
  }

  renderServiceIcon() {
    if (['instagram', 'twitter'].indexOf(this.props.service) >= 0) {
      const className = classNames('icon', `icon-${this.props.service}`);
      const icon = ;
      return this.props.importUrl ? this.wrapServiceIcon(icon) : icon;
    }
  }

  render() {
    const className = classNames('image', {
      '-removed': this.state.removed,
    });
    return (

            <img src={`${this.props.url}/311x313`} onError={() => this.handleImageRemoved()}/>

          {this.props.author.username}
          {this.renderServiceIcon()}

{this.props.caption}

    );
  }
}

Image.propTypes = AssetSchema;

export default Image;
<|endoftext|>


<commit_before>import React from 'react';

import FileDropArea from './FileDropArea';
import PreviewImage from './PreviewImage';
import FileTransferButtons from './FileTransferButtons';
import OptionSettingForm from './OptionSettingForm';

const CanvasExifOrientation = require('canvas-exif-orientation');

const ALLOW_FILE_TYPES = ['image/png', 'image/jpeg'];

const ORIENTATION_NUMBER = {
  0: 1,
  90: 6,
  180: 3,
  270: 8,
};

const applyNegativeFilter = currentCanvas => {
  const ctx = currentCanvas.getContext('2d');
  const src = ctx.getImageData(0, 0, currentCanvas.width, currentCanvas.height);
  const dst = ctx.createImageData(currentCanvas.width, currentCanvas.height);
  for (let i = 0; i < src.data.length; i += 4) {
    dst.data[i] = 255 - src.data[i]; // R
    dst.data[i + 1] = 255 - src.data[i + 1]; // G
    dst.data[i + 2] = 255 - src.data[i + 2]; // B
    dst.data[i + 3] = src.data[i + 3]; // A
  }
  ctx.putImageData(dst, 0, 0);
  return currentCanvas;
};

const resizeImage = (currentCanvas, resizeRatio) => {
  const dstCanvas = document.createElement('canvas');
  const ctx = dstCanvas.getContext('2d');
  dstCanvas.width = currentCanvas.width * resizeRatio;
  dstCanvas.height = currentCanvas.height * resizeRatio;
  ctx.drawImage(currentCanvas, 0, 0, dstCanvas.width, dstCanvas.height);
  return dstCanvas;
};

const rotateImage = (currentCanvas, angle) => {
  const orietationNumber = ORIENTATION_NUMBER[angle];
  return CanvasExifOrientation.drawImage(currentCanvas, orietationNumber);
};

const autoDownload = (url, fileName) => {
  const elem = document.createElement('a');
  elem.href = url;
  elem.download = fileName;
  elem.click();
};

const isAllowedFileType = (uploadedFileType, allowList) => {
  const result = allowList.filter(allowFile => allowFile === uploadedFileType);
  return result.length > 0;
};

export default class ImageEditor extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      userSettings: {
        resizeRatio: 0.5,
        rotateAngle: 0,
      },
      uploadImageDataUrl: null,
      previewImageDataUrl: null,
      downloadImageFileName: null,
      fileMime: null,
      allowAutoDownload: true,
      errorMessage: null,
      isProcessing: false,
    };
    this.onImageSelected = this.onImageSelected.bind(this);
    this.onImageLoad = this.onImageLoad.bind(this);
  }

  onImageLoad(imageDataUrl, originalFileName, originalFileMime) {
    this.setState({
      uploadImageDataUrl: imageDataUrl,
      downloadImageFileName: originalFileName,
      fileMime: originalFileMime,
    });
    this.processImage(this.state.userSettings);
  }

  onImageSelected(fileList) {
    const file = fileList[0];
    if (!file) return; // ファイルアップロードのダイアログでキャンセルした場合の対応
    if (!isAllowedFileType(file.type, ALLOW_FILE_TYPES)) {
      this.setState({errorMessage: '対応しているファイル形式はjpegとpngのみです。'});
      return;
    }
    const fileReader = new FileReader();
    fileReader.onload = () => {
      this.onImageLoad(fileReader.result, file.name, file.type);
    };
    fileReader.readAsDataURL(file);
  }

  processImage(userSettings) {
    this.setState({
      isProcessing: true,
      previewImageDataUrl: null,
      errorMessage: null,
    });
    const {rotateAngle, resizeRatio} = userSettings;
    this.generateUploadedImageCanvas()
      .then(res => rotateImage(res, rotateAngle))
      .then(res => applyNegativeFilter(res))
      .then(res => resizeImage(res, resizeRatio))
      .then(res => {
        this.setState({
          previewImageDataUrl: res.toDataURL(this.state.fileMime),
          isProcessing: false,
        });
        if (!this.state.allowAutoDownload) return;
        autoDownload(
          this.state.previewImageDataUrl,
          this.state.downloadImageFileName
        );
      });
  }

  generateUploadedImageCanvas() {
    return new Promise(resolve => {
      const image = new Image();
      image.onload = () => {
        const canvas = document.createElement('canvas');
        canvas.width = image.width;
        canvas.height = image.height;
        const ctx = canvas.getContext('2d');
        ctx.drawImage(image, 0, 0, image.width, image.height);
        resolve(canvas);
      };
      image.src = this.state.uploadImageDataUrl;
    });
  }

  changeUserSettings(key, value) {
    const newUserSettings = Object.assign({}, this.state.userSettings, {
      [key]: value,
    });
    this.setState({userSettings: newUserSettings});
    if (!this.state.uploadImageDataUrl) return;
    this.processImage(newUserSettings);
  }

  render() {
    const {
      previewImageDataUrl,
      downloadImageFileName,
      allowAutoDownload,
      errorMessage,
      isProcessing,
    } = this.state;
    const {resizeRatio, rotateAngle} = this.state.userSettings;
    return (

        <FileTransferButtons
          onImageSelected={e => {
            this.onImageSelected(e.target.files);
          }}
          previewImageDataUrl={previewImageDataUrl}
          downloadImageFileName={downloadImageFileName}
        />

画像にドロップすることでも、新しい画像をアップロードできます。

        <OptionSettingForm
          resizeRatio={resizeRatio}
          rotateAngle={rotateAngle}
          allowAutoDownload={allowAutoDownload}
          onChangeSelect={(e, stateName) => {
            const {options} = e.target;
            this.changeUserSettings(
              stateName,
              +options[options.selectedIndex].value
            );
          }}
          onChangeAllowAutoDownload={e => {
            this.setState({allowAutoDownload: e.target.checked});
          }}
        />
        {errorMessage &&
{errorMessage}
}
        {!previewImageDataUrl &&
        !isProcessing && <FileDropArea onDrop={this.onImageSelected} />}
        {isProcessing &&
画像生成中……
}
        {previewImageDataUrl && (
          <PreviewImage
            src={previewImageDataUrl}
            onDrop={this.onImageSelected}
          />
        )}

    );
  }
}
<commit_msg>グレースケールを実装。<commit_after>import React from 'react';

import FileDropArea from './FileDropArea';
import PreviewImage from './PreviewImage';
import FileTransferButtons from './FileTransferButtons';
import OptionSettingForm from './OptionSettingForm';

const CanvasExifOrientation = require('canvas-exif-orientation');

const ALLOW_FILE_TYPES = ['image/png', 'image/jpeg'];

const ORIENTATION_NUMBER = {
  0: 1,
  90: 6,
  180: 3,
  270: 8,
};

const applyNegativeFilter = currentCanvas => {
  const ctx = currentCanvas.getContext('2d');
  const src = ctx.getImageData(0, 0, currentCanvas.width, currentCanvas.height);
  const dst = ctx.createImageData(currentCanvas.width, currentCanvas.height);
  for (let i = 0; i < src.data.length; i += 4) {
    dst.data[i] = 255 - src.data[i]; // R
    dst.data[i + 1] = 255 - src.data[i + 1]; // G
    dst.data[i + 2] = 255 - src.data[i + 2]; // B
    dst.data[i + 3] = src.data[i + 3]; // A
  }
  ctx.putImageData(dst, 0, 0);
  return currentCanvas;
};

const applyGrayscaleFilter = currentCanvas => {
  const ctx = currentCanvas.getContext('2d');
  const src = ctx.getImageData(0, 0, currentCanvas.width, currentCanvas.height);
  const dst = ctx.createImageData(currentCanvas.width, currentCanvas.height);
  for (let i = 0; i < src.data.length; i += 4) {
    const pixel = (src.data[i] + src.data[i + 1] + src.data[i + 2]) / 3;
    dst.data[i] = pixel;
    dst.data[i + 1] = pixel;
    dst.data[i + 2] = pixel;
    dst.data[i + 3] = src.data[i + 3];
  }
  ctx.putImageData(dst, 0, 0);
  return currentCanvas;
};

const resizeImage = (currentCanvas, resizeRatio) => {
  const dstCanvas = document.createElement('canvas');
  const ctx = dstCanvas.getContext('2d');
  dstCanvas.width = currentCanvas.width * resizeRatio;
  dstCanvas.height = currentCanvas.height * resizeRatio;
  ctx.drawImage(currentCanvas, 0, 0, dstCanvas.width, dstCanvas.height);
  return dstCanvas;
};

const rotateImage = (currentCanvas, angle) => {
  const orietationNumber = ORIENTATION_NUMBER[angle];
  return CanvasExifOrientation.drawImage(currentCanvas, orietationNumber);
};

const autoDownload = (url, fileName) => {
  const elem = document.createElement('a');
  elem.href = url;
  elem.download = fileName;
  elem.click();
};

const isAllowedFileType = (uploadedFileType, allowList) => {
  const result = allowList.filter(allowFile => allowFile === uploadedFileType);
  return result.length > 0;
};

export default class ImageEditor extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      userSettings: {
        resizeRatio: 0.5,
        rotateAngle: 0,
      },
      uploadImageDataUrl: null,
      previewImageDataUrl: null,
      downloadImageFileName: null,
      fileMime: null,
      allowAutoDownload: true,
      errorMessage: null,
      isProcessing: false,
    };
    this.onImageSelected = this.onImageSelected.bind(this);
    this.onImageLoad = this.onImageLoad.bind(this);
  }

  onImageLoad(imageDataUrl, originalFileName, originalFileMime) {
    this.setState({
      uploadImageDataUrl: imageDataUrl,
      downloadImageFileName: originalFileName,
      fileMime: originalFileMime,
    });
    this.processImage(this.state.userSettings);
  }

  onImageSelected(fileList) {
    const file = fileList[0];
    if (!file) return; // ファイルアップロードのダイアログでキャンセルした場合の対応
    if (!isAllowedFileType(file.type, ALLOW_FILE_TYPES)) {
      this.setState({errorMessage: '対応しているファイル形式はjpegとpngのみです。'});
      return;
    }
    const fileReader = new FileReader();
    fileReader.onload = () => {
      this.onImageLoad(fileReader.result, file.name, file.type);
    };
    fileReader.readAsDataURL(file);
  }

  processImage(userSettings) {
    this.setState({
      isProcessing: true,
      previewImageDataUrl: null,
      errorMessage: null,
    });
    const {rotateAngle, resizeRatio} = userSettings;
    this.generateUploadedImageCanvas()
      .then(res => rotateImage(res, rotateAngle))
      .then(res => applyNegativeFilter(res))
      .then(res => applyGrayscaleFilter(res))
      .then(res => resizeImage(res, resizeRatio))
      .then(res => {
        this.setState({
          previewImageDataUrl: res.toDataURL(this.state.fileMime),
          isProcessing: false,
        });
        if (!this.state.allowAutoDownload) return;
        autoDownload(
          this.state.previewImageDataUrl,
          this.state.downloadImageFileName
        );
      });
  }

  generateUploadedImageCanvas() {
    return new Promise(resolve => {
      const image = new Image();
      image.onload = () => {
        const canvas = document.createElement('canvas');
        canvas.width = image.width;
        canvas.height = image.height;
        const ctx = canvas.getContext('2d');
        ctx.drawImage(image, 0, 0, image.width, image.height);
        resolve(canvas);
      };
      image.src = this.state.uploadImageDataUrl;
    });
  }

  changeUserSettings(key, value) {
    const newUserSettings = Object.assign({}, this.state.userSettings, {
      [key]: value,
    });
    this.setState({userSettings: newUserSettings});
    if (!this.state.uploadImageDataUrl) return;
    this.processImage(newUserSettings);
  }

  render() {
    const {
      previewImageDataUrl,
      downloadImageFileName,
      allowAutoDownload,
      errorMessage,
      isProcessing,
    } = this.state;
    const {resizeRatio, rotateAngle} = this.state.userSettings;
    return (

        <FileTransferButtons
          onImageSelected={e => {
            this.onImageSelected(e.target.files);
          }}
          previewImageDataUrl={previewImageDataUrl}
          downloadImageFileName={downloadImageFileName}
        />

画像にドロップすることでも、新しい画像をアップロードできます。

        <OptionSettingForm
          resizeRatio={resizeRatio}
          rotateAngle={rotateAngle}
          allowAutoDownload={allowAutoDownload}
          onChangeSelect={(e, stateName) => {
            const {options} = e.target;
            this.changeUserSettings(
              stateName,
              +options[options.selectedIndex].value
            );
          }}
          onChangeAllowAutoDownload={e => {
            this.setState({allowAutoDownload: e.target.checked});
          }}
        />
        {errorMessage &&
{errorMessage}
}
        {!previewImageDataUrl &&
        !isProcessing && <FileDropArea onDrop={this.onImageSelected} />}
        {isProcessing &&
画像生成中……
}
        {previewImageDataUrl && (
          <PreviewImage
            src={previewImageDataUrl}
            onDrop={this.onImageSelected}
          />
        )}

    );
  }
}
<|endoftext|>


<commit_before>import React from 'react';

import FileTransferButtons from './FileTransferButtons';

const RESIZE_RATIO = 0.5;
const ALLOW_FILE_TYPES = ['image/png', 'image/jpeg'];

const autoDownload = (url, fileName) => {
  const elem = document.createElement('a');
  elem.href = url;
  elem.download = fileName;
  elem.click();
};

const isAllowedFileType = (uploadedFileType, allowList) => {
  const result = allowList.filter(allowFile => allowFile === uploadedFileType);
  return !!(result.length > 0) || false;
};

const resizeImage = (imageDataUrl, mime, ratio) =>
  new Promise(resolve => {
    const image = new Image();
    image.onload = () => {
      const deformedWidth = image.width * ratio;
      const deformedHeight = image.height * ratio;
      const canvas = document.createElement('canvas');
      canvas.width = deformedWidth;
      canvas.height = deformedHeight;
      const ctx = canvas.getContext('2d');
      ctx.drawImage(image, 0, 0, deformedWidth, deformedHeight);
      resolve(canvas.toDataURL(mime));
    };
    image.src = imageDataUrl;
  });

export default class ImageEditor extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      previewImageDataUrl: null,
      downloadImageFileName: null,
      errorMessage: null,
    };
    this.onImageSelected = this.onImageSelected.bind(this);
    this.onImageLoad = this.onImageLoad.bind(this);
  }

  onImageLoad(imageDataUrl, originalFileName, originalFileMime) {
    resizeImage(imageDataUrl, originalFileMime, RESIZE_RATIO).then(res => {
      this.setState({
        previewImageDataUrl: res,
        /* prettier-ignore */
        downloadImageFileName: originalFileName,
        errorMessage: null,
      });
      autoDownload(
        this.state.previewImageDataUrl,
        this.state.downloadImageFileName
      );
    });
  }

  onImageSelected(e) {
    const file = e.target.files[0];
    if (!isAllowedFileType(file.type, ALLOW_FILE_TYPES)) {
      this.setState({errorMessage: '対応しているファイル形式はjpegとpngのみです。'});
      return;
    }
    const fileReader = new FileReader();
    fileReader.onload = () => {
      this.onImageLoad(fileReader.result, file.name, file.type);
    };
    fileReader.readAsDataURL(file);
  }

  render() {
    const {
      previewImageDataUrl,
      downloadImageFileName,
      errorMessage,
    } = this.state;
    return (

        <FileTransferButtons
          onImageSelected={this.onImageSelected}
          previewImageDataUrl={previewImageDataUrl}
          downloadImageFileName={downloadImageFileName}
        />
        {errorMessage &&
{errorMessage}
}

    );
  }
}
<commit_msg>不要な記述を削除。<commit_after>import React from 'react';

import FileTransferButtons from './FileTransferButtons';

const RESIZE_RATIO = 0.5;
const ALLOW_FILE_TYPES = ['image/png', 'image/jpeg'];

const autoDownload = (url, fileName) => {
  const elem = document.createElement('a');
  elem.href = url;
  elem.download = fileName;
  elem.click();
};

const isAllowedFileType = (uploadedFileType, allowList) => {
  const result = allowList.filter(allowFile => allowFile === uploadedFileType);
  return !!(result.length > 0);
};

const resizeImage = (imageDataUrl, mime, ratio) =>
  new Promise(resolve => {
    const image = new Image();
    image.onload = () => {
      const deformedWidth = image.width * ratio;
      const deformedHeight = image.height * ratio;
      const canvas = document.createElement('canvas');
      canvas.width = deformedWidth;
      canvas.height = deformedHeight;
      const ctx = canvas.getContext('2d');
      ctx.drawImage(image, 0, 0, deformedWidth, deformedHeight);
      resolve(canvas.toDataURL(mime));
    };
    image.src = imageDataUrl;
  });

export default class ImageEditor extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      previewImageDataUrl: null,
      downloadImageFileName: null,
      errorMessage: null,
    };
    this.onImageSelected = this.onImageSelected.bind(this);
    this.onImageLoad = this.onImageLoad.bind(this);
  }

  onImageLoad(imageDataUrl, originalFileName, originalFileMime) {
    resizeImage(imageDataUrl, originalFileMime, RESIZE_RATIO).then(res => {
      this.setState({
        previewImageDataUrl: res,
        /* prettier-ignore */
        downloadImageFileName: originalFileName,
        errorMessage: null,
      });
      autoDownload(
        this.state.previewImageDataUrl,
        this.state.downloadImageFileName
      );
    });
  }

  onImageSelected(e) {
    const file = e.target.files[0];
    if (!isAllowedFileType(file.type, ALLOW_FILE_TYPES)) {
      this.setState({errorMessage: '対応しているファイル形式はjpegとpngのみです。'});
      return;
    }
    const fileReader = new FileReader();
    fileReader.onload = () => {
      this.onImageLoad(fileReader.result, file.name, file.type);
    };
    fileReader.readAsDataURL(file);
  }

  render() {
    const {
      previewImageDataUrl,
      downloadImageFileName,
      errorMessage,
    } = this.state;
    return (

        <FileTransferButtons
          onImageSelected={this.onImageSelected}
          previewImageDataUrl={previewImageDataUrl}
          downloadImageFileName={downloadImageFileName}
        />
        {errorMessage &&
{errorMessage}
}

    );
  }
}
<|endoftext|>