<commit_before>import syllable from 'syllable';
import _ from 'lodash';
import wordnet from 'wordnet';
var $ = require('jquery-deferred');
import stopwords from './data/stopwords.js';
import descriptors from './data/descriptors.js';
import cusseth from './data/cusseth.js';
import nouns from './data/nouns.js';

let adjectives = descriptors.map(word => [word, syllable(word)]);
let curses = cusseth.map(word => [word, syllable(word)]);

const HAIKU_LENGTH = 17;
const separator = '//';
const FOUL = true;

// Chrys' Domain
function addModifier(phrase, length, mod) {
  let tokens = phrase.split(/\W/);
  let nounList = tokens.map(word => _.contains(nouns, word));

  for (var i in nounList) {
    if (nounList[i]) {
      tokens.splice(i, 0, mod);
      return tokens.join(' ');
    }
  }

  return mod + ' ' + phrase;
}

export function addExpletive(phrase, length) {
  var curse = _.sample(curses.filter(tuple => tuple[1] === length));
  if (!curse) {
    return addExpletive(addExpletive(phrase, length - 1), 1);
  }

  return addModifier(phrase, length, curse[0]);
}

export function addAdjective(phrase, length) {
  var adj = _.sample(adjectives.filter(tuple => tuple[1] === length));
  if (!adj) {
    return addAdjective(addAdjective(phrase, length - 1), 1);
  }

  return addModifier(phrase, length, adj[0]);
}

export function removeWords(phrase, length) {
  length *= -1;
  let tokens = phrase.split(/\W/);
  let newTokens = [];
  tokens.forEach(function(token) {
    if (length && _.contains(stopwords, token)) {
      length -= syllable(token);
    } else {
      newTokens.push(token);
    }
  });

  return newTokens.join(' ');
}

function addWords(phrase, length) {
  if (FOUL) {
    return addExpletive(phrase, length);
  } else {
    return addAdjective(phrase, length);
  }
}

// Nicole WUZ HERE
export function shortenWord(word, adjustment) {
  let possibleNewWord = [];
  let theNewWord;
  let newWordSyllables;
  let adjustedList;
  let otheradjustednotasgoodList;
  let currentWordSyllables = syllable(word);

  adjustment = -adjustment;

  let list = []

  var response = possibleSynonymArray(word);

  if (!_.isEmpty(response)){
    for (let x=0; x<response.length; x++) {
      list[x] = [response[x], syllable(response[x])]
    }

    adjustedList = list.filter(synonym => synonym[1] - currentWordSyllables === adjustment);

    if (!_.isEmpty(adjustedList)) {
      return adjustedList[0][0];
    }

    else {
      otheradjustednotasgoodList = list.filter(synonym => (synonym[1] - currentWordSyllables > adjustment) && (synonym[1] < currentWordSyllables)).sort(function(a, b) {return a[1]-b[1]});
    }

    if (!_.isEmpty(otheradjustednotasgoodList)) {
      return otheradjustednotasgoodList[0][0]
    } else {
      return word;
    }
  } else {
    return word;
  }

}

function possibleSynonymArray(word) {
  if (thesaurus[word]) {
    return thesaurus[word];
  }
}

function possibleSynonymArrayAsync(word) {
  var promise = $.Deferred();

  var possibleNewWords = [];
  wordnet.lookup(word, function(err, definitions) {
    if (definitions) {
      definitions.forEach(function(definition) {
        var words = definition.meta.words;
        words.forEach(function(word) {
          if (!_.includes(possibleNewWords, word.word)) {
            possibleNewWords.push(word.word);
          }
        });
      });
    }
    promise.resolve(possibleNewWords);
  });
  return promise;
}

export function expandWord(word, adjustment) {
  let possibleNewWord = [];
  let theNewWord;
  let newWordSyllables;
  let adjustedList;
  let otheradjustednotasgoodList;
  let currentWordSyllables = syllable(word);
  let list = []

  var response = possibleSynonymArray(word);

  if (!_.isEmpty(response)){
    for (let x=0; x<response.length; x++) {
      list[x] = [response[x], syllable(response[x])]
    }

    adjustedList = list.filter(synonym => synonym[1] - currentWordSyllables === adjustment);
    if (!_.isEmpty(adjustedList)) {
      return adjustedList[0][0];
    } else {
      otheradjustednotasgoodList = list.filter(synonym => (synonym[1] - currentWordSyllables < adjustment) && (synonym[1] > currentWordSyllables)).sort(function(a, b) {return b[1]-a[1]});
    }

    if (!_.isEmpty(otheradjustednotasgoodList)) {
      return otheradjustednotasgoodList[0][0]
    } else {
      return word;
    }
  } else {
    return word;
  }
}

let countSyllables = function(phrase) {
  let tokens = phrase.split(/\W/);
  return _.sum(tokens.map(syllable));
};

function adjustWord(word, adjustment) {
  return word;

  if (adjustment > 0) {
    return expandWord(word, adjustment);
  } else if (adjustment < 0) {
    return shortenWord(word, adjustment);
  } else {
    return word;
  }
}

export function adjustPhrase(phrase, adjustment) {
  if (adjustment > 0) {
    return addWords(phrase, adjustment);
  } else if (adjustment < 0) {
    return removeWords(phrase, adjustment);
  } else {
    return phrase;
  }
}

function adjustSyllables(lines, adjustment) {
  let line = 0;
  let idx = 0;
  let token = lines[line][idx];

  // First loop to make small adjustments to existing words' lengths.
  while (adjustment !== 0 && line < 3) {
    let newWord = adjustWord(token, adjustment);

    if (newWord) {
      lines[line][idx] = newWord;
      adjustment -= (syllable(newWord) - syllable(token));
    }

    idx++;
    token = lines[line][idx];
    if (!token) {
      line++;
      idx = 0;
      if (line > 2) {
        break;
      }
      token = lines[line][idx];

    }
  }

  var joined = lines.map(line => line.join(' '));
  line = 0;

  // Second while loop to add / remove extraneous words
  while (adjustment !== 0 && line < 3) {
    let oldPhrase = joined[line];
    let lineCount = countSyllables(oldPhrase);

    if (lineCount === (line === 1 ? 7 : 5)) {
      line++;
    } else {
      var newPhrase;
      var maxLineAdjustment = (line === 1) ? 7 : 5;
      var lineAdjustment;

      if (adjustment < 0) {
        maxLineAdjustment *= -1;
        lineAdjustment = adjustment < maxLineAdjustment ?  maxLineAdjustment : adjustment;
        lineAdjustment += lineCount;
      } else {
        lineAdjustment = adjustment > maxLineAdjustment ? maxLineAdjustment : adjustment;
        lineAdjustment -= lineCount;
      }

      newPhrase = adjustPhrase(oldPhrase, lineAdjustment);

      joined[line] = newPhrase;
      adjustment -= (countSyllables(newPhrase) - countSyllables(oldPhrase));
      line++;
    }

  }

  return joined.join(' ' + separator + ' ');
}

export function isHaiku(msg) {
  return countSyllables(msg, separator) === HAIKU_LENGTH;
}

export function breakIntoHaiku(msg) {
  var tokens = msg.trim().split(/\W/);
  var rows = [[], [], []];
  var rowSyllables = 0;
  var line = 0;
  while (tokens.length) {
    let token = tokens.shift();
    let tokenLength = syllable(token);

    if (line === 0) {
      var diff = (rowSyllables + tokenLength) - 5;
      if (diff > 0) {
        line += 1;
        rowSyllables = 0;
      }

    } else if (line === 1) {

      var diff = (rowSyllables + tokenLength) - 7;
      if (diff > 0) {
        line += 1;
        rowSyllables = 0;
      }

    }

    rowSyllables += tokenLength;
    rows[line].push(token);

  }

  return rows;
}

/**
 * steps to check if it is a haiku
 * 1. check syllable length of commit
 * 2. if longer - > check for haikus in commit..
 *  a) if haiku -> print done !
 *  b) if not -> get most of haiku and continue? choose key words and continue?
 * 3. if shorter -> check syllable counts?
 * for what is missing -> fill in rhyme etc. to make up length ?? nonsensical ??
 */
 export function makeHaiku(msg) {
  let syllables = countSyllables(msg);
  let difference = HAIKU_LENGTH - syllables;

  msg = breakIntoHaiku(msg);
  return adjustSyllables(msg, difference);
}<commit_msg>added new feature // fierce one incorrigible  // incorrigible<commit_after>import syllable from 'syllable';
import _ from 'lodash';
import wordnet from 'wordnet';
var $ = require('jquery-deferred');
import stopwords from './data/stopwords.js';
import descriptors from './data/descriptors.js';
import cusseth from './data/cusseth.js';
import nouns from './data/nouns.js';

let adjectives = descriptors.map(word => [word, syllable(word)]);
let curses = cusseth.map(word => [word, syllable(word)]);

const HAIKU_LENGTH = 17;
const separator = '//';
const FOUL = false;

// Chrys' Domain
function addModifier(phrase, length, mod) {
  let tokens = phrase.split(/\W/);
  let nounList = tokens.map(word => _.contains(nouns, word));

  for (var i in nounList) {
    if (nounList[i]) {
      tokens.splice(i, 0, mod);
      return tokens.join(' ');
    }
  }

  return mod + ' ' + phrase;
}

export function addExpletive(phrase, length) {
  var curse = _.sample(curses.filter(tuple => tuple[1] === length));
  if (!curse) {
    return addExpletive(addExpletive(phrase, length - 1), 1);
  }

  return addModifier(phrase, length, curse[0]);
}

export function addAdjective(phrase, length) {
  var adj = _.sample(adjectives.filter(tuple => tuple[1] === length));
  if (!adj) {
    return addAdjective(addAdjective(phrase, length - 1), 1);
  }

  return addModifier(phrase, length, adj[0]);
}

export function removeWords(phrase, length) {
  length *= -1;
  let tokens = phrase.split(/\W/);
  let newTokens = [];
  tokens.forEach(function(token) {
    if (length && _.contains(stopwords, token)) {
      length -= syllable(token);
    } else {
      newTokens.push(token);
    }
  });

  return newTokens.join(' ');
}

function addWords(phrase, length) {
  if (FOUL) {
    return addExpletive(phrase, length);
  } else {
    return addAdjective(phrase, length);
  }
}

// Nicole WUZ HERE
export function shortenWord(word, adjustment) {
  let possibleNewWord = [];
  let theNewWord;
  let newWordSyllables;
  let adjustedList;
  let otheradjustednotasgoodList;
  let currentWordSyllables = syllable(word);

  adjustment = -adjustment;

  let list = []

  var response = possibleSynonymArray(word);

  if (!_.isEmpty(response)){
    for (let x=0; x<response.length; x++) {
      list[x] = [response[x], syllable(response[x])]
    }

    adjustedList = list.filter(synonym => synonym[1] - currentWordSyllables === adjustment);

    if (!_.isEmpty(adjustedList)) {
      return adjustedList[0][0];
    }

    else {
      otheradjustednotasgoodList = list.filter(synonym => (synonym[1] - currentWordSyllables > adjustment) && (synonym[1] < currentWordSyllables)).sort(function(a, b) {return a[1]-b[1]});
    }

    if (!_.isEmpty(otheradjustednotasgoodList)) {
      return otheradjustednotasgoodList[0][0]
    } else {
      return word;
    }
  } else {
    return word;
  }

}

function possibleSynonymArray(word) {
  if (thesaurus[word]) {
    return thesaurus[word];
  }
}

function possibleSynonymArrayAsync(word) {
  var promise = $.Deferred();

  var possibleNewWords = [];
  wordnet.lookup(word, function(err, definitions) {
    if (definitions) {
      definitions.forEach(function(definition) {
        var words = definition.meta.words;
        words.forEach(function(word) {
          if (!_.includes(possibleNewWords, word.word)) {
            possibleNewWords.push(word.word);
          }
        });
      });
    }
    promise.resolve(possibleNewWords);
  });
  return promise;
}

