<commit_before>/**
 * This object is used as a default one for those charts that do not have any user configuration.
 * @type {Object}
 */
const _default = {
  Barchart: {
    style: {
      '.axis': {
        'font': '10px sans-serif'
      },
      '.axis path,.axis line': {
        'fill': 'none',
        'stroke': '#000',
        'shape-rendering': 'crispEdges'
      },
      '.x.axis path': {
        'display': 'none'
      },
      '.x.axis.label, .y.axis.label': {
        'font': '12px sans-serif'
      }
    },
    xaxis: {
      label: ''
    },
    yaxis: {
      label: 'Y'
    },
    // Set the color scale for the chart. You can use Proteus scales or any D3 scale
    colorScale: Colors.category7(),
    margin: {
      top: 20,
      right: 20,
      bottom: 30,
      left: 50
    },
    width: '100%', // %, auto, or numeric
    height: 350,
    ticks: 5, // ticks for y axis.
    tooltip(data) { // Allows HTML
      return 'Eje x: ' + data.x + '
' +
        'Eje y: ' + data.y;
    },
    tickLabel: '',
    selector: '#chart',
    events: {
      down() {
        d3.select(this).classed('hover', false);
      },
      over() {
        d3.select(this)
          .transition()
          .duration(150)
          .attr('fill-opacity', 0.4);
      },
      leave() {
        d3.select(this)
          .transition()
          .duration(150)
          .attr('fill-opacity', 1);
      },
      click(d, i) {
        console.log(d, i);
      }
    },
    transitionDuration: 300,
    maxNumberOfElements: 0, // used by keepDrawing to reduce the number of elements in the current chart
    sortData: {
      descending: false,
      prop: 'x'
    }
  },
  Linechart: {
    selector: '#chart',
    xaxis: {
      label: 'X',
      ticks: 5
    },
    yaxis: {
      label: 'Y',
      ticks: 5
    },
    colorScale: Colors.category7(),
    area: false,
    areaOpacity: 0.4,
    margin: {
      top: 20,
      right: 20,
      bottom: 30,
      left: 50
    },
    width: '100%', // %, auto, or numeric
    height: 250,
    style: {
      '.line': {
        'stroke-width': 2,
        'fill': 'none'
      },
      '.area': {
        'stroke-width': 0
      },
      '.axis': {
        'font': '10px sans-serif'
      },
      '.axis path,.axis line': {
        'fill': 'none',
        'stroke': '#000',
        'shape-rendering': 'crispEdge'
      },
      '.x.axis path': {
        'display': 'none'
      },
      '.x.axis.label, .y.axis.label': {
        'font': '12px sans-serif'
      }
    },
    markers: {
      shape: 'circle',
      size: 5,
      color: '#FFFCCA',
      outlineColor: '#537780',
      outlineWidth: 2
    },
    tooltip(data) {
      return JSON.stringify(data);
    },
    events: {
      down() {
        d3.select(this).classed('hover', false);
      },
      over() {
        d3.select(this)
          .transition()
          .duration(50)
          .attr('r', 7)
          ;
      },
      leave() {
        d3.select(this)
          .transition()
          .duration(50)
          .attr('r', 5)
          .style('stroke-width', 2);
      },
      click(d, i) {
        console.log(d, i);
      }
    },
    tickLabel: '',
    transitionDuration: 300,
    maxNumberOfElements: 10, // used by keepDrawing to reduce the number of elements in the current chart
    sortData: {
      descending: false,
      prop: 'x'
    }
  },
  Streamgraph: {
    selector: '#chart',
    xDateFormat: '%m/%d/%y',
    colorScale: Colors.category5(),
    xaxis: {
      label: ''
    },
    yaxis: {
      label: ''
    },
    style: {
      '.axis': {
        'font': '10px sans-serif'
      },
      '.axis path,.axis line': {
        'fill': 'none',
        'stroke': '#000',
        'shape-rendering': 'crispEdges'
      },
      '.x.axis path': {
        'display': 'none'
      },
      '.x.axis.label, .y.axis.label': {
        'font': '12px sans-serif'
      }
    },
    margin: {
      top: 20,
      right: 20,
      bottom: 30,
      left: 50
    },
    width: '100%', // %, auto, or numeric
    height: 300,
    ticks: 5, // ticks for y axis.
    tooltip(object) {
      return 'Info: ' + JSON.stringify(object);
    },
    tickLabel: '',
    events: {
      down() {
        d3.select(this).classed('hover', false);
      },
      over() {
        d3.select(this)
          .transition()
          .duration(150)
          .attr('fill-opacity', 0.4);
      },
      leave() {
        d3.select(this)
          .transition()
          .duration(150)
          .attr('fill-opacity', 1);
      },
      click(d, i) {
        console.log(d, i);
      }
    },
    transitionDuration: 300,
    maxNumberOfElements: 0, // used by keepDrawing to reduce the number of elements in the current chart
    sortData: {
      descending: false,
      prop: 'x'
    }
  },
  Gauge: {
    selector: '#chart',
    colorScale: Colors.diverging_red_blue(),
    invertColorScale: true,
    minLevel: 0,
    maxLevel: 100,
    minAngle: -90,
    maxAngle: 180,
    ringWidth: 50,
    ringMargin: 20,
    labelInset: 10,
    needleNutRadius: 25,
    needleLenghtRatio: 0.8,
    numericIndicator: true,
    xaxis: {
      label: 'X'
    },
    yaxis: {
      label: 'Y'
    },
    margin: {
      top: 20,
      right: 20,
      bottom: 30,
      left: 50
    },
    width: '50%', // %, auto, or numeric
    height: 250,
    style: {
      '.labels': {
        'font': '18px sans-serif',
        'text-anchor': 'middle'
      },
      '.text-indicator': {
        'font': '48px sans-serif',
        'text-anchor': 'middle'
      },
      '.needle': {
        'fill': '#666666'
      }
    },
    ticks: 10, // ticks for y axis.
    markers: {
      shape: 'circle',
      size: 5,
      color: '#FFFCCA',
      outlineColor: '#537780',
      outlineWidth: 2
    },
    tooltip(data) {
      return JSON.stringify(data);
    },
    events: {
      down() {
        d3.select(this).classed('hover', false);
      },
      over() {
        d3.select(this)
          .transition()
          .duration(50)
          .attr('r', 7)
          ;
      },
      leave() {
        d3.select(this)
          .transition()
          .duration(50)
          .attr('r', 5)
          .style('stroke-width', 2);
      },
      click(d, i) {
        console.log(d, i);
      }
    },
    tickLabel: '',
    transitionDuration: 300,
    maxNumberOfElements: 5, // used by keepDrawing to reduce the number of elements in the current chart
    sortData: {
      descending: false,
      prop: 'x'
    }
  }
};<commit_msg>Update linechart globals<commit_after>/**
 * This object is used as a default one for those charts that do not have any user configuration.
 * @type {Object}
 */
const _default = {
  Barchart: {
    style: {
      '.axis': {
        'font': '10px sans-serif'
      },
      '.axis path,.axis line': {
        'fill': 'none',
        'stroke': '#000',
        'shape-rendering': 'crispEdges'
      },
      '.x.axis path': {
        'display': 'none'
      },
      '.x.axis.label, .y.axis.label': {
        'font': '12px sans-serif'
      }
    },
    xaxis: {
      label: ''
    },
    yaxis: {
      label: 'Y'
    },
    // Set the color scale for the chart. You can use Proteus scales or any D3 scale
    colorScale: Colors.category7(),
    margin: {
      top: 20,
      right: 20,
      bottom: 30,
      left: 50
    },
    width: '100%', // %, auto, or numeric
    height: 350,
    ticks: 5, // ticks for y axis.
    tooltip(data) { // Allows HTML
      return 'Eje x: ' + data.x + '
' +
        'Eje y: ' + data.y;
    },
    tickLabel: '',
    selector: '#chart',
    events: {
      down() {
        d3.select(this).classed('hover', false);
      },
      over() {
        d3.select(this)
          .transition()
          .duration(150)
          .attr('fill-opacity', 0.4);
      },
      leave() {
        d3.select(this)
          .transition()
          .duration(150)
          .attr('fill-opacity', 1);
      },
      click(d, i) {
        console.log(d, i);
      }
    },
    transitionDuration: 300,
    maxNumberOfElements: 0, // used by keepDrawing to reduce the number of elements in the current chart
    sortData: {
      descending: false,
      prop: 'x'
    }
  },
  Linechart: {
    selector: '#chart',
    width: '100%', // %, auto, or numeric
    height: 250,
    margin: {
      top: 20,
      right: 20,
      bottom: 30,
      left: 50
    },
    colorScale: Colors.category7(),
    area: false,
    areaOpacity: 0.4,
    maxNumberOfElements: 10, // used by keepDrawing to reduce the number of elements in the current chart
    transitionDuration: 300,
    tickLabel: '',
    xaxis: {
      label: 'X',
      ticks: 5
    },
    yaxis: {
      label: 'Y',
      ticks: 5
    },
    style: {
      '.line': {
        'stroke-width': 2,
        'fill': 'none'
      },
      '.area': {
        'stroke-width': 0
      },
      '.axis': {
        'font': '10px sans-serif'
      },
      '.axis path,.axis line': {
        'fill': 'none',
        'stroke': '#000',
        'shape-rendering': 'crispEdge'
      },
      '.x.axis path': {
        'display': 'none'
      },
      '.x.axis.label, .y.axis.label': {
        'font': '12px sans-serif'
      }
    },
    markers: {
      shape: 'circle',
      size: 5,
      color: '#FFFCCA',
      outlineColor: '#537780',
      outlineWidth: 2
    },
    tooltip(data) {
      return 'X axis: ' + data.x + '
' +
        'Y axis: ' + data.y;
    },
    events: {
      down() {
        d3.select(this).classed('hover', false);
      },
      over() {
        d3.select(this)
          .transition()
          .duration(50)
          .attr('r', 7)
          ;
      },
      leave() {
        d3.select(this)
          .transition()
          .duration(50)
          .attr('r', 5)
          .style('stroke-width', 2);
      },
      click(d, i) {
        console.log(d, i);
      }
    },
    sortData: {
      descending: false,
      prop: 'x'
    }
  },
  Streamgraph: {
    selector: '#chart',
    xDateFormat: '%m/%d/%y',
    colorScale: Colors.category5(),
    xaxis: {
      label: ''
    },
    yaxis: {
      label: ''
    },
    style: {
      '.axis': {
        'font': '10px sans-serif'
      },
      '.axis path,.axis line': {
        'fill': 'none',
        'stroke': '#000',
        'shape-rendering': 'crispEdges'
      },
      '.x.axis path': {
        'display': 'none'
      },
      '.x.axis.label, .y.axis.label': {
        'font': '12px sans-serif'
      }
    },
    margin: {
      top: 20,
      right: 20,
      bottom: 30,
      left: 50
    },
    width: '100%', // %, auto, or numeric
    height: 300,
    ticks: 5, // ticks for y axis.
    tooltip(object) {
      return 'Info: ' + JSON.stringify(object);
    },
    tickLabel: '',
    events: {
      down() {
        d3.select(this).classed('hover', false);
      },
      over() {
        d3.select(this)
          .transition()
          .duration(150)
          .attr('fill-opacity', 0.4);
      },
      leave() {
        d3.select(this)
          .transition()
          .duration(150)
          .attr('fill-opacity', 1);
      },
      click(d, i) {
        console.log(d, i);
      }
    },
    transitionDuration: 300,
    maxNumberOfElements: 0, // used by keepDrawing to reduce the number of elements in the current chart
    sortData: {
      descending: false,
      prop: 'x'
    }
  },
  Gauge: {
    selector: '#chart',
    colorScale: Colors.diverging_red_blue(),
    invertColorScale: true,
    minLevel: 0,
    maxLevel: 100,
    minAngle: -90,
    maxAngle: 180,
    ringWidth: 50,
    ringMargin: 20,
    labelInset: 10,
    needleNutRadius: 25,
    needleLenghtRatio: 0.8,
    numericIndicator: true,
    xaxis: {
      label: 'X'
    },
    yaxis: {
      label: 'Y'
    },
    margin: {
      top: 20,
      right: 20,
      bottom: 30,
      left: 50
    },
    width: '50%', // %, auto, or numeric
    height: 250,
    style: {
      '.labels': {
        'font': '18px sans-serif',
        'text-anchor': 'middle'
      },
      '.text-indicator': {
        'font': '48px sans-serif',
        'text-anchor': 'middle'
      },
      '.needle': {
        'fill': '#666666'
      }
    },
    ticks: 10, // ticks for y axis.
    markers: {
      shape: 'circle',
      size: 5,
      color: '#FFFCCA',
      outlineColor: '#537780',
      outlineWidth: 2
    },
    tooltip(data) {
      return JSON.stringify(data);
    },
    events: {
      down() {
        d3.select(this).classed('hover', false);
      },
      over() {
        d3.select(this)
          .transition()
          .duration(50)
          .attr('r', 7)
          ;
      },
      leave() {
        d3.select(this)
          .transition()
          .duration(50)
          .attr('r', 5)
          .style('stroke-width', 2);
      },
      click(d, i) {
        console.log(d, i);
      }
    },
    tickLabel: '',
    transitionDuration: 300,
    maxNumberOfElements: 5, // used by keepDrawing to reduce the number of elements in the current chart
    sortData: {
      descending: false,
      prop: 'x'
    }
  }
};<|endoftext|>


