<commit_before>import { rgb, hsl } from "d3-color";
import { interpolateRgb } from "d3-interpolate";
import { scalePow } from "d3-scale";
import { isColorByGenotype, decodeColorByGenotype } from "./getGenotype";
import { getTraitFromNode } from "./treeMiscHelpers";

/**
 * Average over the visible colours for a given location
 * @param {array} nodes list of nodes whose colours we want to average over
 * @param {array} nodeColours (redux state) -- list of node hexes. Not in 1-1 correspondence with `nodes`.
 * @returns {str} a color hex string representing the average of the array.
 */
export const getAverageColorFromNodes = (nodes, nodeColors) => {
  let r=0, g=0, b=0;
  nodes.forEach((n) => {
    const tmpRGB = rgb(nodeColors[n.arrayIdx]);
    r += tmpRGB.r;
    g += tmpRGB.g;
    b += tmpRGB.b;
  });
  const total = nodes.length;
  const avg = rgb(r/total, g/total, b/total);
  return avg.toString();
};

export const determineColorByGenotypeMutType = (colorBy) => {
  if (isColorByGenotype(colorBy)) {
    const genotype = decodeColorByGenotype(colorBy);
    return genotype.aa
      ? "aa"
      : "nuc";
  }
  return false;
};

/**
* what colorBy trait names are present in the tree but _not_ in the provided scale?
* @param {Array} nodes - list of nodes
* @param {Array|undefined} nodesToo - list of nodes for the second tree
* @param {string} colorBy -
* @param {Array} providedVals - list of provided trait values
* @return {list}
*/
export const getExtraVals = (nodes, nodesToo, colorBy, providedVals) => {
  let valsInTree = nodes.map((n) => getTraitFromNode(n, colorBy));
  if (nodesToo) {
    nodesToo.forEach((n) => valsInTree.push(getTraitFromNode(n, colorBy)));
  }
  valsInTree = [...new Set(valsInTree)];
  return valsInTree.filter((x) => providedVals.indexOf(x) === -1);
};

/* a getter for the value of the colour attribute of the node provided for the currently set colour
note this is not the colour HEX */
export const getTipColorAttribute = (node, colorScale) => {
  if (isColorByGenotype(colorScale.colorBy) && colorScale.genotype) {
    return node.currentGt;
  }
  return getTraitFromNode(node, colorScale.colorBy);
};

/* generates and returns an array of colours (HEXs) for the nodes under the given colorScale */
/* takes around 2ms on a 2000 tip tree */
export const calcNodeColor = (tree, colorScale) => {
  if (tree && tree.nodes && colorScale && colorScale.colorBy) {
    const nodeColorAttr = tree.nodes.map((n) => getTipColorAttribute(n, colorScale));
    // console.log(nodeColorAttr.map((n) => colorScale.scale(n)))
    return nodeColorAttr.map((n) => colorScale.scale(n));
  }
  return null;
};

// scale entropy such that higher entropy maps to a grayer less-certain branch
const branchInterpolateColour = "#BBB";
const branchOpacityConstant = 0.6;
const branchOpacityLowerBound = 0.4;
export const branchOpacityFunction = scalePow()
  .exponent([0.3])
  .domain([0, 1])
  .range([branchOpacityLowerBound, 1])
  .clamp(true);
// entropy calculation precomputed in augur
// export const calcEntropyOfValues = (vals) =>
//   vals.map((v) => v * Math.log(v + 1E-10)).reduce((a, b) => a + b, 0) * -1 / Math.log(vals.length);

/**
 * calculate array of HEXs to actually be displayed.
 * (colorBy) confidences manifest as opacity ramps
 * @param {obj} tree phyloTree object
 * @param {bool} confidence enabled?
 * @return {array} array of hex's. 1-1 with nodes.
 */
export const calcBranchStrokeCols = (tree, confidence, colorBy) => {
  if (confidence === true) {
    return tree.nodeColors.map((col, idx) => {
      const entropy = getTraitFromNode(tree.nodes[idx], colorBy, {entropy: true});
      const opacity = entropy ? branchOpacityFunction(entropy) : branchOpacityConstant;
      return rgb(interpolateRgb(col, branchInterpolateColour)(opacity)).toString();
    });
  }
  return tree.nodeColors.map((col) => {
    return rgb(interpolateRgb(col, branchInterpolateColour)(branchOpacityConstant)).toString();
  });
};

/**
 * Return an emphasized color
 */
export const getEmphasizedColor = (color) => {
  const hslColor = hsl(color);
  hslColor.s *= 1.8; // more saturation
  hslColor.l /= 1.2; // less luminance
  return rgb(hslColor).toString();
};
<commit_msg>restore branch entropy blending values<commit_after>import { rgb, hsl } from "d3-color";
import { interpolateRgb } from "d3-interpolate";
import { scalePow } from "d3-scale";
import { isColorByGenotype, decodeColorByGenotype } from "./getGenotype";
import { getTraitFromNode } from "./treeMiscHelpers";

/**
 * Average over the visible colours for a given location
 * @param {array} nodes list of nodes whose colours we want to average over
 * @param {array} nodeColours (redux state) -- list of node hexes. Not in 1-1 correspondence with `nodes`.
 * @returns {str} a color hex string representing the average of the array.
 */
export const getAverageColorFromNodes = (nodes, nodeColors) => {
  let r=0, g=0, b=0;
  nodes.forEach((n) => {
    const tmpRGB = rgb(nodeColors[n.arrayIdx]);
    r += tmpRGB.r;
    g += tmpRGB.g;
    b += tmpRGB.b;
  });
  const total = nodes.length;
  const avg = rgb(r/total, g/total, b/total);
  return avg.toString();
};

export const determineColorByGenotypeMutType = (colorBy) => {
  if (isColorByGenotype(colorBy)) {
    const genotype = decodeColorByGenotype(colorBy);
    return genotype.aa
      ? "aa"
      : "nuc";
  }
  return false;
};

/**
* what colorBy trait names are present in the tree but _not_ in the provided scale?
* @param {Array} nodes - list of nodes
* @param {Array|undefined} nodesToo - list of nodes for the second tree
* @param {string} colorBy -
* @param {Array} providedVals - list of provided trait values
* @return {list}
*/
export const getExtraVals = (nodes, nodesToo, colorBy, providedVals) => {
  let valsInTree = nodes.map((n) => getTraitFromNode(n, colorBy));
  if (nodesToo) {
    nodesToo.forEach((n) => valsInTree.push(getTraitFromNode(n, colorBy)));
  }
  valsInTree = [...new Set(valsInTree)];
  return valsInTree.filter((x) => providedVals.indexOf(x) === -1);
};

/* a getter for the value of the colour attribute of the node provided for the currently set colour
note this is not the colour HEX */
export const getTipColorAttribute = (node, colorScale) => {
  if (isColorByGenotype(colorScale.colorBy) && colorScale.genotype) {
    return node.currentGt;
  }
  return getTraitFromNode(node, colorScale.colorBy);
};

/* generates and returns an array of colours (HEXs) for the nodes under the given colorScale */
/* takes around 2ms on a 2000 tip tree */
export const calcNodeColor = (tree, colorScale) => {
  if (tree && tree.nodes && colorScale && colorScale.colorBy) {
    const nodeColorAttr = tree.nodes.map((n) => getTipColorAttribute(n, colorScale));
    // console.log(nodeColorAttr.map((n) => colorScale.scale(n)))
    return nodeColorAttr.map((n) => colorScale.scale(n));
  }
  return null;
};

// scale entropy such that higher entropy maps to a grayer less-certain branch
const branchInterpolateColour = "#BBB";
const branchOpacityConstant = 0.6;
export const branchOpacityFunction = scalePow()
  .exponent([0.6])
  .domain([0, 2.0])
  .range([0.4, 1])
  .clamp(true);

// entropy calculation precomputed in augur
// export const calcEntropyOfValues = (vals) =>
//   vals.map((v) => v * Math.log(v + 1E-10)).reduce((a, b) => a + b, 0) * -1 / Math.log(vals.length);

/**
 * calculate array of HEXs to actually be displayed.
 * (colorBy) confidences manifest as opacity ramps
 * @param {obj} tree phyloTree object
 * @param {bool} confidence enabled?
 * @return {array} array of hex's. 1-1 with nodes.
 */
export const calcBranchStrokeCols = (tree, confidence, colorBy) => {
  if (confidence === true) {
    return tree.nodeColors.map((col, idx) => {
      const entropy = getTraitFromNode(tree.nodes[idx], colorBy, {entropy: true});
      const opacity = entropy ? branchOpacityFunction(entropy) : branchOpacityConstant;
      return rgb(interpolateRgb(col, branchInterpolateColour)(opacity)).toString();
    });
  }
  return tree.nodeColors.map((col) => {
    return rgb(interpolateRgb(col, branchInterpolateColour)(branchOpacityConstant)).toString();
  });
};

/**
 * Return an emphasized color
 */
export const getEmphasizedColor = (color) => {
  const hslColor = hsl(color);
  hslColor.s *= 1.8; // more saturation
  hslColor.l /= 1.2; // less luminance
  return rgb(hslColor).toString();
};
<|endoftext|>


<commit_before>function WeightedRandomPicker (initialWeightedValues) {

  //clones the initialWeightedValues:
  //set default to empty object for the JSON lib clone trick to work
  var initialWeightedValues = initialWeightedValues || {};
  // clones param object to property of this
  this.weightedValues = JSON.parse(JSON.stringify(initialWeightedValues));

  this.randomPick = function(options){
    var options = options || {};
    var uniq = options.uniq || false;
    var times = options.times || 1;
    var picks = [];

    if (uniq && times > this.getKeys().length) {
      throw new InvalidUniqTimesException();
    } else {
      for (var i=0; i<times; i++){
        picks.push(_randomPick.call(this, uniq));
      }
      if (uniq) {
        // uniq picks deletes from this.weightedValues
        // re-clone from initial after uniq pick completes
        this.weightedValues = JSON.parse(JSON.stringify(initialWeightedValues));
      }
    }

    return options.times > 1 ? picks : picks[0];
  };

  this.getKeys = function(){
    return Object.keys(this.weightedValues);
  };

  this.getValues = function(){
    var values = this.getKeys().map(function(key) {
      return this.weightedValues[key];
    }.bind(this));
    return values;
  };

  var InvalidUniqTimesException = function(){
    this.toString = function(){
      return "While uniq is set to true, times option cannot be more than what the actual number of key/values in object. Please confirm.";
    };
  };

  var _getSumOfWeights = function(){
    var sumOfWeights = this.getValues().reduce(function(a,b){
        return a + b;
      });
    return sumOfWeights;
  };

  var _getRandom = function(max, min){
    var min = min || 0;
    var max = max || _getSumOfWeights.call(this);
    return Math.random() * max;
  };

  var _randomPick = function(uniq){
    var random = _getRandom.call(this);
    var pick;
    this.getKeys().forEach(function(key, index){
      var weight = this.weightedValues[key];
      if (weight >= random) {
        pick = pick || key; //only store the first pick
        if (uniq && this.weightedValues[pick]) {
          // delete from pick not from key since
          // it is not breaking out from forEach
          // also checks if the pick exists in
          // the object
          delete this.weightedValues[pick];
        }
      }
      random -= weight;
    }.bind(this));
    return pick;
  };
}

