<commit_before>/**
 * @description
The VML engine implementation.

 *
 * @namespace imageCreator.engine
 * @name vml
 * @version 1.0
 * @author mbaijs
 */
define(
[
    // App core modules
    //
    "config"
,   "cache"
],
function( config, cache )
{
    var module =
        {
            name : "vml"
        }

    ,   $imageCreatorViewport
    ,   $imageCreatorCanvas

    ,   canvasWidth
    ,   canvasHeight

    //,   vmlSelect
    ;

    module.initialize = function()
    {
        // Get basic app DOM elements.
        //
        $imageCreatorViewport = $( ".imageCreatorViewport" );
        $imageCreatorCanvas   = $( ".imageCreatorCanvas" );

        // Set the viewport's dimensions.
        //
        canvasWidth  = config.options.viewport.width;
        canvasHeight = config.options.viewport.height;

        $imageCreatorCanvas.css( { width : canvasWidth, height : canvasHeight } );

        // Initialize VML.
        //
        document.createStyleSheet().cssText = 'rvml\\:* { behavior:url(#default#VML); }';

        if( "object" === typeof document.namespaces )
        {
            document.namespaces.add('rvml', 'urn:schemas-microsoft-com:vml', '#default#VML');
        }

        // Create a selection rectangle to put around selected layers.
        //
        var vmlSelect = $( "<rvml:rect id='vmlSelect' strokecolor='#000000' strokeweight='1px'><rvml:stroke dashstyle='dash'></rvml:stroke></rvml:rect>" )[0];
        $imageCreatorCanvas.html( vmlSelect );

        // Remove other engines that may be listening.
        //
        $.unsubscribe( ".engine" );

        // Listen to global app events.
        //
        $.subscribe( "layerUpdate.engine", vmlLayerCheck );
        $.subscribe( "layerSelect.engine", vmlLayerCheck );
        $.subscribe( "layerVisibility.engine", vmlLayerVisibility );
        $.subscribe( "layerRemove.engine", vmlLayerRemove );
        $.subscribe( "layersRedraw.engine", vmlBuildLayers );

        // Do we have any layers allready?
        //
        vmlBuildLayers();
    };

    function vmlBuildLayers()
    {
        $imageCreatorCanvas.find( ".vmlObject" ).remove();

        $.each( cache.getLayers(), vmlLayerCheck );
    }

    function vmlLayerCheck( event, layer, partial )
    {
        if( layer )
        {
            // If we dont have a layer in the dom its new so create it.
            //
            if( ! $( "#" + layer.id + module.name )[0] )
            {
                vmlLayerCreate( event, layer );
            }

            // Update layer properties
            //
            vmlLayerUpdate( event, layer, partial );
        }

        // Set the selection rectangle around the current layer or hide it.
        //
        vmlLayerSelect( event, layer );
    }

    function vmlLayerCreate( event, layer )
    {
        var vmlLayerCurrent;

        if( "image" === layer.type )
        {
            vmlLayerCurrent = document.createElement( "img" );
            vmlLayerCurrent.setAttribute( "src", layer.image.src );

            vmlLayerCurrent.style.setAttribute( "height", layer.sizeReal.height + "px" );
            vmlLayerCurrent.style.setAttribute( "width", layer.sizeReal.width + "px" );
        }

        if( "text" === layer.type )
        {
            vmlLayerCurrent = document.createElement( "div" );
            vmlLayerCurrent.style.setAttribute( 'width', layer.sizeCurrent.width + "px" );

            vmlLayerCurrent.appendChild( document.createElement( "p" ) );
        }

        vmlLayerCurrent.setAttribute( "id", layer.id + module.name );
        vmlLayerCurrent.setAttribute( "class", "vmlObject" );

        vmlLayerCurrent.style.setAttribute( "zoom", "1" );
        vmlLayerCurrent.style.setAttribute( "position", "absolute" );

        // Append new layer to DOM and reappend the selection layer so its always on top.
        //
        $imageCreatorCanvas.append( vmlSelect );
        $imageCreatorCanvas.append( vmlLayerCurrent );
    }

    function vmlLayerUpdate( event, layer, partial )
    {
        var vmlLayerCurrent = $( "#" + layer.id + module.name )[0];

        // Set type specific attributes.
        //
        if( "text" === layer.type && ! partial )
        {
            var htmlParagraphCurrent = $( vmlLayerCurrent ).find( "p" )[0];

            htmlParagraphCurrent.innerText        = layer.text;
            htmlParagraphCurrent.style.color      = layer.color;
            htmlParagraphCurrent.style.fontSize   = layer.fontSize + "px";
            htmlParagraphCurrent.style.fontFamily = layer.font;
            htmlParagraphCurrent.style.fontWeight = layer.weight ? "bold" : "normal";
            htmlParagraphCurrent.style.fontStyle  = layer.style ? "italic" : "normal";
            htmlParagraphCurrent.style.lineHeight = Math.floor( layer.fontSize * layer.lineHeight ) + "px";

            vmlLayerCurrent.style.setAttribute( 'height', layer.sizeCurrent.height + "px" );
            vmlLayerCurrent.style.setAttribute( 'width', layer.sizeCurrent.width + "px" );
        }

        // MS Filters suck.. So lets use them only if we really have too.
        //
        if( layer.type !== "text" || layer.rotation.degrees > 0 && layer.rotation.degrees < 360)
        {
            vmlLayerCurrent.style.setAttribute( "filter", 'progid:DXImageTransform.Microsoft.Matrix(' + 'M11=' + layer.matrix[ 0 ] + ', M12=' + layer.matrix[ 1 ] + ', M21=' + layer.matrix[ 3 ] + ', M22=' + layer.matrix[ 4 ] + ', sizingMethod=\'auto expand\'' + ')' );
        }
        else
        {
            vmlLayerCurrent.style.removeAttribute( "filter" );
        }

        // Since we unfortunately do not have the possibility to use translate with sizing method 'auto expand', we need to do
        // something hacky to work around the lack of support for the transform-origin property.
        //
        var originCorrection = ieCorrectOrigin( layer.sizeCurrent, layer.rotation.radians );

        vmlLayerCurrent.style.setAttribute( "top",  layer.position.y + originCorrection.y + "px" );
        vmlLayerCurrent.style.setAttribute( "left", layer.position.x + originCorrection.x + "px" );
    }

    function vmlLayerSelect( event, layer )
    {
        // If we have a layer change selection properties to match its dimensions.
        //
        if( layer && layer.selected )
        {
            vmlSelect.style.setAttribute( "rotation", layer.rotation.degrees );
            vmlSelect.style.setAttribute( "height", ( layer.sizeCurrent.height + 1 ) + "px" );
            vmlSelect.style.setAttribute( "width",  ( layer.sizeCurrent.width  + 1 )+ "px" );
            vmlSelect.style.setAttribute( "position", "absolute" );
            vmlSelect.style.setAttribute( "top",  ( layer.position.y - 1 ) + "px" );
            vmlSelect.style.setAttribute( "left", ( layer.position.x - 1 ) + "px" );

            // It is no longer possible to create/append a VML element outside of the DOM in >= ie8. This hack fixes that.
            //
            if( document.all && document.querySelector )
            {
                vmlSelect.outerHTML = vmlSelect.outerHTML;
            }

            // Somehow setting the fillcolor only works after we have done the hack above :/
            //
            vmlSelect.fillcolor = "none";
        }

        // If we have no layer to select or if its hidden hide the selection rectangle as well.
        //
        if( event.type !== "layerUpdate" )
        {
            vmlSelect.style.setAttribute( 'visibility', layer.visible ? 'visible' : 'hidden' );
        }
    }

    function vmlLayerVisibility( event, layer )
    {
        $( "#" + layer.id + module.name ).css( 'visibility', layer.visible ? 'visible' : 'hidden' );

        // We only want to hide the selection layer if its around the currently selected layer.
        //
        if( layer.selected )
        {
            vmlSelect.style.setAttribute( 'visibility', layer.visible ? 'visible' : 'hidden' );
        }
    }

    function vmlLayerRemove( event, layerID )
    {
        $( "#" + layerID + module.name ).remove();
    }

    // http://balzerg.blogspot.co.il/2012/08/analytical-fix-for-ie-rotation-origin.html
    //
    function ieCorrectOrigin( size, radians )
    {
        var rad = radians %= Math.PI;

        if( rad > Math.PI / 2 )
        {
            rad = Math.PI - rad;
        }

        var sin = Math.sin( -rad )
        ,   cos = Math.cos(  rad )
        ;

        return {
            y : ( size.height - size.height * cos + size.width  * sin ) / 2
        ,   x : ( size.width  - size.width  * cos + size.height * sin ) / 2
        };
    }

    return module;
} );<commit_msg>Engine VML: The selection now only show itself when a layer is really selected. I also changed some single quotes to double quotes.<commit_after>/**
 * @description
The VML engine implementation.

 *
 * @namespace imageCreator.engine
 * @name vml
 * @version 1.0
 * @author mbaijs
 */