<commit_before>/* global window */

import memoize from 'lodash/memoize';

const getCanvasContext = memoize(() => {
  if (!window) {
    return false;
  }

  return window.document.createElement('canvas').getContext('2d');
});

/**
 * Measure rendered string dimensions
 * @param {String} str -> the string to measure
 * @param {String} font -> https://developer.mozilla.org/en-US/docs/Web/CSS/font
 * @param {CanvasRenderingContext2D} [canvasContext]
 * @return {object}
 */
export const getRenderedStringDimensions = (
  str = '',
  font = '10px Helvetica',
  canvasContext = getCanvasContext(),
) => {
  if (!canvasContext && !window) {
    return 0;
  }

  // eslint-disable-next-line no-param-reassign
  canvasContext.font = font;

  // see https://developer.mozilla.org/en-US/docs/Web/API/TextMetrics
  return canvasContext.measureText(str);
};

/**
 * Measure rendered width of string
 * @param {String} str -> the string to measure
 * @param {String} font -> https://developer.mozilla.org/en-US/docs/Web/CSS/font
 * @param {CanvasRenderingContext2D} [canvasContext]
 * @return {number}
 */
export const getRenderedStringWidth = (
  str = '',
  font = '10px Helvetica',
  canvasContext = getCanvasContext(),
) => {
  const { width } = getRenderedStringDimensions(str, font, canvasContext);
  return Math.ceil(width);
};

/**
 * Measure rendered height of string
 * @param {String} str -> the string to measure
 * @param {String} font -> https://developer.mozilla.org/en-US/docs/Web/CSS/font
 * @param {CanvasRenderingContext2D} [canvasContext]
 * @return {number}
 */
export const getRenderedStringHeight = (
  str = '',
  font = '10px Helvetica',
  canvasContext = getCanvasContext(),
) => {
  const { height } = getRenderedStringDimensions(str, font, canvasContext);
  return Math.ceil(height);
};

export const sizeOfLongestRotatedString = (
  values,
  height, // tickLabelHeight
  rotationAngle = -45,
) => values.reduce((result, label) => {
  const width = Math.floor(getRenderedStringWidth(label));
  const size = Math.ceil(
    (height * Math.abs(Math.cos(rotationAngle))) + (width * Math.abs(Math.sin(rotationAngle)))
  );
  return size > result ? size : result;
}, 0);
<commit_msg>Add scaling factor for determining text height based on font-size and remove non-functional getRenderedStringHeight method.<commit_after>/* global window */

import memoize from 'lodash/memoize';

/** Due to difficulties in measuring font height, canvasContext.measureText() does not
 * return a height value. By comparing various rendered SVG text element heights with
 * their specified font-size, 1.15 is a reasonable scaling factor to
 * approximate text height.
 * https://videlais.com/2014/03/16/the-many-and-varied-problems-with-measuring-font-height-for-html5-canvas/
*/
export const FONT_HEIGHT_SCALING_FACTOR = 1.15;

const getCanvasContext = memoize(() => {
  if (!window) {
    return false;
  }

  return window.document.createElement('canvas').getContext('2d');
});

/**
 * Measure rendered string dimensions
 * @param {String} str -> the string to measure
 * @param {String} font -> https://developer.mozilla.org/en-US/docs/Web/CSS/font
 * @param {CanvasRenderingContext2D} [canvasContext]
 * @return {object}
 */
export const getRenderedStringDimensions = (
  str = '',
  font = '10px Helvetica',
  canvasContext = getCanvasContext(),
) => {
  if (!canvasContext && !window) {
    return 0;
  }

  // eslint-disable-next-line no-param-reassign
  canvasContext.font = font;

  // see https://developer.mozilla.org/en-US/docs/Web/API/TextMetrics
  return canvasContext.measureText(str);
};

/**
 * Measure rendered width of string
 * @param {String} str -> the string to measure
 * @param {String} font -> https://developer.mozilla.org/en-US/docs/Web/CSS/font
 * @param {CanvasRenderingContext2D} [canvasContext]
 * @return {number}
 */
export const getRenderedStringWidth = (
  str = '',
  font = '10px Helvetica',
  canvasContext = getCanvasContext(),
) => {
  const { width } = getRenderedStringDimensions(str, font, canvasContext);
  return Math.ceil(width);
};

export const sizeOfLongestRotatedString = (
  values,
  height, // tickLabelHeight
  rotationAngle = -45,
) => values.reduce((result, label) => {
  const width = Math.floor(getRenderedStringWidth(label));
  const scaledHeight = height * FONT_HEIGHT_SCALING_FACTOR;
  const size = Math.ceil(
    (scaledHeight * Math.abs(Math.cos(rotationAngle))) + (width * Math.abs(Math.sin(rotationAngle)))
  );
  return size > result ? size : result;
}, 0);
<|endoftext|>


<commit_before>(function(){"use strict";function t(t){var s,o,u=n(t.units),a=r(t.from,u),f=r(t.until,u,Date.now()),l=f-a;for(s in e)if(e.hasOwnProperty(s)&&l>=e[s])return o=Math.floor(l/e[s]),o+" "+i(s,o)+" ago";return"just now"}function n(e){if(typeof e=="undefined")return"s";if(e!=="s"&&e!=="ms")throw new Error("Invalid units");return e}function r(e,t,n){if(typeof e=="undefined"&&typeof n=="number")return n;typeof e=="string"&&(e=parseInt(e,10));if(typeof e!="number"||isNaN(e))throw new Error("Invalid timestamp");return t==="s"?e*1e3:e}function i(e,t){return e+(t>1?"s":"")}var e={year:315576e5,month:26298e5,week:6048e5,day:864e5,hour:36e5,minute:6e4};typeof exports=="undefined"?window.vagueTime={get:t}:exports.get=t})();<commit_msg>Update minified script.<commit_after>(function(){"use strict";function t(t){var s,o,u=n(t.units),a=r(t.from,u),f=r(t.until,u,Date.now()),l=f-a;for(s in e)if(e.hasOwnProperty(s)&&l>=e[s])return o=Math.floor(l/e[s]),o+" "+i(s,o)+" ago";return"just now"}function n(e){if(typeof e=="undefined")return"s";if(e==="s"||e==="ms")return e;throw new Error("Invalid units")}function r(e,t,n){if(typeof e=="undefined"&&typeof n=="number")return n;typeof e=="string"&&(e=parseInt(e,10));if(typeof e!="number"||isNaN(e))throw new Error("Invalid timestamp");return t==="s"?e*1e3:e}function i(e,t){return e+(t>1?"s":"")}var e={year:315576e5,month:26298e5,week:6048e5,day:864e5,hour:36e5,minute:6e4};typeof exports=="undefined"?window.vagueTime={get:t}:exports.get=t})();<|endoftext|>


<commit_before>import {Matrix} from 'transformation-matrix-js';
import {
  MODE_IDLE,
  MODE_PANNING
} from './constants';

export default class ViewerHelper {

  static getDefaultValue() {
    let matrix = new Matrix();

    return {
      matrix: {a: matrix.a, b: matrix.b, c: matrix.c, d: matrix.d, e: matrix.e, f: matrix.f},
      mode: MODE_IDLE
    };
  }

  static zoom(value, scaleFactor, centerX, centerY) {
    let {a, b, c, d, e, f} = value.matrix;
    let matrix = Matrix.from(a, b, c, d, e, f);

    let act = new Matrix();
    act = act.translate(centerX, centerY);
    act = act.scaleU(scaleFactor);
    act = act.translate(-centerX, -centerY);

    matrix = matrix.multiply(act);

    return {
      mode: MODE_IDLE,
      matrix: {a: matrix.a, b: matrix.b, c: matrix.c, d: matrix.d, e: matrix.e, f: matrix.f}
    };
  }

  static pan(value, deltaX, deltaY) {
    let {a, b, c, d, e, f} = value.matrix;
    let matrix = Matrix.from(a, b, c, d, e, f);

    let act = new Matrix();
    act = act.translate(deltaX, deltaY);

    matrix = matrix.multiply(act);

    return {
      mode: MODE_IDLE,
      matrix: {a: matrix.a, b: matrix.b, c: matrix.c, d: matrix.d, e: matrix.e, f: matrix.f}
    };
  }

  static startPan(value, startX, startY) {
    return {
      mode: MODE_PANNING,
      startX,
      startY,
      matrix: Object.assign({}, value.matrix)
    };
  }

  static updatePan(value, x, y) {

    if (value.mode !== MODE_PANNING) throw new Error('update pan not allowed in this mode ' + value.mode);

    let {a, b, c, d, e, f} = value.matrix;
    let matrix = Matrix.from(a, b, c, d, e, f);
    let zoomLevel = matrix.decompose(false).scale.x;

    let deltaX = (value.startX - x) / zoomLevel;
    let deltaY = (value.startY - y) / zoomLevel;

    let act = new Matrix();
    act = act.translate(-deltaX, -deltaY);

    matrix = matrix.multiply(act);

    return {
      mode: MODE_PANNING,
      startX: x,
      startY: y,
      matrix: {a: matrix.a, b: matrix.b, c: matrix.c, d: matrix.d, e: matrix.e, f: matrix.f}
    };

  }

  static fitSelectionToViewer(value, selectionX, selectionY, selectionWidth, selectionHeight, viewerWidth, viewerHeight){

    let scaleX = viewerWidth / selectionWidth;
    let scaleY = viewerHeight / selectionHeight;

    let scale = Math.min(scaleX, scaleY);

    let matrix = new Matrix();
    matrix = matrix.scaleU(scale);
    matrix = matrix.translate(-selectionX, -selectionY);

    return {
      mode: MODE_IDLE,
      matrix: {a: matrix.a, b: matrix.b, c: matrix.c, d: matrix.d, e: matrix.e, f: matrix.f}
    };
  }

  static getArtboardPoint(value, x, y) {
    let {a, b, c, d, e, f} = value.matrix;
    let matrix = Matrix.from(a, b, c, d, e, f);

    let inverseMatrix = matrix.inverse();
    return inverseMatrix.applyToPoint(x, y);
  }

  static decomposeValue(value) {
    let {a, b, c, d, e, f} = value.matrix;
    let matrix = Matrix.from(a, b, c, d, e, f);
    let decompose = matrix.decompose(false);

    return {
      scaleFactor: decompose.scale.x,
      translationX: decompose.translate.x,
      translationY: decompose.translate.y
    }
  }

  static stopPan(value) {
    return {
      mode: MODE_IDLE,
      matrix: Object.assign({}, value.matrix)
    };
  }

}
<commit_msg>code clean<commit_after>import {Matrix} from 'transformation-matrix-js';
import {
  MODE_IDLE,
  MODE_PANNING
} from './constants';

export default class ViewerHelper {

  static getDefaultValue() {
    let matrix = new Matrix();

    return {
      matrix: {a: matrix.a, b: matrix.b, c: matrix.c, d: matrix.d, e: matrix.e, f: matrix.f},
      mode: MODE_IDLE
    };
  }

  static zoom(value, scaleFactor, centerX, centerY) {
    let {a, b, c, d, e, f} = value.matrix;
    let matrix = Matrix.from(a, b, c, d, e, f);

    let act = new Matrix();
    act = act.translate(centerX, centerY);
    act = act.scaleU(scaleFactor);
    act = act.translate(-centerX, -centerY);

    matrix = matrix.multiply(act);

    return {
      mode: MODE_IDLE,
      matrix: {a: matrix.a, b: matrix.b, c: matrix.c, d: matrix.d, e: matrix.e, f: matrix.f}
    };
  }

