<commit_before>"use strict";

const width=960, height=500;
const margin=10, pad=12;

function graph_get(success, error) {
    $.post($SCRIPT_ROOT+'/progressivis/scheduler/?short=False')
        .done(success)
        .fail(error);
};

function graph_setup(){
  var outer = d3.select("#module-graph")
    .attr({width: width, height: height, "pointer-events": "all"});

  outer.append('rect')
    .attr({ 'class': 'background', width: "100%", height: "100%"})
    .call(d3.behavior.zoom().on("zoom", zoom));

  outer.append('svg:defs').append('svg:marker')
    .attr({
        id: 'end-arrow',
        viewBox: '0 -5 10 10',
        refX: 8,
        markerWidth: 6,
        markerHeight: 6,
        orient: 'auto'
    })
    .append('svg:path')
      .attr({
        d: 'M0,-5L10,0L0,5L2,0',
        'stroke-width': '0px',
        fill: '#000'});

   var vis = outer
   .append('g');

   function zoom(){
     vis.attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
   }
}

/**
 * Similar to lodash's uniq, but allows comparing (nested) objects.
 */
function deep_uniq(coll){
  return _.reduce(coll, function(results, item) {
    return _.any(results, function(result) {
      return _.isEqual(result, item);
      }) ? results : results.concat([ item ]);
  }, []);
}

function graph_update(data) {
    run_number = data['run_number'];
    graph_update_vis(data.modules);
}

/**
 * @param modules - list of modules in long format (including slot information)
 * @param name2id - map of progressivis module id to numerical index (cola requires numerical IDs)
 */
function collect_edges(modules, name2id){
  var retval = [];
  $.each(modules, function(i, module){
    $.each(module.output_slots, function(name, slot){
      if(slot){
        $.each(slot, function(j, link){
          retval.push({source: name2id[link.output_module], target: name2id[link.input_module]});
        });
      }
    });
  });

  //We draw a graph at the module (not slot) level, hence the need to remove possible duplicates.
  return deep_uniq(retval);
}

function graph_update_vis(modules){
  var nodes = _.map(modules, function(module, index){
    return {
      id: index,
      name: module.id,
      state: module.state
    };
  });

  var name2id = _.reduce(nodes, function(acc, node){
    acc[node.name] = node.id;
    return acc;
  }, {});

  var edges = collect_edges(modules, name2id);

  var d3cola = cola.d3adaptor().convergenceThreshold(0.1);

  d3cola
    .avoidOverlaps(true)
    .convergenceThreshold(1e-3)
    .flowLayout('x', 150)
    .size([width, height])
    .nodes(nodes)
    .links(edges)
    .jaccardLinkLengths(150);

  var vis = d3.select("#module-graph g");

  var node = vis.selectAll(".node")
    .data(nodes)
    .enter().append("rect")
    .attr("class", function(d){ return "node " + d.state; })
    .attr({ rx: 5, ry: 5 });

  vis.selectAll(".node")
    .data(nodes)
    .attr("class", function(d){ return "node " + d.state; });

    var label = vis.selectAll(".label")
    .data(nodes)
    .enter().append("text")
    .attr("class", "label")
    .text(function (d) { return d.name; })
    .each(function (d) {
            var b = this.getBBox();
            var extra = 2 * margin + 2 * pad;
            d.width = b.width + extra;
            d.height = b.height + extra;
    });

    var link = vis.selectAll(".link")
      .data(edges)
      .enter().append("path")
      .attr("class", "link");

    var lineFunction = d3.svg.line()
      .x(function (d) { return d.x; })
      .y(function (d) { return d.y; })
      .interpolate("linear");

    var routeEdges = function(){
      d3cola.prepareEdgeRouting();
      link.attr("d", function(d){ return lineFunction(d3cola.routeEdge(d)); });
    };

    d3cola.start(50, 100, 200).on("tick", function () {
      node.each(function (d) { d.innerBounds = d.bounds.inflate(-margin); })
        .attr("x", function (d) { return d.innerBounds.x; })
        .attr("y", function (d) { return d.innerBounds.y; })
        .attr("width", function (d) {
            return d.innerBounds.width();
        })
        .attr("height", function (d) { return d.innerBounds.height(); });

      label.attr("x", function(d){ return d.x; })
        .attr("y", function(d){ return d.y + (margin + pad) / 2; });

    }).on("end", routeEdges);

}

function graph_refresh() {
  graph_get(graph_update, module_error);
}

function graph_ready() {
    refresh = graph_refresh; // function to call to refresh
    graph_setup();
    module_ready();
}
<commit_msg>fix graph<commit_after>"use strict";

const width=960, height=500;
const margin=10, pad=12;

function graph_get(success, error) {
    $.post($SCRIPT_ROOT+'/progressivis/scheduler/?short=False')
        .done(success)
        .fail(error);
};

function graph_setup(){
  var outer = d3.select("#module-graph")
    .attr({width: width, height: height, "pointer-events": "all"});

  outer.append('rect')
    .attr({ 'class': 'background', width: "100%", height: "100%"})
    .call(d3.behavior.zoom().on("zoom", zoom));

  outer.append('svg:defs').append('svg:marker')
    .attr({
        id: 'end-arrow',
        viewBox: '0 -5 10 10',
        refX: 8,
        markerWidth: 6,
        markerHeight: 6,
        orient: 'auto'
    })
    .append('svg:path')
      .attr({
        d: 'M0,-5L10,0L0,5L2,0',
        'stroke-width': '0px',
        fill: '#000'});

   var vis = outer
   .append('g');

   function zoom(){
     vis.attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
   }
}

/**
 * Similar to lodash's uniq, but allows comparing (nested) objects.
 */
function deep_uniq(coll){
  return _.reduce(coll, function(results, item) {
    return _.any(results, function(result) {
      return _.isEqual(result, item);
      }) ? results : results.concat([ item ]);
  }, []);
}

function graph_update(data) {
    module_update(data);
    graph_update_vis(data.modules);
}

/**
 * @param modules - list of modules in long format (including slot information)
 * @param name2id - map of progressivis module id to numerical index (cola requires numerical IDs)
 */
function collect_edges(modules, name2id){
  var retval = [];
  $.each(modules, function(i, module){
    $.each(module.output_slots, function(name, slot){
      if(slot){
        $.each(slot, function(j, link){
          retval.push({source: name2id[link.output_module], target: name2id[link.input_module]});
        });
      }
    });
  });

  //We draw a graph at the module (not slot) level, hence the need to remove possible duplicates.
  return deep_uniq(retval);
}

function graph_update_vis(modules){
  var nodes = _.map(modules, function(module, index){
    return {
      id: index,
      name: module.id,
      state: module.state
    };
  });

  var name2id = _.reduce(nodes, function(acc, node){
    acc[node.name] = node.id;
    return acc;
  }, {});

  var edges = collect_edges(modules, name2id);

  var d3cola = cola.d3adaptor().convergenceThreshold(0.1);

  d3cola
    .avoidOverlaps(true)
    .convergenceThreshold(1e-3)
    .flowLayout('x', 150)
    .size([width, height])
    .nodes(nodes)
    .links(edges)
    .jaccardLinkLengths(150);

  var vis = d3.select("#module-graph g");

  var node = vis.selectAll(".node")
    .data(nodes)
    .enter().append("rect")
    .attr("class", function(d){ return "node " + d.state; })
    .attr({ rx: 5, ry: 5 });

  vis.selectAll(".node")
    .data(nodes)
    .attr("class", function(d){ return "node " + d.state; });

    var label = vis.selectAll(".label")
    .data(nodes)
    .enter().append("text")
    .attr("class", "label")
    .text(function (d) { return d.name; })
    .each(function (d) {
            var b = this.getBBox();
            var extra = 2 * margin + 2 * pad;
            d.width = b.width + extra;
            d.height = b.height + extra;
    });

    var link = vis.selectAll(".link")
      .data(edges)
      .enter().append("path")
      .attr("class", "link");

    var lineFunction = d3.svg.line()
      .x(function (d) { return d.x; })
      .y(function (d) { return d.y; })
      .interpolate("linear");

    var routeEdges = function(){
      d3cola.prepareEdgeRouting();
      link.attr("d", function(d){ return lineFunction(d3cola.routeEdge(d)); });
    };

    d3cola.start(50, 100, 200).on("tick", function () {
      node.each(function (d) { d.innerBounds = d.bounds.inflate(-margin); })
        .attr("x", function (d) { return d.innerBounds.x; })
        .attr("y", function (d) { return d.innerBounds.y; })
        .attr("width", function (d) {
            return d.innerBounds.width();
        })
        .attr("height", function (d) { return d.innerBounds.height(); });

      label.attr("x", function(d){ return d.x; })
        .attr("y", function(d){ return d.y + (margin + pad) / 2; });

    }).on("end", routeEdges);

}

function graph_refresh() {
  graph_get(graph_update, error);
}

function graph_ready() {
    refresh = graph_refresh; // function to call to refresh
    graph_setup();
    module_ready();
}
<|endoftext|>


<commit_before><commit_msg>=== instead of ==<commit_after><|endoftext|>


<commit_before>'use strict';

let paper = require('js/paper-core.min'),
    IconBase = require('js/IconBase'),
    Icon = require('js/Icon'),
    ColorPicker = require('js/ColorPicker'),
    paperScope = require('js/PaperScopeManager'),
    exportManager = require('js/ExportManager'),
    errors = require('js/errors');

// Default android icon size (48 DIP)
const CANVAS_SIZE = 48;

/**
 * Handles adding icon + base.
 */
class IconManager {

    /**
     * @param canvas - jquery canvas object
     * @param containerEdit - jquery edit objects (can be multiple)
     * @param btnDownload - jquery download button
     * @param iconColorPicker - jquery icon color picker object
     * @param baseColorPicker - jquery base color picker object
     * @param sliderIconSize - jquery slider object for changing icon size
     * @param sliderShadowLength - jquery slider object for changing shadow length
     * @param sliderShadowIntensity - jquery slider object for changing shadow intensity
     * @param sliderShadowFading - jquery slider object for changing shadow fading
     * @param checkBoxCenterIcon - jquery check box object for centering the icon
     */
    constructor(canvas, containerEdit,
                btnDownload, iconColorPicker, baseColorPicker,
                sliderIconSize, sliderShadowLength, sliderShadowIntensity,
                sliderShadowFading, checkBoxCenterIcon) {

        this.canvas = canvas;
        this.containerEdit = containerEdit;
        this.iconColorPicker = iconColorPicker;
        this.baseColorPicker = baseColorPicker;
        this.sliderIconSize = sliderIconSize;
        this.sliderShadowLength = sliderShadowLength;
        this.sliderShadowIntensity = sliderShadowIntensity;
        this.sliderShadowFading = sliderShadowFading;
        this.btnDownload = btnDownload;
        this.checkBoxCenterIcon = checkBoxCenterIcon;
        this.loadingOverlay = this.containerEdit.find('#canvas-loading');

        this.initCanvas();
        this.initControls();

    }