WeightedRandomPicker.pickFrom = function(weightedValues, options){
  var options = options || {};
  return (new WeightedRandomPicker(weightedValues)).randomPick(options);
};<commit_msg>remove redundant var delcarations in randomPick<commit_after>function WeightedRandomPicker (initialWeightedValues) {

  //clones the initialWeightedValues:
  //set default to empty object for the JSON lib clone trick to work
  var initialWeightedValues = initialWeightedValues || {};
  // clones param object to property of this
  this.weightedValues = JSON.parse(JSON.stringify(initialWeightedValues));

  this.randomPick = function(options){
    var picks = [];
    var options = options || {};
    options.uniq = options.uniq || false;
    options.times = options.times || 1;

    if (options.uniq && options.times > this.getKeys().length) {
      throw new InvalidUniqTimesException();
    } else {
      for (var i=0; i<options.times; i++){
        picks.push(_randomPick.call(this, options.uniq));
      }
      if (options.uniq) {
        // uniq picks deletes from this.weightedValues
        // re-clone from initial after uniq pick completes
        this.weightedValues = JSON.parse(JSON.stringify(initialWeightedValues));
      }
    }

    return options.times > 1 ? picks : picks[0];
  };

  this.getKeys = function(){
    return Object.keys(this.weightedValues);
  };

  this.getValues = function(){
    var values = this.getKeys().map(function(key) {
      return this.weightedValues[key];
    }.bind(this));
    return values;
  };

  var InvalidUniqTimesException = function(){
    this.toString = function(){
      return "While uniq is set to true, times option cannot be more than what the actual number of key/values in object. Please confirm.";
    };
  };

  var _getSumOfWeights = function(){
    var sumOfWeights = this.getValues().reduce(function(a,b){
        return a + b;
      });
    return sumOfWeights;
  };

  var _getRandom = function(max, min){
    var min = min || 0;
    var max = max || _getSumOfWeights.call(this);
    return Math.random() * max;
  };

  var _randomPick = function(uniq){
    var random = _getRandom.call(this);
    var pick;
    this.getKeys().forEach(function(key, index){
      var weight = this.weightedValues[key];
      if (weight >= random) {
        pick = pick || key; //only store the first pick
        if (uniq && this.weightedValues[pick]) {
          // delete from pick not from key since
          // it is not breaking out from forEach
          // also checks if the pick exists in
          // the object
          delete this.weightedValues[pick];
        }
      }
      random -= weight;
    }.bind(this));
    return pick;
  };
}

WeightedRandomPicker.pickFrom = function(weightedValues, options){
  var options = options || {};
  return (new WeightedRandomPicker(weightedValues)).randomPick(options);
};<|endoftext|>


<commit_before>define(
  'ephox.alloy.construct.ComponentEvents',

  [
    'ephox.alloy.util.ObjIndex',
    'ephox.alloy.util.PrioritySort',
    'ephox.boulder.api.FieldPresence',
    'ephox.boulder.api.FieldSchema',
    'ephox.boulder.api.Objects',
    'ephox.boulder.api.ValueSchema',
    'ephox.compass.Arr',
    'ephox.compass.Obj',
    'ephox.highway.Merger',
    'ephox.numerosity.api.JSON',
    'ephox.peanut.Fun',
    'ephox.perhaps.Result',
    'global!Array',
    'global!Error'
  ],

  function (ObjIndex, PrioritySort, FieldPresence, FieldSchema, Objects, ValueSchema, Arr, Obj, Merger, Json, Fun, Result, Array, Error) {
    var behaviourListener = function (name, listener) {
      return {
        name: Fun.constant(name),
        listener: Fun.constant(listener)
      };
    };

    var handler = function (parts) {
      return ValueSchema.asRaw('Extracting handler', ValueSchema.objOf([
        FieldSchema.field('can', 'can', FieldPresence.defaulted(Fun.constant(true)), ValueSchema.anyValue()),
        FieldSchema.field('abort', 'abort', FieldPresence.defaulted(Fun.constant(false)), ValueSchema.anyValue()),
        FieldSchema.field('run', 'run', FieldPresence.defaulted(Fun.noop), ValueSchema.anyValue())
      ]), parts).getOrDie();
    };

    var nameToHandlers = function (behaviours, info) {
      var r = {};
      Arr.each(behaviours, function (behaviour) {
        r[behaviour.name()] = behaviour.handlers(info);
      });
      return r;
    };

    var combine = function (info, behaviours, base) {
      // FIX: behaviourEvents['lab.custom.definition.events'] = CustomDefinition.toEvents(info);
      var behaviourEvents = Merger.deepMerge(base, nameToHandlers(behaviours, info));
      console.log('behaviourEvents', JSON.stringify(behaviourEvents, null, 2));

      // Now, with all of these events, we need to get a list of behaviours
      var eventChains = ObjIndex.byInnerKey(behaviourEvents, behaviourListener);

      // so event chains should be:
      // { eventName => [ (behaviourName, (can,run,abort)) ] }

      console.log('event.chains', JSON.stringify(eventChains, null, 2));

      var output = combineEventLists(eventChains, info.eventOrder());
      console.log('output', output);
      return output;
    };

    var assemble = function (listener) {
      console.log('assemble.listener', listener);
      return function (component, simulatedEvent/*, others */) {
        console.log('running.listener', listener);
        var args = Array.prototype.slice.call(arguments, 0);
        if (listener.abort.apply(undefined, args)) {
          simulatedEvent.stop();
        } else if (listener.can.apply(undefined, args)) {
          listener.run.apply(undefined, args);
        }
      };
    };

    var missingOrderError = function (eventName, listeners) {
      return new Result.error(
        'The event (' + eventName + ') has more than one behaviour that listens to it.\nWhen this occurs, you must ' +
        'specify an event ordering for the behaviours in your spec (e.g. [ "listing", "toggling" ]).\nThe behaviours that ' +
        'can trigger it are: ' + Json.stringify(Arr.map(listeners, function (c) { return c.name(); }), null, 2)
      );
    };

    var fuse = function (listeners, eventOrder, eventName) {
      console.log('fuse.listeners', listeners);
      var order = eventOrder[eventName];
      if (! order) return missingOrderError(eventName, listeners);
      else return PrioritySort.sortKeys('Event', 'name', listeners, order).map(function (sorted) {
        console.log('sorted');
        var can = function () {
          var args = Array.prototype.slice.call(arguments, 0);
          return Arr.foldl(sorted, function (acc, listener) {
            return acc && listener.can.apply(undefined, args);
          }, true);
        };

        var run = function () {
          var args = Array.prototype.slice.call(arguments, 0);
          Arr.each(sorted, function (listener) {
            listener.run.apply(undefined, args);
          });
        };

        var abort = function () {
          var args = Array.prototype.slice.call(arguments, 0);
          return Arr.foldl(sorted, function (acc, listener) {
            return acc || listener.abort.apply(undefined, args);
          }, false);
        };

        return handler({
          can: can,
          abort: abort,
          run: run
        });
      });
    };

    var combineEventLists = function (eventLists, eventOrder) {
      console.log('eventLists', eventLists);
      return Obj.map(eventLists, function (listeners, eventName) {
        console.log('listeners', listeners);
        var combined = listeners.length === 1 ? Result.value(listeners[0].listener()) : fuse(listeners, eventOrder, eventName);
        console.log('combine.events', combined.getOr('none'), listeners);
        return combined.map(assemble).getOrDie();
      });
    };

    return {
      combine: combine,
      handler: handler
    };
  }
);<commit_msg>VAN-1: cleanup and renaming<commit_after>define(
  'ephox.alloy.construct.ComponentEvents',

  [
    'ephox.alloy.util.ObjIndex',
    'ephox.alloy.util.PrioritySort',
    'ephox.boulder.api.FieldPresence',
    'ephox.boulder.api.FieldSchema',
    'ephox.boulder.api.Objects',
    'ephox.boulder.api.ValueSchema',
    'ephox.compass.Arr',
    'ephox.compass.Obj',
    'ephox.highway.Merger',
    'ephox.numerosity.api.JSON',
    'ephox.peanut.Fun',
    'ephox.perhaps.Result',
    'global!Array',
    'global!Error'
  ],

  function (ObjIndex, PrioritySort, FieldPresence, FieldSchema, Objects, ValueSchema, Arr, Obj, Merger, Json, Fun, Result, Array, Error) {
    var behaviourListener = function (name, listener) {
      return {
        name: Fun.constant(name),
        listener: Fun.constant(listener)
      };
    };

    var handler = function (parts) {
      return ValueSchema.asRaw('Extracting handler', ValueSchema.objOf([
        FieldSchema.field('can', 'can', FieldPresence.defaulted(Fun.constant(true)), ValueSchema.anyValue()),
        FieldSchema.field('abort', 'abort', FieldPresence.defaulted(Fun.constant(false)), ValueSchema.anyValue()),
        FieldSchema.field('run', 'run', FieldPresence.defaulted(Fun.noop), ValueSchema.anyValue())
      ]), parts).getOrDie();
    };

    var nameToHandlers = function (behaviours, info) {
      var r = {};
      Arr.each(behaviours, function (behaviour) {
        r[behaviour.name()] = behaviour.handlers(info);
      });
      return r;
    };

    var combine = function (info, behaviours, base) {
      // FIX: behaviourEvents['lab.custom.definition.events'] = CustomDefinition.toEvents(info);
      var behaviourEvents = Merger.deepMerge(base, nameToHandlers(behaviours, info));

      // Now, with all of these events, we need to index by event name
      var byEventName = ObjIndex.byInnerKey(behaviourEvents, behaviourListener);

      return combineEventLists(byEventName, info.eventOrder());
    };

    var assemble = function (listener) {
      console.log('assemble.listener', listener);
      return function (component, simulatedEvent/*, others */) {
        console.log('running.listener', listener);
        var args = Array.prototype.slice.call(arguments, 0);
        if (listener.abort.apply(undefined, args)) {
          simulatedEvent.stop();
        } else if (listener.can.apply(undefined, args)) {
          listener.run.apply(undefined, args);
        }
      };
    };

    var missingOrderError = function (eventName, listeners) {
      return new Result.error(
        'The event (' + eventName + ') has more than one behaviour that listens to it.\nWhen this occurs, you must ' +
        'specify an event ordering for the behaviours in your spec (e.g. [ "listing", "toggling" ]).\nThe behaviours that ' +
        'can trigger it are: ' + Json.stringify(Arr.map(listeners, function (c) { return c.name(); }), null, 2)
      );
    };

    var fuse = function (listeners, eventOrder, eventName) {
      console.log('fuse.listeners', listeners);
      var order = eventOrder[eventName];
      if (! order) return missingOrderError(eventName, listeners);
      else return PrioritySort.sortKeys('Event', 'name', listeners, order).map(function (sorted) {
        console.log('sorted');
        var can = function () {
          var args = Array.prototype.slice.call(arguments, 0);
          return Arr.foldl(sorted, function (acc, listener) {
            return acc && listener.can.apply(undefined, args);
          }, true);
        };

        var run = function () {
          var args = Array.prototype.slice.call(arguments, 0);
          Arr.each(sorted, function (listener) {
            listener.run.apply(undefined, args);
          });
        };

        var abort = function () {
          var args = Array.prototype.slice.call(arguments, 0);
          return Arr.foldl(sorted, function (acc, listener) {
            return acc || listener.abort.apply(undefined, args);
          }, false);
        };

        return handler({
          can: can,
          abort: abort,
          run: run
        });
      });
    };

    var combineEventLists = function (eventLists, eventOrder) {
      console.log('eventLists', eventLists);
      return Obj.map(eventLists, function (listeners, eventName) {
        console.log('listeners', listeners);
        var combined = listeners.length === 1 ? Result.value(listeners[0].listener()) : fuse(listeners, eventOrder, eventName);
        console.log('combine.events', combined.getOr('none'), listeners);
        return combined.map(assemble).getOrDie();
      });
    };

    return {
      combine: combine,
      handler: handler
    };
  }
);<|endoftext|>