  static pan(value, deltaX, deltaY) {
    let {a, b, c, d, e, f} = value.matrix;
    let matrix = Matrix.from(a, b, c, d, e, f);

    let act = new Matrix();
    act = act.translate(deltaX, deltaY);

    matrix = matrix.multiply(act);

    return {
      mode: MODE_IDLE,
      matrix: {a: matrix.a, b: matrix.b, c: matrix.c, d: matrix.d, e: matrix.e, f: matrix.f}
    };
  }

  static startPan(value, startX, startY) {
    return {
      mode: MODE_PANNING,
      startX,
      startY,
      matrix: Object.assign({}, value.matrix)
    };
  }

  static updatePan(value, x, y) {

    if (value.mode !== MODE_PANNING) throw new Error('update pan not allowed in this mode ' + value.mode);

    let {a, b, c, d, e, f} = value.matrix;
    let matrix = Matrix.from(a, b, c, d, e, f);
    let zoomLevel = matrix.decompose(false).scale.x;

    let deltaX = (value.startX - x) / zoomLevel;
    let deltaY = (value.startY - y) / zoomLevel;

    let act = new Matrix();
    act = act.translate(-deltaX, -deltaY);

    matrix = matrix.multiply(act);

    return {
      mode: MODE_PANNING,
      startX: x,
      startY: y,
      matrix: {a: matrix.a, b: matrix.b, c: matrix.c, d: matrix.d, e: matrix.e, f: matrix.f}
    };

  }

  static stopPan(value) {
    return {
      mode: MODE_IDLE,
      matrix: Object.assign({}, value.matrix)
    };
  }

  static fitSelectionToViewer(value, selectionX, selectionY, selectionWidth, selectionHeight, viewerWidth, viewerHeight){

    let scaleX = viewerWidth / selectionWidth;
    let scaleY = viewerHeight / selectionHeight;

    let scale = Math.min(scaleX, scaleY);

    let matrix = new Matrix();
    matrix = matrix.scaleU(scale);
    matrix = matrix.translate(-selectionX, -selectionY);

    return {
      mode: MODE_IDLE,
      matrix: {a: matrix.a, b: matrix.b, c: matrix.c, d: matrix.d, e: matrix.e, f: matrix.f}
    };
  }

  static getArtboardPoint(value, x, y) {
    let {a, b, c, d, e, f} = value.matrix;
    let matrix = Matrix.from(a, b, c, d, e, f);

    let inverseMatrix = matrix.inverse();
    return inverseMatrix.applyToPoint(x, y);
  }

  static decomposeValue(value) {
    let {a, b, c, d, e, f} = value.matrix;
    let matrix = Matrix.from(a, b, c, d, e, f);
    let decompose = matrix.decompose(false);

    return {
      scaleFactor: decompose.scale.x,
      translationX: decompose.translate.x,
      translationY: decompose.translate.y
    }
  }

}
<|endoftext|>


<commit_before>/* eslint-disable react/forbid-prop-types */
/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

import React from 'react';
import PropTypes from 'prop-types';

import IonIcon from 'react-native-vector-icons/Ionicons';
import FAIcon from 'react-native-vector-icons/FontAwesome';
import FA5Icon from 'react-native-vector-icons/FontAwesome5';
import EvilIcon from 'react-native-vector-icons/EvilIcons';
import EntypoIcon from 'react-native-vector-icons/Entypo';
import MaterialIcon from 'react-native-vector-icons/MaterialCommunityIcons';

import {
  WHITE,
  FINALISE_GREEN,
  SUSSOL_ORANGE,
  FINALISED_RED,
  dataTableColors,
} from '../globalStyles';

export const SortAscIcon = () => <FAIcon name="sort-asc" size={15} style={{ marginRight: 10 }} />;
export const SortNeutralIcon = () => <FAIcon name="sort" size={15} style={{ marginRight: 10 }} />;
export const SortDescIcon = () => <FAIcon name="sort-desc" size={15} style={{ marginRight: 10 }} />;

export const CheckedIcon = () => (
  <IonIcon name="md-radio-button-on" size={15} color={dataTableColors.checkableCellChecked} />
);

export const UncheckedIcon = () => (
  <IonIcon name="md-radio-button-off" size={15} color={SUSSOL_ORANGE} />
);

export const DisabledCheckedIcon = () => (
  <IonIcon name="md-radio-button-on" size={15} color={dataTableColors.checkableCellDisabled} />
);
export const DisabledUncheckedIcon = () => <MaterialIcon name="cancel" size={15} />;

export const OpenModalIcon = () => (
  <FAIcon name="angle-double-up" size={20} color={SUSSOL_ORANGE} />
);

export const MagnifyingGlassIcon = React.memo(({ size, color }) => (
  <EvilIcon name="search" size={size} color={color} />
));
MagnifyingGlassIcon.propTypes = { size: PropTypes.number, color: PropTypes.string };
MagnifyingGlassIcon.defaultProps = { size: 40, color: SUSSOL_ORANGE };

export const CancelIcon = React.memo(() => (
  <EntypoIcon name="cross" color={FINALISED_RED} size={25} />
));

export const CloseIcon = React.memo(() => <IonIcon name="md-close" size={36} color="white" />);

export const ExpandIcon = React.memo(() => (
  <FAIcon name="external-link" size={16} color={SUSSOL_ORANGE} />
));

export const ConfirmIcon = React.memo(({ style }) => <FAIcon name="check-circle" style={style} />);
ConfirmIcon.defaultProps = { style: { color: FINALISE_GREEN, fontSize: 40 } };
ConfirmIcon.propTypes = { style: PropTypes.object };

export const LockIcon = React.memo(({ style }) => <FAIcon name="lock" size={28} style={style} />);
LockIcon.defaultProps = { style: { color: FINALISED_RED, marginHorizontal: 8, bottom: 6 } };
LockIcon.propTypes = { style: PropTypes.object };

export const ChevronDownIcon = React.memo(({ color, size }) => (
  <FA5Icon name="chevron-down" color={color} size={size} />
));
ChevronDownIcon.defaultProps = { color: WHITE, size: 20 };
ChevronDownIcon.propTypes = { color: PropTypes.string, size: PropTypes.number };

export const PencilIcon = React.memo(({ color, size }) => (
  <FAIcon name="pencil" color={color} size={size} />
));
PencilIcon.defaultProps = { color: WHITE, size: 20 };
PencilIcon.propTypes = { color: PropTypes.string, size: PropTypes.number };

export const HistoryIcon = React.memo(() => <FA5Icon name="history" size={20} color={WHITE} />);

export const ChevronRightIcon = () => (
  <FA5Icon name="chevron-right" color={SUSSOL_ORANGE} size={20} />
);

export const FavouriteStarIcon = () => <FAIcon name="star-o" color={SUSSOL_ORANGE} size={20} />;
export const BurgerMenuIcon = () => <EntypoIcon name="menu" color={SUSSOL_ORANGE} size={30} />;
export const AddIcon = ({ size, color }) => <IonIcon name="ios-add" size={size} color={color} />;
AddIcon.defaultProps = { color: WHITE, size: 30 };
AddIcon.propTypes = { color: PropTypes.string, size: PropTypes.number };

export const MinusIcon = ({ size, color }) => (
  <IonIcon name="ios-remove" size={size} color={color} />
);
MinusIcon.defaultProps = { color: WHITE, size: 30 };
MinusIcon.propTypes = { color: PropTypes.string, size: PropTypes.number };
<commit_msg>Add calendarIcon<commit_after>/* eslint-disable react/forbid-prop-types */
/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

import React from 'react';
import PropTypes from 'prop-types';

import IonIcon from 'react-native-vector-icons/Ionicons';
import FAIcon from 'react-native-vector-icons/FontAwesome';
import FA5Icon from 'react-native-vector-icons/FontAwesome5';
import EvilIcon from 'react-native-vector-icons/EvilIcons';
import EntypoIcon from 'react-native-vector-icons/Entypo';
import MaterialIcon from 'react-native-vector-icons/MaterialCommunityIcons';

import {
  WHITE,
  FINALISE_GREEN,
  SUSSOL_ORANGE,
  FINALISED_RED,
  dataTableColors,
} from '../globalStyles';

export const SortAscIcon = () => <FAIcon name="sort-asc" size={15} style={{ marginRight: 10 }} />;
export const SortNeutralIcon = () => <FAIcon name="sort" size={15} style={{ marginRight: 10 }} />;
export const SortDescIcon = () => <FAIcon name="sort-desc" size={15} style={{ marginRight: 10 }} />;

export const CheckedIcon = () => (
  <IonIcon name="md-radio-button-on" size={15} color={dataTableColors.checkableCellChecked} />
);

export const UncheckedIcon = () => (
  <IonIcon name="md-radio-button-off" size={15} color={SUSSOL_ORANGE} />
);

export const DisabledCheckedIcon = () => (
  <IonIcon name="md-radio-button-on" size={15} color={dataTableColors.checkableCellDisabled} />
);
export const DisabledUncheckedIcon = () => <MaterialIcon name="cancel" size={15} />;

export const OpenModalIcon = () => (
  <FAIcon name="angle-double-up" size={20} color={SUSSOL_ORANGE} />
);

export const MagnifyingGlassIcon = React.memo(({ size, color }) => (
  <EvilIcon name="search" size={size} color={color} />
));
MagnifyingGlassIcon.propTypes = { size: PropTypes.number, color: PropTypes.string };
MagnifyingGlassIcon.defaultProps = { size: 40, color: SUSSOL_ORANGE };

export const CancelIcon = React.memo(() => (
  <EntypoIcon name="cross" color={FINALISED_RED} size={25} />
));

export const CloseIcon = React.memo(() => <IonIcon name="md-close" size={36} color="white" />);

export const ExpandIcon = React.memo(() => (
  <FAIcon name="external-link" size={16} color={SUSSOL_ORANGE} />
));

export const ConfirmIcon = React.memo(({ style }) => <FAIcon name="check-circle" style={style} />);
ConfirmIcon.defaultProps = { style: { color: FINALISE_GREEN, fontSize: 40 } };
ConfirmIcon.propTypes = { style: PropTypes.object };

export const LockIcon = React.memo(({ style }) => <FAIcon name="lock" size={28} style={style} />);
LockIcon.defaultProps = { style: { color: FINALISED_RED, marginHorizontal: 8, bottom: 6 } };
LockIcon.propTypes = { style: PropTypes.object };

export const ChevronDownIcon = React.memo(({ color, size }) => (
  <FA5Icon name="chevron-down" color={color} size={size} />
));
ChevronDownIcon.defaultProps = { color: WHITE, size: 20 };
ChevronDownIcon.propTypes = { color: PropTypes.string, size: PropTypes.number };

export const PencilIcon = React.memo(({ color, size }) => (
  <FAIcon name="pencil" color={color} size={size} />
));
PencilIcon.defaultProps = { color: WHITE, size: 20 };
PencilIcon.propTypes = { color: PropTypes.string, size: PropTypes.number };

export const HistoryIcon = React.memo(() => <FA5Icon name="history" size={20} color={WHITE} />);

export const ChevronRightIcon = () => (
  <FA5Icon name="chevron-right" color={SUSSOL_ORANGE} size={20} />
);

export const FavouriteStarIcon = () => <FAIcon name="star-o" color={SUSSOL_ORANGE} size={20} />;
export const BurgerMenuIcon = () => <EntypoIcon name="menu" color={SUSSOL_ORANGE} size={30} />;
export const AddIcon = ({ size, color }) => <IonIcon name="ios-add" size={size} color={color} />;
AddIcon.defaultProps = { color: WHITE, size: 30 };
AddIcon.propTypes = { color: PropTypes.string, size: PropTypes.number };

export const MinusIcon = ({ size, color }) => (
  <IonIcon name="ios-remove" size={size} color={color} />
);
MinusIcon.defaultProps = { color: WHITE, size: 30 };
MinusIcon.propTypes = { color: PropTypes.string, size: PropTypes.number };

export const CalendarIcon = () => <FAIcon name="calendar" size={20} color={WHITE} />;
<|endoftext|>


<commit_before>/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

export { Button, ProgressBar } from 'react-native-ui-components';