    /**
     * Initial canvas setup.
     */
    initCanvas() {
        // setup canvas
        paper.install(window);
        let canvasHeight = this.canvas.height();
        this.canvas.attr('height', canvasHeight);
        this.canvas.attr('width', canvasHeight);
        paperScope.setCanvases(this.canvas, this.containerEdit);
        paperScope.activateDraw();

        // place icon in center on canvas
        this.canvasSize = CANVAS_SIZE;
        paperScope.draw().view.center = new paper.Point(CANVAS_SIZE / 2, CANVAS_SIZE / 2);
        paperScope.draw().view.zoom = canvasHeight / CANVAS_SIZE;
        this.center = new paper.Point(this.canvasSize / 2, this.canvasSize / 2);
    }

    /**
     * Initial controls setup.
     */
    initControls() {
        // setup base color picker
        let defaultBaseColor = '#512DA8';
        this.setIconBaseColorFunction = function(event, disableDraw) {
            this.iconBase.setColor(this.baseColorPicker.getColor());
            if (!disableDraw) paperScope.draw().view.draw();
        }.bind(this);
        this.baseColorPicker = new ColorPicker(this.baseColorPicker, defaultBaseColor, this.setIconBaseColorFunction);

        // setup icon color picker
        let defaultIconColor = '#ffffff';
        this.setIconColorFunction = function (event, disableDraw) {
            this.icon.setColor(this.iconColorPicker.getColor());
            if (!disableDraw) paperScope.draw().view.draw();
        }.bind(this);
        this.iconColorPicker = new ColorPicker(this.iconColorPicker, defaultIconColor, this.setIconColorFunction);

        // setup shadow length slider
        this.setShadowLengthFunction = function (event, disableDraw) {
            this.icon.getIconShadow().setLength(this.sliderShadowLengthData.getValue());
            if (!disableDraw) paperScope.draw().view.draw();
        }.bind(this);
        this.sliderShadowLengthData = this.sliderShadowLength.slider()
            .on('slide', this.setShadowLengthFunction)
            .data('slider');

        // setup shadow intensity slider
        this.setShadowIntensityFunction = function (event, disableDraw) {
            this.icon.getIconShadow().setIntensity(this.sliderShadowIntensityData.getValue());
            if (!disableDraw) paperScope.draw().view.draw();
        }.bind(this);
        this.sliderShadowIntensityData  = this.sliderShadowIntensity.slider()
            .on('slide', this.setShadowIntensityFunction)
            .data('slider');

        // setup shadow fading slider
        this.setShadowFadingFunction = function (event, disableDraw) {
            this.icon.getIconShadow().setFading(this.sliderShadowFadingData.getValue());
            if (!disableDraw) paperScope.draw().view.draw();
        }.bind(this);
        this.sliderShadowFadingData = this.sliderShadowFading.slider()
            .on('slide', this.setShadowFadingFunction)
            .data('slider');

        // setup icon size picker
        this.setSizeFunction = function (event, disableDraw) {
            let size = this.sliderIconSizeData.getValue();
            let scale = 0.0954548 * Math.exp(0.465169 * size);
            this.icon.setScale(scale);
            if (!disableDraw) paperScope.draw().view.draw();
        }.bind(this);
        this.sliderIconSizeData = this.sliderIconSize.slider()
            .on('slide', this.setSizeFunction)
            .data('slider');

        // setup center icon
        this.checkBoxCenterIcon.bootstrapToggle();
        this.checkBoxCenterIcon.change(function() {
            let checked = this.checkBoxCenterIcon.prop('checked');
            if (checked) this.icon.center();
            paperScope.draw().view.draw();
        }.bind(this));

        // setup download
        this.btnDownload.click(function() {
            exportManager.createAndDownloadZip();
        }.bind(this));
    }

    /**
     * @param {function} errorCallback - will be called if there was an error processing the svg file.
     * Callback has a single argument, the error code.
     */
    setErrorCallback(errorCallback) {
        this.errorCallback = errorCallback;
    }

    /**
     * Handles the svg file loaded callback.
     * @param svgData either raw svg data or a URL pointing to a svg file.
     */
    onSvgLoaded(svgData) {
        // remove any previous icons
        if (this.icon) this.icon.remove();
        if (this.iconBase) this.iconBase.remove();

        this.loadingSvg = true;
        paperScope.draw().project.importSVG(svgData, {
            applyMatrix: true,
            expandShapes: true,
            onLoad: function (importedItem) {
                if (!this.loadingSvg) return;
                this.loadingSvg = false; // on error callback is fired twice. This flag stops that

                // valid svg file?
                if (!importedItem) {
                    this.errorCallback(errors.ERROR_INVALID_SVG_FILE);
                    return;
                }

                // check svg paths
                let importedPath = this.getPathFromImport(importedItem);
                if (!importedPath) {
                    this.errorCallback(errors.ERROR_INVALID_SVG_STRUCTURE);
                    return;
                }
                importedPath.strokeWidth = 0;

                // one time base setup
                this.setupBase();

                // create icon and shadow
                this.setupIcon(importedPath);

                this.loadingOverlay.css('opacity', 0);
                this.canvas.css('opacity', 1);
                setTimeout(function() {
                    this.loadingOverlay.hide();
                }.bind(this), 500);

            }.bind(this)
        });
    }

    setupBase() {
        this.baseRadius = this.canvasSize / 2 * 0.9;
        this.iconBase = new IconBase(
            this.center,
            this.baseRadius,
            this.containerEdit.find('#base-shape'),
            function () {
                this.icon.applyIcon();
                this.icon.getIconShadow().applyShadow();
                paperScope.draw().view.draw();
            }.bind(this));

        // set default values
        this.setIconBaseColorFunction(null, true);
    }

    setupIcon(importedPath) {
        // create icon + shadow
        this.icon = new Icon(this.center, importedPath, this.iconBase, function() {
            let checked = this.checkBoxCenterIcon.prop('checked');
            if (checked) this.checkBoxCenterIcon.prop('checked', false).change();
        }.bind(this));

        // set default icon values
        this.icon.setSize(this.baseRadius * 2 * 0.60);
        this.setIconColorFunction(null, true);
        this.setShadowLengthFunction(null, true);
        this.setShadowIntensityFunction(null, true);
        this.setShadowFadingFunction(null, true);
        this.icon.getIconShadow().applyShadow();
    }

    getPathFromImport(importedItem) {
        // recursive search for paths in group
        if (importedItem instanceof paper.Group) {
            let possiblePaths = [];
            for (let i = 0; i < importedItem.children.length; ++i) {
                let path = this.getPathFromImport(importedItem.children[i]);
                if (path) possiblePaths.push(path);
            }

            // if only one path, return that one
            if (possiblePaths.length == 0) return null;
            if (possiblePaths.length == 1) return possiblePaths[0];

            // if multiple paths, select all with fill color
            // (helps importing Google material icons, which always have a second 'invisible' path)
            let filledPaths = [];
            for (let i = 0; i < possiblePaths.length; ++i) {
                let path = possiblePaths[i];
                if (path.fillColor) filledPaths.push(possiblePaths[i]);
                path.remove();
            }

            // create new CompoundPath from single paths
            let simplePaths = [];
            for (let i = 0; i < filledPaths.length; ++i) {
                let path = filledPaths[i];
                if (path instanceof paper.Path) {
                    simplePaths.push(path);
                } else if (path instanceof paper.CompoundPath) {
                    for (let j = 0; j < path.children.length; ++j) {
                        simplePaths.push(path.children[j]);
                    }
                }
                simplePaths[simplePaths.length - 1].fillColor = new paper.Color(0, 0, 0, 0);
            }
            // sorting paths by area seems to be necessary to properly determine inside / outside of paths
            // (yes, it shouldn't, but ic_child_care_* seems to do just this)
            simplePaths.sort(function(a , b) {
                return a.area - b.area;
            });
            return new paper.CompoundPath({ children: simplePaths });
        }

        if (importedItem instanceof paper.PathItem) {
            return importedItem;
        }

        return null;
    }

    hide() {
        this.canvas.css('opacity', 0);
        this.loadingOverlay.show();
        this.loadingOverlay.css('opacity', 1);
    }

}

module.exports = IconManager;<commit_msg>Set base radius to 20dp<commit_after>'use strict';

let paper = require('js/paper-core.min'),
    IconBase = require('js/IconBase'),
    Icon = require('js/Icon'),
    ColorPicker = require('js/ColorPicker'),
    paperScope = require('js/PaperScopeManager'),
    exportManager = require('js/ExportManager'),
    errors = require('js/errors');

// Default android icon size (48 DIP)
const
    CANVAS_SIZE = 48,
    BASE_RADIUS = 20;

/**
 * Handles adding icon + base.
 */
class IconManager {

    /**
     * @param canvas - jquery canvas object
     * @param containerEdit - jquery edit objects (can be multiple)
     * @param btnDownload - jquery download button
     * @param iconColorPicker - jquery icon color picker object
     * @param baseColorPicker - jquery base color picker object
     * @param sliderIconSize - jquery slider object for changing icon size
     * @param sliderShadowLength - jquery slider object for changing shadow length
     * @param sliderShadowIntensity - jquery slider object for changing shadow intensity
     * @param sliderShadowFading - jquery slider object for changing shadow fading
     * @param checkBoxCenterIcon - jquery check box object for centering the icon
     */
    constructor(canvas, containerEdit,
                btnDownload, iconColorPicker, baseColorPicker,
                sliderIconSize, sliderShadowLength, sliderShadowIntensity,
                sliderShadowFading, checkBoxCenterIcon) {

        this.canvas = canvas;
        this.containerEdit = containerEdit;
        this.iconColorPicker = iconColorPicker;
        this.baseColorPicker = baseColorPicker;
        this.sliderIconSize = sliderIconSize;
        this.sliderShadowLength = sliderShadowLength;
        this.sliderShadowIntensity = sliderShadowIntensity;
        this.sliderShadowFading = sliderShadowFading;
        this.btnDownload = btnDownload;
        this.checkBoxCenterIcon = checkBoxCenterIcon;
        this.loadingOverlay = this.containerEdit.find('#canvas-loading');

        this.initCanvas();
        this.initControls();

    }

    /**
     * Initial canvas setup.
     */
    initCanvas() {
        // setup canvas
        paper.install(window);
        let canvasHeight = this.canvas.height();
        this.canvas.attr('height', canvasHeight);
        this.canvas.attr('width', canvasHeight);
        paperScope.setCanvases(this.canvas, this.containerEdit);
        paperScope.activateDraw();

        // place icon in center on canvas
        this.canvasSize = CANVAS_SIZE;
        paperScope.draw().view.center = new paper.Point(CANVAS_SIZE / 2, CANVAS_SIZE / 2);
        paperScope.draw().view.zoom = canvasHeight / CANVAS_SIZE;
        this.center = new paper.Point(this.canvasSize / 2, this.canvasSize / 2);
    }