<commit_before>/**
 * Copyright 2013 Facebook, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @providesModule Transaction
 */

"use strict";

var invariant = require('invariant');

/**
 * `Transaction` creates a black box that is able to wrap any method such that
 * certain invariants are maintained before and after the method is invoked
 * (Even if an exception is thrown while invoking the wrapped method). Whoever
 * instantiates a transaction can provide enforcers of the invariants at
 * creation time. The `Transaction` class itself will supply one additional
 * automatic invariant for you - the invariant that any transaction instance
 * should not be ran while it is already being ran. You would typically create a
 * single instance of a `Transaction` for reuse multiple times, that potentially
 * is used to wrap several different methods. Wrappers are extremely simple -
 * they only require implementing two methods.
 *
 *

 *                       wrappers (injected at creation time)
 *                                      +        +
 *                                      |        |
 *                    +-----------------|--------|--------------+
 *                    |                 v        |              |
 *                    |      +---------------+   |              |
 *                    |   +--|    wrapper1   |---|----+         |
 *                    |   |  +---------------+   v    |         |
 *                    |   |          +-------------+  |         |
 *                    |   |     +----|   wrapper2  |--------+   |
 *                    |   |     |    +-------------+  |     |   |
 *                    |   |     |                     |     |   |
 *                    |   v     v                     v     v   | wrapper
 *                    | +---+ +---+   +---------+   +---+ +---+ | invariants
 * perform(anyMethod) | |   | |   |   |         |   |   | |   | | maintained
 * +----------------->|-|---|-|---|-->|anyMethod|---|---|-|---|-|-------->
 *                    | |   | |   |   |         |   |   | |   | |
 *                    | |   | |   |   |         |   |   | |   | |
 *                    | |   | |   |   |         |   |   | |   | |
 *                    | +---+ +---+   +---------+   +---+ +---+ |
 *                    |  initialize                    close    |
 *                    +-----------------------------------------+
 *

 *
 * Bonus:
 * - Reports timing metrics by method name and wrapper index.
 *
 * Use cases:
 * - Preserving the input selection ranges before/after reconciliation.
 *   Restoring selection even in the event of an unexpected error.
 * - Deactivating events while rearranging the DOM, preventing blurs/focuses,
 *   while guaranteeing that afterwards, the event system is reactivated.
 * - Flushing a queue of collected DOM mutations to the main UI thread after a
 *   reconciliation takes place in a worker thread.
 * - Invoking any collected `componentDidRender` callbacks after rendering new
 *   content.
 * - (Future use case): Wrapping particular flushes of the `ReactWorker` queue
 *   to preserve the `scrollTop` (an automatic scroll aware DOM).
 * - (Future use case): Layout calculations before and after DOM upates.
 *
 * Transactional plugin API:
 * - A module that has an `initialize` method that returns any precomputation.
 * - and a `close` method that accepts the precomputation. `close` is invoked
 *   when the wrapped process is completed, or has failed.
 *
 * @param {Array<TransactionalWrapper>} transactionWrapper Wrapper modules
 * that implement `initialize` and `close`.
 * @return {Transaction} Single transaction for reuse in thread.
 *
 * @class Transaction
 */
var Mixin = {
  /**
   * Sets up this instance so that it is prepared for collecting metrics. Does
   * so such that this setup method may be used on an instance that is already
   * initialized, in a way that does not consume additional memory upon reuse.
   * That can be useful if you decide to make your subclass of this mixin a
   * "PooledClass".
   */
  reinitializeTransaction: function() {
    this.transactionWrappers = this.getTransactionWrappers();
    if (!this.wrapperInitData) {
      this.wrapperInitData = [];
    } else {
      this.wrapperInitData.length = 0;
    }
    if (!this.timingMetrics) {
      this.timingMetrics = {};
    }
    this.timingMetrics.methodInvocationTime = 0;
    if (!this.timingMetrics.wrapperInitTimes) {
      this.timingMetrics.wrapperInitTimes = [];
    } else {
      this.timingMetrics.wrapperInitTimes.length = 0;
    }
    if (!this.timingMetrics.wrapperCloseTimes) {
      this.timingMetrics.wrapperCloseTimes = [];
    } else {
      this.timingMetrics.wrapperCloseTimes.length = 0;
    }
    this._isInTransaction = false;
  },

  _isInTransaction: false,

  /**
   * @abstract
   * @return {Array<TransactionWrapper>} Array of transaction wrappers.
   */
  getTransactionWrappers: null,

  isInTransaction: function() {
    return !!this._isInTransaction;
  },

  /**
   * Executes the function within a safety window. Use this for the top level
   * methods that result in large amounts of computation/mutations that would
   * need to be safety checked.
   *
   * @param {function} method Member of scope to call.
   * @param {Object} scope Scope to invoke from.
   * @param {Object?=} args... Arguments to pass to the method (optional).
   *                           Helps prevent need to bind in many cases.
   * @return Return value from `method`.
   */
  perform: function(method, scope, a, b, c, d, e, f) {
    invariant(
      !this.isInTransaction(),
      'Transaction.perform(...): Cannot initialize a transaction when there ' +
      'is already an outstanding transaction.'
    );
    var memberStart = Date.now();
    var errorToThrow = null;
    var ret;
    try {
      this.initializeAll();
      ret = method.call(scope, a, b, c, d, e, f);
    } catch (error) {
      // IE8 requires `catch` in order to use `finally`.
      errorToThrow = error;
    } finally {
      var memberEnd = Date.now();
      this.methodInvocationTime += (memberEnd - memberStart);
      try {
        this.closeAll();
      } catch (closeError) {
        // If `method` throws, prefer to show that stack trace over any thrown
        // by invoking `closeAll`.
        errorToThrow = errorToThrow || closeError;
      }
    }
    if (errorToThrow) {
      throw errorToThrow;
    }
    return ret;
  },

  initializeAll: function() {
    this._isInTransaction = true;
    var transactionWrappers = this.transactionWrappers;
    var wrapperInitTimes = this.timingMetrics.wrapperInitTimes;
    var errorToThrow = null;
    for (var i = 0; i < transactionWrappers.length; i++) {
      var initStart = Date.now();
      var wrapper = transactionWrappers[i];
      try {
        this.wrapperInitData[i] = wrapper.initialize ?
          wrapper.initialize.call(this) :
          null;
      } catch (initError) {
        // Prefer to show the stack trace of the first error.
        errorToThrow = errorToThrow || initError;
        this.wrapperInitData[i] = Transaction.OBSERVED_ERROR;
      } finally {
        var curInitTime = wrapperInitTimes[i];
        var initEnd = Date.now();
        wrapperInitTimes[i] = (curInitTime || 0) + (initEnd - initStart);
      }
    }
    if (errorToThrow) {
      throw errorToThrow;
    }
  },

  /**
   * Invokes each of `this.transactionWrappers.close[i]` functions, passing into
   * them the respective return values of `this.transactionWrappers.init[i]`
   * (`close`rs that correspond to initializers that failed will not be
   * invoked).
   */
  closeAll: function() {
    invariant(
      this.isInTransaction(),
      'Transaction.closeAll(): Cannot close transaction when none are open.'
    );
    var transactionWrappers = this.transactionWrappers;
    var wrapperCloseTimes = this.timingMetrics.wrapperCloseTimes;
    var errorToThrow = null;
    for (var i = 0; i < transactionWrappers.length; i++) {
      var wrapper = transactionWrappers[i];
      var closeStart = Date.now();
      var initData = this.wrapperInitData[i];
      try {
        if (initData !== Transaction.OBSERVED_ERROR) {
          wrapper.close && wrapper.close.call(this, initData);
        }
      } catch (closeError) {
        // Prefer to show the stack trace of the first error.
        errorToThrow = errorToThrow || closeError;
      } finally {
        var closeEnd = Date.now();
        var curCloseTime = wrapperCloseTimes[i];
        wrapperCloseTimes[i] = (curCloseTime || 0) + (closeEnd - closeStart);
      }
    }
    this.wrapperInitData.length = 0;
    this._isInTransaction = false;
    if (errorToThrow) {
      throw errorToThrow;
    }
  }
};

var Transaction = {

  Mixin: Mixin,

  /**
   * Token to look for to determine if an error occured.
   */
  OBSERVED_ERROR: {}

};

module.exports = Transaction;
<commit_msg>fix transaction comment from componentDidRender to componentDidUpdate<commit_after>/**
 * Copyright 2013 Facebook, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @providesModule Transaction
 */

"use strict";

var invariant = require('invariant');

/**
 * `Transaction` creates a black box that is able to wrap any method such that
 * certain invariants are maintained before and after the method is invoked
 * (Even if an exception is thrown while invoking the wrapped method). Whoever
 * instantiates a transaction can provide enforcers of the invariants at
 * creation time. The `Transaction` class itself will supply one additional
 * automatic invariant for you - the invariant that any transaction instance
 * should not be ran while it is already being ran. You would typically create a
 * single instance of a `Transaction` for reuse multiple times, that potentially
 * is used to wrap several different methods. Wrappers are extremely simple -
 * they only require implementing two methods.
 *
 *

 *                       wrappers (injected at creation time)
 *                                      +        +
 *                                      |        |
 *                    +-----------------|--------|--------------+
 *                    |                 v        |              |
 *                    |      +---------------+   |              |
 *                    |   +--|    wrapper1   |---|----+         |
 *                    |   |  +---------------+   v    |         |
 *                    |   |          +-------------+  |         |
 *                    |   |     +----|   wrapper2  |--------+   |
 *                    |   |     |    +-------------+  |     |   |
 *                    |   |     |                     |     |   |
 *                    |   v     v                     v     v   | wrapper
 *                    | +---+ +---+   +---------+   +---+ +---+ | invariants
 * perform(anyMethod) | |   | |   |   |         |   |   | |   | | maintained
 * +----------------->|-|---|-|---|-->|anyMethod|---|---|-|---|-|-------->
 *                    | |   | |   |   |         |   |   | |   | |
 *                    | |   | |   |   |         |   |   | |   | |
 *                    | |   | |   |   |         |   |   | |   | |
 *                    | +---+ +---+   +---------+   +---+ +---+ |
 *                    |  initialize                    close    |
 *                    +-----------------------------------------+
 *

 *
 * Bonus:
 * - Reports timing metrics by method name and wrapper index.
 *
 * Use cases:
 * - Preserving the input selection ranges before/after reconciliation.
 *   Restoring selection even in the event of an unexpected error.
 * - Deactivating events while rearranging the DOM, preventing blurs/focuses,
 *   while guaranteeing that afterwards, the event system is reactivated.
 * - Flushing a queue of collected DOM mutations to the main UI thread after a
 *   reconciliation takes place in a worker thread.
 * - Invoking any collected `componentDidUpdate` callbacks after rendering new
 *   content.
 * - (Future use case): Wrapping particular flushes of the `ReactWorker` queue
 *   to preserve the `scrollTop` (an automatic scroll aware DOM).
 * - (Future use case): Layout calculations before and after DOM upates.
 *
 * Transactional plugin API:
 * - A module that has an `initialize` method that returns any precomputation.
 * - and a `close` method that accepts the precomputation. `close` is invoked
 *   when the wrapped process is completed, or has failed.
 *
 * @param {Array<TransactionalWrapper>} transactionWrapper Wrapper modules
 * that implement `initialize` and `close`.
 * @return {Transaction} Single transaction for reuse in thread.
 *
 * @class Transaction
 */