define(
[
    // App core modules
    //
    "config"
,   "cache"
],
function( config, cache )
{
    var module =
        {
            name : "vml"
        }

    ,   $imageCreatorViewport
    ,   $imageCreatorCanvas

    ,   canvasWidth
    ,   canvasHeight

    //,   vmlSelect
    ;

    module.initialize = function()
    {
        // Get basic app DOM elements.
        //
        $imageCreatorViewport = $( ".imageCreatorViewport" );
        $imageCreatorCanvas   = $( ".imageCreatorCanvas" );

        // Set the viewport's dimensions.
        //
        canvasWidth  = config.options.viewport.width;
        canvasHeight = config.options.viewport.height;

        $imageCreatorCanvas.css( { width : canvasWidth, height : canvasHeight } );

        // Initialize VML.
        //
        document.createStyleSheet().cssText = 'rvml\\:* { behavior:url(#default#VML); }';

        if( "object" === typeof document.namespaces )
        {
            document.namespaces.add('rvml', 'urn:schemas-microsoft-com:vml', '#default#VML');
        }

        // Create a selection rectangle to put around selected layers.
        //
        var vmlSelect = $( "<rvml:rect id='vmlSelect' strokecolor='#000000' strokeweight='1px'><rvml:stroke dashstyle='dash'></rvml:stroke></rvml:rect>" )[0];
        $imageCreatorCanvas.html( vmlSelect );

        // Remove other engines that may be listening.
        //
        $.unsubscribe( ".engine" );

        // Listen to global app events.
        //
        $.subscribe( "layerUpdate.engine", vmlLayerCheck );
        $.subscribe( "layerSelect.engine", vmlLayerCheck );
        $.subscribe( "layerVisibility.engine", vmlLayerVisibility );
        $.subscribe( "layerRemove.engine", vmlLayerRemove );
        $.subscribe( "layersRedraw.engine", vmlBuildLayers );

        // Do we have any layers allready?
        //
        vmlBuildLayers();
    };

    function vmlBuildLayers()
    {
        $imageCreatorCanvas.find( ".vmlObject" ).remove();

        $.each( cache.getLayers(), vmlLayerCheck );
    }

    function vmlLayerCheck( event, layer, partial )
    {
        if( layer )
        {
            // If we dont have a layer in the dom its new so create it.
            //
            if( ! $( "#" + layer.id + module.name )[0] )
            {
                vmlLayerCreate( event, layer );
            }

            // Update layer properties
            //
            vmlLayerUpdate( event, layer, partial );
        }

        // Set the selection rectangle around the current layer or hide it.
        //
        vmlLayerSelect( event, layer );
    }

    function vmlLayerCreate( event, layer )
    {
        var vmlLayerCurrent;

        if( "image" === layer.type )
        {
            vmlLayerCurrent = document.createElement( "img" );
            vmlLayerCurrent.setAttribute( "src", layer.image.src );

            vmlLayerCurrent.style.setAttribute( "height", layer.sizeReal.height + "px" );
            vmlLayerCurrent.style.setAttribute( "width", layer.sizeReal.width + "px" );
        }

        if( "text" === layer.type )
        {
            vmlLayerCurrent = document.createElement( "div" );
            vmlLayerCurrent.style.setAttribute( 'width', layer.sizeCurrent.width + "px" );

            vmlLayerCurrent.appendChild( document.createElement( "p" ) );
        }

        vmlLayerCurrent.setAttribute( "id", layer.id + module.name );
        vmlLayerCurrent.setAttribute( "class", "vmlObject" );

        vmlLayerCurrent.style.setAttribute( "zoom", "1" );
        vmlLayerCurrent.style.setAttribute( "position", "absolute" );

        // Append new layer to DOM and reappend the selection layer so its always on top.
        //
        $imageCreatorCanvas.append( vmlSelect );
        $imageCreatorCanvas.append( vmlLayerCurrent );
    }

    function vmlLayerUpdate( event, layer, partial )
    {
        var vmlLayerCurrent = $( "#" + layer.id + module.name )[0];

        // Set type specific attributes.
        //
        if( "text" === layer.type && ! partial )
        {
            var htmlParagraphCurrent = $( vmlLayerCurrent ).find( "p" )[0];

            htmlParagraphCurrent.innerText        = layer.text;
            htmlParagraphCurrent.style.color      = layer.color;
            htmlParagraphCurrent.style.fontSize   = layer.fontSize + "px";
            htmlParagraphCurrent.style.fontFamily = layer.font;
            htmlParagraphCurrent.style.fontWeight = layer.weight ? "bold" : "normal";
            htmlParagraphCurrent.style.fontStyle  = layer.style ? "italic" : "normal";
            htmlParagraphCurrent.style.lineHeight = Math.floor( layer.fontSize * layer.lineHeight ) + "px";

            vmlLayerCurrent.style.setAttribute( 'height', layer.sizeCurrent.height + "px" );
            vmlLayerCurrent.style.setAttribute( 'width', layer.sizeCurrent.width + "px" );
        }

        // MS Filters suck.. So lets use them only if we really have too.
        //
        if( layer.type !== "text" || layer.rotation.degrees > 0 && layer.rotation.degrees < 360)
        {
            vmlLayerCurrent.style.setAttribute( "filter", 'progid:DXImageTransform.Microsoft.Matrix(' + 'M11=' + layer.matrix[ 0 ] + ', M12=' + layer.matrix[ 1 ] + ', M21=' + layer.matrix[ 3 ] + ', M22=' + layer.matrix[ 4 ] + ', sizingMethod=\'auto expand\'' + ')' );
        }
        else
        {
            vmlLayerCurrent.style.removeAttribute( "filter" );
        }

        // Since we unfortunately do not have the possibility to use translate with sizing method 'auto expand', we need to do
        // something hacky to work around the lack of support for the transform-origin property.
        //
        var originCorrection = ieCorrectOrigin( layer.sizeCurrent, layer.rotation.radians );

        vmlLayerCurrent.style.setAttribute( "top",  layer.position.y + originCorrection.y + "px" );
        vmlLayerCurrent.style.setAttribute( "left", layer.position.x + originCorrection.x + "px" );
    }

    function vmlLayerSelect( event, layer )
    {
        // If we have a layer change selection properties to match its dimensions.
        //
        if( layer && layer.selected )
        {
            vmlSelect.style.setAttribute( "rotation", layer.rotation.degrees );
            vmlSelect.style.setAttribute( "height", ( layer.sizeCurrent.height + 1 ) + "px" );
            vmlSelect.style.setAttribute( "width",  ( layer.sizeCurrent.width  + 1 )+ "px" );
            vmlSelect.style.setAttribute( "position", "absolute" );
            vmlSelect.style.setAttribute( "top",  ( layer.position.y - 1 ) + "px" );
            vmlSelect.style.setAttribute( "left", ( layer.position.x - 1 ) + "px" );

            // It is no longer possible to create/append a VML element outside of the DOM in >= ie8. This hack fixes that.
            //
            if( document.all && document.querySelector )
            {
                vmlSelect.outerHTML = vmlSelect.outerHTML;
            }

            // Somehow setting the fillcolor only works after we have done the hack above :/
            //
            vmlSelect.fillcolor = "none";
        }

        // If we have no layer to select or if its hidden hide the selection rectangle as well.
        //
        if( event.type !== "layerUpdate" )
        {
            vmlSelect.style.setAttribute( "visibility", layer.visible && layer.selected ? "visible" : "hidden" );
        }
    }

    function vmlLayerVisibility( event, layer )
    {
        $( "#" + layer.id + module.name ).css( "visibility", layer.visible ? "visible" : "hidden" );

        // We only want to hide the selection layer if its around the currently selected layer.
        //
        if( layer.selected )
        {
            vmlSelect.style.setAttribute( 'visibility', layer.visible ? 'visible' : 'hidden' );
        }
    }

    function vmlLayerRemove( event, layerID )
    {
        $( "#" + layerID + module.name ).remove();
    }

    // http://balzerg.blogspot.co.il/2012/08/analytical-fix-for-ie-rotation-origin.html
    //
    function ieCorrectOrigin( size, radians )
    {
        var rad = radians %= Math.PI;

        if( rad > Math.PI / 2 )
        {
            rad = Math.PI - rad;
        }

        var sin = Math.sin( -rad )
        ,   cos = Math.cos(  rad )
        ;

        return {
            y : ( size.height - size.height * cos + size.width  * sin ) / 2
        ,   x : ( size.width  - size.width  * cos + size.height * sin ) / 2
        };
    }

    return module;
} );<|endoftext|>


<commit_before>var Tree = (function () {

  /**
   * Format a number to be displayed with abbreviated suffixes.
   * This function is more complicated than one would think it need be,
   * this is due to lack of predictability in Number.toPrecision, Number.toFixed
   * and some rounding issues.
   */
  var formatNumericLabel = function(value) {
    if (value === 0) return "0";

    var magnitudes = {
      million:  {value: 1e6, suffix:"m"},
      thousand: {value: 1e3, suffix:"k"},
      unit:     {value: 1, suffix:""}
    };

    var magnitude = function(num, n) {
          return Math.pow(10, n - Math.ceil(Math.log(Math.abs(num)) / Math.LN10));
        },
        roundToSignificantFigures = function(num, n) {
          return Math.round(num * magnitude(num, n)) / magnitude(num, n);
        },
        thresholds = [ magnitudes.million, magnitudes.thousand ],
        roundedValue = roundToSignificantFigures(value, 3),
        significantFigures = null;

    for (var i = 0; i < thresholds.length; i++) {
      if (roundedValue >= (thresholds[i].value / 2)) {
        if (roundedValue < thresholds[i].value) {
          significantFigures = 2;
        } else {
          significantFigures = 3;
          value = roundedValue;
        }
        value = roundToSignificantFigures(value, significantFigures) / thresholds[i].value;
        return value.toPrecision(value < 1 ? 2 : 3) + thresholds[i].suffix;
      }
    }
    return roundedValue.toString();
  };

  // http://stackoverflow.com/questions/2901102/how-to-print-a-number-with-commas-as-thousands-separators-in-javascript
  var numberWithCommas = function(x) {
    return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
  };

  var hasValue;

  var valuesFrom = function(selection) {
    return selection[0].map(function (row) {
      var volume = parseInt(row.getAttribute("data-volume"), 10);
        volume = isNaN(volume) ? 0 : volume;
      return {
        name: row.getAttribute("data-title"),
        size: volume,
        volumeShortened: formatNumericLabel(volume),
        volumeLabel: row.getAttribute("data-volumelabel"),
        url: row.getAttribute("data-href"),
        color: row.getAttribute("data-color"),
        textColor: row.getAttribute('data-text-color'),
        cost: row.getAttribute('data-cost'),
        deptClass: row.getAttribute('data-dept-class')
      };
    });
  };

  var max = function (memo, v) {
    return (memo.size > v.size) ? memo : v;
  };

  var sum = function (memo, v) {
    memo.size += v.size;
    return memo;
  };

  var partition = function(col, partitionFunction) {
    return col.reduce(function (memo, v) {
      if (partitionFunction(v)) {
        memo.left.push(v)
      } else {
        memo.right.push(v);
      }
      return memo;
    }, { left: [], right: [] });
  };

  var condenseValuesUnderThreshold = function(values, thresholdRatio) {
    if (thresholdRatio == null) {
      return values;
    }
    var sumVals = d3.sum(values,function(val){
      return val.size;
    });

    var threshold = sumVals / thresholdRatio;
    var splitValues = partition(values, function (v) {
      return (v.size > threshold || v.url); });
    var children = splitValues.left;
    if (splitValues.right.length) {
      var otherValue = splitValues.right.reduce(sum);
      children.push({
        name: "Others",
        size: otherValue.size,
        volumeLabel : numberWithCommas(otherValue.size),
        cost : otherValue.cost

      });
    }
    return children;
  };

  return {
    formatNumericLabel: formatNumericLabel,
    numberWithCommas: numberWithCommas,
    fromHtmlTable: function(selection, thresholdRatio) {
      var values = valuesFrom(selection);

      return {
        name: "Service Explorer",
        children: condenseValuesUnderThreshold(values, thresholdRatio)
      };
    }
  }

}());