    /**
     * Initial controls setup.
     */
    initControls() {
        // setup base color picker
        let defaultBaseColor = '#512DA8';
        this.setIconBaseColorFunction = function(event, disableDraw) {
            this.iconBase.setColor(this.baseColorPicker.getColor());
            if (!disableDraw) paperScope.draw().view.draw();
        }.bind(this);
        this.baseColorPicker = new ColorPicker(this.baseColorPicker, defaultBaseColor, this.setIconBaseColorFunction);

        // setup icon color picker
        let defaultIconColor = '#ffffff';
        this.setIconColorFunction = function (event, disableDraw) {
            this.icon.setColor(this.iconColorPicker.getColor());
            if (!disableDraw) paperScope.draw().view.draw();
        }.bind(this);
        this.iconColorPicker = new ColorPicker(this.iconColorPicker, defaultIconColor, this.setIconColorFunction);

        // setup shadow length slider
        this.setShadowLengthFunction = function (event, disableDraw) {
            this.icon.getIconShadow().setLength(this.sliderShadowLengthData.getValue());
            if (!disableDraw) paperScope.draw().view.draw();
        }.bind(this);
        this.sliderShadowLengthData = this.sliderShadowLength.slider()
            .on('slide', this.setShadowLengthFunction)
            .data('slider');

        // setup shadow intensity slider
        this.setShadowIntensityFunction = function (event, disableDraw) {
            this.icon.getIconShadow().setIntensity(this.sliderShadowIntensityData.getValue());
            if (!disableDraw) paperScope.draw().view.draw();
        }.bind(this);
        this.sliderShadowIntensityData  = this.sliderShadowIntensity.slider()
            .on('slide', this.setShadowIntensityFunction)
            .data('slider');

        // setup shadow fading slider
        this.setShadowFadingFunction = function (event, disableDraw) {
            this.icon.getIconShadow().setFading(this.sliderShadowFadingData.getValue());
            if (!disableDraw) paperScope.draw().view.draw();
        }.bind(this);
        this.sliderShadowFadingData = this.sliderShadowFading.slider()
            .on('slide', this.setShadowFadingFunction)
            .data('slider');

        // setup icon size picker
        this.setSizeFunction = function (event, disableDraw) {
            let size = this.sliderIconSizeData.getValue();
            let scale = 0.0954548 * Math.exp(0.465169 * size);
            this.icon.setScale(scale);
            if (!disableDraw) paperScope.draw().view.draw();
        }.bind(this);
        this.sliderIconSizeData = this.sliderIconSize.slider()
            .on('slide', this.setSizeFunction)
            .data('slider');

        // setup center icon
        this.checkBoxCenterIcon.bootstrapToggle();
        this.checkBoxCenterIcon.change(function() {
            let checked = this.checkBoxCenterIcon.prop('checked');
            if (checked) this.icon.center();
            paperScope.draw().view.draw();
        }.bind(this));

        // setup download
        this.btnDownload.click(function() {
            exportManager.createAndDownloadZip();
        }.bind(this));
    }

    /**
     * @param {function} errorCallback - will be called if there was an error processing the svg file.
     * Callback has a single argument, the error code.
     */
    setErrorCallback(errorCallback) {
        this.errorCallback = errorCallback;
    }

    /**
     * Handles the svg file loaded callback.
     * @param svgData either raw svg data or a URL pointing to a svg file.
     */
    onSvgLoaded(svgData) {
        // remove any previous icons
        if (this.icon) this.icon.remove();
        if (this.iconBase) this.iconBase.remove();

        this.loadingSvg = true;
        paperScope.draw().project.importSVG(svgData, {
            applyMatrix: true,
            expandShapes: true,
            onLoad: function (importedItem) {
                if (!this.loadingSvg) return;
                this.loadingSvg = false; // on error callback is fired twice. This flag stops that

                // valid svg file?
                if (!importedItem) {
                    this.errorCallback(errors.ERROR_INVALID_SVG_FILE);
                    return;
                }

                // check svg paths
                let importedPath = this.getPathFromImport(importedItem);
                if (!importedPath) {
                    this.errorCallback(errors.ERROR_INVALID_SVG_STRUCTURE);
                    return;
                }
                importedPath.strokeWidth = 0;

                // one time base setup
                this.setupBase();

                // create icon and shadow
                this.setupIcon(importedPath);

                this.loadingOverlay.css('opacity', 0);
                this.canvas.css('opacity', 1);
                setTimeout(function() {
                    this.loadingOverlay.hide();
                }.bind(this), 500);

            }.bind(this)
        });
    }

    setupBase() {
        this.iconBase = new IconBase(
            this.center,
            BASE_RADIUS,
            this.containerEdit.find('#base-shape'),
            function () {
                this.icon.applyIcon();
                this.icon.getIconShadow().applyShadow();
                paperScope.draw().view.draw();
            }.bind(this));

        // set default values
        this.setIconBaseColorFunction(null, true);
    }

    setupIcon(importedPath) {
        // create icon + shadow
        this.icon = new Icon(this.center, importedPath, this.iconBase, function() {
            let checked = this.checkBoxCenterIcon.prop('checked');
            if (checked) this.checkBoxCenterIcon.prop('checked', false).change();
        }.bind(this));

        // set default icon values
        this.icon.setSize(BASE_RADIUS * 2 * 0.60);
        this.setIconColorFunction(null, true);
        this.setShadowLengthFunction(null, true);
        this.setShadowIntensityFunction(null, true);
        this.setShadowFadingFunction(null, true);
        this.icon.getIconShadow().applyShadow();
    }

    getPathFromImport(importedItem) {
        // recursive search for paths in group
        if (importedItem instanceof paper.Group) {
            let possiblePaths = [];
            for (let i = 0; i < importedItem.children.length; ++i) {
                let path = this.getPathFromImport(importedItem.children[i]);
                if (path) possiblePaths.push(path);
            }

            // if only one path, return that one
            if (possiblePaths.length == 0) return null;
            if (possiblePaths.length == 1) return possiblePaths[0];

            // if multiple paths, select all with fill color
            // (helps importing Google material icons, which always have a second 'invisible' path)
            let filledPaths = [];
            for (let i = 0; i < possiblePaths.length; ++i) {
                let path = possiblePaths[i];
                if (path.fillColor) filledPaths.push(possiblePaths[i]);
                path.remove();
            }

            // create new CompoundPath from single paths
            let simplePaths = [];
            for (let i = 0; i < filledPaths.length; ++i) {
                let path = filledPaths[i];
                if (path instanceof paper.Path) {
                    simplePaths.push(path);
                } else if (path instanceof paper.CompoundPath) {
                    for (let j = 0; j < path.children.length; ++j) {
                        simplePaths.push(path.children[j]);
                    }
                }
                simplePaths[simplePaths.length - 1].fillColor = new paper.Color(0, 0, 0, 0);
            }
            // sorting paths by area seems to be necessary to properly determine inside / outside of paths
            // (yes, it shouldn't, but ic_child_care_* seems to do just this)
            simplePaths.sort(function(a , b) {
                return a.area - b.area;
            });
            return new paper.CompoundPath({ children: simplePaths });
        }

        if (importedItem instanceof paper.PathItem) {
            return importedItem;
        }

        return null;
    }

    hide() {
        this.canvas.css('opacity', 0);
        this.loadingOverlay.show();
        this.loadingOverlay.css('opacity', 1);
    }

}

module.exports = IconManager;<|endoftext|>


<commit_before>'use strict';

// Declare app level module which depends on filters, and services
var app = angular.module( 'ngDrawing', [
  'ngRoute',
  'ngDrawing.services',
  'ngDrawing.directives',
  'ngDrawing.controllers'
]);

app.config([ '$routeProvider', function( $routeProvider ) {
  $routeProvider.when( '/intro', {
    activePage: 'intro',
    templateUrl: 'assets/partials/intro.html',
    controller: 'introController'
  });
  $routeProvider.when( '/playground', {
    activePage: 'playground',
    templateUrl: 'assets/partials/playground.html',
    controller: 'playgroundController'
  });

  $routeProvider.otherwise({ redirectTo: '/intro' });
}]);

// used to save the status of the view activated
app.run(['$rootScope', "$route",
  function( $rootScope, $route ) {

    $rootScope.$on("$routeChangeSuccess", function( scope, next, current ) {
      $rootScope.view = $route.current.activePage;
    });
  }
]);

var services = angular.module('ngDrawing.services', []);

<commit_msg>Update application.js<commit_after>'use strict';

// Declare app level module which depends on filters, and services
var app = angular.module( 'ngDrawing', [
  'ngRoute',
  'ngDrawing.services',
  'ngDrawing.directives',
  'ngDrawing.controllers'
]);

app.config([ '$routeProvider', function( $routeProvider ) {
  $routeProvider.when( '/intro', {
    activePage: 'intro',
    templateUrl: 'assets/partials/intro.html',
    controller: 'introController'
  });
  $routeProvider.when( '/playground', {
    activePage: 'playground',
    templateUrl: 'assets/partials/playground.html',
    controller: 'playgroundController'
  });

  $routeProvider.otherwise({ redirectTo: '/intro' });
}]);

// used to save the status of the view activated
app.run(['$rootScope', '$route',
  function( $rootScope, $route ) {

    $rootScope.$on('$routeChangeSuccess', function( scope, next, current ) {
      $rootScope.view = $route.current.activePage;
    });
  }
]);

var services = angular.module('ngDrawing.services', []);
<|endoftext|>


<commit_before>"use strict";

module.exports = {

  init: () => {

    // Hack since Node v5
    window.jade = require('jade/runtime');

    console.log('Configuring Angular app...');

    require('./app.config')();

    console.log('App initialized.');
  }
};
<commit_msg>Fix: could not build with Node 4<commit_after>"use strict";

module.exports = {

  init: () => {

    // Hack since Node v5
    try {
      window.jade = require('jade' + '/' + 'runtime');
    } catch (e) {
    }

    console.log('Configuring Angular app...');

    require('./app.config')();

    console.log('App initialized.');
  }
};
<|endoftext|>


<commit_before>import {app, dialog, protocol} from 'electron';
import async from 'async';
import debug from 'debug';
import yargs from 'yargs';

import prefs from 'browser/utils/prefs';
import filePaths from 'common/utils/file-paths';
import distroDetector from 'browser/components/distro-detector';
import platform from 'common/utils/platform';

// Handle uncaught exceptions
process.on('uncaughtException', function (err) {
  dialog.showErrorBox('JavaScript error in the main process', err.stack);
  logFatal(err);
});