export function expandWord(word, adjustment) {
  let possibleNewWord = [];
  let theNewWord;
  let newWordSyllables;
  let adjustedList;
  let otheradjustednotasgoodList;
  let currentWordSyllables = syllable(word);
  let list = []

  var response = possibleSynonymArray(word);

  if (!_.isEmpty(response)){
    for (let x=0; x<response.length; x++) {
      list[x] = [response[x], syllable(response[x])]
    }

    adjustedList = list.filter(synonym => synonym[1] - currentWordSyllables === adjustment);
    if (!_.isEmpty(adjustedList)) {
      return adjustedList[0][0];
    } else {
      otheradjustednotasgoodList = list.filter(synonym => (synonym[1] - currentWordSyllables < adjustment) && (synonym[1] > currentWordSyllables)).sort(function(a, b) {return b[1]-a[1]});
    }

    if (!_.isEmpty(otheradjustednotasgoodList)) {
      return otheradjustednotasgoodList[0][0]
    } else {
      return word;
    }
  } else {
    return word;
  }
}

let countSyllables = function(phrase) {
  let tokens = phrase.split(/\W/);
  return _.sum(tokens.map(syllable));
};

function adjustWord(word, adjustment) {
  return word;

  if (adjustment > 0) {
    return expandWord(word, adjustment);
  } else if (adjustment < 0) {
    return shortenWord(word, adjustment);
  } else {
    return word;
  }
}

export function adjustPhrase(phrase, adjustment) {
  if (adjustment > 0) {
    return addWords(phrase, adjustment);
  } else if (adjustment < 0) {
    return removeWords(phrase, adjustment);
  } else {
    return phrase;
  }
}

function adjustSyllables(lines, adjustment) {
  let line = 0;
  let idx = 0;
  let token = lines[line][idx];

  // First loop to make small adjustments to existing words' lengths.
  while (adjustment !== 0 && line < 3) {
    let newWord = adjustWord(token, adjustment);

    if (newWord) {
      lines[line][idx] = newWord;
      adjustment -= (syllable(newWord) - syllable(token));
    }

    idx++;
    token = lines[line][idx];
    if (!token) {
      line++;
      idx = 0;
      if (line > 2) {
        break;
      }
      token = lines[line][idx];

    }
  }

  var joined = lines.map(line => line.join(' '));
  line = 0;

  // Second while loop to add / remove extraneous words
  while (adjustment !== 0 && line < 3) {
    let oldPhrase = joined[line];
    let lineCount = countSyllables(oldPhrase);

    if (lineCount === (line === 1 ? 7 : 5)) {
      line++;
    } else {
      var newPhrase;
      var maxLineAdjustment = (line === 1) ? 7 : 5;
      var lineAdjustment;

      if (adjustment < 0) {
        maxLineAdjustment *= -1;
        lineAdjustment = adjustment < maxLineAdjustment ?  maxLineAdjustment : adjustment;
        lineAdjustment += lineCount;
      } else {
        lineAdjustment = adjustment > maxLineAdjustment ? maxLineAdjustment : adjustment;
        lineAdjustment -= lineCount;
      }

      newPhrase = adjustPhrase(oldPhrase, lineAdjustment);

      joined[line] = newPhrase;
      adjustment -= (countSyllables(newPhrase) - countSyllables(oldPhrase));
      line++;
    }

  }

  return joined.join(' ' + separator + ' ');
}

export function isHaiku(msg) {
  return countSyllables(msg, separator) === HAIKU_LENGTH;
}

export function breakIntoHaiku(msg) {
  var tokens = msg.trim().split(/\W/);
  var rows = [[], [], []];
  var rowSyllables = 0;
  var line = 0;
  while (tokens.length) {
    let token = tokens.shift();
    let tokenLength = syllable(token);

    if (line === 0) {
      var diff = (rowSyllables + tokenLength) - 5;
      if (diff > 0) {
        line += 1;
        rowSyllables = 0;
      }

    } else if (line === 1) {

      var diff = (rowSyllables + tokenLength) - 7;
      if (diff > 0) {
        line += 1;
        rowSyllables = 0;
      }

    }

    rowSyllables += tokenLength;
    rows[line].push(token);

  }

  return rows;
}

/**
 * steps to check if it is a haiku
 * 1. check syllable length of commit
 * 2. if longer - > check for haikus in commit..
 *  a) if haiku -> print done !
 *  b) if not -> get most of haiku and continue? choose key words and continue?
 * 3. if shorter -> check syllable counts?
 * for what is missing -> fill in rhyme etc. to make up length ?? nonsensical ??
 */
 export function makeHaiku(msg) {
  let syllables = countSyllables(msg);
  let difference = HAIKU_LENGTH - syllables;

  msg = breakIntoHaiku(msg);
  return adjustSyllables(msg, difference);
}<|endoftext|>


<commit_before>import EVENTS from '../events.js';
import external from '../externalModules.js';
// State
import { getters } from './../store/index.js';
// Import anyHandlesOutsideImage from '../manipulators/anyHandlesOutsideImage.js';
import getHandleNearImagePoint from '../manipulators/getHandleNearImagePoint.js';
import touchMoveHandle from '../manipulators/touchMoveHandle.js';
import moveNewHandleTouch from '../manipulators/moveNewHandleTouch.js';
import touchMoveAllHandles from '../manipulators/touchMoveAllHandles.js';
import {
  addToolState,
  // RemoveToolState,
  getToolState
} from '../stateManagement/toolState.js';
import triggerEvent from '../util/triggerEvent.js';
// Todo: Where should these live?
import getActiveToolsForElement from './../store/getActiveToolsForElement.js';
import getInteractiveToolsForElement from './../store/getInteractiveToolsForElement.js';
import getToolsWithDataForElement from './../store/getToolsWithDataForElement.js';

const cornerstone = external.cornerstone;
// Todo: better place for this
// Tap, Start, StartActive
let isAwaitingTouchUp = false;

/**
 * These listeners are emitted in order, and can be cancelled/prevented from bubbling
 * by any previous event.
 * - tap:
 * - touchStart: check to see if we are close to an existing annotation, grab it
 * - touchDrag:
 * - touchStartActive: createNewMeasurement (usually)
 * - touchPress:
 * - doubleTap: usually a one-time apply specialty action
 * - onImageRendered: redraw visible tool data
 * @param {*} element
 */
const enable = function (element) {
  console.log('enableTouch', element);
  element.addEventListener(EVENTS.TAP, tap);
  element.addEventListener(EVENTS.TOUCH_START, touchStart);
  element.addEventListener(EVENTS.TOUCH_DRAG, touchDrag);
  element.addEventListener(EVENTS.TOUCH_END, touchEnd);
  // Mouse equivelant is `mouse_down_activate`
  // Should the naming pattern here match?
  element.addEventListener(EVENTS.TOUCH_START_ACTIVE, touchStartActive);
  element.addEventListener(EVENTS.TOUCH_PRESS, touchPress);
  element.addEventListener(EVENTS.DOUBLE_TAP, doubleTap);
  element.addEventListener(EVENTS.TOUCH_PINCH, touchPinch);
  element.addEventListener(EVENTS.IMAGE_RENDERED, onImageRendered);
};

function tap (evt) {
  console.log('tap');
  if (isAwaitingTouchUp) {
    return;
  }

  let tools;
  const distanceFromHandle = 28;
  const element = evt.detail.element;
  const coords = evt.detail.currentPoints.canvas;

  tools = getActiveToolsForElement(element, getters.touchTools());
  tools = getToolsWithDataForElement(element, tools);

  // Deactivate everything
  // DeactivateAllToolInstances(toolData);

  // Find all tools w/ handles that we are near
  const toolsWithMoveableHandles = tools.filter((tool) => {
    const toolState = getToolState(element, tool.name);

    for (let i = 0; i < toolState.data.length; i++) {
      if (
        getHandleNearImagePoint(
          element,
          toolState.data[i].handles,
          coords,
          distanceFromHandle
        ) !== undefined
      ) {
        return true;
      }
    }

    return false;
  });

  // TODO: More than one? Which one was moved most recently?
  // We'll just grab the first one we encounter for now
  if (toolsWithMoveableHandles.length > 0) {
    // Todo: ignore: touch_start, tap

    const firstToolWithMoveableHandles = toolsWithMoveableHandles[0];
    const toolState = getToolState(element, firstToolWithMoveableHandles.name);
    const moveableHandle = toolState.data.find(
      (d) =>
        getHandleNearImagePoint(
          element,
          d.handles,
          coords,
          distanceFromHandle
        ) !== undefined
    );

    toolState.data.active = true;
    moveableHandle.active = true; // Why here, but not touchStart?
    cornerstone.updateImage(element);

    touchMoveHandle(
      evt,
      firstToolWithMoveableHandles.name,
      toolState.data,
      moveableHandle,
      () => {
        deactivateAllToolInstances(toolState);
        // If (anyHandlesOutsideImage(eventData, data.handles)) {
        //   // Delete the measurement
        //   RemoveToolState(element, touchToolInterface.toolType, data);
        // }

        cornerstone.updateImage(element);
        // TODO: LISTEN: TAP + TOUCH_START
      }
    );
    evt.stopImmediatePropagation();
    evt.preventDefault();
    // Why no stopPropagation?

    return;
  }

  // Find all tools near our point
  const toolsNearPoint = tools.filter((tool) => {
    const toolState = getToolState(element, tool.name);
    const isNearPoint =
      tool.pointNearTool &&
      toolState.data.some((data) => tool.pointNearTool(element, data, coords));

    return isNearPoint;
  });

  // TODO: More than one? Which one was moved most recently?
  // We'll just grab the first one we encounter for now
  if (toolsNearPoint.length > 0) {
    // Todo: Ignore: TAP, START, PRESS
    const firstToolNearPoint = toolsNearPoint[0];
    const toolState = getToolState(element, firstToolNearPoint.name);
    const firstAnnotationNearPoint = toolState.data.find((data) =>
      firstToolNearPoint.pointNearTool(element, data, coords)
    );

    // Todo: ignore: touch_start, tap
    firstAnnotationNearPoint.active = true;
    cornerstone.updateImage(element);

    touchMoveAllHandles(
      evt,
      firstAnnotationNearPoint,
      toolState,
      firstToolNearPoint.name,
      true,
      () => {
        deactivateAllToolInstances(toolState);
        // If (anyHandlesOutsideImage(eventData, data.handles)) {
        //   // Delete the measurement
        //   RemoveToolState(element, touchToolInterface.toolType, data);
        // }

        cornerstone.updateImage(element);
        // TODO: LISTEN: TAP + TOUCH_START
      }
    );
    evt.stopImmediatePropagation();
    evt.preventDefault();
    // Why no stop propagation?

    return;
  }

  // If there is nothing to move, add a new instance of the tool
  // Need to check here to see if activation is allowed!
  // TODO: What would this be? First active tool?
  // Or should _always_ pass through to our larger event handler that checks
  // All tools anyway?
  const allActiveTools = getActiveToolsForElement(
    element,
    getters.touchTools()
  );

  if (allActiveTools.length > 0 && allActiveTools[0].touchStartActiveCallback) {
    allActiveTools[0].touchStartActiveCallback(evt);
  } else {
    touchStartActive(evt);
  }

  return false;
}