export { AutocompleteSelector } from './AutocompleteSelector';
// eslint-disable-next-line import/no-cycle
export { MultiSelectList } from './MultiSelectList';
export { FinaliseButton } from './FinaliseButton';
export { GenericChoiceList } from './GenericChoiceList';
export { IconCell } from './IconCell';
// eslint-disable-next-line import/named
export { NavigationBar } from './NavigationBar';
export { OnePressButton } from './OnePressButton';
export { PageButton } from './PageButton';
export { PageInfo } from './PageInfo';
export { Step } from './Step';
export { Spinner } from './Spinner';
export { SyncState } from './SyncState';
export { TextEditor } from './TextEditor';
export { TextInput } from './TextInput';
export { ToggleBar } from './ToggleBar';
export { ToggleSelector } from './ToggleSelector';
export { ExpiryDateInput } from './ExpiryDateInput';
export { SearchBar } from './SearchBar';
export { DataTablePageView } from './DataTablePageView';
export { Flag } from './Flag';
export {
  SortAscIcon,
  SortNeutralIcon,
  SortDescIcon,
  CheckedComponent,
  UncheckedComponent,
  DisabledCheckedComponent,
  DisabledUncheckedComponent,
  Cancel,
  CloseIcon,
  OpenModal,
  Expand,
  ConfirmIcon,
  LockIcon,
} from './icons';
export {
  KiribatiFlag,
  EnglishFlag,
  LaosFlag,
  TetumFlag,
  FrenchFlag,
  CustomerImage,
  SupplierImage,
  StockImage,
  ModulesImage,
} from './images';
export { InfoBadge } from './InfoBadge';
<commit_msg>Add components to widgets/index<commit_after>/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

export { Button, ProgressBar } from 'react-native-ui-components';

export { AutocompleteSelector } from './AutocompleteSelector';
// eslint-disable-next-line import/no-cycle
export { MultiSelectList } from './MultiSelectList';
export { FinaliseButton } from './FinaliseButton';
export { GenericChoiceList } from './GenericChoiceList';
export { IconCell } from './IconCell';
// eslint-disable-next-line import/named
export { NavigationBar } from './NavigationBar';
export { OnePressButton } from './OnePressButton';
export { PageButton } from './PageButton';
export { PageInfo } from './PageInfo';
export { Step } from './Step';
export { Spinner } from './Spinner';
export { SyncState } from './SyncState';
export { TextEditor } from './TextEditor';
export { TextInput } from './TextInput';
export { ToggleBar } from './ToggleBar';
export { ToggleSelector } from './ToggleSelector';
export { ExpiryDateInput } from './ExpiryDateInput';
export { SearchBar } from './SearchBar';
export { DataTablePageView } from './DataTablePageView';
export { Flag } from './Flag';
export { BarChart } from './BarChart';
export { LineChart } from './LineChart';
export { PieChart } from './PieChart';
export { ReportTable } from './ReportTable';
export {
  SortAscIcon,
  SortNeutralIcon,
  SortDescIcon,
  CheckedComponent,
  UncheckedComponent,
  DisabledCheckedComponent,
  DisabledUncheckedComponent,
  Cancel,
  CloseIcon,
  OpenModal,
  Expand,
  ConfirmIcon,
  LockIcon,
} from './icons';
export {
  KiribatiFlag,
  EnglishFlag,
  LaosFlag,
  TetumFlag,
  FrenchFlag,
  CustomerImage,
  SupplierImage,
  StockImage,
  ModulesImage,
} from './images';
export { InfoBadge } from './InfoBadge';
<|endoftext|>


<commit_before>String.prototype.hashCode = function () {
	var h = 0, i = 0, l = this.length;
	if (l === 0) return h;
	for (; i < l; i++) {
		h = ((h << 5) - h) + this.charCodeAt(i);
		h |= 0; // Convert to 32bit integer
	}
	return h;
};