(function () {
  // Define the CLI arguments and parse them
  const cliArgs = process.argv.slice(1);
  const options = yargs(cliArgs)
    .usage('Usage: $0 [options]')
    .option('os-startup', {
      type: 'boolean',
      description: 'Flag to indicate the app is being run by the OS on startup.'
    })
    .option('portable', {
      type: 'boolean',
      description: 'Run in portable mode.'
    })
    .option('debug', {
      type: 'boolean',
      description: 'Run in debug mode.'
    })
    .option('console-logs', {
      type: 'boolean',
      description: 'Allow usage of console.log and friends.',
      default: true
    })
    .option('repl', {
      type: 'boolean',
      description: 'Listen for REPL connections.'
    })
    .option('repl-port', {
      type: 'number',
      description: 'The port to listen for REPL connections on.',
      default: 3499
    })
    .option('mas', {
      type: 'boolean',
      description: 'Run in Mac App Store release mode.'
    })
    .option('version', {
      type: 'boolean',
      description: 'Print the app version.',
      alias: 'v'
    })
    .option('squirrel-install', {
      type: 'boolean',
      description: 'Squirrel.Windows flag, called when the app is installed.'
    })
    .option('squirrel-uninstall', {
      type: 'boolean',
      description: 'Squirrel.Windows flag, called after the app is updated.'
    })
    .option('squirrel-updated', {
      type: 'boolean',
      description: 'Squirrel.Windows flag, called when the app is uninstalled.'
    })
    .option('squirrel-obsolete', {
      type: 'boolean',
      description: 'Squirrel.Windows flag, called before updating to a new version.'
    })
    .option('squirrel-firstrun', {
      type: 'boolean',
      description: 'Squirrel.Windows flag, called only once after installation.'
    })
    .help('help', 'Print this help message.').alias('help', 'h')
    .epilog('Coded with <3 by ' + global.manifest.author)
    .argv;

  options.mas = options.mas || !!process.mas;
  options.portable = options.portable || !!global.manifest.portable;
  options.debug = options.debug || !!process.env.DEBUG;
  global.options = options;

  // Force-enable debug
  if (options.debug && !process.env.DEBUG) {
    process.env.DEBUG = global.manifest.name + ':*';
    debug.enable(process.env.DEBUG);
  }

  // Log args
  log('cli args parsed', JSON.stringify(options));

  // Check for debug mode
  if (options.debug) {
    log('running in debug mode');
  }

  // Check for mas mode
  if (options.mas) {
    log('running in mas mode');
  }

  // Change the userData path if in portable mode
  if (options.portable) {
    log('running in portable mode');
    const userDataPath = filePaths.getCustomUserDataPath();
    log('setting userData path', userDataPath);
    app.setPath('userData', userDataPath);
  }

  // Check for Squirrel.Windows CLI args
  if (platform.isWindows) {
    const SquirrelEvents = require('browser/components/squirrel-events').default;
    if (SquirrelEvents.check(options)) {
      log('Squirrel.Windows event detected');
      return;
    }
  }

  // Quit the app immediately if required
  if (prefs.get('launch-quit')) {
    log('launch-quit pref is true, quitting');
    prefs.unsetSync('launch-quit');
    return app.quit();
  }

  // Print the version and exit
  if (options.version) {
    console.log(`${app.getName()} ${app.getVersion()} (${global.manifest.buildNum})`);
    console.log(`Electron ${process.versions.electron}`);
    console.log(`Chromium ${process.versions.chrome}`);
    return app.quit();
  }

  // Enforce single instance
  const isDuplicateInstance = app.makeSingleInstance((argv, cwd) => {
    log('another instance tried to run argv:', argv, 'cwd:', cwd);
    if (global.application) {
      global.application.mainWindowManager.showOrCreate();
    }
  });

  // Quit if another instance is already running
  if (isDuplicateInstance) {
    console.log('Another instance of ' + global.manifest.productName + ' is already running.');
    return app.quit();
  }

  // Init and launch app
  async.parallel([
    async.apply(async.series, [
      function (callback) {
        app.on('ready', function () {
          log('ready');
          callback();
        });
      },
      function (callback) {
        log('intercepting protocol http');
        protocol.interceptHttpProtocol('http', function (request, callback) {
          if (request.url.startsWith(global.manifest.virtualUrl)) {
            const newPath = request.url.replace(global.manifest.virtualUrl, 'file://' + app.getAppPath());
            const newPathShort = request.url.replace(global.manifest.virtualUrl, 'file://<app>');
            log('intercepted http', request.method, request.url, '=>', newPathShort);
            request.url = newPath;
            callback(request);
          }
        }, function (err) {
          if (err) {
            log('intercepting protocol http failed, not going to launch the app anymore');
            callback(err);
          } else {
            callback();
          }
        });
      }
    ]),
    function (callback) {
      options.distro = {};
      async.parallel({
        isElementaryOS: distroDetector.isElementaryOS
      }, (err, results) => {
        if (err) {
          return callback(err);
        }
        options.distro = results;
        callback();
      });
    }
  ], (err, results) => {
    if (err) {
      logFatal(err);
      return;
    }

    log('launching app');
    const Application = require('browser/application').default;
    global.application = new Application();
    global.application.init();
    global.ready = true;
  });

  // If the REPL is enabled, launch it
  if (options.repl) {
    const repl = require('browser/utils/repl');
    repl.createServer(options.replPort);
  }
})();
<commit_msg>Handle promise rejections<commit_after>import {app, dialog, protocol} from 'electron';
import async from 'async';
import debug from 'debug';
import yargs from 'yargs';

import prefs from 'browser/utils/prefs';
import filePaths from 'common/utils/file-paths';
import distroDetector from 'browser/components/distro-detector';
import platform from 'common/utils/platform';

// Handle uncaught exceptions
process.on('uncaughtException', function (err) {
  dialog.showErrorBox('JavaScript error in the main process', err.stack);
  logFatal(err);
});

// Handle promise rejections
process.on('unhandledRejection', function (err) {
  log('unhandled promise rejection');
  logFatal(err);
});

(function () {
  // Define the CLI arguments and parse them
  const cliArgs = process.argv.slice(1);
  const options = yargs(cliArgs)
    .usage('Usage: $0 [options]')
    .option('os-startup', {
      type: 'boolean',
      description: 'Flag to indicate the app is being run by the OS on startup.'
    })
    .option('portable', {
      type: 'boolean',
      description: 'Run in portable mode.'
    })
    .option('debug', {
      type: 'boolean',
      description: 'Run in debug mode.'
    })
    .option('console-logs', {
      type: 'boolean',
      description: 'Allow usage of console.log and friends.',
      default: true
    })
    .option('repl', {
      type: 'boolean',
      description: 'Listen for REPL connections.'
    })
    .option('repl-port', {
      type: 'number',
      description: 'The port to listen for REPL connections on.',
      default: 3499
    })
    .option('mas', {
      type: 'boolean',
      description: 'Run in Mac App Store release mode.'
    })
    .option('version', {
      type: 'boolean',
      description: 'Print the app version.',
      alias: 'v'
    })
    .option('squirrel-install', {
      type: 'boolean',
      description: 'Squirrel.Windows flag, called when the app is installed.'
    })
    .option('squirrel-uninstall', {
      type: 'boolean',
      description: 'Squirrel.Windows flag, called after the app is updated.'
    })
    .option('squirrel-updated', {
      type: 'boolean',
      description: 'Squirrel.Windows flag, called when the app is uninstalled.'
    })
    .option('squirrel-obsolete', {
      type: 'boolean',
      description: 'Squirrel.Windows flag, called before updating to a new version.'
    })
    .option('squirrel-firstrun', {
      type: 'boolean',
      description: 'Squirrel.Windows flag, called only once after installation.'
    })
    .help('help', 'Print this help message.').alias('help', 'h')
    .epilog('Coded with <3 by ' + global.manifest.author)
    .argv;

  options.mas = options.mas || !!process.mas;
  options.portable = options.portable || !!global.manifest.portable;
  options.debug = options.debug || !!process.env.DEBUG;
  global.options = options;

  // Force-enable debug
  if (options.debug && !process.env.DEBUG) {
    process.env.DEBUG = global.manifest.name + ':*';
    debug.enable(process.env.DEBUG);
  }

  // Log args
  log('cli args parsed', JSON.stringify(options));

  // Check for debug mode
  if (options.debug) {
    log('running in debug mode');
  }

  // Check for mas mode
  if (options.mas) {
    log('running in mas mode');
  }

  // Change the userData path if in portable mode
  if (options.portable) {
    log('running in portable mode');
    const userDataPath = filePaths.getCustomUserDataPath();
    log('setting userData path', userDataPath);
    app.setPath('userData', userDataPath);
  }

  // Check for Squirrel.Windows CLI args
  if (platform.isWindows) {
    const SquirrelEvents = require('browser/components/squirrel-events').default;
    if (SquirrelEvents.check(options)) {
      log('Squirrel.Windows event detected');
      return;
    }
  }

  // Quit the app immediately if required
  if (prefs.get('launch-quit')) {
    log('launch-quit pref is true, quitting');
    prefs.unsetSync('launch-quit');
    return app.quit();
  }

  // Print the version and exit
  if (options.version) {
    console.log(`${app.getName()} ${app.getVersion()} (${global.manifest.buildNum})`);
    console.log(`Electron ${process.versions.electron}`);
    console.log(`Chromium ${process.versions.chrome}`);
    return app.quit();
  }

  // Enforce single instance
  const isDuplicateInstance = app.makeSingleInstance((argv, cwd) => {
    log('another instance tried to run argv:', argv, 'cwd:', cwd);
    if (global.application) {
      global.application.mainWindowManager.showOrCreate();
    }
  });

  // Quit if another instance is already running
  if (isDuplicateInstance) {
    console.log('Another instance of ' + global.manifest.productName + ' is already running.');
    return app.quit();
  }

  // Init and launch app
  async.parallel([
    async.apply(async.series, [
      function (callback) {
        app.on('ready', function () {
          log('ready');
          callback();
        });
      },
      function (callback) {
        log('intercepting protocol http');
        protocol.interceptHttpProtocol('http', function (request, callback) {
          if (request.url.startsWith(global.manifest.virtualUrl)) {
            const newPath = request.url.replace(global.manifest.virtualUrl, 'file://' + app.getAppPath());
            const newPathShort = request.url.replace(global.manifest.virtualUrl, 'file://<app>');
            log('intercepted http', request.method, request.url, '=>', newPathShort);
            request.url = newPath;
            callback(request);
          }
        }, function (err) {
          if (err) {
            log('intercepting protocol http failed, not going to launch the app anymore');
            callback(err);
          } else {
            callback();
          }
        });
      }
    ]),
    function (callback) {
      options.distro = {};
      async.parallel({
        isElementaryOS: distroDetector.isElementaryOS
      }, (err, results) => {
        if (err) {
          return callback(err);
        }
        options.distro = results;
        callback();
      });
    }
  ], (err, results) => {
    if (err) {
      logFatal(err);
      return;
    }

    log('launching app');
    const Application = require('browser/application').default;
    global.application = new Application();
    global.application.init();
    global.ready = true;
  });

  // If the REPL is enabled, launch it
  if (options.repl) {
    const repl = require('browser/utils/repl');
    repl.createServer(options.replPort);
  }
})();
<|endoftext|>


<commit_before>'use strict';

/* Controllers */