function touchStart (evt) {
  console.log('touchStart');
  if (isAwaitingTouchUp) {
    return;
  }

  let tools;
  const distanceFromHandle = 28;
  const eventData = evt.detail;
  const element = eventData.element;
  const coords = eventData.startPoints.canvas;

  tools = getInteractiveToolsForElement(element, getters.touchTools());
  tools = getToolsWithDataForElement(element, tools);

  // Find all tools w/ handles that we are near
  const toolsWithMoveableHandles = tools.filter((tool) => {
    const toolState = getToolState(element, tool.name);

    for (let i = 0; i < toolState.data.length; i++) {
      if (
        getHandleNearImagePoint(
          eventData.element,
          toolState.data[i].handles,
          coords,
          distanceFromHandle
        ) !== undefined
      ) {
        return true;
      }
    }

    return false;
  });

  // TODO: More than one? Which one was moved most recently?
  // We'll just grab the first one we encounter for now
  if (toolsWithMoveableHandles.length > 0) {
    // Todo: Ignore TAP, START, PRESS

    const firstToolWithMoveableHandles = toolsWithMoveableHandles[0];
    const toolState = getToolState(element, firstToolWithMoveableHandles.name);
    const moveableHandle = toolState.data.find(
      (d) =>
        getHandleNearImagePoint(
          element,
          d.handles,
          coords,
          distanceFromHandle
        ) !== undefined
    );

    toolState.data.active = true;
    touchMoveHandle(
      evt,
      firstToolWithMoveableHandles.name,
      toolState.data,
      moveableHandle,
      () => {} // HandleDoneMove
    );

    evt.stopImmediatePropagation();
    evt.preventDefault();
    evt.stopPropagation();

    return;
  }

  // Find all tools near our point
  const toolsNearPoint = tools.filter((tool) => {
    const toolState = getToolState(element, tool.name);
    const isNearPoint =
      tool.pointNearTool &&
      toolState.data.some((data) => tool.pointNearTool(element, data, coords));

    return isNearPoint;
  });

  // TODO: More than one? Which one was moved most recently?
  // We'll just grab the first one we encounter for now
  if (toolsNearPoint.length > 0) {
    // Todo: Ignore: TAP, START, PRESS
    const firstToolNearPoint = toolsNearPoint[0];
    const toolState = getToolState(element, firstToolNearPoint.name);
    const firstAnnotationNearPoint = toolState.data.find((data) =>
      firstToolNearPoint.pointNearTool(element, data, coords)
    );

    touchMoveAllHandles(
      evt,
      firstAnnotationNearPoint,
      toolState,
      firstToolNearPoint.name,
      true,
      (lastEvent, lastEventData) => {
        firstAnnotationNearPoint.active = false;
        firstAnnotationNearPoint.invalidated = true;
        //   If (anyHandlesOutsideImage(eventData, data.handles)) {
        //     // Delete the measurement
        //     RemoveToolState(
        //       EventData.element,
        //       TouchToolInterface.toolType,
        //       Data
        //     );
        //   }

        cornerstone.updateImage(element);
        // Todo: LISTEN: TAP, START, PRESS

        if (lastEvent && lastEvent.type === EVENTS.TOUCH_PRESS) {
          triggerEvent(element, lastEvent.type, lastEventData);
        }
      }
    );
    evt.stopImmediatePropagation();
    evt.preventDefault();
    evt.stopPropagation();

    return;
  }
}

// TODO:
// Note: some touchDrag tools don't want to fire on touchStart
// Drag tools have an option `fireOnTouchStart` used to filter
// Them out of TOUCH_START handler
// Active: dragTool only
// Or tool has `touchDragCallback` method
function touchDrag (evt) {
  console.log('touchDrag');

  let tools;
  const element = evt.detail.element;

  tools = getActiveToolsForElement(element, getters.touchTools());
  tools = tools.filter((tool) => typeof tool.touchDragCallback === 'function');

  if (tools.length === 0) {
    return;
  }
  const firstActiveTool = tools[0];

  firstActiveTool.touchDragCallback(evt);
}

// Active: dragTool only
// Or tool has `touchEndCallbach` method
function touchEnd (evt) {
  console.log('touchEnd');
}

function touchStartActive (evt) {
  console.log('touchStartActive');
  if (isAwaitingTouchUp) {
    return;
  }

  const element = evt.detail.element;
  const tools = getActiveToolsForElement(element, getters.touchTools());
  const activeTool = tools[0];

  // Note: custom `addNewMeasurement` will need to prevent event bubbling
  if (activeTool.addNewMeasurement) {
    activeTool.addNewMeasurement(evt, 'touch');
  } else if (activeTool.isAnnotationTool) {
    addNewMeasurement(evt, activeTool);
  }
}

function touchPress (evt) {
  // Active?
  console.warn('touchPress not implemented');
}

function doubleTap (evt) {
  // Active?
  console.warn('doubleTap not implemented');
}

function touchPinch (evt) {
  console.log('touchPinch');
  if (isAwaitingTouchUp) {
    return;
  }

  let tools;
  const element = evt.detail.element;

  // Filter out disabled, enabled, and passive
  tools = getActiveToolsForElement(element, getters.touchTools());
  tools = tools.filter((tool) => typeof tool.touchPinchCallback === 'function');

  if (tools.length === 0) {
    return;
  }

  tools[0].touchPinchCallback(evt);
}

function onImageRendered (evt) {
  const eventData = evt.detail;
  const element = eventData.element;

  const toolsToRender = getters.
    touchTools().
    filter(
      (tool) =>
        tool.element === element &&
        (tool.mode === 'active' ||
          tool.mode === 'passive' ||
          tool.mode === 'enabled')
    );

  toolsToRender.forEach((tool) => {
    if (tool.renderToolData) {
      tool.renderToolData(evt);
    }
  });
}

function addNewMeasurement (evt, tool) {
  console.log('touch: addNewMeasurement');
  //
  evt.preventDefault();
  evt.stopPropagation();
  //
  const touchEventData = evt.detail;
  const element = touchEventData.element;
  const measurementData = tool.createNewMeasurement(touchEventData);

  if (!measurementData) {
    return;
  }

  addToolState(element, tool.name, measurementData);

  // Todo: Looks like we're handling the "up" of the tap?
  if (
    Object.keys(measurementData.handles).length === 1 &&
    touchEventData.type === EVENTS.TAP
  ) {
    // Todo: bold assumptions about measurement data for all tools?
    measurementData.active = false;
    measurementData.handles.end.active = false;
    measurementData.handles.end.highlight = false;
    measurementData.invalidated = true;

    // TODO: IFF the tool supports this feature
    // If (anyHandlesOutsideImage(touchEventData, measurementData.handles)) {
    //   // Delete the measurement
    //   RemoveToolState(element, tool.name, measurementData);
    // }

    cornerstone.updateImage(element);

    return;
  }

  isAwaitingTouchUp = true;
  cornerstone.updateImage(element);

  moveNewHandleTouch(
    touchEventData,
    tool.name,
    measurementData,
    measurementData.handles.end,
    function () {
      console.log('addNewMeasurement: touchUp');
      measurementData.active = false;
      measurementData.invalidated = true;
      //   If (anyHandlesOutsideImage(touchEventData, measurementData.handles)) {
      //     // Delete the measurement
      //     RemoveToolState(element, touchToolInterface.toolType, measurementData);
      //   }

      isAwaitingTouchUp = false;
      cornerstone.updateImage(element);
    }
  );
}

export default {
  enable
  // Disable
};

// HELPER - UTILITY
function deactivateAllHandles (handles) {
  Object.keys(handles).forEach(function (name) {
    const handle = handles[name];

    handle.active = false;
  });
}

function deactivateAllToolInstances (toolData) {
  if (!toolData) {
    return;
  }

  for (let i = 0; i < toolData.data.length; i++) {
    const data = toolData.data[i];

    data.active = false;
    if (!data.handles) {
      continue;
    }

    deactivateAllHandles(data.handles);
  }
}
<commit_msg>Fix for grabbing tools by their handles with touch<commit_after>import EVENTS from '../events.js';
import external from '../externalModules.js';
// State
import { getters } from './../store/index.js';
// Import anyHandlesOutsideImage from '../manipulators/anyHandlesOutsideImage.js';
import getHandleNearImagePoint from '../manipulators/getHandleNearImagePoint.js';
import touchMoveHandle from '../manipulators/touchMoveHandle.js';
import moveNewHandleTouch from '../manipulators/moveNewHandleTouch.js';
import touchMoveAllHandles from '../manipulators/touchMoveAllHandles.js';
import {
  addToolState,
  // RemoveToolState,
  getToolState
} from '../stateManagement/toolState.js';
import triggerEvent from '../util/triggerEvent.js';
// Todo: Where should these live?
import getActiveToolsForElement from './../store/getActiveToolsForElement.js';
import getInteractiveToolsForElement from './../store/getInteractiveToolsForElement.js';
import getToolsWithDataForElement from './../store/getToolsWithDataForElement.js';

const cornerstone = external.cornerstone;
// Todo: better place for this
// Tap, Start, StartActive
let isAwaitingTouchUp = false;

/**
 * These listeners are emitted in order, and can be cancelled/prevented from bubbling
 * by any previous event.
 * - tap:
 * - touchStart: check to see if we are close to an existing annotation, grab it
 * - touchDrag:
 * - touchStartActive: createNewMeasurement (usually)
 * - touchPress:
 * - doubleTap: usually a one-time apply specialty action
 * - onImageRendered: redraw visible tool data
 * @param {*} element
 */
const enable = function (element) {
  console.log('enableTouch', element);
  element.addEventListener(EVENTS.TAP, tap);
  element.addEventListener(EVENTS.TOUCH_START, touchStart);
  element.addEventListener(EVENTS.TOUCH_DRAG, touchDrag);
  element.addEventListener(EVENTS.TOUCH_END, touchEnd);
  // Mouse equivelant is `mouse_down_activate`
  // Should the naming pattern here match?
  element.addEventListener(EVENTS.TOUCH_START_ACTIVE, touchStartActive);
  element.addEventListener(EVENTS.TOUCH_PRESS, touchPress);
  element.addEventListener(EVENTS.DOUBLE_TAP, doubleTap);
  element.addEventListener(EVENTS.TOUCH_PINCH, touchPinch);
  element.addEventListener(EVENTS.IMAGE_RENDERED, onImageRendered);
};

function tap (evt) {
  console.log('tap');
  if (isAwaitingTouchUp) {
    return;
  }

  let tools;
  const distanceFromHandle = 28;
  const element = evt.detail.element;
  const coords = evt.detail.currentPoints.canvas;

  tools = getActiveToolsForElement(element, getters.touchTools());
  tools = getToolsWithDataForElement(element, tools);

  // Deactivate everything
  // DeactivateAllToolInstances(toolData);

  // Find all tools w/ handles that we are near
  const toolsWithMoveableHandles = tools.filter((tool) => {
    const toolState = getToolState(element, tool.name);

    for (let i = 0; i < toolState.data.length; i++) {
      if (
        getHandleNearImagePoint(
          element,
          toolState.data[i].handles,
          coords,
          distanceFromHandle
        ) !== undefined
      ) {
        return true;
      }
    }

    return false;
  });

  // TODO: More than one? Which one was moved most recently?
  // We'll just grab the first one we encounter for now
  if (toolsWithMoveableHandles.length > 0) {
    // Todo: ignore: touch_start, tap

    const firstToolWithMoveableHandles = toolsWithMoveableHandles[0];
    const toolState = getToolState(element, firstToolWithMoveableHandles.name);
    const moveableHandle = toolState.data.find(
      (d) =>
        getHandleNearImagePoint(
          element,
          d.handles,
          coords,
          distanceFromHandle
        ) !== undefined
    );

    toolState.data.active = true;
    moveableHandle.active = true; // Why here, but not touchStart?
    cornerstone.updateImage(element);

    touchMoveHandle(
      evt,
      firstToolWithMoveableHandles.name,
      toolState.data,
      moveableHandle,
      () => {
        deactivateAllToolInstances(toolState);
        // If (anyHandlesOutsideImage(eventData, data.handles)) {
        //   // Delete the measurement
        //   RemoveToolState(element, touchToolInterface.toolType, data);
        // }

        cornerstone.updateImage(element);
        // TODO: LISTEN: TAP + TOUCH_START
      }
    );
    evt.stopImmediatePropagation();
    evt.preventDefault();
    // Why no stopPropagation?

    return;
  }

  // Find all tools near our point
  const toolsNearPoint = tools.filter((tool) => {
    const toolState = getToolState(element, tool.name);
    const isNearPoint =
      tool.pointNearTool &&
      toolState.data.some((data) => tool.pointNearTool(element, data, coords));

    return isNearPoint;
  });

  // TODO: More than one? Which one was moved most recently?
  // We'll just grab the first one we encounter for now
  if (toolsNearPoint.length > 0) {
    // Todo: Ignore: TAP, START, PRESS
    const firstToolNearPoint = toolsNearPoint[0];
    const toolState = getToolState(element, firstToolNearPoint.name);
    const firstAnnotationNearPoint = toolState.data.find((data) =>
      firstToolNearPoint.pointNearTool(element, data, coords)
    );

    // Todo: ignore: touch_start, tap
    firstAnnotationNearPoint.active = true;
    cornerstone.updateImage(element);

    touchMoveAllHandles(
      evt,
      firstAnnotationNearPoint,
      toolState,
      firstToolNearPoint.name,
      true,
      () => {
        deactivateAllToolInstances(toolState);
        // If (anyHandlesOutsideImage(eventData, data.handles)) {
        //   // Delete the measurement
        //   RemoveToolState(element, touchToolInterface.toolType, data);
        // }

        cornerstone.updateImage(element);
        // TODO: LISTEN: TAP + TOUCH_START
      }
    );
    evt.stopImmediatePropagation();
    evt.preventDefault();
    // Why no stop propagation?

    return;
  }

  // If there is nothing to move, add a new instance of the tool
  // Need to check here to see if activation is allowed!
  // TODO: What would this be? First active tool?
  // Or should _always_ pass through to our larger event handler that checks
  // All tools anyway?
  const allActiveTools = getActiveToolsForElement(
    element,
    getters.touchTools()
  );

  if (allActiveTools.length > 0 && allActiveTools[0].touchStartActiveCallback) {
    allActiveTools[0].touchStartActiveCallback(evt);
  } else {
    touchStartActive(evt);
  }

  return false;
}