(function ($) {
	$.fn.uploader = function (conf) {
		var
			conf = $.extend({
				url: 'upload.php',
				maxSize: 10240,
				locale: {
					dropZoneTitle: 'Drop files here',
					dropZoneSubText: 'In order to attach them to this post'
				},
				fileDataValidator: function (name, size, type) {
					if (size > maxSize) {
						alert(name + ': icon too large. Try to get it to be under ' + (maxSize / 1024) + ' KB.');
						return false;
					}
					return true;
				},
				ajaxEvents: {
					error: function (jqXHR, textStatus, errorThrown, hash) {
						var errData = $.parseJSON(jqXHR.responseText), ret = '';
						$.each(errData, function (index, value) {
							ret += '
' + value + '
';
						});
						$('div#icon' + hash).addClass('errorbox').removeClass('information').html(ret);
					},
					success: function (data, hash) {
						$('div#icon' + hash).html(data);
						$('input[type=file]#icon').remove();
					}
				},
				xhrUploadEvents: {
					progress: function (e, hash) {
						if (e.lengthComputable) {
							var percentComplete = e.loaded / e.total;
							$('progress#icon' + hash).attr({value: e.loaded, max: e.total});
						}
						else {
							// Unable to compute progress information since the total size is unknown
						}
					},
					loadstart: function (e, hash) {
						$('#icon_container').children('div:not([id^=dropzone])').remove().end().append('
<progress id="icon' + hash + '"/>
');
					},
					abort: function (e, hash) {
						$('progress').remove();
					},
					cancel: function (e, hash) {
						$('progress').remove();
					}
				}
			}, conf),

			attachFiles = function (files) {
				$.each(files, function (index, file) {
					var name = file.name, size = file.size, type = file.type, hash = name.hashCode() + Math.random();

					if (conf.fileDataValidator(name, size, type) === true) {
						if (conf.reader) {
							var reader = new FileReader();
							reader.onload = function () {
								$('textarea#' + $element.attr('id')).val(this.result);
							}
							reader.readAsText(file);

							return;
						}
						var formData = new FormData();
						formData.append('filename', file);

						$.ajax({
							url: conf.url + ';filename=' + encodeURIComponent(name),
							type: 'POST',
							xhr: function () {  // custom xhr
								myXhr = $.ajaxSettings.xhr();

								if (myXhr.upload) { // check if upload property exists
									$.each(xhrUploadEvents, function (index, xhrUploadEvent) {
										myXhr.upload.addEventListener(index, xhrUploadEvent, false);
									});
								}
								return myXhr;
							},
							// Ajax events
							success: conf.ajaxEvents.success,
							error: conf.ajaxEvents.error,
							// Form data
							data: formData,
							// Options to tell JQuery not to process data or worry about content-type
							cache: false,
							contentType: false,
							processData: false
						});
					}
				});
			};

		// Chain methods!
		return this.each(function () {
			var element = this, $element = $(element);

			var $dropZone = $('

' + conf.locale.dropZoneTitle + '
' + conf.locale.dropZoneSubText + '
').hide().prependTo($element.parent());

			var dragUIOpened = false;
			var dragTimer = new Date().getTime(),

				documentEvents = {
					dragover: function (e) {
						if ($element.parent().parent().parent().is(':visible')) {
							e.dataTransfer.dropEffect = 'none';

							e.stopPropagation();
							e.preventDefault();

							// Expand the additional option if it's collapsed
							if (!dragUIOpened) {
								// Show a neat "Drop the file here" notice
								$element.fadeOut('fast', function () {
									$dropZone.fadeIn();
								});
								dragUIOpened = true;
							}
							dragTimer = new Date().getTime();
						}
					},

					dragleave: function (e) {
						if ($element.parent().parent().parent().is(':visible')) {
							setTimeout(function () {
								if (new Date().getTime() - dragTimer > 200) {
									$dropZone.fadeOut('fast', function () {
										$element.fadeIn();
									});
									dragUIOpened = false;
								}
							}, 200);
						}
					}
				},

				dropZoneEvents = {
					dragover: function (e) {
						e.dataTransfer.dropEffect = 'copy';
						dragTimer = new Date().getTime();
						$dropZone.toggleClass('highlight', true);
						e.stopPropagation();
						e.preventDefault();
					},
					dragleave: function (e) {
						$dropZone.toggleClass('highlight', false);
					},

					drop: function (e) {
						var dt = e.dataTransfer;
						e.stopPropagation();
						e.preventDefault();

						// Make sure we are dragging a file over
						if (!dt && !(dt.files || (!$.browser.webkit && dt.types.contains && dt.types.contains('Files'))))
							return false;

						dragUIOpened = false;

						var files = dt.files;
						$dropZone.fadeOut('fast', function () {
							$element.fadeIn(function () {
								attachFiles(files);
							});
						});
					}
				};

			$.each(documentEvents, function (index, documentEvent) {
				document.body.addEventListener(index, documentEvent, false);
			});

			$.each(dropZoneEvents, function (index, dropZoneEvent) {
				document.getElementById("dropzone" + element.id).addEventListener(index, dropZoneEvent, false);
			});

			$element.change(function () {
				attachFiles(this.files);
			});
		});
	};
})(jQuery);<commit_msg>tooBig<commit_after>String.prototype.hashCode = function () {
	var h = 0, i = 0, l = this.length;
	if (l === 0) return h;
	for (; i < l; i++) {
		h = ((h << 5) - h) + this.charCodeAt(i);
		h |= 0; // Convert to 32bit integer
	}
	return h;
};

(function ($) {
	$.fn.uploader = function (conf) {
		var
			conf = $.extend({
				url: 'upload.php',
				maxSize: 10240,
				locale: {
					dropZoneTitle: 'Drop files here',
					dropZoneSubText: 'In order to attach them to this post',
					tooBig: 'icon too large. Try to get it to be under $d KB.'
				},
				fileDataValidator: function (name, size, type) {
					if (size > maxSize) {
						alert(name + ': ' + conf.locale.tooBig.replace('$d', maxSize / 1024));
						return false;
					}
					return true;
				},
				ajaxEvents: {
					error: function (jqXHR, textStatus, errorThrown, hash) {
						var errData = $.parseJSON(jqXHR.responseText), ret = '';
						$.each(errData, function (index, value) {
							ret += '
' + value + '
';
						});
						$('div#icon' + hash).addClass('errorbox').removeClass('information').html(ret);
					},
					success: function (data, hash) {
						$('div#icon' + hash).html(data);
						$('input[type=file]#icon').remove();
					}
				},
				xhrUploadEvents: {
					progress: function (e, hash) {
						if (e.lengthComputable) {
							var percentComplete = e.loaded / e.total;
							$('progress#icon' + hash).attr({value: e.loaded, max: e.total});
						}
						else {
							// Unable to compute progress information since the total size is unknown
						}
					},
					loadstart: function (e, hash) {
						$('#icon_container').children('div:not([id^=dropzone])').remove().end().append('
<progress id="icon' + hash + '"/>
');
					},
					abort: function (e, hash) {
						$('progress').remove();
					},
					cancel: function (e, hash) {
						$('progress').remove();
					}
				}
			}, conf),

			attachFiles = function (files) {
				$.each(files, function (index, file) {
					var name = file.name, size = file.size, type = file.type, hash = name.hashCode() + Math.random();

					if (conf.fileDataValidator(name, size, type) === true) {
						if (conf.reader) {
							var reader = new FileReader();
							reader.onload = function () {
								$('textarea#' + $element.attr('id')).val(this.result);
							}
							reader.readAsText(file);

							return;
						}
						var formData = new FormData();
						formData.append('filename', file);

						$.ajax({
							url: conf.url + ';filename=' + encodeURIComponent(name),
							type: 'POST',
							xhr: function () {  // custom xhr
								myXhr = $.ajaxSettings.xhr();

								if (myXhr.upload) { // check if upload property exists
									$.each(xhrUploadEvents, function (index, xhrUploadEvent) {
										myXhr.upload.addEventListener(index, xhrUploadEvent, false);
									});
								}
								return myXhr;
							},
							// Ajax events
							success: conf.ajaxEvents.success,
							error: conf.ajaxEvents.error,
							// Form data
							data: formData,
							// Options to tell JQuery not to process data or worry about content-type
							cache: false,
							contentType: false,
							processData: false
						});
					}
				});
			};

		// Chain methods!
		return this.each(function () {
			var element = this, $element = $(element);

			var $dropZone = $('

' + conf.locale.dropZoneTitle + '
' + conf.locale.dropZoneSubText + '
').hide().prependTo($element.parent());

			var dragUIOpened = false;
			var dragTimer = new Date().getTime(),

				documentEvents = {
					dragover: function (e) {
						if ($element.parent().parent().parent().is(':visible')) {
							e.dataTransfer.dropEffect = 'none';

							e.stopPropagation();
							e.preventDefault();

							// Expand the additional option if it's collapsed
							if (!dragUIOpened) {
								// Show a neat "Drop the file here" notice
								$element.fadeOut('fast', function () {
									$dropZone.fadeIn();
								});
								dragUIOpened = true;
							}
							dragTimer = new Date().getTime();
						}
					},

					dragleave: function (e) {
						if ($element.parent().parent().parent().is(':visible')) {
							setTimeout(function () {
								if (new Date().getTime() - dragTimer > 200) {
									$dropZone.fadeOut('fast', function () {
										$element.fadeIn();
									});
									dragUIOpened = false;
								}
							}, 200);
						}
					}
				},

				dropZoneEvents = {
					dragover: function (e) {
						e.dataTransfer.dropEffect = 'copy';
						dragTimer = new Date().getTime();
						$dropZone.toggleClass('highlight', true);
						e.stopPropagation();
						e.preventDefault();
					},
					dragleave: function (e) {
						$dropZone.toggleClass('highlight', false);
					},

					drop: function (e) {
						var dt = e.dataTransfer;
						e.stopPropagation();
						e.preventDefault();

						// Make sure we are dragging a file over
						if (!dt && !(dt.files || (!$.browser.webkit && dt.types.contains && dt.types.contains('Files'))))
							return false;

						dragUIOpened = false;

						var files = dt.files;
						$dropZone.fadeOut('fast', function () {
							$element.fadeIn(function () {
								attachFiles(files);
							});
						});
					}
				};

			$.each(documentEvents, function (index, documentEvent) {
				document.body.addEventListener(index, documentEvent, false);
			});

			$.each(dropZoneEvents, function (index, dropZoneEvent) {
				document.getElementById("dropzone" + element.id).addEventListener(index, dropZoneEvent, false);
			});

			$element.change(function () {
				attachFiles(this.files);
			});
		});
	};
})(jQuery);<|endoftext|>


<commit_before>Sis.SubtaskController = Sis.TaskController.extend({
  needs: "project",
  isEditing: false,
  showingDispute: false,
  isHovering: false,
  disputeReason: null,

  // Computed Properties
  ///////////////////////
  disputeModalId: function() {
    return "dispute-modal-" + this.get('content.id');
  }.property('content'),

  isOpen: function() {
    var isOpen = this.get('model.isOpen');
    // If our model is completed then we need to show the completed tasks.
    if (this.get('model.isCompleted') && isOpen) {
      this.get('target').set('showingCompleted', true);
    }
    this.set('isEditing', isOpen);
  }.observes('model.isOpen'),

  isCompleted: function(key, value){
    var model = this.get('model');

    if (value === undefined) {
      return model.get('isCompleted') || model.get('inLimbo');
    } else {
      // Set this model as undisputed. This is to make sure completed tasks get undisputed.
      if (value) {
        this.get('target.uncompletedSubtasks').removeObject(model);
        this.get('target.completedLimboSubtasks').addObject(model);
        this.set('model.inLimbo', true);
        Ember.run.later(this, function() {
          this.completeTask(model, value);
        }, 3000);
      } else {
        this.completeTask(model, value);
      }
      return value;
    }
  }.property('model.isCompleted', 'model.inLimbo'),

  completeTask: function(model, value) {
    var self = this;
    model.setProperties({ 'isDisputed': false, 'inLimbo': false, 'isCompleted': value });
    this.get('target.completedLimboSubtasks').removeObject(model);
    model.save().then(function() {
      Sis.updateHistoryTrackers(self.get('model.projectGroup'));
    });
  },

  // Actions
  ///////////
  actions: {
    editTask: function() {
      this.set('isEditing', true);
    },
    cancelEdit: function() {
      this.set('isEditing', false);
      this.set('model.isOpen', false);
    },
    disputeSubtask: function() {
      var modalId = this.get('disputeModalId');
      $('#' + modalId).modal({});
    },

    // TODO: This action is causing scrolling to break on the entire page. Not sure why!
    submitDisputed: function() {
      var subtask = this.get('model'),
          disputeComment = this.store.createRecord(Sis.Comment, {}),
          disputeReason = this.get('disputeReason'),
          modalId = this.get('disputeModalId'),
          user = this.get('auth.currentUser');

      // Create the new comment which disputes this subtask being completed.
      // currentUser.model returns a promise so we need to wrap the save in this 'then'
      disputeComment.set('isDisputed', true);
      disputeComment.set('body', disputeReason);
      disputeComment.set('subtask', subtask);
      disputeComment.set('student', user);
      disputeComment.save();

      // Set this subtask as disputed, not completed, add the new comment, and save.
      subtask.set('isDisputed', true);
      subtask.set('isCompleted', false);
      subtask.get('comments').pushObject(disputeComment);
      subtask.save();

      // Reset the disputeReason and hide the modal
      this.set('disputeReason', null);
      $('#' + modalId).modal('hide');
    },
  }
});<commit_msg>Just saw that it was 3 seconds > 4. Doh<commit_after>Sis.SubtaskController = Sis.TaskController.extend({
  needs: "project",
  isEditing: false,
  showingDispute: false,
  isHovering: false,
  disputeReason: null,

  // Computed Properties
  ///////////////////////
  disputeModalId: function() {
    return "dispute-modal-" + this.get('content.id');
  }.property('content'),

  isOpen: function() {
    var isOpen = this.get('model.isOpen');
    // If our model is completed then we need to show the completed tasks.
    if (this.get('model.isCompleted') && isOpen) {
      this.get('target').set('showingCompleted', true);
    }
    this.set('isEditing', isOpen);
  }.observes('model.isOpen'),

  isCompleted: function(key, value){
    var model = this.get('model');

    if (value === undefined) {
      return model.get('isCompleted') || model.get('inLimbo');
    } else {
      // Set this model as undisputed. This is to make sure completed tasks get undisputed.
      if (value) {
        this.get('target.uncompletedSubtasks').removeObject(model);
        this.get('target.completedLimboSubtasks').addObject(model);
        this.set('model.inLimbo', true);
        Ember.run.later(this, function() {
          this.completeTask(model, value);
        }, 4000);
      } else {
        this.completeTask(model, value);
      }
      return value;
    }
  }.property('model.isCompleted', 'model.inLimbo'),

  completeTask: function(model, value) {
    var self = this;
    model.setProperties({ 'isDisputed': false, 'inLimbo': false, 'isCompleted': value });
    this.get('target.completedLimboSubtasks').removeObject(model);
    model.save().then(function() {
      Sis.updateHistoryTrackers(self.get('model.projectGroup'));
    });
  },

  // Actions
  ///////////
  actions: {
    editTask: function() {
      this.set('isEditing', true);
    },
    cancelEdit: function() {
      this.set('isEditing', false);
      this.set('model.isOpen', false);
    },
    disputeSubtask: function() {
      var modalId = this.get('disputeModalId');
      $('#' + modalId).modal({});
    },

    // TODO: This action is causing scrolling to break on the entire page. Not sure why!
    submitDisputed: function() {
      var subtask = this.get('model'),
          disputeComment = this.store.createRecord(Sis.Comment, {}),
          disputeReason = this.get('disputeReason'),
          modalId = this.get('disputeModalId'),
          user = this.get('auth.currentUser');

      // Create the new comment which disputes this subtask being completed.
      // currentUser.model returns a promise so we need to wrap the save in this 'then'
      disputeComment.set('isDisputed', true);
      disputeComment.set('body', disputeReason);
      disputeComment.set('subtask', subtask);
      disputeComment.set('student', user);
      disputeComment.save();

      // Set this subtask as disputed, not completed, add the new comment, and save.
      subtask.set('isDisputed', true);
      subtask.set('isCompleted', false);
      subtask.get('comments').pushObject(disputeComment);
      subtask.save();

      // Reset the disputeReason and hide the modal
      this.set('disputeReason', null);
      $('#' + modalId).modal('hide');
    },
  }
});<|endoftext|>


<commit_before>/**@ngInject*/
function DashboardController($scope, serviceAllCount,
                             serviceProjectCount, projectList) {
  "use strict";
  var d3, angular;
  this.projectList = projectList;
  $scope.budgetCharts = [];

  angular.forEach(this.projectList, function (value) {
    $scope.temp = {
      title: value.name,
      subtitle: '($ USD)',
      ranges: [0, value.budget],
      measures: [value.spent],
      markers: [0]
    };
    $scope.budgetCharts.push($scope.temp);

  });

  $scope.chartCollection = [{
    options: {
      chart: {
        type: "multiBarChart",
        height: 350,
        margin: {
          top: 20,
          right: 20,
          bottom: 60,
          left: 45
        },
        clipEdge: true,
        staggerLabels: true,
        transitionDuration: 500,
        stacked: true,
        xAxis: {
          axisLabel: "",
          showMaxMin: false,
          tickFormat: function (d) {
            return d;
          }
        },
        yAxis: {
          axisLabel: "Number of Instances",
          axisLabelDistance: 50,
          tickFormat: function (d) {
            return d;
          }
        }
      },
      title: {
        enable: true,
        text: "Service Allocation by Project",
        class: "h4",
        css: {width: "nullpx", textAlign: "center"}
      }
    },
    data: []
  }, {
    options: {
      chart: {
        type: "pieChart",
        height: 350,
        pie: {
          growOnHover: true
        },
        noData: "Data Incoming, Thanks for your patience.",
        x: function (d) {
          return d.key;
        },
        y: function (d) {
          return d.value;
        },
        showLabels: false,
        transitionDuration: 500,
        labelThreshold: 0.01,
        legend: {
          margin: {
            top: 5,
            right: 35,
            bottom: 5,
            left: 0
          }
        }
      },
      title: {
        enable: true,
        text: "Cumulative Service Allocation",
        class: "h3",
        css: {width: "nullpx", textAlign: "center"}
      }
    },
    data: []
  }, {
    options: {
      chart: {
        type: "bulletChart",
        transitionDuration: 500,
        bullet: {
          dispatch: {},
          forceX: [
            0
          ],
          tickFormat: null,
          margin: {
            top: 0,
            right: 10,
            bottom: 0,
            left: 10
          },
          orient: "left"
        },
        dispatch: {},
        forceX: [
          0
        ],
        width: null,
        height: 50,
        tickFormat: null,
        margin: {
          top: 5,
          right: 40,
          bottom: 20,
          left: 180
        },
        orient: "left",
        tooltips: true,
        noData: "Data Incoming, Thanks for your patience."
      },
      styles: {
        classes: {
          "with-3d-shadow": true,
          "with-transitions": true,
          "gallery": true
        },
        "css": {}
      }
    },
    data: {}
  }, {
    options: {
      chart: {
        type: "discreteBarChart",
        height: 350,
        margin: {
          top: 20,
          right: 20,
          bottom: 60,
          left: 55
        },
        x: function (d) {
          return d.key;
        },
        y: function (d) {
          return d.value;
        },
        showValues: true,
        valueFormat: function (d) {
          return d3.format(",.4f")(d);
        },
        transitionDuration: 500,
        xAxis: {
          axisLabel: "Week"
        },
        yAxis: {
          axisLabel: "Volume",
          axisLabelDistance: 30
        }
      },
      title: {
        enable: true,
        text: "*NOTIONAL* Order Frequency Insight",
        class: "h3",
        css: {width: "nullpx", textAlign: "center"}
      }
    },
    data: [{
      key: "Cumulative Return",
      values: [{
        key: 1,
        value: -29.765957771107
      }, {
        key: 2,
        value: 0
      }, {
        key: 3,
        value: 32.807804682612
      }, {
        key: 4,
        value: 196.45946739256
      }, {
        key: 5,
        value: 0.19434030906893
      }, {
        key: 6,
        value: -98.079782601442
      }, {
        key: 7,
        value: -13.925743130903
      }, {
        key: 8,
        value: -5.1387322875705
      }]
    }]
  }
  ];

  angular.forEach($scope.chartCollection, function (value) {
    switch (value.options.chart.type) {
      case "pieChart":
        value.data = serviceAllCount;
        break;
      case "multiBarChart":
        value.data = serviceProjectCount;
        break;
    }
  });

  $scope.onDropComplete = function (index, obj) {
    var secondObj = $scope.chartCollection[index];
    var secondIndex = $scope.chartCollection.indexOf(obj);
    $scope.chartCollection[index] = obj;
    $scope.chartCollection[secondIndex] = secondObj;
  };
}

DashboardController.resolve = {
  /**@ngInject*/
  serviceAllCount: function (ServiceAllCountResource) {

    return ServiceAllCountResource.query().$promise;
  },
  /**@ngInject*/
  serviceProjectCount: function (ServiceProjectCountResource) {
    return ServiceProjectCountResource.query().$promise;
  },
  /**@ngInject*/
  projectList: function (ProjectsResource) {
    return ProjectsResource.query().$promise;
  }
};

window.DashboardController = DashboardController;<commit_msg>hound.ci<commit_after>/**@ngInject*/
function DashboardController($scope, serviceAllCount,
                             serviceProjectCount, projectList) {
  "use strict";

  this.projectList = projectList;
  $scope.budgetCharts = [];

  angular.forEach(this.projectList, function (value) {
    $scope.temp = {
      title: value.name,
      subtitle: '($ USD)',
      ranges: [0, value.budget],
      measures: [value.spent],
      markers: [0]
    };
    $scope.budgetCharts.push($scope.temp);

  });

  $scope.chartCollection = [{
    options: {
      chart: {
        type: "multiBarChart",
        height: 350,
        margin: {
          top: 20,
          right: 20,
          bottom: 60,
          left: 45
        },
        clipEdge: true,
        staggerLabels: true,
        transitionDuration: 500,
        stacked: true,
        xAxis: {
          axisLabel: "",
          showMaxMin: false,
          tickFormat: function (d) {
            return d;
          }
        },
        yAxis: {
          axisLabel: "Number of Instances",
          axisLabelDistance: 50,
          tickFormat: function (d) {
            return d;
          }
        }
      },
      title: {
        enable: true,
        text: "Service Allocation by Project",
        class: "h4",
        css: {width: "nullpx", textAlign: "center"}
      }
    },
    data: []
  }, {
    options: {
      chart: {
        type: "pieChart",
        height: 350,
        pie: {
          growOnHover: true
        },
        noData: "Data Incoming, Thanks for your patience.",
        x: function (d) {
          return d.key;
        },
        y: function (d) {
          return d.value;
        },
        showLabels: false,
        transitionDuration: 500,
        labelThreshold: 0.01,
        legend: {
          margin: {
            top: 5,
            right: 35,
            bottom: 5,
            left: 0
          }
        }
      },
      title: {
        enable: true,
        text: "Cumulative Service Allocation",
        class: "h3",
        css: {width: "nullpx", textAlign: "center"}
      }
    },
    data: []
  }, {
    options: {
      chart: {
        type: "bulletChart",
        transitionDuration: 500,
        bullet: {
          dispatch: {},
          forceX: [
            0
          ],
          tickFormat: null,
          margin: {
            top: 0,
            right: 10,
            bottom: 0,
            left: 10
          },
          orient: "left"
        },
        dispatch: {},
        forceX: [
          0
        ],
        width: null,
        height: 50,
        tickFormat: null,
        margin: {
          top: 5,
          right: 40,
          bottom: 20,
          left: 180
        },
        orient: "left",
        tooltips: true,
        noData: "Data Incoming, Thanks for your patience."
      },
      styles: {
        classes: {
          "with-3d-shadow": true,
          "with-transitions": true,
          "gallery": true
        },
        "css": {}
      }
    },
    data: {}
  }, {
    options: {
      chart: {
        type: "discreteBarChart",
        height: 350,
        margin: {
          top: 20,
          right: 20,
          bottom: 60,
          left: 55
        },
        x: function (d) {
          return d.key;
        },
        y: function (d) {
          return d.value;
        },
        showValues: true,
        valueFormat: function (d) {
          return d;
        },
        transitionDuration: 500,
        xAxis: {
          axisLabel: "Week"
        },
        yAxis: {
          axisLabel: "Volume",
          axisLabelDistance: 30
        }
      },
      title: {
        enable: true,
        text: "*NOTIONAL* Order Frequency Insight",
        class: "h3",
        css: {width: "nullpx", textAlign: "center"}
      }
    },
    data: [{
      key: "Cumulative Return",
      values: [{
        key: 1,
        value: -29.765957771107
      }, {
        key: 2,
        value: 0
      }, {
        key: 3,
        value: 32.807804682612
      }, {
        key: 4,
        value: 196.45946739256
      }, {
        key: 5,
        value: 0.19434030906893
      }, {
        key: 6,
        value: -98.079782601442
      }, {
        key: 7,
        value: -13.925743130903
      }, {
        key: 8,
        value: -5.1387322875705
      }]
    }]
  }
  ];

  angular.forEach($scope.chartCollection, function (value) {
    switch (value.options.chart.type) {
      case "pieChart":
        value.data = serviceAllCount;
        break;
      case "multiBarChart":
        value.data = serviceProjectCount;
        break;
    }
  });

  $scope.onDropComplete = function (index, obj) {
    var secondObj = $scope.chartCollection[index];
    var secondIndex = $scope.chartCollection.indexOf(obj);
    $scope.chartCollection[index] = obj;
    $scope.chartCollection[secondIndex] = secondObj;
  };
}

DashboardController.resolve = {
  /**@ngInject*/
  serviceAllCount: function (ServiceAllCountResource) {

    return ServiceAllCountResource.query().$promise;
  },
  /**@ngInject*/
  serviceProjectCount: function (ServiceProjectCountResource) {
    return ServiceProjectCountResource.query().$promise;
  },
  /**@ngInject*/
  projectList: function (ProjectsResource) {
    return ProjectsResource.query().$promise;
  }
};

window.DashboardController = DashboardController;<|endoftext|>


<commit_before>/**
  This view renders a menu below a post. It uses buffered rendering for performance.

  @class PostMenuView
  @extends Discourse.View
  @namespace Discourse
  @module Discourse
**/
Discourse.PostMenuView = Discourse.View.extend({
  tagName: 'section',
  classNames: ['post-menu-area', 'clearfix'],

  render: function(buffer) {
    var post = this.get('post');
    this.renderReplies(post, buffer);
    buffer.push("");

    var postMenuView = this;
    Discourse.get('postButtons').forEach(function(button) {
      var renderer = "render" + button;
      if(postMenuView[renderer]) postMenuView[renderer](post, buffer);
    });
    buffer.push("");
  },

  // Delegate click actions
  click: function(e) {
    var $target = $(e.target);
    var action = $target.data('action') || $target.parent().data('action');
    if (!action) return;

    var handler = this["click" + action.capitalize()];
    if (!handler) return;

    handler.call(this);
  },

  // Trigger re-rendering
  needsToRender: function() {
    this.rerender();
  }.observes('post.deleted_at', 'post.flagsAvailable.@each', 'post.url', 'post.bookmarked', 'post.reply_count', 'post.showRepliesBelow', 'post.can_delete'),

  // Replies Button
  renderReplies: function(post, buffer) {
    if (!post.get('showRepliesBelow')) return;

    var reply_count = post.get('reply_count');
    buffer.push("<button class='show-replies' data-action='replies'>");
    buffer.push("" + reply_count + "");
    buffer.push(Em.String.i18n("post.has_replies", { count: reply_count }));

    var icon = this.get('postView.repliesShown') ? 'icon-chevron-up' : 'icon-chevron-down';
    return buffer.push("</button>");
  },

  clickReplies: function() {
    this.get('postView').showReplies();
  },

  // Delete button
  renderDelete: function(post, buffer) {
    if (post.get('post_number') === 1 && this.get('controller.content.can_delete')) {
      buffer.push("<button title=\"" +
                  (Em.String.i18n("topic.actions.delete")) +
                  "\" data-action=\"deleteTopic\"></button>");
      return;
    }
    // Show the correct button (undo or delete)
    if (post.get('deleted_at')) {
      if (post.get('can_recover')) {
        buffer.push("<button title=\"" +
                    (Em.String.i18n("post.controls.undelete")) +
                    "\" data-action=\"recover\" class=\"delete\"></button>");
      }
    } else if (post.get('can_delete')) {
      buffer.push("<button title=\"" +
                 (Em.String.i18n("post.controls.delete")) +
                 "\" data-action=\"delete\" class=\"delete\"></button>");
    }
  },

  clickDeleteTopic: function() {
    this.get('controller').deleteTopic();
  },

  clickRecover: function() {
    this.get('controller').recoverPost(this.get('post'));
  },

  clickDelete: function() {
    this.get('controller').deletePost(this.get('post'));
  },

  // Like button
  renderLike: function(post, buffer) {
    if (!post.get('actionByName.like.can_act')) return;
    buffer.push("<button title=\"" +
                (Em.String.i18n("post.controls.like")) +
                "\" data-action=\"like\" class='like'></button>");
  },

  clickLike: function() {
    var likeAction = this.get('post.actionByName.like');
    if (likeAction) likeAction.act();
  },

  // Flag button
  renderFlag: function(post, buffer) {
    if (!this.present('post.flagsAvailable')) return;
    buffer.push("<button title=\"" +
                (Em.String.i18n("post.controls.flag")) +
                "\" data-action=\"flag\"></button>");
  },

  clickFlag: function() {
    this.get('controller').send('showFlags', this.get('post'));
  },

  // Edit button
  renderEdit: function(post, buffer) {
    if (!post.get('can_edit')) return;
    buffer.push("<button title=\"" +
                 (Em.String.i18n("post.controls.edit")) +
                 "\" data-action=\"edit\"></button>");
  },

  clickEdit: function() {
    this.get('controller').editPost(this.get('post'));
  },

  // Share button
  renderShare: function(post, buffer) {
    buffer.push("<button title=\"" +
                 (Em.String.i18n("post.controls.share")) +
                 "\" data-share-url=\"" + (post.get('shareUrl')) + "\"></button>");
  },

  // Reply button
  renderReply: function(post, buffer) {
    if (!this.get('controller.content.can_create_post')) return;
    buffer.push("<button title=\"" +
                 (Em.String.i18n("post.controls.reply")) +
                 "\" class='create' data-action=\"reply\">" +
                 (Em.String.i18n("topic.reply.title")) + "</button>");
  },

  clickReply: function() {
    this.get('controller').replyToPost(this.get('post'));
  },

  // Bookmark button
  renderBookmark: function(post, buffer) {
    if (!Discourse.User.current()) return;

    var icon = 'bookmark';
    if (!this.get('post.bookmarked')) {
      icon += '-empty';
    }
    buffer.push("<button title=\"" +
                (Em.String.i18n("post.controls.bookmark")) +
                "\" data-action=\"bookmark\"></button>");
  },

  clickBookmark: function() {
    this.get('post').toggleProperty('bookmarked');
  }

});
<commit_msg>Add classes to all post menu buttons<commit_after>/**
  This view renders a menu below a post. It uses buffered rendering for performance.

  @class PostMenuView
  @extends Discourse.View
  @namespace Discourse
  @module Discourse
**/
Discourse.PostMenuView = Discourse.View.extend({
  tagName: 'section',
  classNames: ['post-menu-area', 'clearfix'],

  render: function(buffer) {
    var post = this.get('post');
    this.renderReplies(post, buffer);
    buffer.push("");

    var postMenuView = this;
    Discourse.get('postButtons').forEach(function(button) {
      var renderer = "render" + button;
      if(postMenuView[renderer]) postMenuView[renderer](post, buffer);
    });
    buffer.push("");
  },

  // Delegate click actions
  click: function(e) {
    var $target = $(e.target);
    var action = $target.data('action') || $target.parent().data('action');
    if (!action) return;

    var handler = this["click" + action.capitalize()];
    if (!handler) return;

    handler.call(this);
  },

  // Trigger re-rendering
  needsToRender: function() {
    this.rerender();
  }.observes('post.deleted_at', 'post.flagsAvailable.@each', 'post.url', 'post.bookmarked', 'post.reply_count', 'post.showRepliesBelow', 'post.can_delete'),

  // Replies Button
  renderReplies: function(post, buffer) {
    if (!post.get('showRepliesBelow')) return;

    var reply_count = post.get('reply_count');
    buffer.push("<button class='show-replies' data-action='replies'>");
    buffer.push("" + reply_count + "");
    buffer.push(Em.String.i18n("post.has_replies", { count: reply_count }));

    var icon = this.get('postView.repliesShown') ? 'icon-chevron-up' : 'icon-chevron-down';
    return buffer.push("</button>");
  },

  clickReplies: function() {
    this.get('postView').showReplies();
  },

  // Delete button
  renderDelete: function(post, buffer) {
    if (post.get('post_number') === 1 && this.get('controller.content.can_delete')) {
      buffer.push("<button title=\"" +
                  (Em.String.i18n("topic.actions.delete")) +
                  "\" data-action=\"deleteTopic\" class='delete'></button>");
      return;
    }
    // Show the correct button (undo or delete)
    if (post.get('deleted_at')) {
      if (post.get('can_recover')) {
        buffer.push("<button title=\"" +
                    (Em.String.i18n("post.controls.undelete")) +
                    "\" data-action=\"recover\" class=\"delete\"></button>");
      }
    } else if (post.get('can_delete')) {
      buffer.push("<button title=\"" +
                 (Em.String.i18n("post.controls.delete")) +
                 "\" data-action=\"delete\" class=\"delete\"></button>");
    }
  },

  clickDeleteTopic: function() {
    this.get('controller').deleteTopic();
  },

  clickRecover: function() {
    this.get('controller').recoverPost(this.get('post'));
  },

  clickDelete: function() {
    this.get('controller').deletePost(this.get('post'));
  },

  // Like button
  renderLike: function(post, buffer) {
    if (!post.get('actionByName.like.can_act')) return;
    buffer.push("<button title=\"" +
                (Em.String.i18n("post.controls.like")) +
                "\" data-action=\"like\" class='like'></button>");
  },

  clickLike: function() {
    var likeAction = this.get('post.actionByName.like');
    if (likeAction) likeAction.act();
  },

  // Flag button
  renderFlag: function(post, buffer) {
    if (!this.present('post.flagsAvailable')) return;
    buffer.push("<button title=\"" +
                (Em.String.i18n("post.controls.flag")) +
                "\" data-action=\"flag\" class='flag'></button>");
  },

  clickFlag: function() {
    this.get('controller').send('showFlags', this.get('post'));
  },

  // Edit button
  renderEdit: function(post, buffer) {
    if (!post.get('can_edit')) return;
    buffer.push("<button title=\"" +
                 (Em.String.i18n("post.controls.edit")) +
                 "\" data-action=\"edit\" class='edit'></button>");
  },

  clickEdit: function() {
    this.get('controller').editPost(this.get('post'));
  },

  // Share button
  renderShare: function(post, buffer) {
    buffer.push("<button title=\"" +
                 (Em.String.i18n("post.controls.share")) +
                 "\" data-share-url=\"" + (post.get('shareUrl')) + "\" class='share'></button>");
  },

  // Reply button
  renderReply: function(post, buffer) {
    if (!this.get('controller.content.can_create_post')) return;
    buffer.push("<button title=\"" +
                 (Em.String.i18n("post.controls.reply")) +
                 "\" class='create' data-action=\"reply\">" +
                 (Em.String.i18n("topic.reply.title")) + "</button>");
  },

  clickReply: function() {
    this.get('controller').replyToPost(this.get('post'));
  },

  // Bookmark button
  renderBookmark: function(post, buffer) {
    if (!Discourse.User.current()) return;

    var icon = 'bookmark';
    if (!this.get('post.bookmarked')) {
      icon += '-empty';
    }
    buffer.push("<button title=\"" +
                (Em.String.i18n("post.controls.bookmark")) +
                "\" data-action=\"bookmark\" class='bookmark'></button>");
  },

  clickBookmark: function() {
    this.get('post').toggleProperty('bookmarked');
  }

});
<|endoftext|>


<commit_before>import Vue from 'vue';
import VueResource from 'vue-resource';

Vue.use(VueResource);

export default class GroupsService {
  constructor(endpoint) {
    this.groups = Vue.resource(endpoint);
  }

  getGroups(parentId, page = 1) {
    const data = {};

    if (parentId) {
      data.parent_id = parentId;
    }

    if (page) {
      data.page = page;
    }

    return this.groups.get(data);
  }
}
<commit_msg>Do not send page param when loading sub groups<commit_after>import Vue from 'vue';
import VueResource from 'vue-resource';

Vue.use(VueResource);

export default class GroupsService {
  constructor(endpoint) {
    this.groups = Vue.resource(endpoint);
  }

  getGroups(parentId, page = 1) {
    const data = {};

    if (parentId) {
      data.parent_id = parentId;
    } else {
      // Do not send this param for sub groups
      if (page) {
        data.page = page;
      }
    }

    return this.groups.get(data);
  }
}
<|endoftext|>


<commit_before>import React, { Component } from "react";
import { ipcRenderer } from "electron";
import { autorun } from "mobx";

import { ElementTypes } from "../../../constants";
import elements from "../../../elements";
import { IMAGE } from "../../../assets/icons";
import commonStyles from "../index.css";
import styles from "./image.css";
import notificationSystem from "../../../notifications";

const defaultImageSource = elements[ElementTypes.IMAGE].props.src;

const normalizeUrl = (url) => {
  if (url.indexOf("http://") === 0 || url.indexOf("https://") === 0) {
    return url;
  }

  return `http://${url}`;
};

export default class ImageMenu extends Component {
  static contextTypes = {
    store: React.PropTypes.object
  };

  constructor(props) {
    super(props);

    this.state = {
      currentElement: null
    };
  }

  componentDidMount() {
    autorun(() => {
      const { currentElement } = this.context.store;

      window.clearTimeout(this.stateTimeout);

      if (!currentElement) {
        this.stateTimeout = window.setTimeout(() => {
          this.setState({ currentElement });
        }, 400);

        return;
      }

      if (currentElement.type === ElementTypes.IMAGE) {
        this.setState({ currentElement });
      }
    });
  }

  onImageUpload = (ev) => {
    const imageObj = ev.target.files && ev.target.files[0];

    if (imageObj) {
      const { path, type, name, size } = imageObj;

      if (size <= 3000000) {
        ipcRenderer.once("image-encoded", (event, encodedImageString) => {
          if (!encodedImageString) {
            notificationSystem.addNotification({
              message: "Error loading file",
              level: "error"
            });
          }
          const imgSrc = `data:${type};base64, ${encodedImageString}`;

          this.getScaledHeightAndWidth(imgSrc, ({ height, width, src }) => {
            this.context.store.updateElementProps({
              src,
              imageName: name,
              height,
              width,
              style: {
                opacity: 1
              }
            });
          });
        });

        ipcRenderer.send("encode-image", path);
      } else {
        notificationSystem.addNotification({
          message: "Error: images must be smaller than 3MB",
          level: "error"
        });
      }
    }
  }

  onSourceChange = (ev) => {
    const imageSrc = ev.target.value;

    if (imageSrc) {
      this.getScaledHeightAndWidth(imageSrc, ({ src, height, width }) => {
        this.context.store.updateElementProps({
          src,
          imageName: null,
          height,
          width,
          style: {
            opacity: 1
          }
        });
      });
    }
  }

  onSourceBlur = (ev) => {
    const imageSrc = ev.target.value;

    if (!imageSrc) {
      return;
    }

    const normalizedUrl = normalizeUrl(imageSrc);

    if (imageSrc !== normalizedUrl) {
      this.context.store.updateElementProps({
        src: normalizedUrl
      });
    }
  }

  getScaledHeightAndWidth(src, cb) {
    const imageElement = new Image();
    imageElement.src = src;
    imageElement.addEventListener("load", () => {
      const { props } = this.context.store.currentElement;
      const { height, width } = imageElement;
      const aspectRatio = Math.min(height, width) / Math.max(height, width);

      cb({
        src,
        height: height > width ? props.height : props.width * aspectRatio,
        width: height < width ? props.width : props.height * aspectRatio,
        aspectRatio
      });
    });
  }

  render() {
    const { currentElement } = this.state;

    let srcValue = "";
    let fileName = "";

    if (currentElement) {
      const { src, imageName } = currentElement.props;

      // If not the default source or we don't have an imageName show src
      if (src !== defaultImageSource && !imageName) {
        srcValue = src;
      }

      if (imageName) {
        fileName = imageName;
      }
    }

    return (

Image

          Image source

        <input
          className={`globalInput`}
          type="text"
          name="imagesSource"
          onChange={this.onSourceChange}
          onBlur={this.onSourceBlur}
          value={srcValue}
        />

File Upload

        { fileName ?

              {fileName}

          : ""
        }
        <label className={`globalButton ${styles.fileUpload}`}>
          {fileName ?
            `Replace this file...` :
            `Choose a file...`
          }
          <input
            className={styles.visuallyHidden}
            type="file"
            name="imageFile"
            accept="image/x-png, image/gif, image/jpeg"
            onChange={this.onImageUpload}
          />

    );
  }
}
<commit_msg>add return statement<commit_after>import React, { Component } from "react";
import { ipcRenderer } from "electron";
import { autorun } from "mobx";

import { ElementTypes } from "../../../constants";
import elements from "../../../elements";
import { IMAGE } from "../../../assets/icons";
import commonStyles from "../index.css";
import styles from "./image.css";
import notificationSystem from "../../../notifications";

const defaultImageSource = elements[ElementTypes.IMAGE].props.src;

const normalizeUrl = (url) => {
  if (url.indexOf("http://") === 0 || url.indexOf("https://") === 0) {
    return url;
  }

  return `http://${url}`;
};

export default class ImageMenu extends Component {
  static contextTypes = {
    store: React.PropTypes.object
  };

  constructor(props) {
    super(props);

    this.state = {
      currentElement: null
    };
  }

  componentDidMount() {
    autorun(() => {
      const { currentElement } = this.context.store;

      window.clearTimeout(this.stateTimeout);

      if (!currentElement) {
        this.stateTimeout = window.setTimeout(() => {
          this.setState({ currentElement });
        }, 400);

        return;
      }

      if (currentElement.type === ElementTypes.IMAGE) {
        this.setState({ currentElement });
      }
    });
  }

  onImageUpload = (ev) => {
    const imageObj = ev.target.files && ev.target.files[0];

    if (imageObj) {
      const { path, type, name, size } = imageObj;

      if (size <= 3000000) {
        ipcRenderer.once("image-encoded", (event, encodedImageString) => {
          if (!encodedImageString) {
            notificationSystem.addNotification({
              message: "Error loading file",
              level: "error"
            });

            return;
          }

          const imgSrc = `data:${type};base64, ${encodedImageString}`;

          this.getScaledHeightAndWidth(imgSrc, ({ height, width, src }) => {
            this.context.store.updateElementProps({
              src,
              imageName: name,
              height,
              width,
              style: {
                opacity: 1
              }
            });
          });
        });

        ipcRenderer.send("encode-image", path);
      } else {
        notificationSystem.addNotification({
          message: "Error: images must be smaller than 3MB",
          level: "error"
        });
      }
    }
  }

  onSourceChange = (ev) => {
    const imageSrc = ev.target.value;

    if (imageSrc) {
      this.getScaledHeightAndWidth(imageSrc, ({ src, height, width }) => {
        this.context.store.updateElementProps({
          src,
          imageName: null,
          height,
          width,
          style: {
            opacity: 1
          }
        });
      });
    }
  }

  onSourceBlur = (ev) => {
    const imageSrc = ev.target.value;

    if (!imageSrc) {
      return;
    }

    const normalizedUrl = normalizeUrl(imageSrc);

    if (imageSrc !== normalizedUrl) {
      this.context.store.updateElementProps({
        src: normalizedUrl
      });
    }
  }

  getScaledHeightAndWidth(src, cb) {
    const imageElement = new Image();
    imageElement.src = src;

    imageElement.addEventListener("load", () => {
      const { props } = this.context.store.currentElement;
      const { height, width } = imageElement;
      const aspectRatio = Math.min(height, width) / Math.max(height, width);

      cb({
        src,
        height: height > width ? props.height : props.width * aspectRatio,
        width: height < width ? props.width : props.height * aspectRatio,
        aspectRatio
      });
    });
  }

  render() {
    const { currentElement } = this.state;

    let srcValue = "";
    let fileName = "";

    if (currentElement) {
      const { src, imageName } = currentElement.props;

      // If not the default source or we don't have an imageName show src
      if (src !== defaultImageSource && !imageName) {
        srcValue = src;
      }

      if (imageName) {
        fileName = imageName;
      }
    }

    return (

Image

          Image source

        <input
          className={`globalInput`}
          type="text"
          name="imagesSource"
          onChange={this.onSourceChange}
          onBlur={this.onSourceBlur}
          value={srcValue}
        />

File Upload

        { fileName ?

              {fileName}

          : ""
        }
        <label className={`globalButton ${styles.fileUpload}`}>
          {fileName ?
            `Replace this file...` :
            `Choose a file...`
          }
          <input
            className={styles.visuallyHidden}
            type="file"
            name="imageFile"
            accept="image/x-png, image/gif, image/jpeg"
            onChange={this.onImageUpload}
          />

    );
  }
}
<|endoftext|>


<commit_before>'use strict';

/*
 * Module dependencies.
 */
var mongoose = require('mongoose'),
	errorHandler = require('../../errors'),
	Group = mongoose.model('Group'),
	User = mongoose.model('User'),
	MyResponse = require('../../../custom_objects/MyResponse'),
	serverJSON = require('../../../local_files/ui/server.ui.json'),
	Helper = require('../../../custom_objects/Helper'),
	util = require('util'),
	_ = require('lodash');

/**
 * Create a Group
 */
exports.create = function(req, res) {

	var group = new Group(req.body);
	console.log('original group: ' + group);
	var myResponse = new MyResponse();
	//var data = _.merge(group,{admins: req.user,createdBy : req.user},Helper.cleanMergeObj);
	//_.extend(group,data);

	//detect if group with this name has already been created
	console.log(JSON.stringify(req.body,null,4));

	var query = {
		createdBy:  mongoose.Types.ObjectId(req.body.createdBy._id),
		name: group.name
	};

	Helper.find(Group,query,function(err,mod) {

		console.log('mod length: ' + mod.length);
		if(err)
		{
			myResponse.transformMongooseError(Group.errPath,String(err));
			Helper.output(Group,null,myResponse,res);
		}
		else if(mod.length !== 0)
		{
			console.log('error: ' + err);
			myResponse.addMessages(serverJSON.api.groups.name.duplicate);
			Helper.output(Group,null,myResponse,res);
		}
		else
		{
			console.log('create group: ' + group);
			group.save(function(err) {
				console.log('in save');

				if (err) {
					console.log('error: ' + err);
					myResponse.transformMongooseError(Group.errPath,String(err));
					Helper.output(Group,group,myResponse,res);
				}
				else {
					console.log('saved successfully');
					Helper.output(Group,group,myResponse,res);
					/*
					req.user.createdGroup = group;
					req.user.save(function(userErr) {
						Helper.populateModel(Group,group,Group.errPath,function(mod) {
							myResponse.setData(mod);
							Helper.output(myResponse,res);
						});
					});
				*/
				}
			});
		}
	});
};

/**
 * Show the current Group
 */
exports.read = function(req, res) {

	var groupID = req.params.groupId;
	var query = {
		_id : groupID
	};

	Helper.find(Group,query, function(err,mod) {

		var myResponse = new MyResponse();
		var group = mod[0];

		if (err) {
			console.log('error: ' + err);
			myResponse.transformMongooseError(Group.errPath,String(err));
		}
		else if(mod.length === 0)
		{
			myResponse.addMessages(serverJSON.api.groups._id.exist);
		}

		Helper.output(Group,group,myResponse,res);
	});
};

/**
 * Update a Group
 */
exports.update = function(req, res) {

	var myResponse = new MyResponse();
	var id = req.params.groupId;

	Group.findOne({_id: id}, function(err,group) {

		var myResponse = new MyResponse();
		console.log(err);
		if(err)
		{
			console.log(err);
			myResponse.transformMongooseError(Group.errPath,String(err));
			Helper.output(Group,group,myResponse,res);
		}
		else if(!group)
		{
			myResponse.addMessages(serverJSON.api.groups._id.invalid);
			Helper.output(Group,group,myResponse,res);
		}
		else
		{
			var data = _.merge(group,req.body,Helper.cleanMergeObj);
			_.extend(group,data);

			group.updated = Date.now();

			group.save(function(err) {
				console.log('in save');

				if (err) {
					console.log('error: ' + err);
					myResponse.transformMongooseError(Group.errPath,String(err));
				}
					Helper.output(Group,group,myResponse,res);
			});
		}
	});
};

/**
 * Delete an Group
 */
exports.delete = function(req, res) {

	var id = req.params.groupId;
	console.log('group id: ' + id);
	var myResponse = new MyResponse();

	Group.findOne({_id: id}, function(err,group) {

		var myResponse = new MyResponse();

		if(err)
		{
			console.log(err);
			myResponse.transformMongooseError(Group.errPath,String(err));
			Helper.output(Group,group,myResponse,res);
		}
		else if(!group)
		{
			myResponse.addMessages(serverJSON.api.groups._id.invalid);
			myResponse.setError(res);
			Helper.output(Group,group,myResponse,res);
		}
		else
		{
			group.remove(function(err) {
				if (err) {
					console.log('error: ' + err);
					myResponse.transformMongooseError(Group.errPath,String(err));
				}
				Helper.output(Group,group,myResponse,res);
			});
		}
	});
};

/**
 * List of Groups
 */
exports.list = function(req, res) { Group.find().sort('-created').exec(function(err, groups) {

		var myResponse = new MyResponse();

		if (err) {
			myResponse.transformMongooseError(Group.errPath,String(err));
		}

		Helper.output(Group,groups,myResponse,res);
	});
};

/**
 * Group middleware
 */
exports.groupByID = function(req, res, next, id) {

	var myResponse = new MyResponse();

	if(!mongoose.Types.ObjectId.isValid(id))
	{
		myResponse.addMessages(serverJSON.api.groups._id.invalid);
		Helper.output(Group,null,myResponse,res);
		return;
	}

	Group.findById(id).populate('user', 'displayName').exec(function(err, group) {
		//if (err) return next(err);
		//if (! group) return next(new Error('Failed to load Group ' + id));
		req.group = group ;

		next();
	});
};

/**
 * Group authorization middleware
 */
exports.hasAuthorization = function(req, res, next) {
	if (req.group.user.id !== req.user.id) {
		return res.status(403).send('User is not authorized');
	}
	next();
};
<commit_msg>commit for #43<commit_after>'use strict';

/*
 * Module dependencies.
 */
var mongoose = require('mongoose'),
	errorHandler = require('../../errors'),
	Group = mongoose.model('Group'),
	User = mongoose.model('User'),
	MyResponse = require('../../../custom_objects/MyResponse'),
	serverJSON = require('../../../local_files/ui/server.ui.json'),
	Helper = require('../../../custom_objects/Helper'),
	util = require('util'),
	_ = require('lodash');

/**
 * Create a Group
 */
exports.create = function(req, res) {

	var group = new Group(req.body);
	console.log('original group: ' + group);
	var myResponse = new MyResponse();
	//var data = _.merge(group,{admins: req.user,createdBy : req.user},Helper.cleanMergeObj);
	//_.extend(group,data);

	//detect if group with this name has already been created
	console.log(JSON.stringify(req.body,null,4));

	var query = {
		createdBy:  mongoose.Types.ObjectId(req.body.createdBy._id),
		name: group.name
	};

	Helper.find(Group,query,function(err,mod) {

		console.log('mod length: ' + mod.length);
		if(err)
		{
			myResponse.transformMongooseError(Group.errPath,String(err));
			Helper.output(Group,null,myResponse,res);
		}
		else if(mod.length !== 0)
		{
			console.log('error: ' + err);
			myResponse.addMessages(serverJSON.api.groups.name.duplicate);
			Helper.output(Group,null,myResponse,res);
		}
		else
		{
			console.log('create group: ' + group);
			group.save(function(err) {
				console.log('in save');

				if (err) {
					console.log('error: ' + err);
					myResponse.transformMongooseError(Group.errPath,String(err));
					Helper.output(Group,group,myResponse,res);
				}
				else {
					console.log('saved successfully');
					Helper.output(Group,group,myResponse,res);
					/*
					req.user.createdGroup = group;
					req.user.save(function(userErr) {
						Helper.populateModel(Group,group,Group.errPath,function(mod) {
							myResponse.setData(mod);
							Helper.output(myResponse,res);
						});
					});
				*/
				}
			});
		}
	});
};

/**
 * Show the current Group
 */
exports.read = function(req, res) {

	var groupID = req.params.groupId;
	var query = {
		_id : groupID
	};

	Helper.find(Group,query, function(err,mod) {

		var myResponse = new MyResponse();
		var group = mod[0];

		if (err) {
			console.log('error: ' + err);
			myResponse.transformMongooseError(Group.errPath,String(err));
		}
		else if(mod.length === 0)
		{
			myResponse.addMessages(serverJSON.api.groups._id.exist);
		}

		Helper.output(Group,group,myResponse,res);
	});
};

/**
 * Update a Group
 */
exports.update = function(req, res) {

	var myResponse = new MyResponse();
	var id = req.params.groupId;

	Group.findOne({_id: id}, function(err,group) {

		var myResponse = new MyResponse();
		console.log(err);
		if(err)
		{
			console.log(err);
			myResponse.transformMongooseError(Group.errPath,String(err));
			Helper.output(Group,group,myResponse,res);
		}
		else if(!group)
		{
			myResponse.addMessages(serverJSON.api.groups._id.invalid);
			Helper.output(Group,group,myResponse,res);
		}
		else
		{
			var data = _.merge(group,req.body,Helper.cleanMergeObj);
			_.extend(group,data);

			group.updated = Date.now();

			group.save(function(err) {
				console.log('in save');

				if (err) {
					console.log('error: ' + err);
					myResponse.transformMongooseError(Group.errPath,String(err));
				}
					Helper.output(Group,group,myResponse,res);
			});
		}
	});
};

/**
 * Delete an Group
 */
exports.delete = function(req, res) {

	var id = req.params.groupId;
	console.log('group id: ' + id);
	var myResponse = new MyResponse();

	Group.findOne({_id: id}, function(err,group) {

		var myResponse = new MyResponse();

		if(err)
		{
			console.log(err);
			myResponse.transformMongooseError(Group.errPath,String(err));
			Helper.output(Group,group,myResponse,res);
		}
		else if(!group)
		{
			myResponse.addMessages(serverJSON.api.groups._id.invalid);
			myResponse.setError(res);
			Helper.output(Group,group,myResponse,res);
		}
		else
		{
			group.remove(function(err) {
				if (err) {
					console.log('error: ' + err);
					myResponse.transformMongooseError(Group.errPath,String(err));
				}
				Helper.output(Group,group,myResponse,res);
			});
		}
	});
};

/**
 * List of Groups
 */
exports.list = function(req, res) { Group.find().sort('-created').exec(function(err, groups) {

		var myResponse = new MyResponse();

		var groupName = req.query.group_name;
		console.log('groupName: ' + groupName);
		if(groupName)
		{
			var regex = ".*"+groupName+".*";
			console.log('regex: ' + regex);

			var query = {
				name:
				{
					$regex: new RegExp(regex,'i')
				}
			};

			Helper.find(Group,query,function(err,mod) {

				if (err) {
					myResponse.transformMongooseError(Group.errPath,String(err));
					Helper.output(Group,null,myResponse,res);
				}
				else if(!mod) {
					myResponse.addMessages(serverJSON.api.events._id.exist);
					Helper.output(Group,null,myResponse,res);
				}
				else
				{
					Helper.output(Group,mod,myResponse,res);
				}
			});
		}
		//Show all events if none are found in the DB
		else
		{
			if (err) {
				myResponse.transformMongooseError(Group.errPath,String(err));
			}

			Helper.output(Group,groups,myResponse,res);
		}
	});
};

/**
 * Group middleware
 */
exports.groupByID = function(req, res, next, id) {

	var myResponse = new MyResponse();

	if(!mongoose.Types.ObjectId.isValid(id))
	{
		myResponse.addMessages(serverJSON.api.groups._id.invalid);
		Helper.output(Group,null,myResponse,res);
		return;
	}

	Group.findById(id).populate('user', 'displayName').exec(function(err, group) {
		//if (err) return next(err);
		//if (! group) return next(new Error('Failed to load Group ' + id));
		req.group = group ;

		next();
	});
};

/**
 * Group authorization middleware
 */
exports.hasAuthorization = function(req, res, next) {
	if (req.group.user.id !== req.user.id) {
		return res.status(403).send('User is not authorized');
	}
	next();
};
<|endoftext|>