angular.module('myApp.controllers', [])
.controller('SimonController', ['$scope','$timeout','GameGeneratorMovement', function($scope,$timeout,GameGeneratorMovement) {

  	$scope.Movements = ['blue', 'green', 'yellow', 'red'];
 		$scope.GameMovements = [];
 		$scope.UserMovements = [];
		$scope.GameFinished = false;
    $scope.Level = 800;
    $scope.Round = 0;

		$scope.StartGame = function(){
      $scope.Round = 1;
      $scope.Level = 800;
      $scope.GameFinished = false;
      $scope.GameMovements = [];
 			$scope.UserMovements = [];
 			$scope.AddGameMovement();
      $scope.StartAnimation();

  	}

  	$scope.AddGameMovement = function(){
        var nextMovement = GameGeneratorMovement.Get($scope.Movements);
        $scope.GameMovements.push(nextMovement);
    };

  		$scope.AddUserMovement = function(movement)
  		{

       if ($scope.AnimationInProgress || $scope.GameFinished)
          return;

      $scope.movement = movement;
			$scope.UserMovements.push(movement);

      $timeout(function(){
          $scope.movement = "";
          $scope.ValidMovements($scope.GameMovements,$scope.UserMovements);
      }, 150)

  		}

  		$scope.ValidMovements = function(gameMovements, userMovements)
  		{
  		  var validMovements = true;
  			for(var i = 0; i <= userMovements.length-1; ++i)
  			{
          if (gameMovements[i] != userMovements[i]){
            $scope.GameFinished = true;
  					validMovements = false;
            return false;
  				}

  			}

        if (validMovements && gameMovements.length == userMovements.length)
        {
           $scope.AddGameMovement();
           $scope.StartAnimation();
           $scope.UserMovements = [];
           $scope.Round +=1;
        }
    }

$scope.StartAnimation = function()
  {
    $scope.AnimationInProgress = true;
    var i = $scope.GameMovements.length;

    var k= 0;

    var ticker = function() {
      if (k==i)
        $scope.AnimationInProgress = false;
      if (k<i)
      {
    $scope.movement = $scope.GameMovements[k];
    $timeout(function(){
            $scope.movement = "";
      $timeout(ticker, $scope.Level)
          }, $scope.Level)
      }
      else
      {
        $scope.movement = "";
    if($scope.Level > 200)
      $scope.Level -= 100;
    }
      k++;
    };

    $timeout(ticker, $scope.Level)
  }

  }])

<commit_msg>change<commit_after>'use strict';

/* Controllers */

angular.module('myApp.controllers', [])
.controller('SimonController', ['$scope','$timeout','GameGeneratorMovement', function($scope,$timeout,GameGeneratorMovement) {

  	$scope.Movements = ['blue', 'green', 'yellow', 'red'];
 	$scope.GameMovements = [];
 	$scope.UserMovements = [];
	$scope.GameFinished = false;
    $scope.Level = 800;
    $scope.Round = 0;

	$scope.StartGame = function(){
    	$scope.Round = 1;
      	$scope.Level = 800;
      	$scope.GameFinished = false;
      	$scope.GameMovements = [];
 		$scope.UserMovements = [];
 		$scope.AddGameMovement();
        $scope.StartAnimation();
    }

  	$scope.AddGameMovement = function(){
        var nextMovement = GameGeneratorMovement.Get($scope.Movements);
        $scope.GameMovements.push(nextMovement);
    };

  	$scope.AddUserMovement = function(movement){
		if ($scope.AnimationInProgress || $scope.GameFinished)
          return;

		$scope.movement = movement;
			$scope.UserMovements.push(movement);

      	$timeout(function(){
          $scope.movement = "";
          $scope.ValidMovements($scope.GameMovements,$scope.UserMovements);
      	}, 150)
	}

	$scope.ValidMovements = function(gameMovements, userMovements){
  		var validMovements = true;
  		for(var i = 0; i <= userMovements.length-1; ++i){
          if (gameMovements[i] != userMovements[i]){
            $scope.GameFinished = true;
  					validMovements = false;
            return false;
  				}
		}
        if (validMovements && gameMovements.length == userMovements.length){
           $scope.AddGameMovement();
           $scope.StartAnimation();
           $scope.UserMovements = [];
           $scope.Round +=1;
        }
    }

	$scope.StartAnimation = function(){
    	$scope.AnimationInProgress = true;
    	var i = $scope.GameMovements.length;
	    var k= 0;

   		var ticker = function() {
      		if (k==i)
        		$scope.AnimationInProgress = false;
      			if (k<i){
    				$scope.movement = $scope.GameMovements[k];
    				$timeout(function(){
            			$scope.movement = "";
      					$timeout(ticker, $scope.Level)
         			 }, $scope.Level)
     		 	}
      		else{
        		$scope.movement = "";
    			if($scope.Level > 200)
      				$scope.Level -= 100;
    			}
      			k++;
    	};
  		$timeout(ticker, $scope.Level)
  }}])<|endoftext|>