function touchStart (evt) {
  console.log('touchStart');
  if (isAwaitingTouchUp) {
    return;
  }

  let tools;
  const distanceFromHandle = 28;
  const eventData = evt.detail;
  const element = eventData.element;
  const coords = eventData.startPoints.canvas;

  tools = getInteractiveToolsForElement(element, getters.touchTools());
  tools = getToolsWithDataForElement(element, tools);

  // Find all tools w/ handles that we are near
  const toolsWithMoveableHandles = tools.filter((tool) => {
    const toolState = getToolState(element, tool.name);

    for (let i = 0; i < toolState.data.length; i++) {
      if (
        getHandleNearImagePoint(
          eventData.element,
          toolState.data[i].handles,
          coords,
          distanceFromHandle
        ) !== undefined
      ) {
        return true;
      }
    }

    return false;
  });

  // TODO: More than one? Which one was moved most recently?
  // We'll just grab the first one we encounter for now
  if (toolsWithMoveableHandles.length > 0) {
    // Todo: Ignore TAP, START, PRESS

    const firstToolWithMoveableHandles = toolsWithMoveableHandles[0];
    const toolState = getToolState(element, firstToolWithMoveableHandles.name);
    const dataWithMoveableHandle = toolState.data.find(
      (d) =>
        getHandleNearImagePoint(
          element,
          d.handles,
          coords,
          distanceFromHandle
        ) !== undefined
    );
    const moveableHandle = getHandleNearImagePoint(
      element,
      dataWithMoveableHandle.handles,
      coords,
      distanceFromHandle
    );

    toolState.data.active = true;
    touchMoveHandle(
      evt,
      firstToolWithMoveableHandles.name,
      toolState.data,
      moveableHandle,
      () => {} // HandleDoneMove
    );

    evt.stopImmediatePropagation();
    evt.preventDefault();
    evt.stopPropagation();

    return;
  }

  // Find all tools near our point
  const toolsNearPoint = tools.filter((tool) => {
    const toolState = getToolState(element, tool.name);
    const isNearPoint =
      tool.pointNearTool &&
      toolState.data.some((data) => tool.pointNearTool(element, data, coords));

    return isNearPoint;
  });

  // TODO: More than one? Which one was moved most recently?
  // We'll just grab the first one we encounter for now
  if (toolsNearPoint.length > 0) {
    // Todo: Ignore: TAP, START, PRESS
    const firstToolNearPoint = toolsNearPoint[0];
    const toolState = getToolState(element, firstToolNearPoint.name);
    const firstAnnotationNearPoint = toolState.data.find((data) =>
      firstToolNearPoint.pointNearTool(element, data, coords)
    );

    touchMoveAllHandles(
      evt,
      firstAnnotationNearPoint,
      toolState,
      firstToolNearPoint.name,
      true,
      (lastEvent, lastEventData) => {
        firstAnnotationNearPoint.active = false;
        firstAnnotationNearPoint.invalidated = true;
        //   If (anyHandlesOutsideImage(eventData, data.handles)) {
        //     // Delete the measurement
        //     RemoveToolState(
        //       EventData.element,
        //       TouchToolInterface.toolType,
        //       Data
        //     );
        //   }

        cornerstone.updateImage(element);
        // Todo: LISTEN: TAP, START, PRESS

        if (lastEvent && lastEvent.type === EVENTS.TOUCH_PRESS) {
          triggerEvent(element, lastEvent.type, lastEventData);
        }
      }
    );
    evt.stopImmediatePropagation();
    evt.preventDefault();
    evt.stopPropagation();

    return;
  }
}

// TODO:
// Note: some touchDrag tools don't want to fire on touchStart
// Drag tools have an option `fireOnTouchStart` used to filter
// Them out of TOUCH_START handler
// Active: dragTool only
// Or tool has `touchDragCallback` method
function touchDrag (evt) {
  console.log('touchDrag');

  let tools;
  const element = evt.detail.element;

  tools = getActiveToolsForElement(element, getters.touchTools());
  tools = tools.filter((tool) => typeof tool.touchDragCallback === 'function');

  if (tools.length === 0) {
    return;
  }
  const firstActiveTool = tools[0];

  firstActiveTool.touchDragCallback(evt);
}

// Active: dragTool only
// Or tool has `touchEndCallbach` method
function touchEnd (evt) {
  console.log('touchEnd');
}

function touchStartActive (evt) {
  console.log('touchStartActive');
  if (isAwaitingTouchUp) {
    return;
  }

  const element = evt.detail.element;
  const tools = getActiveToolsForElement(element, getters.touchTools());
  const activeTool = tools[0];

  // Note: custom `addNewMeasurement` will need to prevent event bubbling
  if (activeTool.addNewMeasurement) {
    activeTool.addNewMeasurement(evt, 'touch');
  } else if (activeTool.isAnnotationTool) {
    addNewMeasurement(evt, activeTool);
  }
}

function touchPress (evt) {
  // Active?
  console.warn('touchPress not implemented');
}

function doubleTap (evt) {
  // Active?
  console.warn('doubleTap not implemented');
}

function touchPinch (evt) {
  console.log('touchPinch');
  if (isAwaitingTouchUp) {
    return;
  }

  let tools;
  const element = evt.detail.element;

  // Filter out disabled, enabled, and passive
  tools = getActiveToolsForElement(element, getters.touchTools());
  tools = tools.filter((tool) => typeof tool.touchPinchCallback === 'function');

  if (tools.length === 0) {
    return;
  }

  tools[0].touchPinchCallback(evt);
}

function onImageRendered (evt) {
  const eventData = evt.detail;
  const element = eventData.element;

  const toolsToRender = getters.
    touchTools().
    filter(
      (tool) =>
        tool.element === element &&
        (tool.mode === 'active' ||
          tool.mode === 'passive' ||
          tool.mode === 'enabled')
    );

  toolsToRender.forEach((tool) => {
    if (tool.renderToolData) {
      tool.renderToolData(evt);
    }
  });
}

function addNewMeasurement (evt, tool) {
  console.log('touch: addNewMeasurement');
  //
  evt.preventDefault();
  evt.stopPropagation();
  //
  const touchEventData = evt.detail;
  const element = touchEventData.element;
  const measurementData = tool.createNewMeasurement(touchEventData);

  if (!measurementData) {
    return;
  }

  addToolState(element, tool.name, measurementData);

  // Todo: Looks like we're handling the "up" of the tap?
  if (
    Object.keys(measurementData.handles).length === 1 &&
    touchEventData.type === EVENTS.TAP
  ) {
    // Todo: bold assumptions about measurement data for all tools?
    measurementData.active = false;
    measurementData.handles.end.active = false;
    measurementData.handles.end.highlight = false;
    measurementData.invalidated = true;

    // TODO: IFF the tool supports this feature
    // If (anyHandlesOutsideImage(touchEventData, measurementData.handles)) {
    //   // Delete the measurement
    //   RemoveToolState(element, tool.name, measurementData);
    // }

    cornerstone.updateImage(element);

    return;
  }

  isAwaitingTouchUp = true;
  cornerstone.updateImage(element);

  moveNewHandleTouch(
    touchEventData,
    tool.name,
    measurementData,
    measurementData.handles.end,
    function () {
      console.log('addNewMeasurement: touchUp');
      measurementData.active = false;
      measurementData.invalidated = true;
      //   If (anyHandlesOutsideImage(touchEventData, measurementData.handles)) {
      //     // Delete the measurement
      //     RemoveToolState(element, touchToolInterface.toolType, measurementData);
      //   }

      isAwaitingTouchUp = false;
      cornerstone.updateImage(element);
    }
  );
}

export default {
  enable
  // Disable
};

// HELPER - UTILITY
function deactivateAllHandles (handles) {
  Object.keys(handles).forEach(function (name) {
    const handle = handles[name];

    handle.active = false;
  });
}

function deactivateAllToolInstances (toolData) {
  if (!toolData) {
    return;
  }

  for (let i = 0; i < toolData.data.length; i++) {
    const data = toolData.data[i];

    data.active = false;
    if (!data.handles) {
      continue;
    }

    deactivateAllHandles(data.handles);
  }
}
<|endoftext|>


<commit_before>'use strict';

var CRp = {};

CRp.safeDrawImage = function( context, img, ix, iy, iw, ih, x, y, w, h ){
  var r = this;

  // detect problematic cases for old browsers with bad images (cheaper than try-catch)
  if( iw <= 0 || ih <= 0 || w <= 0 || h <= 0 ){
    return;
  }

  context.drawImage( img, ix, iy, iw, ih, x, y, w, h );
};

CRp.drawInscribedImage = function( context, img, node, index ){
  var r = this;
  var pos = node.position();
  var nodeX = pos.x;
  var nodeY = pos.y;
  var getIndexedStyle = node.cy().style().getIndexedStyle;
  var fit = getIndexedStyle( node, 'background-fit', 'value', index );
  var repeat = getIndexedStyle( node, 'background-repeat', 'value', index );
  var nodeW = node.width();
  var nodeH = node.height();
  var paddingX2 = node.padding() * 2;
  var nodeTW = nodeW + ( getIndexedStyle( node, 'background-width-relative-to', 'value', index ) === 'inner' ? 0 : paddingX2 );
  var nodeTH = nodeH + ( getIndexedStyle( node, 'background-height-relative-to', 'value', index ) === 'inner' ? 0 : paddingX2 );
  var rs = node._private.rscratch;
  var clip = node.pstyle( 'background-clip' ).value;
  var shouldClip = clip === 'node';
  var imgOpacity = getIndexedStyle( node, 'background-image-opacity', 'value', index );

  var imgW = img.width || img.cachedW;
  var imgH = img.height || img.cachedH;

  // workaround for broken browsers like ie
  if( null == imgW || null == imgH ){
    document.body.appendChild( img ); // eslint-disable-line no-undef

    imgW = img.cachedW = img.width || img.offsetWidth;
    imgH = img.cachedH = img.height || img.offsetHeight;

    document.body.removeChild( img ); // eslint-disable-line no-undef
  }

  var w = imgW;
  var h = imgH;

  if( getIndexedStyle( node, 'background-width', 'value', index ) !== 'auto' ){
    if( getIndexedStyle( node, 'background-width', 'units', index ) === '%' ){
      w = getIndexedStyle( node, 'background-width', 'pfValue', index ) * nodeTW;
    } else {
      w = getIndexedStyle( node, 'background-width', 'pfValue', index );
    }
  }

  if( getIndexedStyle( node, 'background-height', 'value', index ) !== 'auto' ){
    if( getIndexedStyle( node, 'background-height', 'units', index ) === '%' ){
      h = getIndexedStyle( node, 'background-height', 'pfValue', index ) * nodeTH;
    } else {
      h = getIndexedStyle( node, 'background-height', 'pfValue', index );
    }
  }

  if( w === 0 || h === 0 ){
    return; // no point in drawing empty image (and chrome is broken in this case)
  }

  if( fit === 'contain' ){
    var scale = Math.min( nodeTW / w, nodeTH / h );

    w *= scale;
    h *= scale;

  } else if( fit === 'cover' ){
    var scale = Math.max( nodeTW / w, nodeTH / h );

    w *= scale;
    h *= scale;
  }

  var x = (nodeX - nodeTW / 2); // left
  if( getIndexedStyle( node, 'background-position-x', 'units', index ) === '%' ){
    x += (nodeTW - w) * getIndexedStyle( node, 'background-position-x', 'pfValue', index );
  } else {
    x += getIndexedStyle( node, 'background-position-x', 'pfValue', index );
  }

  var y = (nodeY - nodeTH / 2); // top
  if( getIndexedStyle( node, 'background-position-y', 'units', index ) === '%' ){
    y += (nodeTH - h) * getIndexedStyle( node, 'background-position-y', 'pfValue', index );
  } else {
    y += getIndexedStyle( node, 'background-position-y', 'pfValue', index );
  }

  if( rs.pathCache ){
    x -= nodeX;
    y -= nodeY;

    nodeX = 0;
    nodeY = 0;
  }

  var gAlpha = context.globalAlpha;

  context.globalAlpha = imgOpacity;

  if( repeat === 'no-repeat' ){

    if( shouldClip ){
      context.save();

      if( rs.pathCache ){
        context.clip( rs.pathCache );
      } else {
        r.nodeShapes[ r.getNodeShape( node ) ].draw(
          context,
          nodeX, nodeY,
          nodeTW, nodeTH );

        context.clip();
      }
    }

    r.safeDrawImage( context, img, 0, 0, imgW, imgH, x, y, w, h );

    if( shouldClip ){
      context.restore();
    }
  } else {
    var pattern = context.createPattern( img, repeat );
    context.fillStyle = pattern;

    r.nodeShapes[ r.getNodeShape( node ) ].draw(
        context,
        nodeX, nodeY,
        nodeTW, nodeTH );

    context.translate( x, y );
    context.fill();
    context.translate( -x, -y );
  }

  context.globalAlpha = gAlpha;

};