var Mixin = {
  /**
   * Sets up this instance so that it is prepared for collecting metrics. Does
   * so such that this setup method may be used on an instance that is already
   * initialized, in a way that does not consume additional memory upon reuse.
   * That can be useful if you decide to make your subclass of this mixin a
   * "PooledClass".
   */
  reinitializeTransaction: function() {
    this.transactionWrappers = this.getTransactionWrappers();
    if (!this.wrapperInitData) {
      this.wrapperInitData = [];
    } else {
      this.wrapperInitData.length = 0;
    }
    if (!this.timingMetrics) {
      this.timingMetrics = {};
    }
    this.timingMetrics.methodInvocationTime = 0;
    if (!this.timingMetrics.wrapperInitTimes) {
      this.timingMetrics.wrapperInitTimes = [];
    } else {
      this.timingMetrics.wrapperInitTimes.length = 0;
    }
    if (!this.timingMetrics.wrapperCloseTimes) {
      this.timingMetrics.wrapperCloseTimes = [];
    } else {
      this.timingMetrics.wrapperCloseTimes.length = 0;
    }
    this._isInTransaction = false;
  },

  _isInTransaction: false,

  /**
   * @abstract
   * @return {Array<TransactionWrapper>} Array of transaction wrappers.
   */
  getTransactionWrappers: null,

  isInTransaction: function() {
    return !!this._isInTransaction;
  },

  /**
   * Executes the function within a safety window. Use this for the top level
   * methods that result in large amounts of computation/mutations that would
   * need to be safety checked.
   *
   * @param {function} method Member of scope to call.
   * @param {Object} scope Scope to invoke from.
   * @param {Object?=} args... Arguments to pass to the method (optional).
   *                           Helps prevent need to bind in many cases.
   * @return Return value from `method`.
   */
  perform: function(method, scope, a, b, c, d, e, f) {
    invariant(
      !this.isInTransaction(),
      'Transaction.perform(...): Cannot initialize a transaction when there ' +
      'is already an outstanding transaction.'
    );
    var memberStart = Date.now();
    var errorToThrow = null;
    var ret;
    try {
      this.initializeAll();
      ret = method.call(scope, a, b, c, d, e, f);
    } catch (error) {
      // IE8 requires `catch` in order to use `finally`.
      errorToThrow = error;
    } finally {
      var memberEnd = Date.now();
      this.methodInvocationTime += (memberEnd - memberStart);
      try {
        this.closeAll();
      } catch (closeError) {
        // If `method` throws, prefer to show that stack trace over any thrown
        // by invoking `closeAll`.
        errorToThrow = errorToThrow || closeError;
      }
    }
    if (errorToThrow) {
      throw errorToThrow;
    }
    return ret;
  },

  initializeAll: function() {
    this._isInTransaction = true;
    var transactionWrappers = this.transactionWrappers;
    var wrapperInitTimes = this.timingMetrics.wrapperInitTimes;
    var errorToThrow = null;
    for (var i = 0; i < transactionWrappers.length; i++) {
      var initStart = Date.now();
      var wrapper = transactionWrappers[i];
      try {
        this.wrapperInitData[i] = wrapper.initialize ?
          wrapper.initialize.call(this) :
          null;
      } catch (initError) {
        // Prefer to show the stack trace of the first error.
        errorToThrow = errorToThrow || initError;
        this.wrapperInitData[i] = Transaction.OBSERVED_ERROR;
      } finally {
        var curInitTime = wrapperInitTimes[i];
        var initEnd = Date.now();
        wrapperInitTimes[i] = (curInitTime || 0) + (initEnd - initStart);
      }
    }
    if (errorToThrow) {
      throw errorToThrow;
    }
  },

  /**
   * Invokes each of `this.transactionWrappers.close[i]` functions, passing into
   * them the respective return values of `this.transactionWrappers.init[i]`
   * (`close`rs that correspond to initializers that failed will not be
   * invoked).
   */
  closeAll: function() {
    invariant(
      this.isInTransaction(),
      'Transaction.closeAll(): Cannot close transaction when none are open.'
    );
    var transactionWrappers = this.transactionWrappers;
    var wrapperCloseTimes = this.timingMetrics.wrapperCloseTimes;
    var errorToThrow = null;
    for (var i = 0; i < transactionWrappers.length; i++) {
      var wrapper = transactionWrappers[i];
      var closeStart = Date.now();
      var initData = this.wrapperInitData[i];
      try {
        if (initData !== Transaction.OBSERVED_ERROR) {
          wrapper.close && wrapper.close.call(this, initData);
        }
      } catch (closeError) {
        // Prefer to show the stack trace of the first error.
        errorToThrow = errorToThrow || closeError;
      } finally {
        var closeEnd = Date.now();
        var curCloseTime = wrapperCloseTimes[i];
        wrapperCloseTimes[i] = (curCloseTime || 0) + (closeEnd - closeStart);
      }
    }
    this.wrapperInitData.length = 0;
    this._isInTransaction = false;
    if (errorToThrow) {
      throw errorToThrow;
    }
  }
};

var Transaction = {

  Mixin: Mixin,

  /**
   * Token to look for to determine if an error occured.
   */
  OBSERVED_ERROR: {}

};

module.exports = Transaction;
<|endoftext|>


<commit_before>/**
 * Calculate the position of tooltip
 *
 * @params
 * - `e` {Event} the event of current mouse
 * - `target` {Element} the currentTarget of the event
 * - `node` {DOM} the react-tooltip object
 * - `place` {String} top / right / bottom / left
 * - `effect` {String} float / solid
 * - `offset` {Object} the offset to default position
 *
 * @return {Object
 * - `isNewState` {Bool} required
 * - `newState` {Object}
 * - `position` {OBject} {left: {Number}, top: {Number}}
 */
export default function (e, target, node, place, desiredPlace, effect, offset) {
  const {
    width: tipWidth,
    height: tipHeight
  } = getDimensions(node)

  const {
    width: targetWidth,
    height: targetHeight
  } = getDimensions(target)

  const {mouseX, mouseY} = getCurrentOffset(e, target, effect)
  const defaultOffset = getDefaultPosition(effect, targetWidth, targetHeight, tipWidth, tipHeight)
  const {extraOffset_X, extraOffset_Y} = calculateOffset(offset)

  const windowWidth = window.innerWidth
  const windowHeight = window.innerHeight

  const {parentTop, parentLeft} = getParent(node)

  // Get the edge offset of the tooltip
  const getTipOffsetLeft = (place) => {
    const offset_X = defaultOffset[place].l
    return mouseX + offset_X + extraOffset_X
  }
  const getTipOffsetRight = (place) => {
    const offset_X = defaultOffset[place].r
    return mouseX + offset_X + extraOffset_X
  }
  const getTipOffsetTop = (place) => {
    const offset_Y = defaultOffset[place].t
    return mouseY + offset_Y + extraOffset_Y
  }
  const getTipOffsetBottom = (place) => {
    const offset_Y = defaultOffset[place].b
    return mouseY + offset_Y + extraOffset_Y
  }

  // Judge if the tooltip has over the window(screen)
  const outsideVertical = () => {
    let result = false
    let newPlace
    if (getTipOffsetTop('left') < 0 &&
      getTipOffsetBottom('left') <= windowHeight &&
      getTipOffsetBottom('bottom') <= windowHeight) {
      result = true
      newPlace = 'bottom'
    } else if (getTipOffsetBottom('left') > windowHeight &&
      getTipOffsetTop('left') >= 0 &&
      getTipOffsetTop('top') >= 0) {
      result = true
      newPlace = 'top'
    }
    return {result, newPlace}
  }
  const outsideLeft = () => {
    let {result, newPlace} = outsideVertical() // Deal with vertical as first priority
    if (result && outsideHorizontal().result) {
      return {result: false} // No need to change, if change to vertical will out of space
    }
    if (!result && getTipOffsetLeft('left') < 0 && getTipOffsetRight('right') <= windowWidth) {
      result = true // If vertical ok, but let out of side and right won't out of side
      newPlace = 'right'
    }
    return {result, newPlace}
  }
  const outsideRight = () => {
    let {result, newPlace} = outsideVertical()
    if (result && outsideHorizontal().result) {
      return {result: false} // No need to change, if change to vertical will out of space
    }
    if (!result && getTipOffsetRight('right') > windowWidth && getTipOffsetLeft('left') >= 0) {
      result = true
      newPlace = 'left'
    }
    return {result, newPlace}
  }

  const outsideHorizontal = () => {
    let result = false
    let newPlace
    if (getTipOffsetLeft('top') < 0 &&
      getTipOffsetRight('top') <= windowWidth &&
      getTipOffsetRight('right') <= windowWidth) {
      result = true
      newPlace = 'right'
    } else if (getTipOffsetRight('top') > windowWidth &&
      getTipOffsetLeft('top') >= 0 &&
      getTipOffsetLeft('left') >= 0) {
      result = true
      newPlace = 'left'
    }
    return {result, newPlace}
  }
  const outsideTop = () => {
    let {result, newPlace} = outsideHorizontal()
    if (result && outsideVertical().result) {
      return {result: false}
    }
    if (!result && getTipOffsetTop('top') < 0 && getTipOffsetBottom('bottom') <= windowHeight) {
      result = true
      newPlace = 'bottom'
    }
    return {result, newPlace}
  }
  const outsideBottom = () => {
    let {result, newPlace} = outsideHorizontal()
    if (result && outsideVertical().result) {
      return {result: false}
    }
    if (!result && getTipOffsetBottom('bottom') > windowHeight && getTipOffsetTop('top') >= 0) {
      result = true
      newPlace = 'top'
    }
    return {result, newPlace}
  }

  // Return new state to change the placement to the reverse if possible
  const outsideLeftResult = outsideLeft()
  const outsideRightResult = outsideRight()
  const outsideTopResult = outsideTop()
  const outsideBottomResult = outsideBottom()

  if (place === 'left' && outsideLeftResult.result) {
    return {
      isNewState: true,
      newState: {place: outsideLeftResult.newPlace}
    }
  } else if (place === 'right' && outsideRightResult.result) {
    return {
      isNewState: true,
      newState: {place: outsideRightResult.newPlace}
    }
  } else if (place === 'top' && outsideTopResult.result) {
    return {
      isNewState: true,
      newState: {place: outsideTopResult.newPlace}
    }
  } else if (place === 'bottom' && outsideBottomResult.result) {
    return {
      isNewState: true,
      newState: {place: outsideBottomResult.newPlace}
    }
  }

  // Change back to original place if possible
  if (place !== desiredPlace) {
    if (desiredPlace === 'top' && !outsideTopResult.result) {
      return {
        isNewState: true,
        newState: {place: 'top'}
      }
    } else if (desiredPlace === 'left' && !outsideLeftResult.result) {
      return {
        isNewState: true,
        newState: {place: 'left'}
      }
    } else if (desiredPlace === 'right' && !outsideRightResult.result) {
      return {
        isNewState: true,
        newState: {place: 'right'}
      }
    } else if (desiredPlace === 'bottom' && !outsideBottomResult.result) {
      return {
        isNewState: true,
        newState: {place: 'bottom'}
      }
    }
  }

  // Return tooltip offset position
  return {
    isNewState: false,
    position: {
      left: parseInt(getTipOffsetLeft(place) - parentLeft, 10),
      top: parseInt(getTipOffsetTop(place) - parentTop, 10)
    }
  }
}