<commit_before>(function () {

    var root = this;

    var defaults = {
        chart: {
            animations: true,
            // by default take the size of the parent container
            defaultWidth: 400,
            // height = width * ratio
            defaultAspect: 1 / 1.61803398875,
            // calculated at render time based on the options & container
            width: undefined,
            // if defined, height takes precedence over aspect
            height: undefined,
            // margin between the container and the chart (ie labels or axis title)
            margin: {
                top: 0,
                right: 0,
                bottom: 0,
                left: 0
            },
            // padding between the chart area and the inner plot area */
            padding: {
                top: null,
                right: null,
                bottom: 0,
                left: 0
            },
            internalPadding: {
                bottom: 0,
                left: 0
            },
            // automatically false by default anyway; adding here to help generate docs
            rotatedFrame: false,
            // width in pixels of the plot area (area inside the axis if any). This gets calculated on render
            plotWidth: undefined,
            // height in pixels of the plot area (area inside the axis if any). This gets calculated on render
            plotHeight: undefined,
            // top edge in pixels (from the edge of the svg) of the plot area (area inside the axis if any). This gets calculated on render
            plotTop: undefined,
            // left edge in pixels (from the edge of the svg) of the plot area (area inside the axis if any). This gets calculated on render
            plotLeft: undefined,
        },

        xAxis: {
        },

        yAxis: {
        },

        tooltip: {
        }
    };

    /**
    * Creates a Contour instance, based on the core Contour object. This instance can contain a set of related visualizations.
    *
    *   * Pass the constructor any configuration options in the *options* parameter. Make sure the `el` option contains the selector of the container in which the Contour instance will be rendered.
    *   * Set the frame for this Contour instance (e.g. `.cartesian()`).
    *   * Add one or more specific visualizations to this Contour instance (e.g. `.scatter()`, `.trend-line()`). Pass each visualization constructor the data it displays. Pass configuration options if desired.
    *   * Invoke an action for this Contour instance (e.g. `.render()`).
    *
    * ### Example:
    *
    *     new Contour({el: 'myChart'})
    *       .cartesian()
    *       .line([1,3,2,5])
    *       .render()
    *
    *
    * @class Contour() visualizations object
    * @param {object} options The global configuration options object
    *
    */
    function Contour (options) {
        this.init(options);

        return this;
    }

    /**
    * Adds a new kind of visualization to the core Contour object.
    * The *renderer* function is called when you add this visualization to instances of Contour.
    *
    * ### Example:
    *
    *     Contour.export("exampleVisualization", function(data, layer) {
    *           //function body to create exampleVisualization
    *           //for example using SVG and/or D3
    *     });
    *
    *     //to include the visualization into a specific Contour instance
    *     new Contour(options)
    *           .exampleVisualization(data)
    *           .render()
    *
    * @param {String} ctorName Name of the visualization, used as a constructor name.
    * @param {Function} renderer Function called when this visualization is added to a Contour instance. This function receives the data that is passed in to the constructor.
    */
    Contour.export = function (ctorName, renderer) {

        if (typeof renderer !== 'function') throw new Error('Invalid render function for ' + ctorName + ' visualization');

        function sortSeries(data) {
            if(!data || !data.length) return [];

            if(data[0].data) {
                _.each(data, sortSeries);
            }

            var shouldSort = _.isObject(data[0]) && _.isDate(data[0].x);
            var sortFunc = function (a, b) { return a.x - b.x; };
            if(shouldSort) {
                data.sort(sortFunc);
            }

            return data;
        }

        Contour.prototype[ctorName] = function (data, options) {
            var categories = this.options ? this.options.xAxis ? this.options.xAxis.categories : undefined : undefined;
            var opt =  _.extend({}, this.options[ctorName], options);
            var vis;

            data = data || [];
            sortSeries(data);
            vis = new Contour.VisualizationContainer(_.nw.normalizeSeries(data, categories), opt, ctorName, renderer, this);
            this._visualizations.push(vis);

            return this;
        };
    };

    /**
    * Exposes functionality to the core Contour object.
    * Use this to add *functionality* that will be available for any visualizations.
    *
    * ###Example:
    *
    *     Contour.expose("example", {
    *          // when included in the instance, the function `.myFunction` is available in the visualizations
    *         myFunction: function(data) { .... }
    *     });
    *
    *     Contour.export("visualizationThatUsesMyFunction", function(data, layer) {
    *           //function body including call to this.myFunction(data)
    *     });
    *
    *     // to include the functionality into a specific instance
    *     new Contour(options)
    *           .example()
    *           .visualizationThatUsesMyFunction()
    *           .render()
    */
    Contour.expose = function (ctorName, functionalityConstructor) {
        var ctor = function () {
            var functionality = typeof functionalityConstructor === 'function' ? new functionalityConstructor() : functionalityConstructor;
            // extend the --instance-- we don't want all charts to be overriden...
            _.extend(this, _.omit(functionality, 'init'));

            if(functionality.init) functionality.init.call(this, this.options);

            return this;
        };

        Contour.prototype[ctorName] = ctor;

        return this;
    };

    Contour.prototype = _.extend(Contour.prototype, {
        _visualizations: undefined,

        _extraOptions: undefined,

        // Initializes the instance of Narwhal
        init: function (options) {
            // for now, just  store this options here...
            // the final set of options will be composed before rendering
            // after all components/visualizations have been added
            this.options = options || {};

            this._extraOptions = [];
            this._visualizations = [];

            return this;
        },

        calculateWidth: function () {
            var width = _.nw.getStyle(this.options.el, 'width');
            return this.options.el ? (parseInt(width, 10) || this.options.chart.defaultWidth) : this.options.chart.defaultWidth;
        },

        calculateHeight: function () {
            var height = _.nw.getStyle(this.options.el, 'height');
            var containerHeight = this.options.el ? parseInt(height, 10) : undefined;
            var calcWidth = this.options.chart.width;
            var ratio = this.options.chart.aspect || this.options.chart.defaultAspect;

            return !!containerHeight && containerHeight > 1 ?  containerHeight : Math.round(calcWidth * ratio);
        },

        calcMetrics: function () {
            var options = this.options;

            this.adjustPadding();

            this.adjustTitlePadding();

            options.chart.width = options.chart.width || this.calculateWidth();
            options.chart.height = options.chart.height || this.calculateHeight();

            this.options = _.merge(options, {
                chart: {
                    plotWidth: options.chart.width - options.chart.margin.left - options.chart.margin.right - options.chart.internalPadding.left - options.chart.padding.right,
                    plotHeight: options.chart.height - options.chart.margin.top - options.chart.margin.bottom - options.chart.padding.top - options.chart.internalPadding.bottom,
                    plotLeft: options.chart.margin.left + options.chart.internalPadding.left,
                    plotTop: options.chart.margin.top + options.chart.padding.top
                }
            });
        },

        adjustPadding: function () {
            // overriden by components that need to adjust padding
            return this;
        },

        adjustTitlePadding: function () {
            // overriden by components that need to adjust padding
            return this;
        },

        composeOptions: function () {
            var allDefaults = _.merge({}, defaults);
            var mergeExtraOptions = function (opt) { _.merge(allDefaults, opt); };
            var mergeDefaults = function (vis) { _.merge(allDefaults, vis.renderer.defaults); };

            _.each(this._extraOptions, mergeExtraOptions);
            _.each(this._visualizations, mergeDefaults);

            // compose the final list of options right before start rendering
            this.options = _.merge({}, allDefaults, this.options);
        },

        baseRender: function () {
            this.plotArea();

            return this;
        },

        /**
        * Renders this Contour instance and all its visualizations into the DOM.
        *
        * Example:
        *
        *     new Contour({ el:'.myChart' })
        *           .pie([1,2,3])
        *           .render()
        *
        * @function render
        *
        */
        render: function () {
            this.composeOptions();

            this.calcMetrics();

            this.baseRender();

            this.renderVisualizations();

            return this;
        },

        update: function () {
            this.calcMetrics();
            return this;
        },

        plotArea: function () {

            var chartOpt = this.options.chart;

            this.container = d3.select(this.options.el);

            if(!this.svg) {
                this.svg = this.container
                    .append('svg')
                    .attr('viewBox', '0 0 ' + chartOpt.width + ' ' + chartOpt.height)
                    .attr('preserveAspectRatio', 'xMinYMin')
                    .attr('class', 'contour-chart')
                    .attr('height', chartOpt.height)
                    .append('g')
                        .attr('transform', 'translate(' + chartOpt.margin.left + ',' + chartOpt.margin.top + ')');
            }

            return this;
        },

        createVisualizationLayer: function (vis, id) {
            return this.svg.append('g')
                .attr('vis-id', id)
                .attr('vis-type', vis.type)
                .attr('transform', 'translate(' + this.options.chart.internalPadding.left + ',' + this.options.chart.padding.top + ')');
        },

        renderVisualizations: function () {

            _.each(this._visualizations, function (visualization, index) {
                var id = index + 1;
                var layer = visualization.layer || this.createVisualizationLayer(visualization, id);
                var opt = _.merge({}, this.options, visualization.options);
                visualization.layer = layer;
                visualization.parent = this;
                visualization.render(layer, opt, this);
            }, this);

            return this;
        },

        /**
        * Sets the same data into all visualizations for a Contour instance. Useful for creating interactive
        * visualizations: call after getting the additional data from the user.
        *
        * ###Example:
        *
        *     var data = [1,2,3,4,5];
        *     var chart = new Contour({ el:'.myChart' })
        *           .cartesian()
        *           .scatter(data)
        *           .trendLine(data);
        *
        *     data.push(10);
        *     chart.setData(data)
        *           .render();
        *
        * @function setData
        *
        */
        setData: function (data) {
            _.invoke(this._visualizations, 'setData', data);

            return this;
        },

        /**
        * Returns a VisualizationContainer object for the visualization at a given index (0-based).
        *
        * ###Example:
        *
        *     var chart = new Contour({ el:'.myChart' })
        *           .pie([1,2,3])
        *           .render()
        *
        *     var myPie = chart.select(0)
        *
        *     // do something with the visualization, for example updating its data set
        *     myPie.setData([6,7,8,9]).render()
        *
        * @function select
        *
        */
        select: function (index) {
            return this._visualizations[index];
        },

        // place holder function for now
        data: function () {

        }
    });

    // export to our context
    root.Contour = Contour;

})();
<commit_msg>fixed initial values for padding<commit_after>(function () {

    var root = this;

    var defaults = {
        chart: {
            animations: true,
            // by default take the size of the parent container
            defaultWidth: 400,
            // height = width * ratio
            defaultAspect: 1 / 1.61803398875,
            // calculated at render time based on the options & container
            width: undefined,
            // if defined, height takes precedence over aspect
            height: undefined,
            // margin between the container and the chart (ie labels or axis title)
            margin: {
                top: 0,
                right: 0,
                bottom: 0,
                left: 0
            },
            // padding between the chart area and the inner plot area */
            padding: {
                top: null,
                right: null,
                bottom: null,
                left: null
            },
            internalPadding: {
                bottom: 0,
                left: 0
            },
            // automatically false by default anyway; adding here to help generate docs
            rotatedFrame: false,
            // width in pixels of the plot area (area inside the axis if any). This gets calculated on render
            plotWidth: undefined,
            // height in pixels of the plot area (area inside the axis if any). This gets calculated on render
            plotHeight: undefined,
            // top edge in pixels (from the edge of the svg) of the plot area (area inside the axis if any). This gets calculated on render
            plotTop: undefined,
            // left edge in pixels (from the edge of the svg) of the plot area (area inside the axis if any). This gets calculated on render
            plotLeft: undefined,
        },

        xAxis: {
        },

        yAxis: {
        },

        tooltip: {
        }
    };

    /**
    * Creates a Contour instance, based on the core Contour object. This instance can contain a set of related visualizations.
    *
    *   * Pass the constructor any configuration options in the *options* parameter. Make sure the `el` option contains the selector of the container in which the Contour instance will be rendered.
    *   * Set the frame for this Contour instance (e.g. `.cartesian()`).
    *   * Add one or more specific visualizations to this Contour instance (e.g. `.scatter()`, `.trend-line()`). Pass each visualization constructor the data it displays. Pass configuration options if desired.
    *   * Invoke an action for this Contour instance (e.g. `.render()`).
    *
    * ### Example:
    *
    *     new Contour({el: 'myChart'})
    *       .cartesian()
    *       .line([1,3,2,5])
    *       .render()
    *
    *
    * @class Contour() visualizations object
    * @param {object} options The global configuration options object
    *
    */
    function Contour (options) {
        this.init(options);

        return this;
    }

    /**
    * Adds a new kind of visualization to the core Contour object.
    * The *renderer* function is called when you add this visualization to instances of Contour.
    *
    * ### Example:
    *
    *     Contour.export("exampleVisualization", function(data, layer) {
    *           //function body to create exampleVisualization
    *           //for example using SVG and/or D3
    *     });
    *
    *     //to include the visualization into a specific Contour instance
    *     new Contour(options)
    *           .exampleVisualization(data)
    *           .render()
    *
    * @param {String} ctorName Name of the visualization, used as a constructor name.
    * @param {Function} renderer Function called when this visualization is added to a Contour instance. This function receives the data that is passed in to the constructor.
    */
    Contour.export = function (ctorName, renderer) {

        if (typeof renderer !== 'function') throw new Error('Invalid render function for ' + ctorName + ' visualization');

        function sortSeries(data) {
            if(!data || !data.length) return [];

            if(data[0].data) {
                _.each(data, sortSeries);
            }

            var shouldSort = _.isObject(data[0]) && _.isDate(data[0].x);
            var sortFunc = function (a, b) { return a.x - b.x; };
            if(shouldSort) {
                data.sort(sortFunc);
            }

            return data;
        }

        Contour.prototype[ctorName] = function (data, options) {
            var categories = this.options ? this.options.xAxis ? this.options.xAxis.categories : undefined : undefined;
            var opt =  _.extend({}, this.options[ctorName], options);
            var vis;

            data = data || [];
            sortSeries(data);
            vis = new Contour.VisualizationContainer(_.nw.normalizeSeries(data, categories), opt, ctorName, renderer, this);
            this._visualizations.push(vis);

            return this;
        };
    };

    /**
    * Exposes functionality to the core Contour object.
    * Use this to add *functionality* that will be available for any visualizations.
    *
    * ###Example:
    *
    *     Contour.expose("example", {
    *          // when included in the instance, the function `.myFunction` is available in the visualizations
    *         myFunction: function(data) { .... }
    *     });
    *
    *     Contour.export("visualizationThatUsesMyFunction", function(data, layer) {
    *           //function body including call to this.myFunction(data)
    *     });
    *
    *     // to include the functionality into a specific instance
    *     new Contour(options)
    *           .example()
    *           .visualizationThatUsesMyFunction()
    *           .render()
    */
    Contour.expose = function (ctorName, functionalityConstructor) {
        var ctor = function () {
            var functionality = typeof functionalityConstructor === 'function' ? new functionalityConstructor() : functionalityConstructor;
            // extend the --instance-- we don't want all charts to be overriden...
            _.extend(this, _.omit(functionality, 'init'));

            if(functionality.init) functionality.init.call(this, this.options);

            return this;
        };

        Contour.prototype[ctorName] = ctor;

        return this;
    };

    Contour.prototype = _.extend(Contour.prototype, {
        _visualizations: undefined,

        _extraOptions: undefined,

        // Initializes the instance of Narwhal
        init: function (options) {
            // for now, just  store this options here...
            // the final set of options will be composed before rendering
            // after all components/visualizations have been added
            this.options = options || {};

            this._extraOptions = [];
            this._visualizations = [];

            return this;
        },

        calculateWidth: function () {
            var width = _.nw.getStyle(this.options.el, 'width');
            return this.options.el ? (parseInt(width, 10) || this.options.chart.defaultWidth) : this.options.chart.defaultWidth;
        },

        calculateHeight: function () {
            var height = _.nw.getStyle(this.options.el, 'height');
            var containerHeight = this.options.el ? parseInt(height, 10) : undefined;
            var calcWidth = this.options.chart.width;
            var ratio = this.options.chart.aspect || this.options.chart.defaultAspect;

            return !!containerHeight && containerHeight > 1 ?  containerHeight : Math.round(calcWidth * ratio);
        },

        calcMetrics: function () {
            var options = this.options;

            this.adjustPadding();

            this.adjustTitlePadding();

            options.chart.width = options.chart.width || this.calculateWidth();
            options.chart.height = options.chart.height || this.calculateHeight();

            this.options = _.merge(options, {
                chart: {
                    plotWidth: options.chart.width - options.chart.margin.left - options.chart.margin.right - options.chart.internalPadding.left - options.chart.padding.right,
                    plotHeight: options.chart.height - options.chart.margin.top - options.chart.margin.bottom - options.chart.padding.top - options.chart.internalPadding.bottom,
                    plotLeft: options.chart.margin.left + options.chart.internalPadding.left,
                    plotTop: options.chart.margin.top + options.chart.padding.top
                }
            });
        },

        adjustPadding: function () {
            // overriden by components that need to adjust padding
            return this;
        },

        adjustTitlePadding: function () {
            // overriden by components that need to adjust padding
            return this;
        },

        composeOptions: function () {
            var allDefaults = _.merge({}, defaults);
            var mergeExtraOptions = function (opt) { _.merge(allDefaults, opt); };
            var mergeDefaults = function (vis) { _.merge(allDefaults, vis.renderer.defaults); };

            _.each(this._extraOptions, mergeExtraOptions);
            _.each(this._visualizations, mergeDefaults);

            // compose the final list of options right before start rendering
            this.options = _.merge({}, allDefaults, this.options);
        },

        baseRender: function () {
            this.plotArea();

            return this;
        },

        /**
        * Renders this Contour instance and all its visualizations into the DOM.
        *
        * Example:
        *
        *     new Contour({ el:'.myChart' })
        *           .pie([1,2,3])
        *           .render()
        *
        * @function render
        *
        */
        render: function () {
            this.composeOptions();

            this.calcMetrics();

            this.baseRender();

            this.renderVisualizations();

            return this;
        },

        update: function () {
            this.calcMetrics();
            return this;
        },

        plotArea: function () {

            var chartOpt = this.options.chart;

            this.container = d3.select(this.options.el);

            if(!this.svg) {
                this.svg = this.container
                    .append('svg')
                    .attr('viewBox', '0 0 ' + chartOpt.width + ' ' + chartOpt.height)
                    .attr('preserveAspectRatio', 'xMinYMin')
                    .attr('class', 'contour-chart')
                    .attr('height', chartOpt.height)
                    .append('g')
                        .attr('transform', 'translate(' + chartOpt.margin.left + ',' + chartOpt.margin.top + ')');
            }

            return this;
        },

        createVisualizationLayer: function (vis, id) {
            return this.svg.append('g')
                .attr('vis-id', id)
                .attr('vis-type', vis.type)
                .attr('transform', 'translate(' + this.options.chart.internalPadding.left + ',' + this.options.chart.padding.top + ')');
        },

        renderVisualizations: function () {

            _.each(this._visualizations, function (visualization, index) {
                var id = index + 1;
                var layer = visualization.layer || this.createVisualizationLayer(visualization, id);
                var opt = _.merge({}, this.options, visualization.options);
                visualization.layer = layer;
                visualization.parent = this;
                visualization.render(layer, opt, this);
            }, this);

            return this;
        },

        /**
        * Sets the same data into all visualizations for a Contour instance. Useful for creating interactive
        * visualizations: call after getting the additional data from the user.
        *
        * ###Example:
        *
        *     var data = [1,2,3,4,5];
        *     var chart = new Contour({ el:'.myChart' })
        *           .cartesian()
        *           .scatter(data)
        *           .trendLine(data);
        *
        *     data.push(10);
        *     chart.setData(data)
        *           .render();
        *
        * @function setData
        *
        */
        setData: function (data) {
            _.invoke(this._visualizations, 'setData', data);

            return this;
        },

        /**
        * Returns a VisualizationContainer object for the visualization at a given index (0-based).
        *
        * ###Example:
        *
        *     var chart = new Contour({ el:'.myChart' })
        *           .pie([1,2,3])
        *           .render()
        *
        *     var myPie = chart.select(0)
        *
        *     // do something with the visualization, for example updating its data set
        *     myPie.setData([6,7,8,9]).render()
        *
        * @function select
        *
        */
        select: function (index) {
            return this._visualizations[index];
        },

        // place holder function for now
        data: function () {

        }
    });

    // export to our context
    root.Contour = Contour;

})();
<|endoftext|>