module.exports = CRp;
<commit_msg>Bound getIndexedStyle to styleObj<commit_after>'use strict';

var CRp = {};

CRp.safeDrawImage = function( context, img, ix, iy, iw, ih, x, y, w, h ){
  var r = this;

  // detect problematic cases for old browsers with bad images (cheaper than try-catch)
  if( iw <= 0 || ih <= 0 || w <= 0 || h <= 0 ){
    return;
  }

  context.drawImage( img, ix, iy, iw, ih, x, y, w, h );
};

CRp.drawInscribedImage = function( context, img, node, index ){
  var r = this;
  var pos = node.position();
  var nodeX = pos.x;
  var nodeY = pos.y;
  var styleObj = node.cy().style();
  var getIndexedStyle = styleObj.getIndexedStyle.bind( styleObj );
  var fit = getIndexedStyle( node, 'background-fit', 'value', index );
  var repeat = getIndexedStyle( node, 'background-repeat', 'value', index );
  var nodeW = node.width();
  var nodeH = node.height();
  var paddingX2 = node.padding() * 2;
  var nodeTW = nodeW + ( getIndexedStyle( node, 'background-width-relative-to', 'value', index ) === 'inner' ? 0 : paddingX2 );
  var nodeTH = nodeH + ( getIndexedStyle( node, 'background-height-relative-to', 'value', index ) === 'inner' ? 0 : paddingX2 );
  var rs = node._private.rscratch;
  var clip = node.pstyle( 'background-clip' ).value;
  var shouldClip = clip === 'node';
  var imgOpacity = getIndexedStyle( node, 'background-image-opacity', 'value', index );

  var imgW = img.width || img.cachedW;
  var imgH = img.height || img.cachedH;

  // workaround for broken browsers like ie
  if( null == imgW || null == imgH ){
    document.body.appendChild( img ); // eslint-disable-line no-undef

    imgW = img.cachedW = img.width || img.offsetWidth;
    imgH = img.cachedH = img.height || img.offsetHeight;

    document.body.removeChild( img ); // eslint-disable-line no-undef
  }

  var w = imgW;
  var h = imgH;

  if( getIndexedStyle( node, 'background-width', 'value', index ) !== 'auto' ){
    if( getIndexedStyle( node, 'background-width', 'units', index ) === '%' ){
      w = getIndexedStyle( node, 'background-width', 'pfValue', index ) * nodeTW;
    } else {
      w = getIndexedStyle( node, 'background-width', 'pfValue', index );
    }
  }

  if( getIndexedStyle( node, 'background-height', 'value', index ) !== 'auto' ){
    if( getIndexedStyle( node, 'background-height', 'units', index ) === '%' ){
      h = getIndexedStyle( node, 'background-height', 'pfValue', index ) * nodeTH;
    } else {
      h = getIndexedStyle( node, 'background-height', 'pfValue', index );
    }
  }

  if( w === 0 || h === 0 ){
    return; // no point in drawing empty image (and chrome is broken in this case)
  }

  if( fit === 'contain' ){
    var scale = Math.min( nodeTW / w, nodeTH / h );

    w *= scale;
    h *= scale;

  } else if( fit === 'cover' ){
    var scale = Math.max( nodeTW / w, nodeTH / h );

    w *= scale;
    h *= scale;
  }

  var x = (nodeX - nodeTW / 2); // left
  if( getIndexedStyle( node, 'background-position-x', 'units', index ) === '%' ){
    x += (nodeTW - w) * getIndexedStyle( node, 'background-position-x', 'pfValue', index );
  } else {
    x += getIndexedStyle( node, 'background-position-x', 'pfValue', index );
  }

  var y = (nodeY - nodeTH / 2); // top
  if( getIndexedStyle( node, 'background-position-y', 'units', index ) === '%' ){
    y += (nodeTH - h) * getIndexedStyle( node, 'background-position-y', 'pfValue', index );
  } else {
    y += getIndexedStyle( node, 'background-position-y', 'pfValue', index );
  }

  if( rs.pathCache ){
    x -= nodeX;
    y -= nodeY;

    nodeX = 0;
    nodeY = 0;
  }

  var gAlpha = context.globalAlpha;

  context.globalAlpha = imgOpacity;

  if( repeat === 'no-repeat' ){

    if( shouldClip ){
      context.save();

      if( rs.pathCache ){
        context.clip( rs.pathCache );
      } else {
        r.nodeShapes[ r.getNodeShape( node ) ].draw(
          context,
          nodeX, nodeY,
          nodeTW, nodeTH );

        context.clip();
      }
    }

    r.safeDrawImage( context, img, 0, 0, imgW, imgH, x, y, w, h );

    if( shouldClip ){
      context.restore();
    }
  } else {
    var pattern = context.createPattern( img, repeat );
    context.fillStyle = pattern;

    r.nodeShapes[ r.getNodeShape( node ) ].draw(
        context,
        nodeX, nodeY,
        nodeTW, nodeTH );

    context.translate( x, y );
    context.fill();
    context.translate( -x, -y );
  }

  context.globalAlpha = gAlpha;

};

module.exports = CRp;
<|endoftext|>


<commit_before>define([
	'backbone'
    , 'app'
], function (Backbone, App) {
    var Model = Backbone.Model.extend({
		defaults: {
			date: App.dateFormat(new Date())
			// TODO implement now for startTime
			, startTime: null
			, startTimeMachine: 'PACO_FLOOR-00_RIGHT-01'
			, startTimeMotive: null
			// TODO implement now for endTime
			, endTime: null
			, endTimeMachine: 'PACO_FLOOR-00_RIGHT-01'
			, endTimeMotive: null
			, leavingEarly: null
			, officialShift: true
		}

		, validade: function(attr, options) {
			if (attr.date == '')
				return 'Date can\'t be empty';

			// TODO implement more validation
			// if (attr.startTime == '' && attr.endTime == '' && (attr.leavingEarly == '' || attr.leavingEarly == null))
			// 	return 'startTime && endTime && leavingEarly can\'t be empty!';
		}

		, rule30: function(minutes) {
			if (minutes < 30)
				minutes = 0;
			else
				minutes = 30;

			return minutes;
		}

		, originalRule: function(minutes) {
			if (minutes < 21)
				minutes = 0;
			else if (minutes < 41)
				minutes = 30;
			else
				minutes = 60;

			return minutes;
		}
    });

    return Model;
});
<commit_msg>Timesheet<commit_after>define([
	'backbone'
    , 'app'
], function (Backbone, App) {
    var Model = Backbone.Model.extend({
		defaults: {
			date: App.dateFormat(new Date())
			// TODO implement now for startTime
			, startTime: null
			, startTimeMachine: 'PACO_FLOOR-00_LEFT-01'
			, startTimeMotive: null
			// TODO implement now for endTime
			, endTime: null
			, endTimeMachine: 'PACO_FLOOR-00_LEFT-01'
			, endTimeMotive: null
			, leavingEarly: null
			, officialShift: true
		}

		, validade: function(attr, options) {
			if (attr.date == '')
				return 'Date can\'t be empty';

			// TODO implement more validation
			// if (attr.startTime == '' && attr.endTime == '' && (attr.leavingEarly == '' || attr.leavingEarly == null))
			// 	return 'startTime && endTime && leavingEarly can\'t be empty!';
		}

		, rule30: function(minutes) {
			if (minutes < 30)
				minutes = 0;
			else
				minutes = 30;

			return minutes;
		}

		, originalRule: function(minutes) {
			if (minutes < 21)
				minutes = 0;
			else if (minutes < 41)
				minutes = 30;
			else
				minutes = 60;

			return minutes;
		}
    });

    return Model;
});
<|endoftext|>


<commit_before>// @flow

/**
 * This is a helper function to generate links for the With column of DisplayTable.
 */

const withLinkGenerator = (id: string, db: string, name: ?string) => {
  if (id === undefined) {
    return "#"
  }
  // if using converted gene name, link to gene page not uniprot
  if (name) {
    if (name.includes(" ")) {
      return `https://www.ebi.ac.uk/QuickGO/term/GO:${id}`
    }
    if (name.match(/^([A-Z0-9]*$)/)) {
      return `https://www.uniprot.org/uniprot/${id}`
    }
    return `/${name}`
  }
  switch (db) {
    case "CHEBI": {
      return `https://www.ebi.ac.uk/chebi/searchId.do?chebiId=CHEBI:${id}`
    }
    case "DDB":
      return `/${id}`
    case "dictyBase":
      return `/${id}`
    case "FB":
      return `http://flybase.org/reports/${id}.html`
    case "GO":
      return `https://www.ebi.ac.uk/QuickGO/term/GO:${id}`
    case "InterPro":
      return `http://www.ebi.ac.uk/interpro/entry/${id}`
    case "MGI":
      return `http://www.informatics.jax.org/marker/${id}`
    case "PANTHER":
      return `http://www.pantree.org/node/annotationNode.jsp?id=${id}`
    case "PomBase":
      return `https://www.pombase.org/spombe/result/${id}`
    case "RGD":
      return `https://rgd.mcw.edu/rgdweb/report/gene/main.html?id=${id}`
    case "SGD":
      return `https://www.yeastgenome.org/locus/${id}`
    case "SO":
      return `http://www.sequenceontology.org/browser/current_svn/term/SO:${id}`
    case "TAIR":
      return `https://www.arabidopsis.org/servlets/TairObject?accession=${id}`
    case "UniProtKB":
      return `https://www.uniprot.org/uniprot/${id}`
    case "UniProtKB-KW":
      return `https://www.uniprot.org/keywords/${id}`
    case "UniProtKB-SubCell":
      return `https://www.uniprot.org/locations/${id}`
    case "WB":
      return `https://wormbase.org/search/cds/${id}`
    case "ZFIN":
      return `https://zfin.org/${id}`
    default:
      return "#"
  }
}

export default withLinkGenerator
<commit_msg>fix: update go page links to use basename in prod<commit_after>// @flow

/**
 * This is a helper function to generate links for the With column of DisplayTable.
 */