const getDimensions = (node) => {
  const { height, width } = node.getBoundingClientRect()
  return {
    height: parseInt(height, 10),
    width: parseInt(width, 10)
  }
}

// Get current mouse offset
const getCurrentOffset = (e, currentTarget, effect) => {
  const boundingClientRect = currentTarget.getBoundingClientRect()
  const targetTop = boundingClientRect.top
  const targetLeft = boundingClientRect.left
  const {
    width: targetWidth,
    height: targetHeight
  } = getDimensions(currentTarget)

  if (effect === 'float') {
    return {
      mouseX: e.clientX,
      mouseY: e.clientY
    }
  }
  return {
    mouseX: targetLeft + (targetWidth / 2),
    mouseY: targetTop + (targetHeight / 2)
  }
}

// List all possibility of tooltip final offset
// This is useful in judging if it is necessary for tooltip to switch position when out of window
const getDefaultPosition = (effect, targetWidth, targetHeight, tipWidth, tipHeight) => {
  let top
  let right
  let bottom
  let left
  const disToMouse = 3
  const triangleHeight = 2
  const cursorHeight = 12 // Optimize for float bottom only, cause the cursor will hide the tooltip

  if (effect === 'float') {
    top = {
      l: -(tipWidth / 2),
      r: tipWidth / 2,
      t: -(tipHeight + disToMouse + triangleHeight),
      b: -disToMouse
    }
    bottom = {
      l: -(tipWidth / 2),
      r: tipWidth / 2,
      t: disToMouse + cursorHeight,
      b: tipHeight + disToMouse + triangleHeight + cursorHeight
    }
    left = {
      l: -(tipWidth + disToMouse + triangleHeight),
      r: -disToMouse,
      t: -(tipHeight / 2),
      b: tipHeight / 2
    }
    right = {
      l: disToMouse,
      r: tipWidth + disToMouse + triangleHeight,
      t: -(tipHeight / 2),
      b: tipHeight / 2
    }
  } else if (effect === 'solid') {
    top = {
      l: -(tipWidth / 2),
      r: tipWidth / 2,
      t: -(targetHeight / 2 + tipHeight + triangleHeight),
      b: -(targetHeight / 2)
    }
    bottom = {
      l: -(tipWidth / 2),
      r: tipWidth / 2,
      t: targetHeight / 2,
      b: targetHeight / 2 + tipHeight + triangleHeight
    }
    left = {
      l: -(tipWidth + targetWidth / 2 + triangleHeight),
      r: -(targetWidth / 2),
      t: -(tipHeight / 2),
      b: tipHeight / 2
    }
    right = {
      l: targetWidth / 2,
      r: tipWidth + targetWidth / 2 + triangleHeight,
      t: -(tipHeight / 2),
      b: tipHeight / 2
    }
  }

  return {top, bottom, left, right}
}

// Consider additional offset into position calculation
const calculateOffset = (offset) => {
  let extraOffset_X = 0
  let extraOffset_Y = 0

  if (Object.prototype.toString.apply(offset) === '[object String]') {
    offset = JSON.parse(offset.toString().replace(/\'/g, '\"'))
  }
  for (let key in offset) {
    if (key === 'top') {
      extraOffset_Y -= parseInt(offset[key], 10)
    } else if (key === 'bottom') {
      extraOffset_Y += parseInt(offset[key], 10)
    } else if (key === 'left') {
      extraOffset_X -= parseInt(offset[key], 10)
    } else if (key === 'right') {
      extraOffset_X += parseInt(offset[key], 10)
    }
  }

  return {extraOffset_X, extraOffset_Y}
}

// Get the offset of the parent elements
const getParent = (currentTarget) => {
  let currentParent = currentTarget
  while (currentParent) {
    if (window.getComputedStyle(currentParent).getPropertyValue('transform') !== 'none') break
    currentParent = currentParent.parentElement
  }

  const parentTop = currentParent && currentParent.getBoundingClientRect().top || 0
  const parentLeft = currentParent && currentParent.getBoundingClientRect().left || 0

  return {parentTop, parentLeft}
}
<commit_msg>Make sure tooltip is oriented correctly when close to edge<commit_after>/**
 * Calculate the position of tooltip
 *
 * @params
 * - `e` {Event} the event of current mouse
 * - `target` {Element} the currentTarget of the event
 * - `node` {DOM} the react-tooltip object
 * - `place` {String} top / right / bottom / left
 * - `effect` {String} float / solid
 * - `offset` {Object} the offset to default position
 *
 * @return {Object
 * - `isNewState` {Bool} required
 * - `newState` {Object}
 * - `position` {OBject} {left: {Number}, top: {Number}}
 */
export default function (e, target, node, place, desiredPlace, effect, offset) {
  const {
    width: tipWidth,
    height: tipHeight
  } = getDimensions(node)

  const {
    width: targetWidth,
    height: targetHeight
  } = getDimensions(target)

  const {mouseX, mouseY} = getCurrentOffset(e, target, effect)
  const defaultOffset = getDefaultPosition(effect, targetWidth, targetHeight, tipWidth, tipHeight)
  const {extraOffset_X, extraOffset_Y} = calculateOffset(offset)

  const windowWidth = window.innerWidth
  const windowHeight = window.innerHeight

  const {parentTop, parentLeft} = getParent(node)

  // Get the edge offset of the tooltip
  const getTipOffsetLeft = (place) => {
    const offset_X = defaultOffset[place].l
    return mouseX + offset_X + extraOffset_X
  }
  const getTipOffsetRight = (place) => {
    const offset_X = defaultOffset[place].r
    return mouseX + offset_X + extraOffset_X
  }
  const getTipOffsetTop = (place) => {
    const offset_Y = defaultOffset[place].t
    return mouseY + offset_Y + extraOffset_Y
  }
  const getTipOffsetBottom = (place) => {
    const offset_Y = defaultOffset[place].b
    return mouseY + offset_Y + extraOffset_Y
  }

  //
  // Functions to test whether the tooltip's sides are inside
  // the client window for a given orientation p
  //
  //  _____________
  // |             | <-- Right side
  // | p = 'left'  |\
  // |             |/  |\
  // |_____________|   |_\  <-- Mouse
  //      / \           |
  //       |
  //       |
  //  Bottom side
  //
  let outsideLeft = p => getTipOffsetLeft(p) < 0
  let outsideRight = p => getTipOffsetRight(p) > windowWidth
  let outsideTop = p => getTipOffsetTop(p) < 0
  let outsideBottom = p => getTipOffsetBottom(p) > windowHeight

  // Check whether the tooltip with orientation p is completely inside the client window
  let outside = p => outsideLeft(p) || outsideRight(p) || outsideTop(p) || outsideBottom(p)
  let inside = p => !outside(p)

  let placesList = ['left', 'right', 'top', 'bottom']
  let insideList = []
  for (let i = 0; i < 4; i++) {
    let p = placesList[i]
    if (inside(p)) {
      insideList.push(p)
    }
  }

  let isNewState = false
  let newPlace
  if (inside(desiredPlace) && desiredPlace !== place) {
    isNewState = true
    newPlace = desiredPlace
  } else if (insideList.length > 0 && outside(desiredPlace) && outside(place)) {
    isNewState = true
    newPlace = insideList[0]
  }

  if (isNewState)
  {
    return {
      isNewState: true,
      newState: {place: newPlace}
    }
  }

  return {
    isNewState: false,
    position: {
      left: parseInt(getTipOffsetLeft(place) - parentLeft, 10),
      top: parseInt(getTipOffsetTop(place) - parentTop, 10)
    }
  }
}

const getDimensions = (node) => {
  const { height, width } = node.getBoundingClientRect()
  return {
    height: parseInt(height, 10),
    width: parseInt(width, 10)
  }
}

// Get current mouse offset
const getCurrentOffset = (e, currentTarget, effect) => {
  const boundingClientRect = currentTarget.getBoundingClientRect()
  const targetTop = boundingClientRect.top
  const targetLeft = boundingClientRect.left
  const {
    width: targetWidth,
    height: targetHeight
  } = getDimensions(currentTarget)

  if (effect === 'float') {
    return {
      mouseX: e.clientX,
      mouseY: e.clientY
    }
  }
  return {
    mouseX: targetLeft + (targetWidth / 2),
    mouseY: targetTop + (targetHeight / 2)
  }
}

// List all possibility of tooltip final offset
// This is useful in judging if it is necessary for tooltip to switch position when out of window
const getDefaultPosition = (effect, targetWidth, targetHeight, tipWidth, tipHeight) => {
  let top
  let right
  let bottom
  let left
  const disToMouse = 3
  const triangleHeight = 2
  const cursorHeight = 12 // Optimize for float bottom only, cause the cursor will hide the tooltip

  if (effect === 'float') {
    top = {
      l: -(tipWidth / 2),
      r: tipWidth / 2,
      t: -(tipHeight + disToMouse + triangleHeight),
      b: -disToMouse
    }
    bottom = {
      l: -(tipWidth / 2),
      r: tipWidth / 2,
      t: disToMouse + cursorHeight,
      b: tipHeight + disToMouse + triangleHeight + cursorHeight
    }
    left = {
      l: -(tipWidth + disToMouse + triangleHeight),
      r: -disToMouse,
      t: -(tipHeight / 2),
      b: tipHeight / 2
    }
    right = {
      l: disToMouse,
      r: tipWidth + disToMouse + triangleHeight,
      t: -(tipHeight / 2),
      b: tipHeight / 2
    }
  } else if (effect === 'solid') {
    top = {
      l: -(tipWidth / 2),
      r: tipWidth / 2,
      t: -(targetHeight / 2 + tipHeight + triangleHeight),
      b: -(targetHeight / 2)
    }
    bottom = {
      l: -(tipWidth / 2),
      r: tipWidth / 2,
      t: targetHeight / 2,
      b: targetHeight / 2 + tipHeight + triangleHeight
    }
    left = {
      l: -(tipWidth + targetWidth / 2 + triangleHeight),
      r: -(targetWidth / 2),
      t: -(tipHeight / 2),
      b: tipHeight / 2
    }
    right = {
      l: targetWidth / 2,
      r: tipWidth + targetWidth / 2 + triangleHeight,
      t: -(tipHeight / 2),
      b: tipHeight / 2
    }
  }

  return {top, bottom, left, right}
}

// Consider additional offset into position calculation
const calculateOffset = (offset) => {
  let extraOffset_X = 0
  let extraOffset_Y = 0

  if (Object.prototype.toString.apply(offset) === '[object String]') {
    offset = JSON.parse(offset.toString().replace(/\'/g, '\"'))
  }
  for (let key in offset) {
    if (key === 'top') {
      extraOffset_Y -= parseInt(offset[key], 10)
    } else if (key === 'bottom') {
      extraOffset_Y += parseInt(offset[key], 10)
    } else if (key === 'left') {
      extraOffset_X -= parseInt(offset[key], 10)
    } else if (key === 'right') {
      extraOffset_X += parseInt(offset[key], 10)
    }
  }

  return {extraOffset_X, extraOffset_Y}
}

// Get the offset of the parent elements
const getParent = (currentTarget) => {
  let currentParent = currentTarget
  while (currentParent) {
    if (window.getComputedStyle(currentParent).getPropertyValue('transform') !== 'none') break
    currentParent = currentParent.parentElement
  }

  const parentTop = currentParent && currentParent.getBoundingClientRect().top || 0
  const parentLeft = currentParent && currentParent.getBoundingClientRect().left || 0

  return {parentTop, parentLeft}
}
<|endoftext|>


<commit_before>/**
 * Uploads an image to the tsimg cloud service.
 *
 * @export
 * @param {string | File} file A URL as a string or a `File` object that should be uploaded.
 * @param {string} referenceId
 * @param {string} personId A person is that should be associated with the uploaded image.
 * @param {string} siteId A site id that should be associated with the uploaded image.
 * @param {string} [url='https://api.tsimg.cloud/image']
 * @return {object} The response data for the image upload.
 */
export default async function imageUpload(
    file,
    referenceId,
    personId,
    siteId,
    url = 'https://api.tsimg.cloud/image'
) {
    const headers = new Headers({ Accept: 'application/json' });

    if (referenceId) headers.set('X-Reference-Id', referenceId);
    if (personId) headers.set('X-Person-Id', personId);
    if (siteId) headers.set('X-Site-Id', siteId);

    /** @type {string | ArrayBuffer} */
    let body;

    if (typeof file === 'string') {
        headers.set('Content-Type', 'application/json');
        body = JSON.stringify({ url: file });
    } else {
        headers.set('Content-Type', 'image/*');
        body = await getFileArrayBuffer(file);
    }

    const response = await fetch(url, { method: 'POST', body, headers });

    if (response.ok) {
        return response.json();
    }

    throw Error(
        `Uploading the image failed with status code ${response.status}.`
    );
}

/**
 * Returns the array buffer for an input file.
 *
 * @param {File} file The input `File` object.
 * @return {Promise<string | ArrayBuffer>}
 */
function getFileArrayBuffer(file) {
    return new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.onload = (e) => resolve(e.target.result);
        reader.onerror = reject;
        reader.readAsArrayBuffer(file);
    });
}
<commit_msg>:memo: Fix JSDoc annotation in imageUpload function<commit_after>/**
 * Uploads an image to the tsimg cloud service.
 *
 * @export
 * @param {string | File} file A URL as a string or a `File` object that should be uploaded.
 * @param {string} referenceId
 * @param {string} personId A person is that should be associated with the uploaded image.
 * @param {string} siteId A site id that should be associated with the uploaded image.
 * @param {string} [url='https://api.tsimg.cloud/image']
 * @return {Promise<object>} The response data for the image upload.
 */
export default async function imageUpload(
    file,
    referenceId,
    personId,
    siteId,
    url = 'https://api.tsimg.cloud/image'
) {
    const headers = new Headers({ Accept: 'application/json' });

    if (referenceId) headers.set('X-Reference-Id', referenceId);
    if (personId) headers.set('X-Person-Id', personId);
    if (siteId) headers.set('X-Site-Id', siteId);

    /** @type {string | ArrayBuffer} */
    let body;

    if (typeof file === 'string') {
        headers.set('Content-Type', 'application/json');
        body = JSON.stringify({ url: file });
    } else {
        headers.set('Content-Type', 'image/*');
        body = await getFileArrayBuffer(file);
    }

    const response = await fetch(url, { method: 'POST', body, headers });

    if (response.ok) {
        return response.json();
    }

    throw Error(
        `Uploading the image failed with status code ${response.status}.`
    );
}

/**
 * Returns the array buffer for an input file.
 *
 * @param {File} file The input `File` object.
 * @return {Promise<string | ArrayBuffer>}
 */
function getFileArrayBuffer(file) {
    return new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.onload = (e) => resolve(e.target.result);
        reader.onerror = reject;
        reader.readAsArrayBuffer(file);
    });
}
<|endoftext|>