<commit_before>var request = reqwest;

var BookCreateForm = React.createClass({
  handleSubmit: function(e) {
    e.preventDefault();

    var title       = this.refs.title.getDOMNode().value.trim();
    var description = this.refs.description.getDOMNode().value.trim();
    var color       = this.refs.color.getDOMNode().value.trim();

    request({
      url: '/api/books',
      type: 'json',
      method: 'post',
      data: {title: title, description: description, color: color},
      success: function (book) {
        // TODO notify storage
      }
    })

    this.refs.title.getDOMNode().value = '';
    this.refs.description.getDOMNode().value = '';
    this.refs.color.getDOMNode().value = '';
  },

  render: function() {
    return (
      <form onSubmit={this.handleSubmit}>

Create book

Title:* <input type="text" ref="title" />

Description:* <input type="text" ref="description" />

Color:* <input type="text" ref="color" />

        <input type="submit" value="Create" />
      </form>
    );
  }
});

React.render(
  <BookCreateForm />,
  document.getElementById('main')
);
<commit_msg>List available books<commit_after>var request = reqwest;

var BookCreateForm = React.createClass({
  handleSubmit: function(e) {
    e.preventDefault();

    var title       = this.refs.title.getDOMNode().value.trim();
    var description = this.refs.description.getDOMNode().value.trim();
    var color       = this.refs.color.getDOMNode().value.trim();

    request({
      url: '/api/books',
      type: 'json',
      method: 'post',
      data: {title: title, description: description, color: color},
      success: function (book) {
        // TODO notify storage
      }
    })

    this.refs.title.getDOMNode().value = '';
    this.refs.description.getDOMNode().value = '';
    this.refs.color.getDOMNode().value = '';
  },

  render: function() {
    return (
      <form onSubmit={this.handleSubmit}>

Create book

Title:* <input type="text" ref="title" />

Description:* <input type="text" ref="description" />

Color:* <input type="text" ref="color" />

        <input type="submit" value="Create" />
      </form>
    );
  }
});

var BookListing = React.createClass({
  getInitialState: function() {
    return {data: []};
  },

  componentDidMount: function() {
    request({
      url: '/api/books',
      type: 'json',
      success: function(data) {
        this.setState({data: data});
      }.bind(this),
    })
  },

  render: function() {
    var bookNodes = this.state.data.map(function (book) {
      return (

{book.title}

{book.description}

      );
    });
    return (

Book Listing

      {bookNodes}

    );
  }
});

React.render(

    <BookListing />
    <BookCreateForm />

,
  document.getElementById('main')
);
<|endoftext|>


<commit_before>loadScript('https://js.stripe.com/v3/',
           function() {
  var stripe= Stripe('{{ @STRIPE_KEY }}');
  var elements= stripe.elements();
  var button= document.getElementById('stripe-button');
  var error= document.getElementById('stripe-error');

  var card= elements.create("card");
  // Stripe injects an iframe into the DOM
  card.mount("#card-element");

  card.on("change", function (event) {
    // Disable the Pay button if there are no card details in the Element
    button.disabled= event.empty;
    error.textContent= event.error ? event.error.message : ''
    if (event.error) error.classList.remove('hidden')
  });

  var form= document.getElementById("payment-form");
  form.addEventListener("submit", function(event) {
    event.preventDefault();
      return fetch('/sale/{{ @sale.uuid }}/get-stripe-payment-intent', {
        // fake AJAX header so we get JSON errors
        headers: { 'X-Requested-With': 'XMLHttpRequest' }
      })
      .then(function(res) {
        if (!res.ok) {
          return res.json().then((data) => {
            if (data.text == 'Payment already completed.') {
              window.location.href= "/sale/{{ @sale.uuid }}/thanks"
            } else {
              return Promise.reject(new Error(data.text))
            }
          })
        }
        return res.json()
      })
      .then(function(data) {
        payWithCard(stripe, card, data.secret);
      })
  });

  var payWithCard= function(stripe, card, clientSecret) {
    loading(true);
    stripe
      .confirmCardPayment(clientSecret, {
        payment_method: {
          card: card
        }
      })
      .then(function(result) {
        if (result.error) {
          // Show error to your customer
          showError(result.error.message);
        } else {
          // The payment succeeded!
          orderComplete(result.paymentIntent.id);
        }
      });
  };

  var orderComplete= function(paymentIntentId) {
    return fetch('/sale/{{ @sale.uuid }}/process-stripe-payment', {
      method: 'POST',
    }).then(function (data) {
      if (data.ok) {
        reportPurchase()
        window.location.href= "/sale/{{ @sale.uuid }}/thanks"
      }
    });
  };

  // Show the customer the error from Stripe if their card fails to charge
  var showError= function(errorMsgText) {
    loading(false);
    error.textContent= errorMsgText;
    error.classList.remove('hidden');
    setTimeout(function() {
      error.textContent= "";
      error.classList.add('hidden');
    }, 4000);
  };

  // Show a spinner on payment submission
  var loading = function(isLoading) {
    if (isLoading) {
      // Disable the button and show a spinner
      button.disabled= true;
      button.querySelector(".spin").classList.remove("hidden");
    } else {
      button.disabled= false;
      button.querySelector(".spin").classList.add("hidden");
    }
  };

});

loadScript('https://www.paypal.com/sdk/js?client-id={{ @PAYPAL_CLIENT_ID }}',
           function() {
  paypal.Buttons({
    createOrder: function (data, actions) {
      return fetch('/sale/{{ @sale.uuid }}/get-paypal-order', {
        // fake AJAX header so we get JSON errors
        headers: { 'X-Requested-With': 'XMLHttpRequest' }
      })
      .then(function(res) {
        if (!res.ok) {
          return res.json().then((data) => {
            if (data.text == 'Payment already completed.') {
              window.location.href= "/sale/{{ @sale.uuid }}/thanks"
            } else {
              return Promise.reject(new Error(data.text))
            }
          })
        }
        return res.json()
      })
      .then(function(data) {
        return data.id
      })
    },
    onApprove: function(data, actions) {
      // Capture the funds from the transaction
      // XXX show processing...
      return actions.order.capture().then(function(details) {
        let formData= new FormData(document.getElementById('paypal-form'))
        formData.append('order_id', details.id)
        return fetch('/sale/{{ @sale.uuid }}/process-paypal-payment', {
          method: 'post',
          body: formData
        }).then(function (data) {
          if (data.ok) {
            reportPurchase()
            window.location.href= "/sale/{{ @sale.uuid }}/thanks"
          }
        });
      });
    }
  }).render('#paypal-button');
});

$("#giftcard-check").on("submit", function (ev) {
  var $form= $(ev.target);

  $form.find('[type="submit"]').prop('disabled', true);
  $form.find('.errors').addClass('hidden');

  var card= $('[name="giftcard"]', $form).val();

  $.ajax({ dataType: 'json', method: 'POST',
           url: $form.attr('action'),
           data: { card: card } })
   .done(function (data) {
     $form.addClass('hidden');
     var $use= $('#giftcard-use');
     var amount= $use.find('[name="amount"]');
     var text= "Pay $" + Math.min(amount.val(), data.balance).toFixed(2);
     $use.find('button').text(text);
     $use.find('[name="balance"]').val('$' + data.balance);
     $use.removeClass('hidden');
   })
   .fail(function (jqXHR, textStatus, errorThrown) {
     $form.find('.errors').text(jqXHR.responseJSON.text ?
                                jqXHR.responseJSON.text : textStatus);
     $form.find('.errors').removeClass('hidden');
     $form.find('[type="submit"]').prop('disabled', false);
   });

  return false;
});

$("#giftcard-use").on("submit", function (ev) {
  var $form= $(ev.target);

  $form.find('[type="submit"]').prop('disabled', true);
  $form.find('.errors').addClass('hidden');

  var card= $('#giftcard-check [name="giftcard"]').val();

  $.ajax({ dataType: 'json', method: 'POST',
           url: $form.attr('action'),
           data: { card: card }})
   .done(function (data) {
     if (data.paid) {
       window.location.href= "/sale/{{ @sale.uuid }}/thanks";
     } else {
       window.location.href= "/sale/{{ @sale.uuid }}/pay";
     }
   })
   .fail(function (jqXHR, textStatus, errorThrown) {
     $form.find('.errors').text(jqXHR.responseJSON.text ?
                                jqXHR.responseJSON.text : textStatus);
     $form.find('.errors').removeClass('hidden');
     $form.find('[type="submit"]').prop('disabled', false);
   });

  return false;
});