const withLinkGenerator = (id: string, db: string, name: ?string) => {
  let baseUrl
  if (process.env.NODE_ENV === "production") {
    baseUrl = `${process.env.REACT_APP_BASENAME}/`
  } else {
    baseUrl = "/"
  }
  if (id === undefined) {
    return "#"
  }
  // if using converted gene name, link to gene page not uniprot
  if (name) {
    if (name.includes(" ")) {
      return `https://www.ebi.ac.uk/QuickGO/term/GO:${id}`
    }
    if (name.match(/^([A-Z0-9]*$)/)) {
      return `https://www.uniprot.org/uniprot/${id}`
    }
    return `${baseUrl}${name}`
  }
  switch (db) {
    case "CHEBI": {
      return `https://www.ebi.ac.uk/chebi/searchId.do?chebiId=CHEBI:${id}`
    }
    case "DDB":
      return `${baseUrl}${id}`
    case "dictyBase":
      return `${baseUrl}${id}`
    case "FB":
      return `http://flybase.org/reports/${id}.html`
    case "GO":
      return `https://www.ebi.ac.uk/QuickGO/term/GO:${id}`
    case "InterPro":
      return `http://www.ebi.ac.uk/interpro/entry/${id}`
    case "MGI":
      return `http://www.informatics.jax.org/marker/${id}`
    case "PANTHER":
      return `http://www.pantree.org/node/annotationNode.jsp?id=${id}`
    case "PomBase":
      return `https://www.pombase.org/spombe/result/${id}`
    case "RGD":
      return `https://rgd.mcw.edu/rgdweb/report/gene/main.html?id=${id}`
    case "SGD":
      return `https://www.yeastgenome.org/locus/${id}`
    case "SO":
      return `http://www.sequenceontology.org/browser/current_svn/term/SO:${id}`
    case "TAIR":
      return `https://www.arabidopsis.org/servlets/TairObject?accession=${id}`
    case "UniProtKB":
      return `https://www.uniprot.org/uniprot/${id}`
    case "UniProtKB-KW":
      return `https://www.uniprot.org/keywords/${id}`
    case "UniProtKB-SubCell":
      return `https://www.uniprot.org/locations/${id}`
    case "WB":
      return `https://wormbase.org/search/cds/${id}`
    case "ZFIN":
      return `https://zfin.org/${id}`
    default:
      return "#"
  }
}

export default withLinkGenerator
<|endoftext|>


<commit_before>import { Person } from 'blockstack-profiles'
import { parseZoneFile } from 'blockstack-zones'
import {
  makeZoneFileForHostedProfile, resolveZoneFileToProfile
} from '../utils/index'
import {
  validateProofs
} from 'blockstack-proofs'

const UPDATE_CURRENT = 'UPDATE_CURRENT',
      UPDATE_IDENTITIES = 'UPDATE_IDENTITIES',
      CREATE_NEW = 'CREATE_NEW',
      UPDATE_PROFILE = 'UPDATE_PROFILE'

function updateCurrentIdentity(domainName, profile, verifications) {
  return {
    type: UPDATE_CURRENT,
    domainName: domainName,
    profile: profile,
    verifications: verifications
  }
}

function createNewIdentity(domainName) {
  return {
    type: CREATE_NEW,
    domainName: domainName
  }
}

function updateOwnedIdentities(localIdentities, namesOwned) {
  return {
    type: UPDATE_IDENTITIES,
    localIdentities: localIdentities,
    namesOwned: namesOwned
  }
}

function updateProfile(domainName, profile) {
  return {
    type: UPDATE_PROFILE,
    domainName: domainName,
    profile: profile
  }
}

function calculateLocalIdentities(localIdentities, namesOwned) {
  let remoteNamesDict = {},
      localNamesDict = {},
      updatedLocalIdentities = localIdentities

  namesOwned.map(function(name) {
    remoteNamesDict[name] = true
  })

  Object.keys(updatedLocalIdentities).forEach((name) => {
    let identity = updatedLocalIdentities[name]
    localNamesDict[identity.domainName] = true
    if (remoteNamesDict.hasOwnProperty(identity.domainName)) {
      identity.registered = true
    }
  })

  namesOwned.map(function(name) {
    if (!localNamesDict.hasOwnProperty(name)) {
      updatedLocalIdentities[name] = {
        domainName: name,
        profile: {
          '@type': 'Person',
          '@context': 'http://schema.org'
        },
        verifications: [],
        registered: true
      }
    }
  })

  return updatedLocalIdentities
}

function getNamesOwnedFromApiResponse(responseJson) {
  let namesOwned = []
  if (responseJson.hasOwnProperty('results')) {
    responseJson.results.map((addressResult) => {
      if (addressResult.hasOwnProperty('names')) {
        addressResult.names.map((name) => {
          namesOwned.push(name)
        })
      }
    })
  }
  return namesOwned
}

function refreshIdentities(addresses, addressLookupUrl, localIdentities, lastNameLookup) {
  return dispatch => {
    if (addresses.length === 0) {
      let namesOwned = []
      let updatedLocalIdentities = calculateLocalIdentities(localIdentities, namesOwned)
      if (JSON.stringify(updatedLocalIdentities) === JSON.stringify(localIdentities)) {
        // pass
      } else {
        dispatch(updateOwnedIdentities(updatedLocalIdentities, namesOwned))
      }
    } else {
      const url = addressLookupUrl.replace('{address}', addresses.join(','))
      fetch(url)
        .then((response) => response.text())
        .then((responseText) => JSON.parse(responseText))
        .then((responseJson) => {
          let namesOwned = getNamesOwnedFromApiResponse(responseJson),
              updatedLocalIdentities = calculateLocalIdentities(localIdentities, namesOwned)

          if (JSON.stringify(lastNameLookup) === JSON.stringify(namesOwned)) {
            // pass
          } else {
            dispatch(updateOwnedIdentities(updatedLocalIdentities, namesOwned))
          }
        })
        .catch((error) => {
          console.warn(error)
        })
    }
  }
}

function registerName(domainName, recipientAddress, tokenFileUrl, registerUrl,
                      blockstackApiAppId, blockstackApiAppSecret) {
  return dispatch => {
    const zoneFile = makeZoneFileForHostedProfile(domainName, tokenFileUrl),
      authHeader = 'Basic ' + btoa(blockstackApiAppId + ':' + blockstackApiAppSecret)
    const requestHeaders = {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'Authorization': authHeader
    }
    const requestBody = JSON.stringify({
      username: domainName.split('.')[0],
      recipient_address: recipientAddress,
      profile: zoneFile
    })

    fetch(registerUrl, {
      method: 'POST',
      headers: requestHeaders,
      mode: 'cors',
      cache: 'default',
      body: requestBody
    })
      .then((response) => response.text())
      .then((responseText) => JSON.parse(responseText))
      .then((responseJson) => {
        dispatch(createNewIdentity(domainName))
      })
      .catch((error) => {
        console.warn(error)
      })
  }
}

function fetchCurrentIdentity(domainName, lookupUrl) {
  return dispatch => {
    let username
    if (lookupUrl.search('localhost') >= 0) {
      username = domainName
    } else if (lookupUrl.search('api.blockstack.com') >= 0) {
      username = domainName.split('.')[0]
    } else {
      throw "Invalid lookup URL"
    }
    const url = lookupUrl.replace('{name}', username)
    fetch(url)
      .then((response) => response.text())
      .then((responseText) => JSON.parse(responseText))
      .then((responseJson) => {
        let zoneFile
        let ownerAddress

        if (lookupUrl.search('localhost') >= 0) {
          zoneFile = responseJson['zonefile']
          ownerAddress = responseJson['address']
        } else if (lookupUrl.search('api.blockstack.com') >= 0) {
          const userData = responseJson[username]
          zoneFile = userData['zone_file']
          ownerAddress = userData['owner_address']
        } else {
          throw "Invalid lookup URL"
        }

        resolveZoneFileToProfile(zoneFile, ownerAddress, (profile) => {
          let verifications = []
          dispatch(updateCurrentIdentity(domainName, profile, verifications))
          if (profile) {
            validateProofs(profile, domainName).then((proofs) => {
              verifications = proofs
              dispatch(updateCurrentIdentity(domainName, profile, verifications))
            })
          }
        })
      })
      .catch((error) => {
        dispatch(updateCurrentIdentity(domainName, null, []))
        console.warn(error)
      })
  }
}

export const IdentityActions = {
  updateCurrentIdentity: updateCurrentIdentity,
  createNewIdentity: createNewIdentity,
  updateProfile: updateProfile,
  fetchCurrentIdentity: fetchCurrentIdentity,
  refreshIdentities: refreshIdentities,
  updateOwnedIdentities: updateOwnedIdentities,
  registerName: registerName
}

const initialState = {
  current: {
    domainName: null,
    profile: null,
    verifications: null
  },
  localIdentities: {},
  lastNameLookup: []
}

export function IdentityReducer(state = initialState, action) {
  switch (action.type) {
    case UPDATE_CURRENT:
      return Object.assign({}, state, {
        current: {
          domainName: action.domainName,
          profile: action.profile,
          verifications: action.verifications
        }
      })
    case CREATE_NEW:
      return Object.assign({}, state, {
        localIdentities: Object.assign({}, state.localIdentities, {
          [action.domainName]: {
            domainName: action.domainName,
            profile: {
              '@type': 'Person',
              '@context': 'http://schema.org'
            },
            verifications: [],
            registered: false
          }
        })
      })
    case UPDATE_IDENTITIES:
      return Object.assign({}, state, {
        localIdentities: action.localIdentities,
        lastNameLookup: action.namesOwned
      })
    case UPDATE_PROFILE:
      return Object.assign({}, state, {
        localIdentities: Object.assign({}, state.localIdentities, {
          [action.domainName]: Object.assign({}, state.localIdentities[action.domainName], {
            profile: action.profile
          })
        })
      })
    default:
      return state
  }
}

/*{
  domainName: 'ryan.id',
  profile: {},
  verifications: [],
  registered: false,
  blockNumber: null,
  transactionNumber: null
}*/
<commit_msg>one address api request per address<commit_after>import { Person } from 'blockstack-profiles'
import { parseZoneFile } from 'blockstack-zones'
import {
  makeZoneFileForHostedProfile, resolveZoneFileToProfile
} from '../utils/index'
import {
  validateProofs
} from 'blockstack-proofs'

const UPDATE_CURRENT = 'UPDATE_CURRENT',
      UPDATE_IDENTITIES = 'UPDATE_IDENTITIES',
      CREATE_NEW = 'CREATE_NEW',
      UPDATE_PROFILE = 'UPDATE_PROFILE'

function updateCurrentIdentity(domainName, profile, verifications) {
  return {
    type: UPDATE_CURRENT,
    domainName: domainName,
    profile: profile,
    verifications: verifications
  }
}

function createNewIdentity(domainName) {
  return {
    type: CREATE_NEW,
    domainName: domainName
  }
}

function updateOwnedIdentities(localIdentities, namesOwned) {
  return {
    type: UPDATE_IDENTITIES,
    localIdentities: localIdentities,
    namesOwned: namesOwned
  }
}

function updateProfile(domainName, profile) {
  return {
    type: UPDATE_PROFILE,
    domainName: domainName,
    profile: profile
  }
}

function calculateLocalIdentities(localIdentities, namesOwned) {
  let remoteNamesDict = {},
      localNamesDict = {},
      updatedLocalIdentities = localIdentities

  namesOwned.map(function(name) {
    remoteNamesDict[name] = true
  })

  Object.keys(updatedLocalIdentities).forEach((name) => {
    let identity = updatedLocalIdentities[name]
    localNamesDict[identity.domainName] = true
    if (remoteNamesDict.hasOwnProperty(identity.domainName)) {
      identity.registered = true
    }
  })

  namesOwned.map(function(name) {
    if (!localNamesDict.hasOwnProperty(name)) {
      updatedLocalIdentities[name] = {
        domainName: name,
        profile: {
          '@type': 'Person',
          '@context': 'http://schema.org'
        },
        verifications: [],
        registered: true
      }
    }
  })

  return updatedLocalIdentities
}

function refreshIdentities(addresses, addressLookupUrl, localIdentities, lastNameLookup) {
  return dispatch => {
    if (addresses.length === 0) {
      let namesOwned = []
      let updatedLocalIdentities = calculateLocalIdentities(localIdentities, namesOwned)
      if (JSON.stringify(updatedLocalIdentities) === JSON.stringify(localIdentities)) {
        // pass
      } else {
        dispatch(updateOwnedIdentities(updatedLocalIdentities, namesOwned))
      }
    } else {

      let count =  addresses.length
      let namesOwned = []

      addresses.forEach((address) => {
        const url = addressLookupUrl.replace('{address}', address)
        fetch(url)
          .then((response) => response.text())
          .then((responseText) => JSON.parse(responseText))
          .then((responseJson) => {
            count++
            namesOwned = namesOwned.concat(responseJson['names'])

            if(count >= addresses.length) {
              let updatedLocalIdentities = calculateLocalIdentities(localIdentities, namesOwned)

              if (JSON.stringify(lastNameLookup) === JSON.stringify(namesOwned)) {
                // pass
              } else {
                dispatch(updateOwnedIdentities(updatedLocalIdentities, namesOwned))
              }
            }
          }).catch((error) => {
            console.warn(error)
          })

      })
    }
  }
}