<commit_before>var Datepicker = {};

// initiate all datepickers
Datepicker.initAll = function() {
	"use strict";
	var locale = $('html').attr('lang');
	$.datepicker.setDefaults($.datepicker.regional[locale]);
	$('.datepicker').each(function() {
		$(this).datepicker({
			altField : "#" + Datepicker.escapeId($(this).attr('id')) + "_alt",
			altFormat : "yy-mm-dd",
			gotoCurrent : true,
			onSelect : function(d,i) {
				//allways trigger on change event cause
				//this function is also called when pressing enter
				$(this).change();
		    },
			changeMonth : true,
			changeYear : true,
			yearRange : '1970:2040',
			firstDay : 1,
			autoclose : true,
			showButtonPanel: true
		});
	});

	// we need to clear the alt field when the user cleared the visible input
	$(".datepicker").change(function(){
		if (!$(this).val()) {
			$("#" + Datepicker.escapeId($(this).attr('id')) + "_alt").val('');
		}
	});

};

// escape the dot in id attribute
Datepicker.escapeId = function(id) {
	"use strict";
	return id.replace(/\./g, "\\.");
};
<commit_msg>#7 put the date picker under the input lines<commit_after>var Datepicker = {};

// initiate all datepickers
Datepicker.initAll = function() {
	"use strict";
	var locale = $('html').attr('lang');
	$.datepicker.setDefaults($.datepicker.regional[locale]);
	$('.datepicker').each(function() {
		$(this).datepicker({
			altField : "#" + Datepicker.escapeId($(this).attr('id')) + "_alt",
			altFormat : "yy-mm-dd",
			gotoCurrent : true,
			onSelect : function(d,i) {
				//allways trigger on change event cause
				//this function is also called when pressing enter
				$(this).change();
		    },
			changeMonth : true,
			changeYear : true,
			yearRange : '1970:2040',
			firstDay : 1,
			autoclose : true,
			showButtonPanel: true
		});
	});

	// we need to clear the alt field when the user cleared the visible input
	$(".datepicker").change(function(){
		if (!$(this).val()) {
			$("#" + Datepicker.escapeId($(this).attr('id')) + "_alt").val('');
		}
	});

	//the datepicker is under the input line
	$.extend($.datepicker,{
		_checkOffset:function(inst,offset,isFixed){
			return offset;
		}
	});
};

// escape the dot in id attribute
Datepicker.escapeId = function(id) {
	"use strict";
	return id.replace(/\./g, "\\.");
};
<|endoftext|>


<commit_before>'use strict';

const _ = require('lodash');
const Promise = require('bluebird');
const exec = Promise.promisify(require('child_process').exec);
const State = require('../core/state');
const defaultTasks = {tasks: require('../tasks')};
const InvalidConfigError = require('../lib/errors').InvalidConfigError;
const path = require('path');

function tasksHaveDo(config) {
  _.mapValues(config.tasks, (task, key) => {
    if (_.has(task, 'tasks')) {
      tasksHaveDo(task.tasks);
    }
    else {
      if (!_.has(task, 'do')) {
        throw new InvalidConfigError(`task ${key} missing "do" property`);
      }
    }
  });
}

const validators = {
  varsIsObject: config => {
    if (!_.isPlainObject(config.vars)) {
      throw new InvalidConfigError('vars not a valid object');
    }
  },
  tasksIsObject: config => {
    if (!_.isPlainObject(config.tasks)) {
      throw new InvalidConfigError('tasks not a valid object');
    }
  },
  includeIsArray: config => {
    if (config.include && !_.isArray(config.include)) {
      throw new InvalidConfigError('include must be an array');
    }
  },
  includesHaveProperties: config => {
    if (!_.every(config.include, object => _.has(object, 'from'))) {
      throw new InvalidConfigError('includes must all have "from" and "import" properties');
    }
  },
  tasksHaveDo: config => {
    tasksHaveDo(config);
  }
};

function installModule(moduleName) {
  if (_.endsWith(moduleName, '.yml')) {
    return Promise.resolve();
  }

  const usherExePath = path.parse(__filename);
  return exec(`npm install ${moduleName} --prefix ${usherExePath.dir}`);
}

function requireTask(taskList, requireName) {
  return require(requireName);
}

function loadAndParseYmlFile(taskList, filename) {
  const file = require('./parse')(filename);

  return file.tasks;
}

function importTasks(tasks) {
  const builtTasks = {};

  _.each(_.keys(tasks), taskName => {
    _.set(builtTasks, taskName, _.get(tasks, taskName));
  });

  return builtTasks;
}

function getAlias(name) {
  const split = _.split(name, ' as ');

  if (split.length === 1) {
    split.push(name); // Alias name same as name if no alias specified
  }

  return split;
}

function importTasklist(taskList, taskConfig, usherFilePath) {
  const [importName, aliasName] = getAlias(taskConfig.name || taskConfig.from);
  const tasks = _.endsWith(taskConfig.from, '.yml')
    ? loadAndParseYmlFile(taskList, path.join(usherFilePath, taskConfig.from))
    : requireTask(taskList, importName);

  if (!taskConfig.import) {
    taskList[aliasName] = {
      tasks: importTasks(tasks)
    };
  }
  else {
    taskList[aliasName] = {
      tasks: {}
    };
    _.each(taskConfig.import, taskImportName => {
      const propertyAliasName = getAlias(taskImportName)[1];
      taskList[aliasName].tasks[propertyAliasName] = {
        tasks: importTasks(tasks[propertyAliasName])
      };
    });
  }

  return taskList;
}

module.exports = (config, Logger, usherFilePath, nodeModulesPath) => Promise.try(() => {
  _.mapValues(validators, validator => validator(config));
  const modulesToInstall = _.map(config.include, (include) => _.get(include, 'from'));
  return Promise.all(_.map(modulesToInstall, installModule))
  .then(() => {
    const reducedTasks = _.reduce(config.include, (acc, includeConfig) =>
      importTasklist(acc, includeConfig, usherFilePath, nodeModulesPath), {});
    const initialState = _.merge({}, defaultTasks, config.vars, _.pick(config, 'tasks'), {tasks: reducedTasks});
    const state = new State(initialState, Logger);

    return state;
  });
});
<commit_msg>Using home directory for storing shared tasks<commit_after>'use strict';

const _ = require('lodash');
const Promise = require('bluebird');
const exec = Promise.promisify(require('child_process').exec);
const State = require('../core/state');
const defaultTasks = {tasks: require('../tasks')};
const InvalidConfigError = require('../lib/errors').InvalidConfigError;
const path = require('path');

function tasksHaveDo(config) {
  _.mapValues(config.tasks, (task, key) => {
    if (_.has(task, 'tasks')) {
      tasksHaveDo(task.tasks);
    }
    else {
      if (!_.has(task, 'do')) {
        throw new InvalidConfigError(`task ${key} missing "do" property`);
      }
    }
  });
}

const validators = {
  varsIsObject: config => {
    if (!_.isPlainObject(config.vars)) {
      throw new InvalidConfigError('vars not a valid object');
    }
  },
  tasksIsObject: config => {
    if (!_.isPlainObject(config.tasks)) {
      throw new InvalidConfigError('tasks not a valid object');
    }
  },
  includeIsArray: config => {
    if (config.include && !_.isArray(config.include)) {
      throw new InvalidConfigError('include must be an array');
    }
  },
  includesHaveProperties: config => {
    if (!_.every(config.include, object => _.has(object, 'from'))) {
      throw new InvalidConfigError('includes must all have "from" and "import" properties');
    }
  },
  tasksHaveDo: config => {
    tasksHaveDo(config);
  }
};

function getUserHome() {
  return process.env[(process.platform == 'win32') ? 'USERPROFILE' : 'HOME'];
}