var TreeMapLayout = (function () {
  var position = function() {
      this.style("left", function(d) { return d.x + 1 + "px"; })
      .style("top", function(d) { return d.y + 1 + "px"; })
      .style("width", function(d) { return Math.max(0, d.dx - 1) + "px"; })
      .style("height", function(d) { return Math.max(0, d.dy - 1) + "px"; })
      .style('position','absolute')
      .style("cursor", function(d) { return d.url ? "pointer" : ""; });
  };

  var getNodeClass = function (d) {
    var classes = ["none", "ellipsis", "small", "medium", "large", "x-large", "xx-large"],
        keys    = [0     , 1         , 2      , 3       , 4      , 5        , 6],
        dxIndex = d3.scale.threshold().domain([20,50,130,200,250,400]).range(keys),
        dyIndex = d3.scale.threshold().domain([10,40,100,150,200,400]).range(keys),
        type    = d.children ? "group" : "leaf";

    var nClass = ['node', classes[Math.min(dxIndex(d.dx), dyIndex(d.dy))], type].join(' ');
    if(d.deptClass){
      // currently using dashes to replace dept name spaces e.g. 'Home Office' becomes 'home-office'
      nClass += ' ' + d.deptClass.replace(/\s+/g, '-').toLowerCase();
    }
    return nClass;
  };

  var setNodeContent = function (d) {
    // switch to position='static' to enable measurement of true height
    this.style.position = 'static';
    var availableHeight = this.parentNode.clientHeight,
        actualHeight = Infinity,
        name = d.name,
        text,
        node = d3.select(this);

    var volumeSpan = '';
    if (d.volumeShortened) {
      volumeSpan = '' + d.volumeShortened + '';
    }

    // Check if trimming of name could yield a good result
    this.style['white-space'] = 'nowrap';
    text = name + volumeSpan;
    this.innerHTML = text;
    actualHeight = this.offsetHeight;
    this.style['white-space'] = 'normal';
    if (actualHeight > availableHeight) {
      // Content does not fit, even when name is on single line.
      // Simply display volume.
      this.innerHTML = volumeSpan;
      return;
    }

    // Shorten name until everything fits
    actualHeight = Infinity;
    while (actualHeight > availableHeight) {
      text = name;
      if (name.length > 0 && name.length !== d.name.length) {
        text += '…';
      }
      text += volumeSpan;
      this.innerHTML = text;

      if (name.length <= 0) {
        break;
      }
      actualHeight = this.offsetHeight;
      name = name.substring(0, name.length - 4);
    }

    // reset to position='absolute' to make link display across whole node area
    this.style.position = 'absolute';
  };

  var createTip = function(d){
    var tip = null;
    if(d && d.volumeLabel){
      tip = d.name + ': ' + d.volumeLabel + ' transactions per year';
    }
    if(d.cost){
      tip += ' (total cost: ' + d.cost + ')';
    }
    return tip;
  };

  var makeTree = function (divId, treeData, options) {
    var options = options || {},
        el = document.getElementById(divId);

    if (!el) {
      // Wrapper element not found
      return;
    }

    var width = options.width || el.offsetWidth,
        height = options.height || el.offsetHeight;

    // Clean up wrapper element before populating
    el.innerHTML = '';

    var color = d3.scale.category20c();

    var treemap = d3.layout.treemap()
        .size([width, height])
        .value(function(d) { return d.size; })
        .sort(function(a, b) {
          return a.value - b.value;
        });

    var node = d3.select('#'+divId).datum(treeData).selectAll(".node")
      .data(treemap.nodes)
      .enter().append("div")
      .attr("class", getNodeClass)
      .attr('data-tooltip', createTip)
      .call(position)
      .append("a")
        .attr('href',function(d){ return d.url ? d.url : null })
        .style("color", function(d) { return d.textColor ? d.textColor : null; })
        .call(function (selection) {
          selection
            .filter(function (d) {
              return !d.children;
            })
            .each(setNodeContent);
        });

    var rsplit = function (str, matchThis) {
        var length = str.length, whereToSplit = undefined;
        for (var i = length; i > 0; i--) {
            if (str[i] === matchThis) {
                whereToSplit = i;
                break;
            }
        }
        return [
            str.slice(0,whereToSplit + 1),
            str.slice(whereToSplit + 1, length)
        ];
    };

    if (window.$) {
      var $figure = $('#' + divId);
      var $cap = $('
').appendTo($figure);

      // hide the first wrapping node
      $figure.find('.node').first().hide();

      $figure.find('.node').on('mouseenter',function(){
        var $this = $(this),
            bg = $this.css('background-color'),
            tooltipText = $this.data('tooltip'),
            serviceDetails = rsplit(tooltipText, ':');
        $cap.html('' + serviceDetails[0] + '' + serviceDetails[1] + '');
        $('').css('background-color',bg).prependTo($cap);
      });
      $figure.on('mouseleave', function () {
        $cap.empty();
      });
    }

  };

  return {
    display: makeTree
  }
}());
<commit_msg>Remove total cost from all services page<commit_after>var Tree = (function () {

  /**
   * Format a number to be displayed with abbreviated suffixes.
   * This function is more complicated than one would think it need be,
   * this is due to lack of predictability in Number.toPrecision, Number.toFixed
   * and some rounding issues.
   */
  var formatNumericLabel = function(value) {
    if (value === 0) return "0";

    var magnitudes = {
      million:  {value: 1e6, suffix:"m"},
      thousand: {value: 1e3, suffix:"k"},
      unit:     {value: 1, suffix:""}
    };

    var magnitude = function(num, n) {
          return Math.pow(10, n - Math.ceil(Math.log(Math.abs(num)) / Math.LN10));
        },
        roundToSignificantFigures = function(num, n) {
          return Math.round(num * magnitude(num, n)) / magnitude(num, n);
        },
        thresholds = [ magnitudes.million, magnitudes.thousand ],
        roundedValue = roundToSignificantFigures(value, 3),
        significantFigures = null;

    for (var i = 0; i < thresholds.length; i++) {
      if (roundedValue >= (thresholds[i].value / 2)) {
        if (roundedValue < thresholds[i].value) {
          significantFigures = 2;
        } else {
          significantFigures = 3;
          value = roundedValue;
        }
        value = roundToSignificantFigures(value, significantFigures) / thresholds[i].value;
        return value.toPrecision(value < 1 ? 2 : 3) + thresholds[i].suffix;
      }
    }
    return roundedValue.toString();
  };

  // http://stackoverflow.com/questions/2901102/how-to-print-a-number-with-commas-as-thousands-separators-in-javascript
  var numberWithCommas = function(x) {
    return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
  };

  var hasValue;

  var valuesFrom = function(selection) {
    return selection[0].map(function (row) {
      var volume = parseInt(row.getAttribute("data-volume"), 10);
        volume = isNaN(volume) ? 0 : volume;
      return {
        name: row.getAttribute("data-title"),
        size: volume,
        volumeShortened: formatNumericLabel(volume),
        volumeLabel: row.getAttribute("data-volumelabel"),
        url: row.getAttribute("data-href"),
        color: row.getAttribute("data-color"),
        textColor: row.getAttribute('data-text-color'),
        cost: row.getAttribute('data-cost'),
        deptClass: row.getAttribute('data-dept-class')
      };
    });
  };

  var max = function (memo, v) {
    return (memo.size > v.size) ? memo : v;
  };

  var sum = function (memo, v) {
    memo.size += v.size;
    return memo;
  };

  var partition = function(col, partitionFunction) {
    return col.reduce(function (memo, v) {
      if (partitionFunction(v)) {
        memo.left.push(v)
      } else {
        memo.right.push(v);
      }
      return memo;
    }, { left: [], right: [] });
  };

  var condenseValuesUnderThreshold = function(values, thresholdRatio) {
    if (thresholdRatio == null) {
      return values;
    }
    var sumVals = d3.sum(values,function(val){
      return val.size;
    });

    var threshold = sumVals / thresholdRatio;
    var splitValues = partition(values, function (v) {
      return (v.size > threshold || v.url); });
    var children = splitValues.left;
    if (splitValues.right.length) {
      var otherValue = splitValues.right.reduce(sum);
      children.push({
        name: "Others",
        size: otherValue.size,
        volumeLabel : numberWithCommas(otherValue.size),
        cost : otherValue.cost

      });
    }
    return children;
  };

  return {
    formatNumericLabel: formatNumericLabel,
    numberWithCommas: numberWithCommas,
    fromHtmlTable: function(selection, thresholdRatio) {
      var values = valuesFrom(selection);

      return {
        name: "Service Explorer",
        children: condenseValuesUnderThreshold(values, thresholdRatio)
      };
    }
  }

}());

var TreeMapLayout = (function () {
  var position = function() {
      this.style("left", function(d) { return d.x + 1 + "px"; })
      .style("top", function(d) { return d.y + 1 + "px"; })
      .style("width", function(d) { return Math.max(0, d.dx - 1) + "px"; })
      .style("height", function(d) { return Math.max(0, d.dy - 1) + "px"; })
      .style('position','absolute')
      .style("cursor", function(d) { return d.url ? "pointer" : ""; });
  };

  var getNodeClass = function (d) {
    var classes = ["none", "ellipsis", "small", "medium", "large", "x-large", "xx-large"],
        keys    = [0     , 1         , 2      , 3       , 4      , 5        , 6],
        dxIndex = d3.scale.threshold().domain([20,50,130,200,250,400]).range(keys),
        dyIndex = d3.scale.threshold().domain([10,40,100,150,200,400]).range(keys),
        type    = d.children ? "group" : "leaf";

    var nClass = ['node', classes[Math.min(dxIndex(d.dx), dyIndex(d.dy))], type].join(' ');
    if(d.deptClass){
      // currently using dashes to replace dept name spaces e.g. 'Home Office' becomes 'home-office'
      nClass += ' ' + d.deptClass.replace(/\s+/g, '-').toLowerCase();
    }
    return nClass;
  };

  var setNodeContent = function (d) {
    // switch to position='static' to enable measurement of true height
    this.style.position = 'static';
    var availableHeight = this.parentNode.clientHeight,
        actualHeight = Infinity,
        name = d.name,
        text,
        node = d3.select(this);

    var volumeSpan = '';
    if (d.volumeShortened) {
      volumeSpan = '' + d.volumeShortened + '';
    }

    // Check if trimming of name could yield a good result
    this.style['white-space'] = 'nowrap';
    text = name + volumeSpan;
    this.innerHTML = text;
    actualHeight = this.offsetHeight;
    this.style['white-space'] = 'normal';
    if (actualHeight > availableHeight) {
      // Content does not fit, even when name is on single line.
      // Simply display volume.
      this.innerHTML = volumeSpan;
      return;
    }

    // Shorten name until everything fits
    actualHeight = Infinity;
    while (actualHeight > availableHeight) {
      text = name;
      if (name.length > 0 && name.length !== d.name.length) {
        text += '…';
      }
      text += volumeSpan;
      this.innerHTML = text;

      if (name.length <= 0) {
        break;
      }
      actualHeight = this.offsetHeight;
      name = name.substring(0, name.length - 4);
    }

    // reset to position='absolute' to make link display across whole node area
    this.style.position = 'absolute';
  };

  var createTip = function(d){
    var tip = null;
    if(d && d.volumeLabel){
      tip = d.name + ': ' + d.volumeLabel + ' transactions per year';
    }
    return tip;
  };

  var makeTree = function (divId, treeData, options) {
    var options = options || {},
        el = document.getElementById(divId);

    if (!el) {
      // Wrapper element not found
      return;
    }

    var width = options.width || el.offsetWidth,
        height = options.height || el.offsetHeight;

    // Clean up wrapper element before populating
    el.innerHTML = '';

    var color = d3.scale.category20c();

    var treemap = d3.layout.treemap()
        .size([width, height])
        .value(function(d) { return d.size; })
        .sort(function(a, b) {
          return a.value - b.value;
        });

    var node = d3.select('#'+divId).datum(treeData).selectAll(".node")
      .data(treemap.nodes)
      .enter().append("div")
      .attr("class", getNodeClass)
      .attr('data-tooltip', createTip)
      .call(position)
      .append("a")
        .attr('href',function(d){ return d.url ? d.url : null })
        .style("color", function(d) { return d.textColor ? d.textColor : null; })
        .call(function (selection) {
          selection
            .filter(function (d) {
              return !d.children;
            })
            .each(setNodeContent);
        });

    var rsplit = function (str, matchThis) {
        var length = str.length, whereToSplit = undefined;
        for (var i = length; i > 0; i--) {
            if (str[i] === matchThis) {
                whereToSplit = i;
                break;
            }
        }
        return [
            str.slice(0,whereToSplit + 1),
            str.slice(whereToSplit + 1, length)
        ];
    };

    if (window.$) {
      var $figure = $('#' + divId);
      var $cap = $('
').appendTo($figure);

      // hide the first wrapping node
      $figure.find('.node').first().hide();

      $figure.find('.node').on('mouseenter',function(){
        var $this = $(this),
            bg = $this.css('background-color'),
            tooltipText = $this.data('tooltip'),
            serviceDetails = rsplit(tooltipText, ':');
        $cap.html('' + serviceDetails[0] + '' + serviceDetails[1] + '');
        $('').css('background-color',bg).prependTo($cap);
      });
      $figure.on('mouseleave', function () {
        $cap.empty();
      });
    }

  };

  return {
    display: makeTree
  }
}());
<|endoftext|>


<commit_before>import Emitter from 'events';
import AWS from 'aws-sdk';
import Consumer from 'sqs-consumer';
import Producer from 'sqs-producer';

export default class SqsBrokerFacade {

  constructor(options) {

    this._options = options;

    this._sqs = new AWS.SQS({
      region: options.region,
      accessKeyId: options.accessKeyId,
      secretAccessKey: options.secretAccessKey
    });

    this._responseEmitter = new Emitter();

  }

  _ensureQueue(name) {

    return new Promise((resolve, reject) => {

      var params = {
        QueueName: name
      };

      this._sqs.createQueue(params, function(err, data) {
        if (err) return reject(err);
        resolve(data.QueueUrl);
      });

    });

  }

  async _setupRequestProducer(domain, lane) {

    let queueUrl = await this._ensureQueue(`${domain}-${lane}-req`);

    let producer = Producer.create({
      sqs: this._sqs,
      queueUrl: queueUrl
    });

    return producer;

  }

  _processResponseMessage(message, done) {

    let response = {
      requestUid: message.MessageAttributes.requestUid.StringValue,
      payload: JSON.parse(message.Body),
      acknowledge: done
    };

    this._responseEmitter.emit(response.requestUid, response);

  }

  async _setupResponseConsumer(domain, lane) {

    let queueUrl = await this._ensureQueue(`${domain}-${lane}-res`);

    let consumer = Consumer.create({
      sqs: this._sqs,
      queueUrl: queueUrl,
      batchSize: 10,
      messageAttributeNames: ['All'],
      handleMessage: (message, done) => { this._processResponseMessage(message, done); }
    });

    consumer.on('error', function (err) {
      console.log(err.message);
    });

    return consumer;

  }

  async setup() {

    this._requestProducer = await this._setupRequestProducer(this._options.serviceDomain, this._options.serviceLane);

    this._responseConsumer = await this._setupResponseConsumer(this._options.clientDomain, this._options.clientLane);

  }

  start() {

    this._responseConsumer.start();

  }

  stop() {

    this._responseConsumer.stop();

  }

  enqueueRequest(request) {

    return new Promise((resolve, reject) => {

      let message = {
        id: request.uid,
        body: JSON.stringify(request.payload),
        messageAttributes: {
          reefDialect: { DataType: 'String', StringValue: request.reefDialect },
          requestUid: { DataType: 'String', StringValue: request.uid },
          queryType: { DataType: 'String', StringValue: request.queryType }
        }
      };

      this._requestProducer.send([message], function(err) {
        if (err) reject(err);
        resolve();
      });

    });

  }

  expectResponse(uid, timeout) {

    return new Promise((resolve, reject) => {

      this._responseEmitter.once(uid, (data) => {

        resolve(data);

      });

      setTimeout(() => reject(new Error('Response timeout')), timeout);

    });

  }

}
<commit_msg>Added message attributes for command type requests<commit_after>import Emitter from 'events';
import AWS from 'aws-sdk';
import Consumer from 'sqs-consumer';
import Producer from 'sqs-producer';

export default class SqsBrokerFacade {

  constructor(options) {

    this._options = options;

    this._sqs = new AWS.SQS({
      region: options.region,
      accessKeyId: options.accessKeyId,
      secretAccessKey: options.secretAccessKey
    });

    this._responseEmitter = new Emitter();

  }

  _ensureQueue(name) {

    return new Promise((resolve, reject) => {

      var params = {
        QueueName: name
      };

      this._sqs.createQueue(params, function(err, data) {
        if (err) return reject(err);
        resolve(data.QueueUrl);
      });

    });

  }

  async _setupRequestProducer(domain, lane) {

    let queueUrl = await this._ensureQueue(`${domain}-${lane}-req`);

    let producer = Producer.create({
      sqs: this._sqs,
      queueUrl: queueUrl
    });

    return producer;

  }

  _processResponseMessage(message, done) {

    let response = {
      requestUid: message.MessageAttributes.requestUid.StringValue,
      payload: JSON.parse(message.Body),
      acknowledge: done
    };

    this._responseEmitter.emit(response.requestUid, response);

  }

  async _setupResponseConsumer(domain, lane) {

    let queueUrl = await this._ensureQueue(`${domain}-${lane}-res`);

    let consumer = Consumer.create({
      sqs: this._sqs,
      queueUrl: queueUrl,
      batchSize: 10,
      messageAttributeNames: ['All'],
      handleMessage: (message, done) => { this._processResponseMessage(message, done); }
    });

    consumer.on('error', function (err) {
      console.log(err.message);
    });

    return consumer;

  }

  async setup() {

    this._requestProducer = await this._setupRequestProducer(this._options.serviceDomain, this._options.serviceLane);

    this._responseConsumer = await this._setupResponseConsumer(this._options.clientDomain, this._options.clientLane);

  }

  start() {

    this._responseConsumer.start();

  }

  stop() {

    this._responseConsumer.stop();

  }

  enqueueRequest(request) {

    return new Promise((resolve, reject) => {

      let message = {
        id: request.uid,
        body: JSON.stringify(request.payload),
        messageAttributes: this._buildSQSMessageAttributes(request)
      };

      this._requestProducer.send([message], function(err) {
        if (err) reject(err);
        resolve();
      });

    });

  }

  _buildSQSMessageAttributes (request) {
    let messageAttributes;
    switch (request.reefDialect) {
        case 'reef-v1-query':
          messageAttributes = {
              reefDialect: { DataType: 'String', StringValue: request.reefDialect },
              requestUid: { DataType: 'String', StringValue: request.uid },
              queryType: { DataType: 'String', StringValue: request.queryType }
          }
          break;

        case 'reef-v1-command':
          messageAttributes = {
              reefDialect: { DataType: 'String', StringValue: request.reefDialect },
              requestUid: { DataType: 'String', StringValue: request.uid },
              commandType: { DataType: 'String', StringValue: request.commandType }
          }
          break;

        default:
          console.error("Unrecognized reefDialect");
          return;
    }
    return messageAttributes;
  }

  expectResponse(uid, timeout) {

    return new Promise((resolve, reject) => {

      this._responseEmitter.once(uid, (data) => {

        resolve(data);

      });

      setTimeout(() => reject(new Error('Response timeout')), timeout);

    });

  }

}
<|endoftext|>


<commit_before>(function () {
    angular
            .module('auth')
            .controller('LogoutController', LogoutController);

    LogoutController.$inject = [
        'AuthService',
        '$location'
    ];

    function LogoutController(AuthService, $location) {

        activate();

        function activate() {
            AuthService.logout().then(function() {
                $location.path('/');
            })
        }
    }

})();
<commit_msg>Added warning message on logout failure<commit_after>(function () {
    angular
            .module('auth')
            .controller('LogoutController', LogoutController);

    LogoutController.$inject = [
        'AuthService',
        '$location',
        'Notification'
    ];

    function LogoutController(AuthService, $location, Notification) {

        activate();

        function activate() {
            AuthService
                    .logout()
                    .then(function () {
                        $location.path("/");
                    })
                    .catch(function () {
                        Notification.warning('An error happened');
                    });
        }
    }

})();
<|endoftext|>


<commit_before>var fs = require('fs'),
    gulp = require('gulp'),
    lazypipe = require('lazypipe'),
    footer = require('gulp-footer'),
    sourcemaps = require('gulp-sourcemaps'),
    rev = require('gulp-rev'),
    htmlmin = require('gulp-htmlmin'),
    ngAnnotate = require('gulp-ng-annotate'),
    prefix = require('gulp-autoprefixer'),
    cssnano = require('gulp-cssnano'),
    uglify = require('gulp-uglify'),
    useref = require("gulp-useref"),
    revReplace = require("gulp-rev-replace")
    plumber = require('gulp-plumber'),
    gulpIf = require('gulp-if'),
    handleErrors = require('./handleErrors');

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

var initTask = lazypipe()
    .pipe(sourcemaps.init)
    .pipe(footer, ';');
var jsTask = lazypipe()
    .pipe(ngAnnotate)
    .pipe(uglify);
var cssTask = lazypipe()
    .pipe(prefix)
    .pipe(cssnano);

module.exports = function() {
    var templates = fs.readFileSync(config.tmp + '/templates.js');
    var manifest = gulp.src(config.revManifest);

    return gulp.src([config.app + '**/*.html',
        '!' + config.app + 'app/**/*.html',
        '!' + config.app + 'swagger-ui/**/*',
        '!' + config.bower + '**/*.html'])
        .pipe(plumber({errorHandler: handleErrors}))
        //init sourcemaps and prepend semicolon
        .pipe(useref({}, initTask))
        //append html templates
        .pipe(gulpIf('**/app.js', footer(templates)))
        .pipe(gulpIf('*.js', jsTask()))
        .pipe(gulpIf('*.css', cssTask()))
        .pipe(gulpIf('*.html', htmlmin({collapseWhitespace: true})))
        .pipe(gulpIf('**/*.!(html)', rev()))
        .pipe(revReplace({manifest: manifest}))
        .pipe(sourcemaps.write('.'))
        .pipe(gulp.dest(config.dist));
}
<commit_msg>add comma after var declaration<commit_after>var fs = require('fs'),
    gulp = require('gulp'),
    lazypipe = require('lazypipe'),
    footer = require('gulp-footer'),
    sourcemaps = require('gulp-sourcemaps'),
    rev = require('gulp-rev'),
    htmlmin = require('gulp-htmlmin'),
    ngAnnotate = require('gulp-ng-annotate'),
    prefix = require('gulp-autoprefixer'),
    cssnano = require('gulp-cssnano'),
    uglify = require('gulp-uglify'),
    useref = require("gulp-useref"),
    revReplace = require("gulp-rev-replace"),
    plumber = require('gulp-plumber'),
    gulpIf = require('gulp-if'),
    handleErrors = require('./handleErrors');

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

var initTask = lazypipe()
    .pipe(sourcemaps.init)
    .pipe(footer, ';');
var jsTask = lazypipe()
    .pipe(ngAnnotate)
    .pipe(uglify);
var cssTask = lazypipe()
    .pipe(prefix)
    .pipe(cssnano);

module.exports = function() {
    var templates = fs.readFileSync(config.tmp + '/templates.js');
    var manifest = gulp.src(config.revManifest);

    return gulp.src([config.app + '**/*.html',
        '!' + config.app + 'app/**/*.html',
        '!' + config.app + 'swagger-ui/**/*',
        '!' + config.bower + '**/*.html'])
        .pipe(plumber({errorHandler: handleErrors}))
        //init sourcemaps and prepend semicolon
        .pipe(useref({}, initTask))
        //append html templates
        .pipe(gulpIf('**/app.js', footer(templates)))
        .pipe(gulpIf('*.js', jsTask()))
        .pipe(gulpIf('*.css', cssTask()))
        .pipe(gulpIf('*.html', htmlmin({collapseWhitespace: true})))
        .pipe(gulpIf('**/*.!(html)', rev()))
        .pipe(revReplace({manifest: manifest}))
        .pipe(sourcemaps.write('.'))
        .pipe(gulp.dest(config.dist));
};
<|endoftext|>


<commit_before>Notifier = {

  did_add_new_event: function(ev) {
    Event.improve(ev);
    // we only notify on items that are latched to us.  TODO: turn back on
    // if (!ev.re || ev.re.resource().architect != This.user.tag) return;

    // don't report if we're watching the thing
    if (ev.re == This.item) return;
    var go = ev.item_tag;

    if (ev.atype == 'signup')       Notifier.growl( go, ev.actor_title + " signed up: “"+ ev.msg   +"”" );
    if (ev.atype == 'reported')     Notifier.growl( go, ev.actor_title + " reports: “"+ ev.msg   +"”" );
    if (ev.atype == 'answered')     Notifier.growl( go, ev.actor_title + " responded: “"+ ev.msg +"”" );
    if (ev.atype == 'answered_yes') Notifier.growl( go, ev.actor_title + " responded: “"+ ev.msg +"”" );
    if (ev.atype == 'answered_no')  Notifier.growl( go, ev.actor_title + " responded: “"+ ev.msg +"”" );
    if (ev.atype == 'accepted')     Notifier.growl( go, ev.actor_title + " has accepted your assignment" );
    if (ev.atype == 'declined')     Notifier.growl( go, ev.actor_title + " has declined your assignment" );
    if (ev.atype == 'completed')    Notifier.growl( go, ev.actor_title + " has completed your assignment" );
    if (ev.atype == 'appreciated')  Notifier.growl( go, ev.actor_title + " has appreciated you" );
    if (ev.atype == 'blocked')      Notifier.growl( go, ev.actor_title + " has blocked you" );
  },

  growl: function(go, msg, options){
    $.jGrowl(msg, {
      life: 15*1000,
      open: function(e,m,o){
        if (go) e.onclick = function(){ go('@' + go); };
      }
    });
  }

};
<commit_msg>growls are now links<commit_after>Notifier = {

  did_add_new_event: function(ev) {
    Event.improve(ev);
    // we only notify on items that are latched to us.  TODO: turn back on
    // if (!ev.re || ev.re.resource().architect != This.user.tag) return;

    // don't report if we're watching the thing
    if (ev.re == This.item) return;
    var go = ev.item_tag;

    if (ev.atype == 'signup')       Notifier.growl( go, ev.actor_title + " signed up: “"+ ev.msg   +"”" );
    if (ev.atype == 'reported')     Notifier.growl( go, ev.actor_title + " reports: “"+ ev.msg   +"”" );
    if (ev.atype == 'answered')     Notifier.growl( go, ev.actor_title + " responded: “"+ ev.msg +"”" );
    if (ev.atype == 'answered_yes') Notifier.growl( go, ev.actor_title + " responded: “"+ ev.msg +"”" );
    if (ev.atype == 'answered_no')  Notifier.growl( go, ev.actor_title + " responded: “"+ ev.msg +"”" );
    if (ev.atype == 'accepted')     Notifier.growl( go, ev.actor_title + " has accepted your assignment" );
    if (ev.atype == 'declined')     Notifier.growl( go, ev.actor_title + " has declined your assignment" );
    if (ev.atype == 'completed')    Notifier.growl( go, ev.actor_title + " has completed your assignment" );
    if (ev.atype == 'appreciated')  Notifier.growl( go, ev.actor_title + " has appreciated you" );
    if (ev.atype == 'blocked')      Notifier.growl( go, ev.actor_title + " has blocked you" );
  },

  growl: function(go, msg, options){
    msg = ""+msg+"";
    $.jGrowl(msg, {
      life: 15*1000,
      open: function(e,m,o){
        $(e).app_paint();
      }
    });
  }

};
<|endoftext|>


<commit_before>import React from 'react';
import styled from 'styled-components';

const StyledLogo = styled.svg`
  width: 100px;
`;

export default () => <StyledLogo x="0px" y="0px" viewBox="0 0 612 792" enable-background="new 0 0 612 792">
<path fill="#6F6F6D" d="M585.576,185.144c-54.506-1.913-98.494,6.694-125.269,25.819c-16.257,28.688-24.862,77.456-23.906,137.7
	c54.506,1.912,102.318-0.956,125.269-25.819C583.664,298.938,586.532,245.388,585.576,185.144"/>
<path fill="#F4A19A" d="M571.232,221.481c-38.25-0.956-68.85,4.781-87.975,18.169c-11.476,20.081-18.169,54.506-17.213,96.581
	c38.25,0.956,71.719-0.956,87.975-18.169C570.276,300.851,572.188,263.557,571.232,221.481"/>
<path fill="#6F6F6D" d="M11.826,185.144c54.506-1.913,98.494,6.694,125.269,25.819c16.256,28.688,24.863,77.456,23.906,137.7
	c-53.55,1.912-102.318-0.956-125.269-25.819C13.739,298.938,10.87,245.388,11.826,185.144"/>
<path fill="#F4A19A" d="M26.17,221.481c38.25-0.956,68.85,4.781,87.975,18.169c11.475,20.081,17.212,54.506,17.212,96.581
	c-38.25,0.956-71.719-0.956-87.975-18.169C27.126,300.851,25.214,263.557,26.17,221.481"/>
<path fill="#89664C" d="M307.912,442.856c-163.519,0-288.787-67.894-288.787,84.149c0,122.4,161.606,155.869,284.962,155.869
	c143.438,0,288.788-33.469,288.788-155.869C592.875,374.962,476.213,442.856,307.912,442.856"/>
<path fill="#9B7861" d="M303.131,289.856c-91.8,0-234.281,10.519-234.281,120.487c0,160.65,473.343,160.65,473.343,0
	C543.15,300.375,400.669,289.856,303.131,289.856"/>
<path fill="#A88673" d="M468.562,249.694c-43.987-98.494-252.45,2.869-216.112-116.663c5.737-18.169,4.781-25.819-8.606-22.95
	c-74.587,15.3-129.094,91.8-109.969,155.869C195.075,470.588,528.807,383.569,468.562,249.694"/>
<path fill="#FFFFFF" d="M275.4,417.994c0,38.25-30.6,68.85-68.85,68.85s-68.85-30.6-68.85-68.85c0-38.25,30.6-68.851,68.85-68.851
	S275.4,379.744,275.4,417.994"/>
<circle fill="#231F20" cx="225.675" cy="417.994" r="34.425"/>
<path fill="#FFFFFF" d="M474.3,417.994c0,38.25-30.6,68.85-68.85,68.85s-68.851-30.6-68.851-68.85
	c0-38.25,30.601-68.851,68.851-68.851C442.744,349.144,474.3,379.744,474.3,417.994"/>
<g>
	<circle fill="#231F20" cx="386.325" cy="417.994" r="34.425"/>
	<path fill="#231F20" d="M363.375,573.862c0,31.557-25.818,57.375-57.375,57.375c-31.556,0-57.375-25.818-57.375-57.375
		c0-31.556,25.819-57.375,57.375-57.375C337.557,516.487,363.375,542.307,363.375,573.862"/>
</g>
<g>
	<path fill="#F6C799" d="M205.116,246.334c-34.425-18.169-57.375-56.419-57.375-56.419
		c-9.562,100.406,26.775,125.269,47.812,125.269C223.284,316.14,245.278,268.327,205.116,246.334"/>
	<path fill="#F6C799" d="M406.885,246.334c34.426-18.169,57.375-56.419,57.375-56.419c9.562,100.406-26.775,125.269-47.812,125.269
		C388.717,316.14,366.723,268.327,406.885,246.334"/>
</g>
</StyledLogo>;
<commit_msg>more logo<commit_after>import React from 'react';
import styled from 'styled-components';

const LogoContainer = styled.div`
`;

const StyledLogo = styled.svg`
  width: 100px;
`;

const LogoTextContainer = styled.div`
	display: inline-block;
	height: 100%;
	font-size: 30px;
	margin: 0.02em;
	vertical-align: middle;
`;

const SvgLogo = () => (
	<StyledLogo x="0px" y="0px" viewBox="0 0 612 792" enable-background="new 0 0 612 792">
		<path fill="#6F6F6D" d="M585.576,185.144c-54.506-1.913-98.494,6.694-125.269,25.819c-16.257,28.688-24.862,77.456-23.906,137.7
			c54.506,1.912,102.318-0.956,125.269-25.819C583.664,298.938,586.532,245.388,585.576,185.144"/>
		<path fill="#F4A19A" d="M571.232,221.481c-38.25-0.956-68.85,4.781-87.975,18.169c-11.476,20.081-18.169,54.506-17.213,96.581
			c38.25,0.956,71.719-0.956,87.975-18.169C570.276,300.851,572.188,263.557,571.232,221.481"/>
		<path fill="#6F6F6D" d="M11.826,185.144c54.506-1.913,98.494,6.694,125.269,25.819c16.256,28.688,24.863,77.456,23.906,137.7
			c-53.55,1.912-102.318-0.956-125.269-25.819C13.739,298.938,10.87,245.388,11.826,185.144"/>
		<path fill="#F4A19A" d="M26.17,221.481c38.25-0.956,68.85,4.781,87.975,18.169c11.475,20.081,17.212,54.506,17.212,96.581
			c-38.25,0.956-71.719-0.956-87.975-18.169C27.126,300.851,25.214,263.557,26.17,221.481"/>
		<path fill="#89664C" d="M307.912,442.856c-163.519,0-288.787-67.894-288.787,84.149c0,122.4,161.606,155.869,284.962,155.869
			c143.438,0,288.788-33.469,288.788-155.869C592.875,374.962,476.213,442.856,307.912,442.856"/>
		<path fill="#9B7861" d="M303.131,289.856c-91.8,0-234.281,10.519-234.281,120.487c0,160.65,473.343,160.65,473.343,0
			C543.15,300.375,400.669,289.856,303.131,289.856"/>
		<path fill="#A88673" d="M468.562,249.694c-43.987-98.494-252.45,2.869-216.112-116.663c5.737-18.169,4.781-25.819-8.606-22.95
			c-74.587,15.3-129.094,91.8-109.969,155.869C195.075,470.588,528.807,383.569,468.562,249.694"/>
		<path fill="#FFFFFF" d="M275.4,417.994c0,38.25-30.6,68.85-68.85,68.85s-68.85-30.6-68.85-68.85c0-38.25,30.6-68.851,68.85-68.851
			S275.4,379.744,275.4,417.994"/>
		<circle fill="#231F20" cx="225.675" cy="417.994" r="34.425"/>
		<path fill="#FFFFFF" d="M474.3,417.994c0,38.25-30.6,68.85-68.85,68.85s-68.851-30.6-68.851-68.85
			c0-38.25,30.601-68.851,68.851-68.851C442.744,349.144,474.3,379.744,474.3,417.994"/>
		<g>
			<circle fill="#231F20" cx="386.325" cy="417.994" r="34.425"/>
			<path fill="#231F20" d="M363.375,573.862c0,31.557-25.818,57.375-57.375,57.375c-31.556,0-57.375-25.818-57.375-57.375
				c0-31.556,25.819-57.375,57.375-57.375C337.557,516.487,363.375,542.307,363.375,573.862"/>
		</g>
		<g>
			<path fill="#F6C799" d="M205.116,246.334c-34.425-18.169-57.375-56.419-57.375-56.419
				c-9.562,100.406,26.775,125.269,47.812,125.269C223.284,316.14,245.278,268.327,205.116,246.334"/>
			<path fill="#F6C799" d="M406.885,246.334c34.426-18.169,57.375-56.419,57.375-56.419c9.562,100.406-26.775,125.269-47.812,125.269
				C388.717,316.14,366.723,268.327,406.885,246.334"/>
		</g>
</StyledLogo>);

export default () => (<LogoContainer>
	<LogoTextContainer></LogoTextContainer>
	<SvgLogo />
	<LogoTextContainer></LogoTextContainer>
</LogoContainer>);
<|endoftext|>


<commit_before>import React, { PropTypes } from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { selectFile, closeFile } from '../actions/files';
import Tab from '../components/Tab';

class Tabs extends React.PureComponent {
  static propTypes = {
    files: ImmutablePropTypes.map,
    handleFile: PropTypes.func,
    handleRemoveFile: PropTypes.func,
    currentFile: PropTypes.string
  }

  constructor(props) {
    super(props);
    this.handleTabClick = ::this.handleTabClick;
    this.handleRemoveTab = ::this.handleRemoveTab;
  }

  handleTabClick(id) {
    this.props.handleFile(id);
  }

  handleRemoveTab(id) {
    this.props.handleRemoveFile(id);
  }

  render() {
    const { files, currentFile } = this.props;

    if (!files) {
      return null;
    }

    const fileComponents = files.map(f =>
      <Tab
        key={f.get('id')}
        id={f.get('id')}
        name={f.get('name')}
        changed={f.get('changed')}
        onTabClick={this.handleTabClick}
        onRemoveTab={this.handleRemoveTab}
        isActive={f.get('id') === currentFile}
      />
    ).valueSeq();

    return (

        { fileComponents }

    );
  }
}

const mapStateToProps = state => ({
  files: state.files.get('files'),
  currentFile: state.files.get('currentFile')
});

const mapDispatchToProps = dispatch => ({
  handleFile: bindActionCreators(selectFile, dispatch),
  handleRemoveFile: bindActionCreators(closeFile, dispatch)
});

export default connect(mapStateToProps, mapDispatchToProps)(Tabs);
<commit_msg>Cleanup the Tabs container a little<commit_after>import React, { PropTypes } from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { selectFile, closeFile } from '../actions/files';
import Tab from '../components/Tab';

class Tabs extends React.PureComponent {
  static propTypes = {
    files: ImmutablePropTypes.map,
    handleFile: PropTypes.func,
    handleRemoveFile: PropTypes.func,
    currentFile: PropTypes.string
  }

  render() {
    const { files, currentFile, handleFile, handleRemoveFile } = this.props;

    if (!files) {
      return null;
    }

    const fileComponents = files.map(f =>
      <Tab
        key={f.get('id')}
        id={f.get('id')}
        name={f.get('name')}
        changed={f.get('changed')}
        onTabClick={handleFile}
        onRemoveTab={handleRemoveFile}
        isActive={f.get('id') === currentFile}
      />
    ).valueSeq();

    return (

        { fileComponents }

    );
  }
}

const mapStateToProps = state => ({
  files: state.files.get('files'),
  currentFile: state.files.get('currentFile')
});

const mapDispatchToProps = dispatch => ({
  handleFile: bindActionCreators(selectFile, dispatch),
  handleRemoveFile: bindActionCreators(closeFile, dispatch)
});

export default connect(mapStateToProps, mapDispatchToProps)(Tabs);
<|endoftext|>


<commit_before>/*
 * @file This PaperScript file controls the main PancakeCreator SVG Editor and
 * all importing/exporting of its data.
 */

var values = {
  paths: 5,
  minPoints: 5,
  maxPoints: 15,
  minRadius: 30,
  maxRadius: 90
};

var hitOptions = {
  segments: true,
  stroke: true,
  fill: true,
  tolerance: 5
};

var pancakeShades = [
  '#d7c3b6',
  '#e9c7af',
  '#a15a2c',
  '#4a2814'
];

var strokeWidth = 5;
paper.settings.handleSize = 10;

var drawPath = null;
var selectionRectangle = null;
var selectionRectangleScale = null;
var selectionRectangleScaleNormalized = null;
var selectionRectangleRotation = null;
var segment, path, selectionRectangleSegment;
var movePath = false;
var polygonalDraw = false;
var pencilDraw = false;
var bandLine = null;

createPaths();

function createPaths() {
  var radiusDelta = values.maxRadius - values.minRadius;
  var pointsDelta = values.maxPoints - values.minPoints;
  for (var i = 0; i < values.paths; i++) {
    var radius = values.minRadius + Math.random() * radiusDelta;
    var points = values.minPoints + Math.floor(Math.random() * pointsDelta);
    var path = createBlob(view.size * Point.random(), radius, points);
    var lightness = (Math.random() - 0.5) * 0.4 + 0.4;
    var hue = Math.random() * 360;
    //path.fillColor = { hue: hue, saturation: 1, lightness: lightness };
    path.strokeColor = pancakeShades[0];
    path.strokeWidth = strokeWidth;
  }
  ;
}

function createBlob(center, maxRadius, points) {
  var path = new Path();
  path.closed = true;
  for (var i = 0; i < points; i++) {
    var delta = new Point({
      length: (maxRadius * 0.5) + (Math.random() * maxRadius * 0.5),
      angle: (360 / points) * i
    });
    path.add(center + delta);
  }
  path.smooth();
  return path;
}

function initSelectionRectangle(path) {
  if (selectionRectangle != null)
    selectionRectangle.remove();

  var reset = path.rotation == 0 && path.scaling.x == 1 && path.scaling.y == 1;
  var bounds;

  if (reset) {
    console.log('reset');
    bounds = path.bounds;
    path.pInitialBounds = path.bounds;
  } else {
    console.log('no reset');
    bounds = path.pInitialBounds;
  }
  console.log('bounds: ' + bounds);
  b = bounds.clone().expand(10, 10);

  selectionRectangle = new Path.Rectangle(b);
  selectionRectangle.pivot = selectionRectangle.position;
  selectionRectangle.insert(2, new Point(b.center.x, b.top));
  selectionRectangle.insert(2, new Point(b.center.x, b.top - 25));
  selectionRectangle.insert(2, new Point(b.center.x, b.top));

  if (!reset) {
    selectionRectangle.position = path.bounds.center;
    selectionRectangle.rotation = path.rotation;
    selectionRectangle.scaling = path.scaling;
  }

  selectionRectangle.strokeWidth = 2;
  selectionRectangle.strokeColor = 'blue';
  selectionRectangle.name = "selection rectangle";
  selectionRectangle.selected = true;
  selectionRectangle.ppath = path;
  selectionRectangle.ppath.pivot = selectionRectangle.pivot;
}

function onMouseDown(event) {
  segment = path = null;

  // Continue drawing polygonal (ignores hitTest while on)
  if (drawPath && polygonalDraw) {
    drawPath.add(event.point);

    // Make a new point, delete the first one
    bandLine.segments[0].point = event.point;
    return;
  }

  var hitResult = project.hitTest(event.point, hitOptions);

  // If we didn't hit anything with hitTest...
  if (!hitResult) {
    // Find any items that are Paths (not layers) that contain the point
    var item = getBoundSelection(event.point);

    // If we actually found something, fake a fill hitResult
    if (item) {
      hitResult = {
        type: 'fill', // SURE it is ;)
        item: item
      };
    } else {

      // Create a new drawPath and set its stroke color
      // (only if we're not deselecting something)
      if (project.selectedItems.length === 0) {
        pencilDraw = true;
        drawPath = new Path({
          segments: [event.point],
          strokeColor: pancakeShades[0],
          strokeWidth: strokeWidth
        });
      }

      // Deselect if nothing clicked (feels natural for deselection)
      if (selectionRectangle != null) {
        selectionRectangle.remove();
      }

      return;
    }
  }

  if (event.modifiers.shift) {
    if (hitResult.type == 'segment') {
      hitResult.segment.remove();
    }
    return;
  }

  if (hitResult) {
    console.log(hitResult);
    path = hitResult.item;

    if (hitResult.type == 'segment') {
      if (selectionRectangle != null && path.name == "selection rectangle")
      {
        console.log('selectionRectangle');
        if (hitResult.segment.index >= 2 && hitResult.segment.index <= 4)
        {
          console.log('rotation');
          selectionRectangleRotation = 0;
        }
        else
        {
          console.log('scale');
          selectionRectangleScale = event.point.subtract(selectionRectangle.bounds.center).length / path.scaling.x;
        }
      }
      else
        segment = hitResult.segment;
    } else if (hitResult.type == 'stroke' && path != selectionRectangle) {
      var location = hitResult.location;
      segment = path.insert(location.index + 1, event.point);
      //path.smooth();
    }
    if ((selectionRectangle == null || selectionRectangle.ppath != path) && selectionRectangle != path)
    {
      initSelectionRectangle(path);
    }
  } else { // Nothing hit
    if (selectionRectangle != null)
      selectionRectangle.remove();
  }

  movePath = hitResult.type == 'fill';

  if (movePath)
    project.activeLayer.addChild(hitResult.item);
}

function onMouseMove(event) {
  // Ignore hover effects when drawing
  if (drawPath && polygonalDraw) {
    bandLine.segments[1].point = event.point;
    return;
  }

  project.activeLayer.selected = false;

  if (event.item) {
    event.item.selected = true;
    setCursor('copy');
  } else if (getBoundSelection(event.point)) {
    setCursor('move');
  } else {
    setCursor();
  }

  if (selectionRectangle) {
    selectionRectangle.selected = true;
  }
}

function onMouseDrag(event) {
  if (selectionRectangleScale != null) {
    ratio = event.point.subtract(selectionRectangle.bounds.center).length / selectionRectangleScale;
    scaling = new Point(ratio, ratio);
    selectionRectangle.scaling = scaling;
    selectionRectangle.ppath.scaling = scaling;
    console.log('scaling: ' + selectionRectangle.ppath);
    return;
  } else if (selectionRectangleRotation != null) {
    console.log('rotation: ' + selectionRectangle.ppath);
    rotation = event.point.subtract(selectionRectangle.pivot).angle + 90;
    selectionRectangle.ppath.rotation = rotation;
    selectionRectangle.rotation = rotation;
    return;
  }

  if (drawPath && !polygonalDraw && pencilDraw) {
    // While the user drags the mouse, points are added to the drawPath
    // at the position of the mouse
    drawPath.add(event.point);
  }

  if (segment) {
    segment.point += event.delta;
    //path.smooth();
    initSelectionRectangle(path);
  } else if (path) {
    if (path != selectionRectangle) {
      path.position += event.delta;
      selectionRectangle.position += event.delta;
    } else {
      selectionRectangle.position += event.delta;
      selectionRectangle.ppath.position += event.delta;
    }
  }
}

function onMouseUp(event) {
  selectionRectangleScale = null;
  selectionRectangleRotation = null;

  if (drawPath) {
    if (drawPath.length === 0) {
      polygonalDraw = true;
      pencilDraw = false;
      bandLine = new Path({
        segments: [event.point, event.point],
        strokeColor: 'red',
        strokeWidth: strokeWidth,
        dashArray: [10, 4]
      });
      bandLine.onDoubleClick = cancelPolygonDraw;
      setCursor('copy');
    }

    if (!polygonalDraw && pencilDraw) {
      // When the mouse is released, simplify it:
      drawPath.simplify(10);

      pencilDraw = false;

      // Select the drawPath, so we can see its segments:
      drawPath.fullySelected = true;
      drawPath = null;
    }
  }
}

function onKeyDown(event) {
  if (_.contains(['escape', 'enter'], event.key)) {
    cancelPolygonDraw();
  }

  if (event.key === 'delete' && selectionRectangle) {
    selectionRectangle.ppath.remove();
    selectionRectangle.remove();
    selectionRectangle = null;
  }
}

function cancelPolygonDraw() {
  if (polygonalDraw) {
    polygonalDraw = false;
    drawPath.fullySelected = true;
    drawPath = null;
    bandLine.remove();
    bandLine = null;
    setCursor();
  }
}

function getBoundSelection(point) {
  // Check for items that are overlapping a rect around the event point
  var items = project.getItems({
    overlapping: new Rectangle(point.x - 2, point.y - 2,point.x + 2, point.y + 2)
  });

  var item = null;
  _.each(items, function (i) {
    if (i instanceof Path) {
      if (i.contains(point)) {
        item = i;
      }
    }
  });

  return item;
}

var $editor = $('#editor');
function setCursor(type) {
  if (!type) type = 'default';
  $editor.css('cursor', type);
}

function onResize(event) {
  project.activeLayer.position = view.center;
  view.zoom = scale/2.5;
}

// Editor should be done loading, trigger loadInit
editorLoadedInit();<commit_msg>Remove single click cancelled orphan node paths on polygon draw<commit_after>/*
 * @file This PaperScript file controls the main PancakeCreator SVG Editor and
 * all importing/exporting of its data.
 */

var values = {
  paths: 5,
  minPoints: 5,
  maxPoints: 15,
  minRadius: 30,
  maxRadius: 90
};

var hitOptions = {
  segments: true,
  stroke: true,
  fill: true,
  tolerance: 5
};

var pancakeShades = [
  '#d7c3b6',
  '#e9c7af',
  '#a15a2c',
  '#4a2814'
];

var strokeWidth = 5;
paper.settings.handleSize = 10;

var drawPath = null;
var selectionRectangle = null;
var selectionRectangleScale = null;
var selectionRectangleScaleNormalized = null;
var selectionRectangleRotation = null;
var segment, path, selectionRectangleSegment;
var movePath = false;
var polygonalDraw = false;
var pencilDraw = false;
var bandLine = null;

createPaths();

function createPaths() {
  var radiusDelta = values.maxRadius - values.minRadius;
  var pointsDelta = values.maxPoints - values.minPoints;
  for (var i = 0; i < values.paths; i++) {
    var radius = values.minRadius + Math.random() * radiusDelta;
    var points = values.minPoints + Math.floor(Math.random() * pointsDelta);
    var path = createBlob(view.size * Point.random(), radius, points);
    var lightness = (Math.random() - 0.5) * 0.4 + 0.4;
    var hue = Math.random() * 360;
    //path.fillColor = { hue: hue, saturation: 1, lightness: lightness };
    path.strokeColor = pancakeShades[0];
    path.strokeWidth = strokeWidth;
  }
  ;
}

function createBlob(center, maxRadius, points) {
  var path = new Path();
  path.closed = true;
  for (var i = 0; i < points; i++) {
    var delta = new Point({
      length: (maxRadius * 0.5) + (Math.random() * maxRadius * 0.5),
      angle: (360 / points) * i
    });
    path.add(center + delta);
  }
  path.smooth();
  return path;
}

function initSelectionRectangle(path) {
  if (selectionRectangle != null)
    selectionRectangle.remove();

  var reset = path.rotation == 0 && path.scaling.x == 1 && path.scaling.y == 1;
  var bounds;

  if (reset) {
    console.log('reset');
    bounds = path.bounds;
    path.pInitialBounds = path.bounds;
  } else {
    console.log('no reset');
    bounds = path.pInitialBounds;
  }
  console.log('bounds: ' + bounds);
  b = bounds.clone().expand(10, 10);

  selectionRectangle = new Path.Rectangle(b);
  selectionRectangle.pivot = selectionRectangle.position;
  selectionRectangle.insert(2, new Point(b.center.x, b.top));
  selectionRectangle.insert(2, new Point(b.center.x, b.top - 25));
  selectionRectangle.insert(2, new Point(b.center.x, b.top));

  if (!reset) {
    selectionRectangle.position = path.bounds.center;
    selectionRectangle.rotation = path.rotation;
    selectionRectangle.scaling = path.scaling;
  }

  selectionRectangle.strokeWidth = 2;
  selectionRectangle.strokeColor = 'blue';
  selectionRectangle.name = "selection rectangle";
  selectionRectangle.selected = true;
  selectionRectangle.ppath = path;
  selectionRectangle.ppath.pivot = selectionRectangle.pivot;
}

function onMouseDown(event) {
  segment = path = null;

  // Continue drawing polygonal (ignores hitTest while on)
  if (drawPath && polygonalDraw) {
    drawPath.add(event.point);

    // Make a new point, delete the first one
    bandLine.segments[0].point = event.point;
    return;
  }

  var hitResult = project.hitTest(event.point, hitOptions);

  // If we didn't hit anything with hitTest...
  if (!hitResult) {
    // Find any items that are Paths (not layers) that contain the point
    var item = getBoundSelection(event.point);

    // If we actually found something, fake a fill hitResult
    if (item) {
      hitResult = {
        type: 'fill', // SURE it is ;)
        item: item
      };
    } else {

      // Create a new drawPath and set its stroke color
      // (only if we're not deselecting something)
      if (project.selectedItems.length === 0) {
        pencilDraw = true;
        drawPath = new Path({
          segments: [event.point],
          strokeColor: pancakeShades[0],
          strokeWidth: strokeWidth
        });
      }

      // Deselect if nothing clicked (feels natural for deselection)
      if (selectionRectangle != null) {
        selectionRectangle.remove();
      }

      return;
    }
  }

  if (event.modifiers.shift) {
    if (hitResult.type == 'segment') {
      hitResult.segment.remove();
    }
    return;
  }

  if (hitResult) {
    console.log(hitResult);
    path = hitResult.item;

    if (hitResult.type == 'segment') {
      if (selectionRectangle != null && path.name == "selection rectangle")
      {
        console.log('selectionRectangle');
        if (hitResult.segment.index >= 2 && hitResult.segment.index <= 4)
        {
          console.log('rotation');
          selectionRectangleRotation = 0;
        }
        else
        {
          console.log('scale');
          selectionRectangleScale = event.point.subtract(selectionRectangle.bounds.center).length / path.scaling.x;
        }
      }
      else
        segment = hitResult.segment;
    } else if (hitResult.type == 'stroke' && path != selectionRectangle) {
      var location = hitResult.location;
      segment = path.insert(location.index + 1, event.point);
      //path.smooth();
    }
    if ((selectionRectangle == null || selectionRectangle.ppath != path) && selectionRectangle != path)
    {
      initSelectionRectangle(path);
    }
  } else { // Nothing hit
    if (selectionRectangle != null)
      selectionRectangle.remove();
  }

  movePath = hitResult.type == 'fill';

  if (movePath)
    project.activeLayer.addChild(hitResult.item);
}

function onMouseMove(event) {
  // Ignore hover effects when drawing
  if (drawPath && polygonalDraw) {
    bandLine.segments[1].point = event.point;
    return;
  }

  project.activeLayer.selected = false;

  if (event.item) {
    event.item.selected = true;
    setCursor('copy');
  } else if (getBoundSelection(event.point)) {
    setCursor('move');
  } else {
    setCursor();
  }

  if (selectionRectangle) {
    selectionRectangle.selected = true;
  }
}

function onMouseDrag(event) {
  if (selectionRectangleScale != null) {
    ratio = event.point.subtract(selectionRectangle.bounds.center).length / selectionRectangleScale;
    scaling = new Point(ratio, ratio);
    selectionRectangle.scaling = scaling;
    selectionRectangle.ppath.scaling = scaling;
    console.log('scaling: ' + selectionRectangle.ppath);
    return;
  } else if (selectionRectangleRotation != null) {
    console.log('rotation: ' + selectionRectangle.ppath);
    rotation = event.point.subtract(selectionRectangle.pivot).angle + 90;
    selectionRectangle.ppath.rotation = rotation;
    selectionRectangle.rotation = rotation;
    return;
  }

  if (drawPath && !polygonalDraw && pencilDraw) {
    // While the user drags the mouse, points are added to the drawPath
    // at the position of the mouse
    drawPath.add(event.point);
  }

  if (segment) {
    segment.point += event.delta;
    //path.smooth();
    initSelectionRectangle(path);
  } else if (path) {
    if (path != selectionRectangle) {
      path.position += event.delta;
      selectionRectangle.position += event.delta;
    } else {
      selectionRectangle.position += event.delta;
      selectionRectangle.ppath.position += event.delta;
    }
  }
}

function onMouseUp(event) {
  selectionRectangleScale = null;
  selectionRectangleRotation = null;

  if (drawPath) {
    if (drawPath.length === 0) {
      polygonalDraw = true;
      pencilDraw = false;
      bandLine = new Path({
        segments: [event.point, event.point],
        strokeColor: 'red',
        strokeWidth: strokeWidth,
        dashArray: [10, 4]
      });
      bandLine.onDoubleClick = cancelPolygonDraw;
      setCursor('copy');
    }

    if (!polygonalDraw && pencilDraw) {
      // When the mouse is released, simplify it:
      drawPath.simplify(10);

      pencilDraw = false;

      // Select the drawPath, so we can see its segments:
      drawPath.fullySelected = true;
      drawPath = null;
    }
  }
}

function onKeyDown(event) {
  if (_.contains(['escape', 'enter'], event.key)) {
    cancelPolygonDraw();
  }

  if (event.key === 'delete' && selectionRectangle) {
    selectionRectangle.ppath.remove();
    selectionRectangle.remove();
    selectionRectangle = null;
  }
}

function cancelPolygonDraw() {
  if (polygonalDraw) {
    polygonalDraw = false;

    // Remove orphan node paths
    if (drawPath.segments.length === 1) {
      drawPath.remove();
    } else {
      drawPath.fullySelected = true;
    }

    drawPath = null;
    bandLine.remove();
    bandLine = null;
    setCursor();
  }
}

function getBoundSelection(point) {
  // Check for items that are overlapping a rect around the event point
  var items = project.getItems({
    overlapping: new Rectangle(point.x - 2, point.y - 2,point.x + 2, point.y + 2)
  });

  var item = null;
  _.each(items, function (i) {
    if (i instanceof Path) {
      if (i.contains(point)) {
        item = i;
      }
    }
  });

  return item;
}

var $editor = $('#editor');
function setCursor(type) {
  if (!type) type = 'default';
  $editor.css('cursor', type);
}

function onResize(event) {
  project.activeLayer.position = view.center;
  view.zoom = scale/2.5;
}

// Editor should be done loading, trigger loadInit
editorLoadedInit();<|endoftext|>


<commit_before>import Controller from '@ember/controller';
import {action} from '@ember/object';
import {getSymbol} from 'ghost-admin/utils/currency';
import {inject as service} from '@ember/service';
import {tracked} from '@glimmer/tracking';

export default class DashboardController extends Controller {
    @service feature;
    @service session;
    @service membersStats;
    @service store;
    @service settings;
    @service whatsNew;

    @tracked
    eventsData = null;
    @tracked
    eventsError = null;
    @tracked
    eventsLoading = false;

    @tracked
    mrrStatsData = null;
    @tracked
    mrrStatsError = null;
    @tracked
    mrrStatsLoading = false;

    @tracked
    memberCountStatsData = null;
    @tracked
    memberCountStatsError = null;
    @tracked
    memberCountStatsLoading = false;

    @tracked
    topMembersData = null;
    @tracked
    topMembersError = null;
    @tracked
    topMembersLoading = false;

    @tracked
    newsletterOpenRatesData = null;
    @tracked
    newsletterOpenRatesError = null;
    @tracked
    newsletterOpenRatesLoading = false;

    @tracked
    whatsNewEntries = null;
    @tracked
    whatsNewEntriesLoading = null;
    @tracked
    whatsNewEntriesError = null;

    get topMembersDataHasOpenRates() {
        return this.topMembersData && this.topMembersData.find((member) => {
            return member.emailOpenRate !== null;
        });
    }

    initialise() {
        this.loadEvents();
        this.loadTopMembers();
        this.loadCharts();
        this.loadWhatsNew();
    }

    loadMRRStats() {
        this.mrrStatsLoading = true;
        this.membersStats.fetchMRR().then((stats) => {
            this.mrrStatsLoading = false;
            const statsData = stats.data;
            let currencyStats = statsData[0] || {
                data: [],
                currency: 'usd'
            };
            if (currencyStats) {
                const currencyStatsData = this.membersStats.fillDates(currencyStats.data) || {};
                const dateValues = Object.values(currencyStatsData).map(val => val / 100);
                const currentMRR = dateValues.length ? dateValues[dateValues.length - 1] : 0;
                const rangeStartMRR = dateValues.length ? dateValues[0] : 0;
                const percentGrowth = rangeStartMRR !== 0 ? ((currentMRR - rangeStartMRR) / rangeStartMRR) * 100 : 0;
                this.mrrStatsData = {
                    currentAmount: currentMRR,
                    currency: getSymbol(currencyStats.currency),
                    percentGrowth: percentGrowth.toFixed(1),
                    percentClass: (percentGrowth > 0 ? 'positive' : (percentGrowth < 0 ? 'negative' : '')),
                    options: {
                        rangeInDays: 30
                    },
                    data: {
                        label: 'MRR',
                        dateLabels: Object.keys(currencyStatsData),
                        dateValues
                    },
                    title: 'MRR',
                    stats: currencyStats
                };
            }
        }, (error) => {
            this.mrrStatsError = error;
            this.mrrStatsLoading = false;
        });
    }

    loadMemberCountStats() {
        this.memberCountStatsLoading = true;
        this.membersStats.fetchCounts().then((stats) => {
            this.memberCountStatsLoading = false;

            if (stats) {
                const statsDateObj = this.membersStats.fillCountDates(stats.data) || {};
                const dateValues = Object.values(statsDateObj);
                const currentAllCount = dateValues.length ? dateValues[dateValues.length - 1].total : 0;
                const currentPaidCount = dateValues.length ? dateValues[dateValues.length - 1].paid : 0;
                const rangeStartAllCount = dateValues.length ? dateValues[0].total : 0;
                const rangeStartPaidCount = dateValues.length ? dateValues[0].paid : 0;
                const allCountPercentGrowth = rangeStartAllCount !== 0 ? ((currentAllCount - rangeStartAllCount) / rangeStartAllCount) * 100 : 0;
                const paidCountPercentGrowth = rangeStartPaidCount !== 0 ? ((currentPaidCount - rangeStartPaidCount) / rangeStartPaidCount) * 100 : 0;

                this.memberCountStatsData = {
                    all: {
                        percentGrowth: allCountPercentGrowth.toFixed(1),
                        percentClass: (allCountPercentGrowth > 0 ? 'positive' : (allCountPercentGrowth < 0 ? 'negative' : '')),
                        total: dateValues.length ? dateValues[dateValues.length - 1].total : 0,
                        options: {
                            rangeInDays: 30
                        },
                        data: {
                            label: 'Members',
                            dateLabels: Object.keys(statsDateObj),
                            dateValues: dateValues.map(d => d.total)
                        },
                        title: 'Total Members',
                        stats: stats
                    },
                    paid: {
                        percentGrowth: paidCountPercentGrowth.toFixed(1),
                        percentClass: (paidCountPercentGrowth > 0 ? 'positive' : (paidCountPercentGrowth < 0 ? 'negative' : '')),
                        total: dateValues.length ? dateValues[dateValues.length - 1].paid : 0,
                        options: {
                            rangeInDays: 30
                        },
                        data: {
                            label: 'Members',
                            dateLabels: Object.keys(statsDateObj),
                            dateValues: dateValues.map(d => d.paid)
                        },
                        title: 'Paid Members',
                        stats: stats
                    }
                };
            }
        }, (error) => {
            this.memberCountStatsError = error;
            this.memberCountStatsLoading = false;
        });
    }

    loadCharts() {
        this.loadMRRStats();
        this.loadMemberCountStats();
        this.loadNewsletterOpenRates();
    }

    loadEvents() {
        this.eventsLoading = true;
        this.membersStats.fetchTimeline({limit: 5}).then(({events}) => {
            this.eventsData = events;
            this.eventsLoading = false;
        }, (error) => {
            this.eventsError = error;
            this.eventsLoading = false;
        });
    }

    loadNewsletterOpenRates() {
        this.newsletterOpenRatesLoading = true;
        this.membersStats.fetchNewsletterStats().then((results) => {
            const rangeStartOpenRate = results.length > 1 ? results[results.length - 2].openRate : 0;
            const rangeEndOpenRate = results.length > 0 ? results[results.length - 1].openRate : 0;
            const percentGrowth = rangeStartOpenRate !== 0 ? ((rangeEndOpenRate - rangeStartOpenRate) / rangeStartOpenRate) * 100 : 0;
            this.newsletterOpenRatesData = {
                percentGrowth: percentGrowth.toFixed(1),
                percentClass: (percentGrowth > 0 ? 'positive' : (percentGrowth < 0 ? 'negative' : '')),
                current: rangeEndOpenRate,
                options: {
                    rangeInDays: 30
                },
                data: {
                    label: 'Open rate',
                    dateLabels: results.map(d => d.subject),
                    dateValues: results.map(d => d.openRate)
                },
                title: 'Open rate',
                stats: results
            };
            this.newsletterOpenRatesLoading = false;
        }, (error) => {
            this.newsletterOpenRatesError = error;
            this.newsletterOpenRatesLoading = false;
        });
    }

    loadTopMembers() {
        this.topMembersLoading = true;
        let query = {
            filter: 'email_open_rate:-null',
            order: 'email_open_rate desc',
            limit: 5
        };
        this.store.query('member', query).then((result) => {
            if (!result.length) {
                return this.store.query('member', {
                    filter: 'status:paid',
                    order: 'created_at asc',
                    limit: 5
                });
            }
            return result;
        }).then((result) => {
            this.topMembersData = result;
            this.topMembersLoading = false;
        }).catch((error) => {
            this.topMembersError = error;
            this.topMembersLoading = false;
        });
    }

    loadWhatsNew() {
        this.whatsNewEntriesLoading = true;
        this.whatsNew.fetchLatest.perform().then(() => {
            this.whatsNewEntriesLoading = false;
            this.whatsNewEntries = this.whatsNew.entries.slice(0, 3);
        }, (error) => {
            this.whatsNewEntriesError = error;
            this.whatsNewEntriesLoading = false;
        });
    }

    @action
    dismissLaunchBanner() {
        this.feature.set('launchComplete', true);
    }
}
<commit_msg>Updated default MRR currency from settings<commit_after>import Controller from '@ember/controller';
import {action} from '@ember/object';
import {getSymbol} from 'ghost-admin/utils/currency';
import {inject as service} from '@ember/service';
import {tracked} from '@glimmer/tracking';

export default class DashboardController extends Controller {
    @service feature;
    @service session;
    @service membersStats;
    @service store;
    @service settings;
    @service whatsNew;

    @tracked
    eventsData = null;
    @tracked
    eventsError = null;
    @tracked
    eventsLoading = false;

    @tracked
    mrrStatsData = null;
    @tracked
    mrrStatsError = null;
    @tracked
    mrrStatsLoading = false;

    @tracked
    memberCountStatsData = null;
    @tracked
    memberCountStatsError = null;
    @tracked
    memberCountStatsLoading = false;

    @tracked
    topMembersData = null;
    @tracked
    topMembersError = null;
    @tracked
    topMembersLoading = false;

    @tracked
    newsletterOpenRatesData = null;
    @tracked
    newsletterOpenRatesError = null;
    @tracked
    newsletterOpenRatesLoading = false;

    @tracked
    whatsNewEntries = null;
    @tracked
    whatsNewEntriesLoading = null;
    @tracked
    whatsNewEntriesError = null;

    get topMembersDataHasOpenRates() {
        return this.topMembersData && this.topMembersData.find((member) => {
            return member.emailOpenRate !== null;
        });
    }

    initialise() {
        this.loadEvents();
        this.loadTopMembers();
        this.loadCharts();
        this.loadWhatsNew();
    }

    loadMRRStats() {
        this.mrrStatsLoading = true;
        this.membersStats.fetchMRR().then((stats) => {
            this.mrrStatsLoading = false;
            const statsData = stats.data || [];
            const defaultCurrency = this.getDefaultCurrency() || 'usd';
            let currencyStats = statsData.find((stat) => {
                return stat.currency === defaultCurrency;
            });
            currencyStats = currencyStats || {
                data: [],
                currency: defaultCurrency
            };
            if (currencyStats) {
                const currencyStatsData = this.membersStats.fillDates(currencyStats.data) || {};
                const dateValues = Object.values(currencyStatsData).map(val => val / 100);
                const currentMRR = dateValues.length ? dateValues[dateValues.length - 1] : 0;
                const rangeStartMRR = dateValues.length ? dateValues[0] : 0;
                const percentGrowth = rangeStartMRR !== 0 ? ((currentMRR - rangeStartMRR) / rangeStartMRR) * 100 : 0;
                this.mrrStatsData = {
                    currentAmount: currentMRR,
                    currency: getSymbol(currencyStats.currency),
                    percentGrowth: percentGrowth.toFixed(1),
                    percentClass: (percentGrowth > 0 ? 'positive' : (percentGrowth < 0 ? 'negative' : '')),
                    options: {
                        rangeInDays: 30
                    },
                    data: {
                        label: 'MRR',
                        dateLabels: Object.keys(currencyStatsData),
                        dateValues
                    },
                    title: 'MRR',
                    stats: currencyStats
                };
            }
        }, (error) => {
            this.mrrStatsError = error;
            this.mrrStatsLoading = false;
        });
    }

    loadMemberCountStats() {
        this.memberCountStatsLoading = true;
        this.membersStats.fetchCounts().then((stats) => {
            this.memberCountStatsLoading = false;

            if (stats) {
                const statsDateObj = this.membersStats.fillCountDates(stats.data) || {};
                const dateValues = Object.values(statsDateObj);
                const currentAllCount = dateValues.length ? dateValues[dateValues.length - 1].total : 0;
                const currentPaidCount = dateValues.length ? dateValues[dateValues.length - 1].paid : 0;
                const rangeStartAllCount = dateValues.length ? dateValues[0].total : 0;
                const rangeStartPaidCount = dateValues.length ? dateValues[0].paid : 0;
                const allCountPercentGrowth = rangeStartAllCount !== 0 ? ((currentAllCount - rangeStartAllCount) / rangeStartAllCount) * 100 : 0;
                const paidCountPercentGrowth = rangeStartPaidCount !== 0 ? ((currentPaidCount - rangeStartPaidCount) / rangeStartPaidCount) * 100 : 0;

                this.memberCountStatsData = {
                    all: {
                        percentGrowth: allCountPercentGrowth.toFixed(1),
                        percentClass: (allCountPercentGrowth > 0 ? 'positive' : (allCountPercentGrowth < 0 ? 'negative' : '')),
                        total: dateValues.length ? dateValues[dateValues.length - 1].total : 0,
                        options: {
                            rangeInDays: 30
                        },
                        data: {
                            label: 'Members',
                            dateLabels: Object.keys(statsDateObj),
                            dateValues: dateValues.map(d => d.total)
                        },
                        title: 'Total Members',
                        stats: stats
                    },
                    paid: {
                        percentGrowth: paidCountPercentGrowth.toFixed(1),
                        percentClass: (paidCountPercentGrowth > 0 ? 'positive' : (paidCountPercentGrowth < 0 ? 'negative' : '')),
                        total: dateValues.length ? dateValues[dateValues.length - 1].paid : 0,
                        options: {
                            rangeInDays: 30
                        },
                        data: {
                            label: 'Members',
                            dateLabels: Object.keys(statsDateObj),
                            dateValues: dateValues.map(d => d.paid)
                        },
                        title: 'Paid Members',
                        stats: stats
                    }
                };
            }
        }, (error) => {
            this.memberCountStatsError = error;
            this.memberCountStatsLoading = false;
        });
    }

    loadCharts() {
        this.loadMRRStats();
        this.loadMemberCountStats();
        this.loadNewsletterOpenRates();
    }

    loadEvents() {
        this.eventsLoading = true;
        this.membersStats.fetchTimeline({limit: 5}).then(({events}) => {
            this.eventsData = events;
            this.eventsLoading = false;
        }, (error) => {
            this.eventsError = error;
            this.eventsLoading = false;
        });
    }

    loadNewsletterOpenRates() {
        this.newsletterOpenRatesLoading = true;
        this.membersStats.fetchNewsletterStats().then((results) => {
            const rangeStartOpenRate = results.length > 1 ? results[results.length - 2].openRate : 0;
            const rangeEndOpenRate = results.length > 0 ? results[results.length - 1].openRate : 0;
            const percentGrowth = rangeStartOpenRate !== 0 ? ((rangeEndOpenRate - rangeStartOpenRate) / rangeStartOpenRate) * 100 : 0;
            this.newsletterOpenRatesData = {
                percentGrowth: percentGrowth.toFixed(1),
                percentClass: (percentGrowth > 0 ? 'positive' : (percentGrowth < 0 ? 'negative' : '')),
                current: rangeEndOpenRate,
                options: {
                    rangeInDays: 30
                },
                data: {
                    label: 'Open rate',
                    dateLabels: results.map(d => d.subject),
                    dateValues: results.map(d => d.openRate)
                },
                title: 'Open rate',
                stats: results
            };
            this.newsletterOpenRatesLoading = false;
        }, (error) => {
            this.newsletterOpenRatesError = error;
            this.newsletterOpenRatesLoading = false;
        });
    }

    loadTopMembers() {
        this.topMembersLoading = true;
        let query = {
            filter: 'email_open_rate:-null',
            order: 'email_open_rate desc',
            limit: 5
        };
        this.store.query('member', query).then((result) => {
            if (!result.length) {
                return this.store.query('member', {
                    filter: 'status:paid',
                    order: 'created_at asc',
                    limit: 5
                });
            }
            return result;
        }).then((result) => {
            this.topMembersData = result;
            this.topMembersLoading = false;
        }).catch((error) => {
            this.topMembersError = error;
            this.topMembersLoading = false;
        });
    }

    loadWhatsNew() {
        this.whatsNewEntriesLoading = true;
        this.whatsNew.fetchLatest.perform().then(() => {
            this.whatsNewEntriesLoading = false;
            this.whatsNewEntries = this.whatsNew.entries.slice(0, 3);
        }, (error) => {
            this.whatsNewEntriesError = error;
            this.whatsNewEntriesLoading = false;
        });
    }

    getDefaultCurrency() {
        const plans = this.settings.get('stripePlans') || [];
        const monthly = plans.find(plan => plan.interval === 'month');
        return monthly && monthly.currency;
    }

    @action
    dismissLaunchBanner() {
        this.feature.set('launchComplete', true);
    }
}
<|endoftext|>