function registerName(domainName, recipientAddress, tokenFileUrl, registerUrl,
                      blockstackApiAppId, blockstackApiAppSecret) {
  return dispatch => {
    const zoneFile = makeZoneFileForHostedProfile(domainName, tokenFileUrl),
      authHeader = 'Basic ' + btoa(blockstackApiAppId + ':' + blockstackApiAppSecret)
    const requestHeaders = {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'Authorization': authHeader
    }
    const requestBody = JSON.stringify({
      username: domainName.split('.')[0],
      recipient_address: recipientAddress,
      profile: zoneFile
    })

    fetch(registerUrl, {
      method: 'POST',
      headers: requestHeaders,
      mode: 'cors',
      cache: 'default',
      body: requestBody
    })
      .then((response) => response.text())
      .then((responseText) => JSON.parse(responseText))
      .then((responseJson) => {
        dispatch(createNewIdentity(domainName))
      })
      .catch((error) => {
        console.warn(error)
      })
  }
}

function fetchCurrentIdentity(domainName, lookupUrl) {
  return dispatch => {
    let username
    if (lookupUrl.search('localhost') >= 0) {
      username = domainName
    } else if (lookupUrl.search('api.blockstack.com') >= 0) {
      username = domainName.split('.')[0]
    } else {
      throw "Invalid lookup URL"
    }
    const url = lookupUrl.replace('{name}', username)
    fetch(url)
      .then((response) => response.text())
      .then((responseText) => JSON.parse(responseText))
      .then((responseJson) => {
        let zoneFile
        let ownerAddress

        if (lookupUrl.search('localhost') >= 0) {
          zoneFile = responseJson['zonefile']
          ownerAddress = responseJson['address']
        } else if (lookupUrl.search('api.blockstack.com') >= 0) {
          const userData = responseJson[username]
          zoneFile = userData['zone_file']
          ownerAddress = userData['owner_address']
        } else {
          throw "Invalid lookup URL"
        }

        resolveZoneFileToProfile(zoneFile, ownerAddress, (profile) => {
          let verifications = []
          dispatch(updateCurrentIdentity(domainName, profile, verifications))
          if (profile) {
            validateProofs(profile, domainName).then((proofs) => {
              verifications = proofs
              dispatch(updateCurrentIdentity(domainName, profile, verifications))
            })
          }
        })
      })
      .catch((error) => {
        dispatch(updateCurrentIdentity(domainName, null, []))
        console.warn(error)
      })
  }
}

export const IdentityActions = {
  updateCurrentIdentity: updateCurrentIdentity,
  createNewIdentity: createNewIdentity,
  updateProfile: updateProfile,
  fetchCurrentIdentity: fetchCurrentIdentity,
  refreshIdentities: refreshIdentities,
  updateOwnedIdentities: updateOwnedIdentities,
  registerName: registerName
}

const initialState = {
  current: {
    domainName: null,
    profile: null,
    verifications: null
  },
  localIdentities: {},
  lastNameLookup: []
}

export function IdentityReducer(state = initialState, action) {
  switch (action.type) {
    case UPDATE_CURRENT:
      return Object.assign({}, state, {
        current: {
          domainName: action.domainName,
          profile: action.profile,
          verifications: action.verifications
        }
      })
    case CREATE_NEW:
      return Object.assign({}, state, {
        localIdentities: Object.assign({}, state.localIdentities, {
          [action.domainName]: {
            domainName: action.domainName,
            profile: {
              '@type': 'Person',
              '@context': 'http://schema.org'
            },
            verifications: [],
            registered: false
          }
        })
      })
    case UPDATE_IDENTITIES:
      return Object.assign({}, state, {
        localIdentities: action.localIdentities,
        lastNameLookup: action.namesOwned
      })
    case UPDATE_PROFILE:
      return Object.assign({}, state, {
        localIdentities: Object.assign({}, state.localIdentities, {
          [action.domainName]: Object.assign({}, state.localIdentities[action.domainName], {
            profile: action.profile
          })
        })
      })
    default:
      return state
  }
}

/*{
  domainName: 'ryan.id',
  profile: {},
  verifications: [],
  registered: false,
  blockNumber: null,
  transactionNumber: null
}*/
<|endoftext|>