function installModule(moduleName) {
  if (_.endsWith(moduleName, '.yml')) {
    return Promise.resolve();
  }

  const sharedTaskDir = `${getUserHome()}/.usher-cli/src/v2/commands`
  return exec(`npm install ${moduleName} --prefix ${sharedTaskDir}`);
}

function requireTask(taskList, requireName) {
  return require(requireName);
}

function loadAndParseYmlFile(taskList, filename) {
  const file = require('./parse')(filename);

  return file.tasks;
}

function importTasks(tasks) {
  const builtTasks = {};

  _.each(_.keys(tasks), taskName => {
    _.set(builtTasks, taskName, _.get(tasks, taskName));
  });

  return builtTasks;
}

function getAlias(name) {
  const split = _.split(name, ' as ');

  if (split.length === 1) {
    split.push(name); // Alias name same as name if no alias specified
  }

  return split;
}

function importTasklist(taskList, taskConfig, usherFilePath) {
  const [importName, aliasName] = getAlias(taskConfig.name || taskConfig.from);
  const tasks = _.endsWith(taskConfig.from, '.yml')
    ? loadAndParseYmlFile(taskList, path.join(usherFilePath, taskConfig.from))
    : requireTask(taskList, importName);

  if (!taskConfig.import) {
    taskList[aliasName] = {
      tasks: importTasks(tasks)
    };
  }
  else {
    taskList[aliasName] = {
      tasks: {}
    };
    _.each(taskConfig.import, taskImportName => {
      const propertyAliasName = getAlias(taskImportName)[1];
      taskList[aliasName].tasks[propertyAliasName] = {
        tasks: importTasks(tasks[propertyAliasName])
      };
    });
  }

  return taskList;
}

module.exports = (config, Logger, usherFilePath, nodeModulesPath) => Promise.try(() => {
  _.mapValues(validators, validator => validator(config));
  const modulesToInstall = _.map(config.include, (include) => _.get(include, 'from'));
  return Promise.all(_.map(modulesToInstall, installModule))
  .then(() => {
    const reducedTasks = _.reduce(config.include, (acc, includeConfig) =>
      importTasklist(acc, includeConfig, usherFilePath, nodeModulesPath), {});
    const initialState = _.merge({}, defaultTasks, config.vars, _.pick(config, 'tasks'), {tasks: reducedTasks});
    const state = new State(initialState, Logger);

    return state;
  });
});
<|endoftext|>


<commit_before>var hookshot = require('hookshot'),
	fs   = require('fs'),
	sh   = require('execSync');

var config = require('./config.json');
var port_number = 9001;

function verifyAccount(incoming_repo){
	if (incoming_repo == config.github_account) return true;
	return false;
}
function createDirGitInit(info){
	var repo_name = info.repository.name;
	fs.mkdirSync('./' + repo_name);
        sh.run('cd ' + repo_name + ' && git init && git remote add origin ' + info.repository.url + '.git');
}
function pullLatest(info){
	var repo_name = info.repository.name;
	if (!fs.existsSync('./' + repo_name)){
		createDirGitInit(info)
	}
	sh.run('cd ' + repo_name + ' &&  git fetch origin && git checkout origin/master');
}
function checkForDeployMsg(info){
	// The last commit in the array is the most recent
	var commit_msg = info.commits[info.commits.length - 1].message,
	    deploy_regx = new RegExp(config.deploy_trigger);
	if (deploy_regx.exec(commit_msg)) return true;
	return false;
}
function deployToS3(info){
	var repo_name = info.repository.name,
	    path      = (config.path) ? config.path + '/' : '';

	if (config.use_year_in_path) { path += new Date().getFullYear() + '/' };

	var deploy_result = sh.exec('aws s3 sync '+repo_name+' s3://'+config.bucket_name+'/'+path+repo_name+'/ --exclude ".git/*" --exclude ".*"');
	console.log(deploy_result.stdout);
}
hookshot('refs/heads/master', function(info){
	var account_verified = verifyAccount(info.repository.owner.name);
	if (account_verified){
		pullLatest(info);
		var deploy_msg = checkForDeployMsg(info);
		if (deploy_msg){
			deployToS3(info);
		}
	}
}).listen(port_number);
console.log('Listening on port... ' + port_number);

<commit_msg>readme tweaks, add port to config<commit_after>var hookshot = require('hookshot'),
	fs   = require('fs'),
	sh   = require('execSync');

var config = require('./config.json');

function verifyAccount(incoming_repo){
	if (incoming_repo == config.github_account) return true;
	return false;
}
function createDirGitInit(info){
	var repo_name = info.repository.name;
	fs.mkdirSync('./' + repo_name);
        sh.run('cd ' + repo_name + ' && git init && git remote add origin ' + info.repository.url + '.git');
}
function pullLatest(info){
	var repo_name = info.repository.name;
	if (!fs.existsSync('./' + repo_name)){
		createDirGitInit(info)
	}
	sh.run('cd ' + repo_name + ' &&  git fetch origin && git checkout origin/master');
}
function checkForDeployMsg(info){
	// The last commit in the array is the most recent
	var commit_msg = info.commits[info.commits.length - 1].message,
	    deploy_regx = new RegExp(config.deploy_trigger);
	if (deploy_regx.exec(commit_msg)) return true;
	return false;
}
function deployToS3(info){
	var repo_name = info.repository.name,
	    path      = (config.path) ? config.path + '/' : '';

	if (config.use_year_in_path) { path += new Date().getFullYear() + '/' };

	var deploy_result = sh.exec('aws s3 sync '+repo_name+' s3://'+config.bucket_name+'/'+path+repo_name+'/ --exclude ".git/*" --exclude ".*"');
	console.log(deploy_result.stdout);
}
hookshot('refs/heads/master', function(info){
	var account_verified = verifyAccount(info.repository.owner.name);
	if (account_verified){
		pullLatest(info);
		var deploy_msg = checkForDeployMsg(info);
		if (deploy_msg){
			deployToS3(info);
		}
	}
}).listen(config.port);
console.log('Listening on port... ' + config.port);

<|endoftext|>


<commit_before>/**
 * Creates static pages' JSON
 * @param collectionId
 * @param releaseDate
 * @param pageType
 * @param parentUrl
 */

function loadT7Creator(collectionId, releaseDate, pageType, parentUrl) {
  var releaseDate = null;             //overwrite scheduled collection date
  var pageName, pageNameTrimmed, newUri, pageData, isNumber;
  if (parentUrl === '/') {        //to check home page
    parentUrl = '';
  }
  var parentUrlData = parentUrl + "/data";
  $.ajax({
    url: parentUrlData,
    dataType: 'json',
    crossDomain: true,
    success: function (checkData) {
      if (pageType === 'static_landing_page' && checkData.type === 'home_page' ||
          (pageType === 'static_qmi' || pageType === 'static_adhoc' || pageType === 'static_methodology' || pageType === 'static_methodology_download') && checkData.type === 'product_page' ||
          (pageType === 'static_landing_page' || pageType === 'static_page' || pageType === 'static_article') && checkData.type === 'home_page_census') {
        submitFormHandler();
        return true;
      } else if ((pageType === 'static_foi' || pageType === 'static_page' || pageType === 'static_article' || pageType === 'static_landing_page') && checkData.type.match(/static_.+/)) {
        submitFormHandler();
        return true;
      } else {
        sweetAlert("This is not a valid place to create this page.");
        loadCreateScreen(collectionId);
      }
    },
    error: function () {
      console.log('No page data returned');
    }
  });

  function submitFormHandler() {
    if (pageType === 'static_qmi' || pageType === 'static_methodology') {
      $('.edition').append(
        '
' +
        'Last revised' +
        '<input id="releaseDate" type="text" placeholder="day month year" />'
      );

      $('#releaseDate').datepicker({dateFormat: 'dd MM yy'});
    } else if (pageType === 'static_adhoc') {
      $('.edition').append(
        'Release date' +
        '<input id="releaseDate" type="text" placeholder="day month year" />' +
        '
' +
        'Reference' +
        '<input id="adhoc-reference" type="text" placeholder="Reference number" />'
      );
      $('#releaseDate').datepicker({dateFormat: 'dd MM yy'});
      $('#adhoc-reference').on('input', function () {
        isNumber = $(this).val();
        if (!isNumber.match(/^\d+$/)) {
          sweetAlert('This needs to be a number');
          $(this).val('');
        }
      });
    }
    else if (!releaseDate && !(pageType === 'static_page' || pageType === 'static_landing_page')) {
      $('.edition').append(
        '
' +
        'Release date' +
        '<input id="releaseDate" type="text" placeholder="day month year" />'
      );
      $('#releaseDate').datepicker({dateFormat: 'dd MM yy'});
    }

    $('form').submit(function (e) {
      e.preventDefault();
      //Check for reserved words
      if ($('#pagename').val().toLowerCase() === 'current' || $('#pagename').val().toLowerCase() === 'latest' || $('#pagename').val().toLowerCase() === 'data') {
        sweetAlert('That is not an accepted value for a title');
        $('#pagename').val('');
        return false;
      }
      pageData = pageTypeDataT7(pageType);
      pageName = $('#pagename').val().trim();
      pageData.description.title = pageName;
      pageNameTrimmed = pageName.replace(/[^A-Z0-9]+/ig, "").toLowerCase();
      pageData.fileName = pageNameTrimmed;
      pageData.description.reference = isNumber;
      var adHocUrl = isNumber + pageNameTrimmed;
      if (pageType === 'static_qmi' && !Florence.globalVars.welsh) {
        newUri = makeUrl(parentUrl, 'qmis', pageNameTrimmed);
      } else if (pageType === 'static_adhoc' && !Florence.globalVars.welsh) {
        newUri = makeUrl(parentUrl, 'adhocs', adHocUrl);
      } else if ((pageType === 'static_methodology' || pageType === 'static_methodology_download') && !Florence.globalVars.welsh) {
        newUri = makeUrl(parentUrl, 'methodologies', pageNameTrimmed);
      } else if (!Florence.globalVars.welsh) {
        newUri = makeUrl(parentUrl, pageNameTrimmed);
      } else {
        sweetAlert('You can not perform that operation in Welsh.');
      }
      var safeNewUri = checkPathSlashes(newUri);
      if (releaseDate && (pageType === 'static_qmi')) {
        date = new Date(releaseDate);
        pageData.description.lastRevised = $.datepicker.formatDate('dd/mm/yy', date);
      } else if (releaseDate) {
        date = new Date(releaseDate);
        pageData.description.releaseDate = $.datepicker.formatDate('dd/mm/yy', date);
      } else if (!releaseDate && (pageType === 'static_qmi')) {
        pageData.description.lastRevised = new Date($('#releaseDate').val()).toISOString();
      } else if (!releaseDate) {
        pageData.description.releaseDate = new Date().toISOString();
      }

      if (pageName.length < 4) {
        sweetAlert("This is not a valid file name");
      } else {
        saveContent(collectionId, safeNewUri, pageData);
      }
    });
  }
}