$("#rewards-use").on("click", function (ev) {
  var $btn= $(ev.target);

  $btn.prop('disabled', true);

  $.ajax({ dataType: 'json', method: 'POST',
           url: "/sale/{{ @sale.uuid }}/process-rewards" })
   .done(function (data) {
     if (data.paid) {
       window.location.href= "/sale/{{ @sale.uuid }}/thanks";
     } else {
       window.location.href= "/sale/{{ @sale.uuid }}/pay";
     }
   })
   .fail(function (jqXHR, textStatus, errorThrown) {
     window.alert(jqXHR.responseJSON.text ?  jqXHR.responseJSON.text : textStatus);
   });

  return false;
});

if (document.getElementById('other-use')) {
  document.getElementById('other-use').addEventListener('submit', (ev) => {
    ev.preventDefault()

    var $form= $(ev.target);
    let formData= new FormData(ev.target);

    $form.find('[type="submit"]').prop('disabled', true);
    $form.find('.errors').addClass('hidden');

    $.ajax({ dataType: 'json', method: 'POST',
             url: $form.attr('action'),
             data: Object.fromEntries(formData)
    })
     .done(function (data) {
       if (data.paid) {
         window.location.href= "/sale/{{ @sale.uuid }}/thanks";
       } else {
         window.location.href= "/sale/{{ @sale.uuid }}/pay";
       }
     })
     .fail(function (jqXHR, textStatus, errorThrown) {
       $form.find('.errors').text(jqXHR.responseJSON.text ?
                                  jqXHR.responseJSON.text : textStatus);
       $form.find('.errors').removeClass('hidden');
       $form.find('[type="submit"]').prop('disabled', false);
     });

    return false;
  });
}

function reportPurchase() {
  try {
    <check if="{{ @sale }}">
      gtag('event', 'purchase', {
        "transaction_id": "{{ @sale.uuid }}",
        "affiliation": "Online Store",
        "value": {{ @sale.total }},
        "currency": "USD",
        "tax": {{ @sale.tax }},
        "shipping": {{ @sale.shipping }},
        "items": [
          <repeat group="{{ @items }}"
                  value="{{ @item }}" counter="{{ @index }}">
            <check if="{{ @index > 1 }}">,</check>
            {
              'id': "p{{ @item.product_id }}",
              'name': "{{ @item.product_name }}",
              'brand': "{{ @item.brand_name }}",
              'variant': "{{ @item.code }}",
              'quantity': "{{ @item.quantity }}",
              'price': "{{ @item.sale_price }}",
            }
          </repeat>
        ]
      });
    </check>
  } catch (error) {
    console.error(error)
  }
}
<commit_msg>Don't initialize PayPal if there's no button<commit_after>loadScript('https://js.stripe.com/v3/',
           function() {
  var stripe= Stripe('{{ @STRIPE_KEY }}');
  var elements= stripe.elements();
  var button= document.getElementById('stripe-button');
  var error= document.getElementById('stripe-error');

  var card= elements.create("card");
  // Stripe injects an iframe into the DOM
  card.mount("#card-element");

  card.on("change", function (event) {
    // Disable the Pay button if there are no card details in the Element
    button.disabled= event.empty;
    error.textContent= event.error ? event.error.message : ''
    if (event.error) error.classList.remove('hidden')
  });

  var form= document.getElementById("payment-form");
  form.addEventListener("submit", function(event) {
    event.preventDefault();
      return fetch('/sale/{{ @sale.uuid }}/get-stripe-payment-intent', {
        // fake AJAX header so we get JSON errors
        headers: { 'X-Requested-With': 'XMLHttpRequest' }
      })
      .then(function(res) {
        if (!res.ok) {
          return res.json().then((data) => {
            if (data.text == 'Payment already completed.') {
              window.location.href= "/sale/{{ @sale.uuid }}/thanks"
            } else {
              return Promise.reject(new Error(data.text))
            }
          })
        }
        return res.json()
      })
      .then(function(data) {
        payWithCard(stripe, card, data.secret);
      })
  });

  var payWithCard= function(stripe, card, clientSecret) {
    loading(true);
    stripe
      .confirmCardPayment(clientSecret, {
        payment_method: {
          card: card
        }
      })
      .then(function(result) {
        if (result.error) {
          // Show error to your customer
          showError(result.error.message);
        } else {
          // The payment succeeded!
          orderComplete(result.paymentIntent.id);
        }
      });
  };

  var orderComplete= function(paymentIntentId) {
    return fetch('/sale/{{ @sale.uuid }}/process-stripe-payment', {
      method: 'POST',
    }).then(function (data) {
      if (data.ok) {
        reportPurchase()
        window.location.href= "/sale/{{ @sale.uuid }}/thanks"
      }
    });
  };

  // Show the customer the error from Stripe if their card fails to charge
  var showError= function(errorMsgText) {
    loading(false);
    error.textContent= errorMsgText;
    error.classList.remove('hidden');
    setTimeout(function() {
      error.textContent= "";
      error.classList.add('hidden');
    }, 4000);
  };

  // Show a spinner on payment submission
  var loading = function(isLoading) {
    if (isLoading) {
      // Disable the button and show a spinner
      button.disabled= true;
      button.querySelector(".spin").classList.remove("hidden");
    } else {
      button.disabled= false;
      button.querySelector(".spin").classList.add("hidden");
    }
  };

});

loadScript('https://www.paypal.com/sdk/js?client-id={{ @PAYPAL_CLIENT_ID }}',
           function() {
  if (!document.getElementById('paypal-button')) return;

  paypal.Buttons({
    createOrder: function (data, actions) {
      return fetch('/sale/{{ @sale.uuid }}/get-paypal-order', {
        // fake AJAX header so we get JSON errors
        headers: { 'X-Requested-With': 'XMLHttpRequest' }
      })
      .then(function(res) {
        if (!res.ok) {
          return res.json().then((data) => {
            if (data.text == 'Payment already completed.') {
              window.location.href= "/sale/{{ @sale.uuid }}/thanks"
            } else {
              return Promise.reject(new Error(data.text))
            }
          })
        }
        return res.json()
      })
      .then(function(data) {
        return data.id
      })
    },
    onApprove: function(data, actions) {
      // Capture the funds from the transaction
      // XXX show processing...
      return actions.order.capture().then(function(details) {
        let formData= new FormData(document.getElementById('paypal-form'))
        formData.append('order_id', details.id)
        return fetch('/sale/{{ @sale.uuid }}/process-paypal-payment', {
          method: 'post',
          body: formData
        }).then(function (data) {
          if (data.ok) {
            reportPurchase()
            window.location.href= "/sale/{{ @sale.uuid }}/thanks"
          }
        });
      });
    }
  }).render('#paypal-button');
});

$("#giftcard-check").on("submit", function (ev) {
  var $form= $(ev.target);

  $form.find('[type="submit"]').prop('disabled', true);
  $form.find('.errors').addClass('hidden');

  var card= $('[name="giftcard"]', $form).val();

  $.ajax({ dataType: 'json', method: 'POST',
           url: $form.attr('action'),
           data: { card: card } })
   .done(function (data) {
     $form.addClass('hidden');
     var $use= $('#giftcard-use');
     var amount= $use.find('[name="amount"]');
     var text= "Pay $" + Math.min(amount.val(), data.balance).toFixed(2);
     $use.find('button').text(text);
     $use.find('[name="balance"]').val('$' + data.balance);
     $use.removeClass('hidden');
   })
   .fail(function (jqXHR, textStatus, errorThrown) {
     $form.find('.errors').text(jqXHR.responseJSON.text ?
                                jqXHR.responseJSON.text : textStatus);
     $form.find('.errors').removeClass('hidden');
     $form.find('[type="submit"]').prop('disabled', false);
   });

  return false;
});

$("#giftcard-use").on("submit", function (ev) {
  var $form= $(ev.target);

  $form.find('[type="submit"]').prop('disabled', true);
  $form.find('.errors').addClass('hidden');

  var card= $('#giftcard-check [name="giftcard"]').val();

  $.ajax({ dataType: 'json', method: 'POST',
           url: $form.attr('action'),
           data: { card: card }})
   .done(function (data) {
     if (data.paid) {
       window.location.href= "/sale/{{ @sale.uuid }}/thanks";
     } else {
       window.location.href= "/sale/{{ @sale.uuid }}/pay";
     }
   })
   .fail(function (jqXHR, textStatus, errorThrown) {
     $form.find('.errors').text(jqXHR.responseJSON.text ?
                                jqXHR.responseJSON.text : textStatus);
     $form.find('.errors').removeClass('hidden');
     $form.find('[type="submit"]').prop('disabled', false);
   });

  return false;
});

$("#rewards-use").on("click", function (ev) {
  var $btn= $(ev.target);

  $btn.prop('disabled', true);

  $.ajax({ dataType: 'json', method: 'POST',
           url: "/sale/{{ @sale.uuid }}/process-rewards" })
   .done(function (data) {
     if (data.paid) {
       window.location.href= "/sale/{{ @sale.uuid }}/thanks";
     } else {
       window.location.href= "/sale/{{ @sale.uuid }}/pay";
     }
   })
   .fail(function (jqXHR, textStatus, errorThrown) {
     window.alert(jqXHR.responseJSON.text ?  jqXHR.responseJSON.text : textStatus);
   });

  return false;
});

if (document.getElementById('other-use')) {
  document.getElementById('other-use').addEventListener('submit', (ev) => {
    ev.preventDefault()

    var $form= $(ev.target);
    let formData= new FormData(ev.target);

    $form.find('[type="submit"]').prop('disabled', true);
    $form.find('.errors').addClass('hidden');

    $.ajax({ dataType: 'json', method: 'POST',
             url: $form.attr('action'),
             data: Object.fromEntries(formData)
    })
     .done(function (data) {
       if (data.paid) {
         window.location.href= "/sale/{{ @sale.uuid }}/thanks";
       } else {
         window.location.href= "/sale/{{ @sale.uuid }}/pay";
       }
     })
     .fail(function (jqXHR, textStatus, errorThrown) {
       $form.find('.errors').text(jqXHR.responseJSON.text ?
                                  jqXHR.responseJSON.text : textStatus);
       $form.find('.errors').removeClass('hidden');
       $form.find('[type="submit"]').prop('disabled', false);
     });

    return false;
  });
}

function reportPurchase() {
  try {
    <check if="{{ @sale }}">
      gtag('event', 'purchase', {
        "transaction_id": "{{ @sale.uuid }}",
        "affiliation": "Online Store",
        "value": {{ @sale.total }},
        "currency": "USD",
        "tax": {{ @sale.tax }},
        "shipping": {{ @sale.shipping }},
        "items": [
          <repeat group="{{ @items }}"
                  value="{{ @item }}" counter="{{ @index }}">
            <check if="{{ @index > 1 }}">,</check>
            {
              'id': "p{{ @item.product_id }}",
              'name': "{{ @item.product_name }}",
              'brand': "{{ @item.brand_name }}",
              'variant': "{{ @item.code }}",
              'quantity': "{{ @item.quantity }}",
              'price': "{{ @item.sale_price }}",
            }
          </repeat>
        ]
      });
    </check>
  } catch (error) {
    console.error(error)
  }
}
<|endoftext|>