<commit_before>$(document).ready(function(){

  var exampleGraphs = [
  {
    "info": {
      "title": "cam to gif",
      "author": "forresto",
      "description": "webcam to animated gif",
      "url": "cam2gif"
    },
    "nodes": [
      {
        "src": "http://forresto.github.com/meemoo-camcanvas/webcam2canvas.html",
        "x": 128,
        "y": 45,
        "w": 343,
        "h": 524,
        "id": 1,
        "state": {
          "quality": 75,
          "width": 320,
          "height": 240
        }
      },
      {
        "src": "http://forresto.github.com/meemoo-canvas2gif/canvas2gif.html",
        "x": 622,
        "y": 43,
        "w": 357,
        "h": 285,
        "id": 3,
        "state": {
          "delay": 200,
          "quality": 75
        }
      },
      {
        "src": "http://forresto.github.com/meemoo-modules/imgur.html",
        "x": 625,
        "y": 398,
        "w": 357,
        "h": 297,
        "id": 5,
        "state": {
          "title": "meemoo/cam2gif image share",
          "caption": "This image was created with a Meemoo composition. http://meemoo.org/iframework/#/example/cam2gif"
        }
      }
    ],
    "edges": [
      {
        "source": [
          1,
          "image"
        ],
        "target": [
          3,
          "image"
        ],
        "color": "#DF151A"
      },
      {
        "source": [
          3,
          "gif"
        ],
        "target": [
          5,
          "dataurl"
        ],
        "color": "#F4F328"
      }
    ]
  },
  {
    "info": {
      "title": "cam to glitch",
      "author": "forresto",
      "description": "webcam to jpg to glitch",
      "url": "cam"
    },
    "nodes": [
      {
        "src": "http://forresto.github.com/meemoo-modules/metronome.html",
        "x": 139,
        "y": 45,
        "w": 200,
        "h": 100,
        "id": 1,
        "state": {
          "bpm": 150
        }
      },
      {
        "src": "http://forresto.github.com/meemoo-camcanvas/webcam2jpg.html",
        "x": 581,
        "y": 49,
        "w": 339,
        "h": 283,
        "id": 2,
        "state": {
          "quality": 50,
          "width": 320,
          "height": 240
        }
      },
      {
        "src": "http://forresto.github.com/meemoo-jpgglitch/index.html",
        "x": 138,
        "y": 220,
        "w": 339,
        "h": 262,
        "id": 4
      },
      {
        "src": "http://forresto.github.com/meemoo-modules/img2canvas.html",
        "x": 282,
        "y": 559,
        "w": 116,
        "h": 98,
        "id": 5
      },
      {
        "src": "http://forresto.github.com/meemoo-modules/reflow.html",
        "x": 646,
        "y": 398,
        "w": 256,
        "h": 297,
        "id": 3
      }
    ],
    "edges": [
      {
        "source": [
          1,
          "beat"
        ],
        "target": [
          2,
          "capture"
        ],
        "color": "#DF151A"
      },
      {
        "source": [
          2,
          "jpg"
        ],
        "target": [
          4,
          "jpg"
        ],
        "color": "#FD8603"
      },
      {
        "source": [
          5,
          "image"
        ],
        "target": [
          3,
          "image"
        ],
        "color": "#F4F328"
      },
      {
        "source": [
          4,
          "jpg"
        ],
        "target": [
          5,
          "dataurl"
        ],
        "color": "#00DA3C"
      }
    ]
  },
  {
    info: {
      title: "processing dot js",
      author: "forresto",
      description: "processing to reflow",
      url: "processing"
    },
    nodes: [
      {
        id: 1,
        src: "http://forresto.github.com/meemoo-modules/metronome.html",
        "x":130,"y":47,"w":230,"h":110,
        state: { bpm: 140, start: true }
      },
      {
        id: 3,
        src: "http://forresto.github.com/meemoo-modules/processing.html",
        "x":148,"y":246,"w":308,"h":348,
        state: {
          processingcode: "void setup() { size(300, 300); colorMode(HSB, 360, 100, 300); noStroke(); background(0); } \n void mousePressed () { fill(random(360), 180, 300); triangle(random(width), random(height), 100, 100, 200, 200);}"
        }
      },
      {
        id: 4,
        src: "http://forresto.github.com/meemoo-modules/reflow.html",
        "x":630,"y":88,"w":449,"h":199
      },
      {
        id: 5,
        src: "http://forresto.github.com/meemoo-modules/reflow.html",
        "x":686,"y":384,"w":440,"h":204
      }
    ],
    edges: [
      { source: [1, "beat"],  target: [3, "pressed"] },
      { source: [3, "image"], target: [4, "image"] }
    ]
  },
  {
    "info": {
      "title": "doodle flipbook",
      "author": "forresto",
      "description": "processing doodle to image stack to animated gif",
      "url": "flipbook"
    },
    "nodes": [
      {
        "src": "http://forresto.github.com/meemoo-modules/processing.html",
        "x": 156,
        "y": 63,
        "w": 308,
        "h": 348,
        "id": 1,
        "state": {
          "processingcode": "void setup() { size(300, 300); background(0); stroke(255); } Integer lastX, lastY; void draw() { if(mousePressed) { if (lastX==null) {lastX = mouseX; lastY = mouseY;} line(mouseX, mouseY, lastX, lastY); lastX = mouseX; lastY = mouseY;} else {lastX = null; lastY = null;}}"
        }
      },
      {
        "src": "http://forresto.github.com/meemoo-modules/canvasstack.html",
        "x": 655,
        "y": 53,
        "w": 308,
        "h": 420,
        "id": 3
      },
      {
        "src": "http://forresto.github.com/meemoo-canvas2gif/canvas2gif.html",
        "x": 154,
        "y": 473,
        "w": 354,
        "h": 341,
        "id": 2,
        "state": {
          "delay": 200,
          "quality": 75
        }
      }
    ],
    "edges": [
      {
        "source": [
          1,
          "image"
        ],
        "target": [
          3,
          "image"
        ],
        "color": "#00CBE7"
      },
      {
        "source": [
          3,
          "image"
        ],
        "target": [
          2,
          "image"
        ],
        "color": "#00DA3C"
      }
    ]
  },
  {
    "info": {
      "title": "cam doodle",
      "author": "forresto",
      "description": "webcam to processing doodle to animated gif",
      "url": "camdoodle"
    },
    "nodes": [
      {
        "src": "http://forresto.github.com/meemoo-camcanvas/webcam2canvas.html",
        "x": 127,
        "y": 45,
        "w": 342,
        "h": 283,
        "id": 4,
        "state": {
          "quality": 75,
          "width": 320,
          "height": 240
        }
      },
      {
        "src": "http://forresto.github.com/meemoo-modules/processing.html",
        "x": 625,
        "y": 44,
        "w": 353,
        "h": 284,
        "id": 1,
        "state": {
          "processingcode": "void setup() { size(320, 240); background(0); stroke(255); } Integer lastX, lastY; void draw() { if(mousePressed) { if (lastX==null) {lastX = mouseX; lastY = mouseY;} line(mouseX, mouseY, lastX, lastY); lastX = mouseX; lastY = mouseY;} else {lastX = null; lastY = null;}}"
        }
      },
      {
        "src": "http://forresto.github.com/meemoo-modules/canvasstack.html",
        "x": 126,
        "y": 423,
        "w": 347,
        "h": 315,
        "id": 3
      },
      {
        "src": "http://forresto.github.com/meemoo-canvas2gif/canvas2gif.html",
        "x": 621,
        "y": 450,
        "w": 354,
        "h": 341,
        "id": 2,
        "state": {
          "delay": 200,
          "quality": 75
        }
      },
      {
        "src": "http://forresto.github.com/meemoo-modules/imgur.html",
        "x": 677,
        "y": 876,
        "w": 357,
        "h": 297,
        "id": 6,
        "state": {
          "title": "meemoo/camdoodle image share",
          "caption": "This image was created with a Meemoo composition. http://meemoo.org/iframework/#/example/camdoodle"
        }
      }
    ],
    "edges": [
      {
        "source": [
          1,
          "image"
        ],
        "target": [
          3,
          "image"
        ],
        "color": "#00CBE7"
      },
      {
        "source": [
          3,
          "image"
        ],
        "target": [
          2,
          "image"
        ],
        "color": "#00DA3C"
      },
      {
        "source": [
          4,
          "image"
        ],
        "target": [
          1,
          "image"
        ],
        "color": "#DF151A"
      },
      {
        "source": [
          2,
          "gif"
        ],
        "target": [
          6,
          "dataurl"
        ],
        "color": "#FD8603"
      }
    ]
  }

  ];

  // Default example
  window.Iframework.showGraph(exampleGraphs[0]);

  // Router
  var IframeworkRouter = Backbone.Router.extend({

    routes: {
      "/example/:url": "loadExample" // #/example/url
    },

    loadExample: function(url) {
      for (var i=0; i<exampleGraphs.length; i++) {
        if (exampleGraphs[i]["info"]["url"] === url) {
          Iframework.showGraph(exampleGraphs[i]);
          document.title = "meemoo/"+url;
          return;
        }
      }
    }

  });
  Iframework.router = new IframeworkRouter();
  Backbone.history.start();

  // Make example links:
  var exampleLinks = "examples: "
  for (var i=0; i<exampleGraphs.length; i++) {
    var url = exampleGraphs[i]["info"]["url"];
    if (url) {
      exampleLinks += ''+url+' ';
    }
  }
  $("footer").append(exampleLinks);

});<commit_msg>onionskin request from @ginger<commit_after>$(document).ready(function(){

  var exampleGraphs = [
  // {
  //   "info": {
  //     "title": "contextfree to gif",
  //     "author": "forresto",
  //     "description": "contextfree to animated gif",
  //     "url": "contextfree"
  //   },
  //   "nodes": [
  //     {
  //       "src": "/modules/contextfree.html",
  //       "x": 128,
  //       "y": 45,
  //       "w": 343,
  //       "h": 524,
  //       "id": 1
  //     },
  //     {
  //       "src": "http://forresto.github.com/meemoo-canvas2gif/canvas2gif.html",
  //       "x": 622,
  //       "y": 43,
  //       "w": 357,
  //       "h": 285,
  //       "id": 3,
  //       "state": {
  //         "delay": 200,
  //         "quality": 75
  //       }
  //     },
  //     {
  //       "src": "http://forresto.github.com/meemoo-modules/imgur.html",
  //       "x": 625,
  //       "y": 398,
  //       "w": 357,
  //       "h": 297,
  //       "id": 5,
  //       "state": {
  //         "title": "meemoo/cam2gif image share",
  //         "caption": "This image was created with a Meemoo composition. http://meemoo.org/iframework/#/example/cam2gif"
  //       }
  //     }
  //   ],
  //   "edges": []
  // },
  {
    "info": {
      "title": "cam to gif",
      "author": "forresto",
      "description": "webcam to animated gif",
      "url": "cam2gif"
    },
    "nodes": [
      {
        "src": "http://forresto.github.com/meemoo-camcanvas/onionskin.html",
        "x": 128,
        "y": 45,
        "w": 343,
        "h": 280,
        "id": 1,
        "state": {
          "quality": 75,
          "width": 320,
          "height": 240
        }
      },
      {
        "src": "http://forresto.github.com/meemoo-canvas2gif/canvas2gif.html",
        "x": 622,
        "y": 43,
        "w": 357,
        "h": 285,
        "id": 3,
        "state": {
          "delay": 200,
          "quality": 75
        }
      },
      {
        "src": "http://forresto.github.com/meemoo-modules/imgur.html",
        "x": 625,
        "y": 398,
        "w": 357,
        "h": 297,
        "id": 5,
        "state": {
          "title": "meemoo/cam2gif image share",
          "caption": "This image was created with a Meemoo composition. http://meemoo.org/iframework/#/example/cam2gif"
        }
      }
    ],
    "edges": [
      {
        "source": [
          1,
          "image"
        ],
        "target": [
          3,
          "image"
        ],
        "color": "#DF151A"
      },
      {
        "source": [
          3,
          "gif"
        ],
        "target": [
          5,
          "dataurl"
        ],
        "color": "#F4F328"
      }
    ]
  },
  {
    "info": {
      "title": "cam to glitch",
      "author": "forresto",
      "description": "webcam to jpg to glitch",
      "url": "cam"
    },
    "nodes": [
      {
        "src": "http://forresto.github.com/meemoo-modules/metronome.html",
        "x": 139,
        "y": 45,
        "w": 200,
        "h": 100,
        "id": 1,
        "state": {
          "bpm": 150
        }
      },
      {
        "src": "http://forresto.github.com/meemoo-camcanvas/webcam2jpg.html",
        "x": 581,
        "y": 49,
        "w": 339,
        "h": 283,
        "id": 2,
        "state": {
          "quality": 50,
          "width": 320,
          "height": 240
        }
      },
      {
        "src": "http://forresto.github.com/meemoo-jpgglitch/index.html",
        "x": 138,
        "y": 220,
        "w": 339,
        "h": 262,
        "id": 4
      },
      {
        "src": "http://forresto.github.com/meemoo-modules/img2canvas.html",
        "x": 282,
        "y": 559,
        "w": 116,
        "h": 98,
        "id": 5
      },
      {
        "src": "http://forresto.github.com/meemoo-modules/reflow.html",
        "x": 646,
        "y": 398,
        "w": 256,
        "h": 297,
        "id": 3
      }
    ],
    "edges": [
      {
        "source": [
          1,
          "beat"
        ],
        "target": [
          2,
          "capture"
        ],
        "color": "#DF151A"
      },
      {
        "source": [
          2,
          "jpg"
        ],
        "target": [
          4,
          "jpg"
        ],
        "color": "#FD8603"
      },
      {
        "source": [
          5,
          "image"
        ],
        "target": [
          3,
          "image"
        ],
        "color": "#F4F328"
      },
      {
        "source": [
          4,
          "jpg"
        ],
        "target": [
          5,
          "dataurl"
        ],
        "color": "#00DA3C"
      }
    ]
  },
  {
    info: {
      title: "processing dot js",
      author: "forresto",
      description: "processing to reflow",
      url: "processing"
    },
    nodes: [
      {
        id: 1,
        src: "http://forresto.github.com/meemoo-modules/metronome.html",
        "x":130,"y":47,"w":230,"h":110,
        state: { bpm: 140, start: true }
      },
      {
        id: 3,
        src: "http://forresto.github.com/meemoo-modules/processing.html",
        "x":148,"y":246,"w":308,"h":348,
        state: {
          processingcode: "void setup() { size(300, 300); colorMode(HSB, 360, 100, 300); noStroke(); background(0); } \n void mousePressed () { fill(random(360), 180, 300); triangle(random(width), random(height), 100, 100, 200, 200);}"
        }
      },
      {
        id: 4,
        src: "http://forresto.github.com/meemoo-modules/reflow.html",
        "x":630,"y":88,"w":449,"h":199
      },
      {
        id: 5,
        src: "http://forresto.github.com/meemoo-modules/reflow.html",
        "x":686,"y":384,"w":440,"h":204
      }
    ],
    edges: [
      { source: [1, "beat"],  target: [3, "pressed"] },
      { source: [3, "image"], target: [4, "image"] }
    ]
  },
  {
    "info": {
      "title": "doodle flipbook",
      "author": "forresto",
      "description": "processing doodle to image stack to animated gif",
      "url": "flipbook"
    },
    "nodes": [
      {
        "src": "http://forresto.github.com/meemoo-modules/processing.html",
        "x": 156,
        "y": 63,
        "w": 308,
        "h": 348,
        "id": 1,
        "state": {
          "processingcode": "void setup() { size(300, 300); background(0); stroke(255); } Integer lastX, lastY; void draw() { if(mousePressed) { if (lastX==null) {lastX = mouseX; lastY = mouseY;} line(mouseX, mouseY, lastX, lastY); lastX = mouseX; lastY = mouseY;} else {lastX = null; lastY = null;}}"
        }
      },
      {
        "src": "http://forresto.github.com/meemoo-modules/canvasstack.html",
        "x": 655,
        "y": 53,
        "w": 308,
        "h": 420,
        "id": 3
      },
      {
        "src": "http://forresto.github.com/meemoo-canvas2gif/canvas2gif.html",
        "x": 154,
        "y": 473,
        "w": 354,
        "h": 341,
        "id": 2,
        "state": {
          "delay": 200,
          "quality": 75
        }
      }
    ],
    "edges": [
      {
        "source": [
          1,
          "image"
        ],
        "target": [
          3,
          "image"
        ],
        "color": "#00CBE7"
      },
      {
        "source": [
          3,
          "image"
        ],
        "target": [
          2,
          "image"
        ],
        "color": "#00DA3C"
      }
    ]
  },
  {
    "info": {
      "title": "cam doodle",
      "author": "forresto",
      "description": "webcam to processing doodle to animated gif",
      "url": "camdoodle"
    },
    "nodes": [
      {
        "src": "http://forresto.github.com/meemoo-camcanvas/webcam2canvas.html",
        "x": 127,
        "y": 45,
        "w": 342,
        "h": 283,
        "id": 4,
        "state": {
          "quality": 75,
          "width": 320,
          "height": 240
        }
      },
      {
        "src": "http://forresto.github.com/meemoo-modules/processing.html",
        "x": 625,
        "y": 44,
        "w": 353,
        "h": 284,
        "id": 1,
        "state": {
          "processingcode": "void setup() { size(320, 240); background(0); stroke(255); } Integer lastX, lastY; void draw() { if(mousePressed) { if (lastX==null) {lastX = mouseX; lastY = mouseY;} line(mouseX, mouseY, lastX, lastY); lastX = mouseX; lastY = mouseY;} else {lastX = null; lastY = null;}}"
        }
      },
      {
        "src": "http://forresto.github.com/meemoo-modules/canvasstack.html",
        "x": 126,
        "y": 423,
        "w": 347,
        "h": 315,
        "id": 3
      },
      {
        "src": "http://forresto.github.com/meemoo-canvas2gif/canvas2gif.html",
        "x": 621,
        "y": 450,
        "w": 354,
        "h": 341,
        "id": 2,
        "state": {
          "delay": 200,
          "quality": 75
        }
      },
      {
        "src": "http://forresto.github.com/meemoo-modules/imgur.html",
        "x": 677,
        "y": 876,
        "w": 357,
        "h": 297,
        "id": 6,
        "state": {
          "title": "meemoo/camdoodle image share",
          "caption": "This image was created with a Meemoo composition. http://meemoo.org/iframework/#/example/camdoodle"
        }
      }
    ],
    "edges": [
      {
        "source": [
          1,
          "image"
        ],
        "target": [
          3,
          "image"
        ],
        "color": "#00CBE7"
      },
      {
        "source": [
          3,
          "image"
        ],
        "target": [
          2,
          "image"
        ],
        "color": "#00DA3C"
      },
      {
        "source": [
          4,
          "image"
        ],
        "target": [
          1,
          "image"
        ],
        "color": "#DF151A"
      },
      {
        "source": [
          2,
          "gif"
        ],
        "target": [
          6,
          "dataurl"
        ],
        "color": "#FD8603"
      }
    ]
  }

  ];

  // Default example
  window.Iframework.showGraph(exampleGraphs[0]);

  // Router
  var IframeworkRouter = Backbone.Router.extend({

    routes: {
      "/example/:url": "loadExample" // #/example/url
    },

    loadExample: function(url) {
      for (var i=0; i<exampleGraphs.length; i++) {
        if (exampleGraphs[i]["info"]["url"] === url) {
          Iframework.showGraph(exampleGraphs[i]);
          document.title = "meemoo/"+url;
          return;
        }
      }
    }

  });
  Iframework.router = new IframeworkRouter();
  Backbone.history.start();

  // Make example links:
  var exampleLinks = "examples: "
  for (var i=0; i<exampleGraphs.length; i++) {
    var url = exampleGraphs[i]["info"]["url"];
    if (url) {
      exampleLinks += ''+url+' ';
    }
  }
  $("footer").append(exampleLinks);

});<|endoftext|>