function pageTypeDataT7(pageType) {

  if (pageType === "static_page") {
    return {
      "description": {
        "title": "",
        "summary": "",
        "releaseDate": "",
        "keywords": [],
        "metaDescription": ""
      },
      "markdown": [],
      "charts": [],
      "tables": [],
      "images": [],
      "downloads": [],
      type: pageType,
      "anchors": [],
      "links": []
    };
  } else if (pageType === "static_landing_page") {
    return {
      "description": {
        "title": "",
        "summary": "",
        "releaseDate": "",
        "keywords": [],
        "metaDescription": "",
      },
      "sections": [],
      "markdown": [],
      type: pageType,
      "links": []
    };
  }
  else if (pageType === "static_article") {
    return {
      "description": {
        "title": "",
        "summary": "",
        "releaseDate": "",
        "keywords": [],
        "metaDescription": ""
      },
      "sections": [],
      "charts": [],
      "tables": [],
      "images": [],
      "anchors": [],
      "links": [],
      "alerts": [],
      type: pageType
    };
  } else if (pageType === "static_methodology") {
    return {
      "description": {
        "title": "",
        "summary": "",
        "releaseDate": "",
        "contact": {
          "name": "",
          "email": "",
          "telephone": ""
        },
        "keywords": [],
        "metaDescription": ""
      },
      "sections": [],
      "accordion": [],
      "relatedDocuments": [],
      "relatedDatasets": [],
      "relatedMethodology": [],
      "relatedMethodologyArticle": [],
      "charts": [],
      "tables": [],
      "images": [],
      "downloads": [],
      "links": [],
      "alerts": [],
      type: pageType
    };
  } else if (pageType === "static_methodology_download") {
    return {
      "description": {
        "title": "",
        "contact": {
          "name": "",
          "email": "",
          "phone": ""
        },
        "releaseDate": "",
        "keywords": [],
        "metaDescription": ""
      },
      "markdown": [],
      "downloads": [],
      "relatedDocuments": [],
      "relatedDatasets": [],
      "relatedMethodology": [],
      "relatedMethodologyArticle": [],
      "links": [],
      "alerts": [],
      type: pageType
    };
  } else if (pageType === "static_qmi") {
    return {
      "description": {
        "title": "",
        "contact": {
          "name": "",
          "email": "",
          "phone": ""
        },
        "surveyName": "",
        "frequency": "",
        "compilation": "",
        "geographicCoverage": "",
        "sampleSize": null,
        "lastRevised": "",
        "nationalStatistic": false,
        "keywords": [],
        "metaDescription": ""
      },
      "markdown": [],
      "downloads": [],
      "relatedDocuments": [],
      "relatedDatasets": [],
      "relatedMethodology": [],
      "relatedMethodologyArticle": [],
      "links": [],
      type: pageType
    };
  } else if (pageType === "static_foi") {
    return {
      "description": {
        "title": "",
        "releaseDate": "",
        "keywords": [],
        "metaDescription": ""
      },
      "downloads": [],
      "markdown": [],
      "links": [],
      type: pageType
    };
  } else if (pageType === "static_adhoc") {
    return {
      "description": {
        "title": "",
        "releaseDate": "",
        "reference": null,
        "keywords": [],
        "metaDescription": ""
      },
      "downloads": [],
      "markdown": [],
      "links": [],
      type: pageType
    };
  } else {
    sweetAlert('Unsupported page type', 'This is not a static page', "info");
  }
}<commit_msg>Try empty dates on static pages and articles. Deserialise error from zebedee<commit_after>/**
 * Creates static pages' JSON
 * @param collectionId
 * @param releaseDate
 * @param pageType
 * @param parentUrl
 */

function loadT7Creator(collectionId, releaseDate, pageType, parentUrl) {
  var releaseDate = null;             //overwrite scheduled collection date
  var pageName, pageNameTrimmed, newUri, pageData, isNumber;
  if (parentUrl === '/') {        //to check home page
    parentUrl = '';
  }
  var parentUrlData = parentUrl + "/data";
  $.ajax({
    url: parentUrlData,
    dataType: 'json',
    crossDomain: true,
    success: function (checkData) {
      if (pageType === 'static_landing_page' && checkData.type === 'home_page' ||
          (pageType === 'static_qmi' || pageType === 'static_adhoc' || pageType === 'static_methodology' || pageType === 'static_methodology_download') && checkData.type === 'product_page' ||
          (pageType === 'static_landing_page' || pageType === 'static_page' || pageType === 'static_article') && checkData.type === 'home_page_census') {
        submitFormHandler();
        return true;
      } else if ((pageType === 'static_foi' || pageType === 'static_page' || pageType === 'static_article' || pageType === 'static_landing_page') && checkData.type.match(/static_.+/)) {
        submitFormHandler();
        return true;
      } else {
        sweetAlert("This is not a valid place to create this page.");
        loadCreateScreen(collectionId);
      }
    },
    error: function () {
      console.log('No page data returned');
    }
  });

  function submitFormHandler() {
    if (pageType === 'static_qmi' || pageType === 'static_methodology') {
      $('.edition').append(
        '
' +
        'Last revised' +
        '<input id="releaseDate" type="text" placeholder="day month year" />'
      );

      $('#releaseDate').datepicker({dateFormat: 'dd MM yy'});
    } else if (pageType === 'static_adhoc') {
      $('.edition').append(
        'Release date' +
        '<input id="releaseDate" type="text" placeholder="day month year" />' +
        '
' +
        'Reference' +
        '<input id="adhoc-reference" type="text" placeholder="Reference number" />'
      );
      $('#releaseDate').datepicker({dateFormat: 'dd MM yy'});
      $('#adhoc-reference').on('input', function () {
        isNumber = $(this).val();
        if (!isNumber.match(/^\d+$/)) {
          sweetAlert('This needs to be a number');
          $(this).val('');
        }
      });
    }
    else if (!releaseDate && !(pageType === 'static_page' || pageType === 'static_landing_page')) {
      $('.edition').append(
        '
' +
        'Release date' +
        '<input id="releaseDate" type="text" placeholder="day month year" />'
      );
      $('#releaseDate').datepicker({dateFormat: 'dd MM yy'});
    }

    $('form').submit(function (e) {
      e.preventDefault();
      //Check for reserved words
      if ($('#pagename').val().toLowerCase() === 'current' || $('#pagename').val().toLowerCase() === 'latest' || $('#pagename').val().toLowerCase() === 'data') {
        sweetAlert('That is not an accepted value for a title');
        $('#pagename').val('');
        return false;
      }
      pageData = pageTypeDataT7(pageType);
      pageName = $('#pagename').val().trim();
      pageData.description.title = pageName;
      pageNameTrimmed = pageName.replace(/[^A-Z0-9]+/ig, "").toLowerCase();
      pageData.fileName = pageNameTrimmed;
      pageData.description.reference = isNumber;
      var adHocUrl = isNumber + pageNameTrimmed;
      if (pageType === 'static_qmi' && !Florence.globalVars.welsh) {
        newUri = makeUrl(parentUrl, 'qmis', pageNameTrimmed);
      } else if (pageType === 'static_adhoc' && !Florence.globalVars.welsh) {
        newUri = makeUrl(parentUrl, 'adhocs', adHocUrl);
      } else if ((pageType === 'static_methodology' || pageType === 'static_methodology_download') && !Florence.globalVars.welsh) {
        newUri = makeUrl(parentUrl, 'methodologies', pageNameTrimmed);
      } else if (!Florence.globalVars.welsh) {
        newUri = makeUrl(parentUrl, pageNameTrimmed);
      } else {
        sweetAlert('You can not perform that operation in Welsh.');
      }
      var safeNewUri = checkPathSlashes(newUri);
      if (releaseDate && (pageType === 'static_qmi')) {
        date = new Date(releaseDate);
        pageData.description.lastRevised = $.datepicker.formatDate('dd/mm/yy', date);
      } else if (releaseDate) {
        date = new Date(releaseDate);
        pageData.description.releaseDate = $.datepicker.formatDate('dd/mm/yy', date);
      } else if (!releaseDate && (pageType === 'static_qmi')) {
        pageData.description.lastRevised = new Date($('#releaseDate').val()).toISOString();
        //} else if (!releaseDate && (pageType === 'static_page' || pageType === 'static_article')) {
        //  pageData.description.releaseDate = "1970-01-01T00:00:00.000Z";  // zebedee throws a JsonSyntaxException
        // if no date is present
      } else if (!releaseDate) {
        pageData.description.releaseDate = new Date().toISOString();
      }

      if (pageName.length < 4) {
        sweetAlert("This is not a valid file name");
      } else {
        saveContent(collectionId, safeNewUri, pageData);
      }
    });
  }
}

function pageTypeDataT7(pageType) {

  if (pageType === "static_page") {
    return {
      "description": {
        "title": "",
        "summary": "",
        "releaseDate": "",
        "keywords": [],
        "metaDescription": ""
      },
      "markdown": [],
      "charts": [],
      "tables": [],
      "images": [],
      "downloads": [],
      type: pageType,
      "anchors": [],
      "links": []
    };
  } else if (pageType === "static_landing_page") {
    return {
      "description": {
        "title": "",
        "summary": "",
        "releaseDate": "",
        "keywords": [],
        "metaDescription": "",
      },
      "sections": [],
      "markdown": [],
      type: pageType,
      "links": []
    };
  }
  else if (pageType === "static_article") {
    return {
      "description": {
        "title": "",
        "summary": "",
        "releaseDate": "",
        "keywords": [],
        "metaDescription": ""
      },
      "sections": [],
      "charts": [],
      "tables": [],
      "images": [],
      "anchors": [],
      "links": [],
      "alerts": [],
      type: pageType
    };
  } else if (pageType === "static_methodology") {
    return {
      "description": {
        "title": "",
        "summary": "",
        "releaseDate": "",
        "contact": {
          "name": "",
          "email": "",
          "telephone": ""
        },
        "keywords": [],
        "metaDescription": ""
      },
      "sections": [],
      "accordion": [],
      "relatedDocuments": [],
      "relatedDatasets": [],
      "relatedMethodology": [],
      "relatedMethodologyArticle": [],
      "charts": [],
      "tables": [],
      "images": [],
      "downloads": [],
      "links": [],
      "alerts": [],
      type: pageType
    };
  } else if (pageType === "static_methodology_download") {
    return {
      "description": {
        "title": "",
        "contact": {
          "name": "",
          "email": "",
          "phone": ""
        },
        "releaseDate": "",
        "keywords": [],
        "metaDescription": ""
      },
      "markdown": [],
      "downloads": [],
      "relatedDocuments": [],
      "relatedDatasets": [],
      "relatedMethodology": [],
      "relatedMethodologyArticle": [],
      "links": [],
      "alerts": [],
      type: pageType
    };
  } else if (pageType === "static_qmi") {
    return {
      "description": {
        "title": "",
        "contact": {
          "name": "",
          "email": "",
          "phone": ""
        },
        "surveyName": "",
        "frequency": "",
        "compilation": "",
        "geographicCoverage": "",
        "sampleSize": null,
        "lastRevised": "",
        "nationalStatistic": false,
        "keywords": [],
        "metaDescription": ""
      },
      "markdown": [],
      "downloads": [],
      "relatedDocuments": [],
      "relatedDatasets": [],
      "relatedMethodology": [],
      "relatedMethodologyArticle": [],
      "links": [],
      type: pageType
    };
  } else if (pageType === "static_foi") {
    return {
      "description": {
        "title": "",
        "releaseDate": "",
        "keywords": [],
        "metaDescription": ""
      },
      "downloads": [],
      "markdown": [],
      "links": [],
      type: pageType
    };
  } else if (pageType === "static_adhoc") {
    return {
      "description": {
        "title": "",
        "releaseDate": "",
        "reference": null,
        "keywords": [],
        "metaDescription": ""
      },
      "downloads": [],
      "markdown": [],
      "links": [],
      type: pageType
    };
  } else {
    sweetAlert('Unsupported page type', 'This is not a static page', "info");
  }
}<|endoftext|>