<commit_before>"use strict";

var Q = require('q');

var fs = require('fs');
var markdown = require('markdown').markdown;

function reduceStep(result, step) {
  result.steps.push({
    $: "STEP",
    ref: result.ref,
    match: step[1]
  });
  return result;
}

// Reduces in Θ(n)
function renderx(result, step) {
  var type = step[0];
  switch (type) {
    case 'header':
      var level = step[1].level;
      // Recursion is not on the stack, it is in the result object
      if (!result.parent || level > result.parent.level) {
        return {
          $: 'SCENARIO',
          ref: result.ref,
          id: step.pop(),
          level: result.level + 1,
          steps: [],
          parent: result
        };
      }
      result.parent.steps.push(result);
      result = result.parent;
      delete result.parent;
      break;
    case 'numberlist':
    case 'bulletlist':
      step.shift();
      step.reduce(reduceStep, result);
      break;
    case "para":
      reduceStep(result, step);
      break;
    default:
      throw Error("Unexpected Markdown " + type);
  }
  return result;
}

// Θ(n) recursion to render flat JsonML into object hierarchy
function render(markdown, ref, level) {
  var result;
  if (Array.isArray(markdown)) {
    var type = markdown.shift();
    switch (type) {
      case 'markdown':
        result = {
          $: 'SCENARIO',
          ref: ref,
          steps: markdown.reduce(function(steps, step) {
            return steps.concat(render(step, ref, 0));
          }, [])
        };
        break;
      case 'header':
        console.log(markdown);
        break;
      case 'bulletlist':
      case 'numberlist':
      case 'listitem':
        result = markdown.map(function(step) {
          return render(step, ref, level);
        });
        break;
      default:
        throw new Error("Unsupported Markdown " + type);
    }
  } else {
    result = {
      $: 'STEP',
      ref: ref,
      symbol: markdown
    };
  }
  if (result === undefined) {
    throw new Error("Unsupported Markdown " + markdown);
  }
  return result;
}

module.exports = function interpret(resource) {
  return Q.when(resource).then(function() {
    // Read resource
    return Q.nfcall(fs.readFile, resource);
  }).then(function(buffer) {
    // Parse markdown
    return markdown.parse(buffer.toString());
  }).then(function(markdown) {
    // Render from Markdown JsonML flat list to scenario hierarchy
    return render(markdown, resource);
  });
};<commit_msg>Working markdown renderer for simplest test scenario.<commit_after>"use strict";

var Q = require('q');

var fs = require('fs');
var markdown = require('markdown').markdown;

function reduceStep(result, step) {
  result.steps.push({
    $: "STEP",
    ref: result.ref,
    match: step[1]
  });
  return result;
}

// Reduces in Θ(n)
function renderx(result, step) {
  var type = step[0];
  switch (type) {
    case 'header':
      var level = step[1].level;
      // Recursion is not on the stack, it is in the result object
      if (!result.parent || level > result.parent.level) {
        return {
          $: 'SCENARIO',
          ref: result.ref,
          id: step.pop(),
          level: result.level + 1,
          steps: [],
          parent: result
        };
      }
      result.parent.steps.push(result);
      result = result.parent;
      delete result.parent;
      break;
    case 'numberlist':
    case 'bulletlist':
      step.shift();
      step.reduce(reduceStep, result);
      break;
    case "para":
      reduceStep(result, step);
      break;
    default:
      throw Error("Unexpected Markdown " + type);
  }
  return result;
}

// Θ(n) recursion to render flat JsonML into object hierarchy
function render(markdown, ref, level) {
  var result;
  if (Array.isArray(markdown)) {
    var type = markdown.shift();
    switch (type) {
      case 'markdown':
        result = {
          $: 'SCENARIO',
          ref: ref,
          steps: markdown.reduce(function(steps, step) {
            return steps.concat(render(step, ref, 0));
          }, [])
        };
        break;
      case 'header':
        console.log(markdown);
        break;
      case 'bulletlist':
      case 'numberlist':
      case 'listitem':
        result = markdown.reduce(function(steps, step) {
          return steps.concat(render(step, ref, level));
        }, []);
        break;
      default:
        throw new Error("Unsupported Markdown " + type);
    }
  } else {
    result = {
      $: 'STEP',
      ref: ref,
      symbol: markdown
    };
  }
  if (result === undefined) {
    throw new Error("Unsupported Markdown " + markdown);
  }
  return result;
}

module.exports = function interpret(resource) {
  return Q.when(resource).then(function() {
    // Read resource
    return Q.nfcall(fs.readFile, resource);
  }).then(function(buffer) {
    // Parse markdown
    return markdown.parse(buffer.toString());
  }).then(function(markdown) {
    // Render from Markdown JsonML flat list to scenario hierarchy
    return render(markdown, resource);
  });
};<|endoftext|>


<commit_before>(function () {
    'use strict';

    /**
     * @ngdoc directive
     * @name talend.widget.directive:rangeSlider
     * @description This directive renders the rangeSlider.
     * @restrict E
     * @usage
     *     <range-slider
     *             id="domId"
     *             width="250"
     *             height="100"
     *             range-limits="statsDetailsCtrl.rangeLimits"
     *             on-brush-end="statsDetailsCtrl.onBrushEndFn"
     *         ></range-slider>
     * @param {string} id The element id
     * @param {number} width The width of the slider
     * @param {number} height The height of the slider
     * @param {object} rangeLimits {min: number, max: number} The limits of the slider
     * @param {function} onBrushEnd The callback on slider move
     * */

    function RangeSlider() {
        return {
            restrict: 'E',
            scope: {
                rangeLimits: '=',
                onBrushEnd: '&'
            },
            controller: 'RangeSliderCtrl',
            controllerAs: 'rangeSliderCtrl',
            link: function (scope, element, attrs, ctrl) {
                var renderTimeout;
                var container = attrs.id;

                var margin = {top: 5, right: 25, bottom: 10, left: 10};
                var width = attrs.width - margin.left - margin.right;
                var height = attrs.height - margin.top - margin.bottom;

                /**
                 * @ngdoc method
                 * @name renderRangerSlider
                 * @methodOf data-prep.rangeSlider.directive:RangeSlider
                 * @description Render the slider and attach the actions listeners
                 **/
                function renderRangerSlider() {
                    var rangeLimits = scope.rangeLimits;

                    var minimum = rangeLimits.min;
                    var maximum = rangeLimits.max;

                    var minBrush = typeof rangeLimits.minBrush !== 'undefined' ? rangeLimits.minBrush : minimum;
                    var maxBrush = typeof rangeLimits.maxBrush !== 'undefined' ? rangeLimits.maxBrush : maximum;

                    var nbDecimals = d3.max([ctrl.decimalPlaces(minBrush), ctrl.decimalPlaces(maxBrush)]);
                    var centerValue = (minBrush + maxBrush) / 2;

                    //--------------------------------------------------------------------------------------------------
                    //----------------------------------------------BASE------------------------------------------------
                    //--------------------------------------------------------------------------------------------------
                    var scale = d3.scale.linear()
                        .domain([minimum, maximum])
                        .range([0, width]);

                    var svg = d3.select('#' + container).append('svg')
                        .attr('width', width + margin.left + margin.right)
                        .attr('height', height + margin.top + margin.bottom)
                        .attr('class', 'range-slider-cls')
                        .append('g')
                        .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');

                    //--------------------------------------------------------------------------------------------------
                    //----------------------------------------------INPUTS----------------------------------------------
                    //--------------------------------------------------------------------------------------------------
                    svg.append('g').append('foreignObject')
                        .attr('width', width)
                        .attr('height', 30)
                        .attr('transform', 'translate(0,' + (height - 45) + ')')
                        .append('xhtml:div')
                        .html('Min <input type="text" name="minRange"> Max  <input type="text" name="maxRange"/>');

                    //It will update Min and Max inputs with the correspondent brush extents
                    function fillInputs() {
                        hideMsgErr();
                        var s = scope.brush.extent();
                        document.getElementsByName('minRange')[0].value = s[0] > 1e4 || s[0] < -1e4 ? d3.format('e')(s[0].toFixed(nbDecimals)) : s[0].toFixed(nbDecimals);
                        document.getElementsByName('maxRange')[0].value = s[1] > 1e4 || s[1] < -1e4 ? d3.format('e')(s[1].toFixed(nbDecimals)) : s[1].toFixed(nbDecimals);
                        return s;
                    }

                    //stop event propagation
                    function stopPropagation(e) {
                        e.stopPropagation();
                    }

                    //create a key listener closure
                    var minCorrectness, maxCorrectness;
                    function handleKey(rangeType) {
                        return function (e) {
                            switch (e.keyCode) {
                                case 9:
                                case 13:
                                    if (minCorrectness !== null && maxCorrectness !== null) {
                                        adjustBrush();
                                    }
                                    else {
                                        fillInputs();
                                        hideMsgErr();
                                    }
                                    break;
                                case 27:
                                    fillInputs();
                                    break;
                                default:
                                    minCorrectness = rangeType === 'min' ? ctrl.toNumber(this.value) : minCorrectness;
                                    maxCorrectness = rangeType === 'max' ? ctrl.toNumber(this.value) : maxCorrectness;

                                    if (minCorrectness === null || maxCorrectness === null) {
                                        showMsgErr();
                                    }
                                    else {
                                        hideMsgErr();
                                    }
                            }
                        };
                    }

                    //attach events on inputs
                    function initRangeInputsListeners() {
                        var minRange = document.getElementsByName('minRange')[0];
                        var maxRange = document.getElementsByName('maxRange')[0];

                        minRange.onblur = adjustBrush;
                        minRange.onkeydown = stopPropagation;
                        minRange.onkeyup = handleKey('min');

                        maxRange.onblur = adjustBrush;
                        maxRange.onkeydown = stopPropagation;
                        maxRange.onkeyup = handleKey('max');
                    }

                    //--------------------------------------------------------------------------------------------------
                    //----------------------------------------------BRUSH-----------------------------------------------
                    //--------------------------------------------------------------------------------------------------
                    scope.brush = d3.svg.brush()
                        .x(scale)
                        .extent([centerValue, centerValue]);

                    var brushg = svg.append('g')
                        .attr('transform', 'translate(0,' + (margin.top + 10) + ')')
                        .attr('class', 'brush')
                        .call(scope.brush);

                    brushg.call(scope.brush.event)
                        .transition().duration(400)
                        .call(scope.brush.extent([minBrush, maxBrush]));

                    brushg.selectAll('.resize')
                        .append('rect')
                        .attr('transform', function (d, i) {
                            return i ? 'translate(-10, 0)' : 'translate(0,0)';
                        })
                        .attr('width', 10);

                    brushg.selectAll('rect')
                        .attr('height', 20);

                    brushg.select('.extent')
                        .attr('height', 14)
                        .attr('transform', 'translate(0,3)');

                    //It will update the brush handlers position and propagate the filter
                    function adjustBrush() {
                        scope.oldRangeLimits = scope.brush.extent();
                        var enteredMin = ctrl.toNumber(document.getElementsByName('minRange')[0].value);
                        var enteredMax = ctrl.toNumber(document.getElementsByName('maxRange')[0].value);

                        if (enteredMax === null || enteredMin === null) {
                            return;
                        }

                        nbDecimals = d3.max([ctrl.decimalPlaces(enteredMin), ctrl.decimalPlaces(enteredMax)]);
                        var finalExtent = ctrl.adaptRangeValues(enteredMin, enteredMax, minimum, maximum, nbDecimals);

                        brushg.transition()
                            .call(scope.brush.extent(finalExtent));

                        fillInputs(); //should be after brush update

                        if(scope.oldRangeLimits[0] !== scope.brush.extent()[0] || scope.oldRangeLimits[1] !== scope.brush.extent()[1]) {
                            //trigger brush end callback
                            scope.onBrushEnd()(scope.brush.extent().map(function (n) {
                                return +n.toFixed(nbDecimals);
                            }));
                        }
                    }

                    //attach brush listeners
                    function initBrushListeners() {
                        scope.brush
                            //Will memorize the ancient extent
                            .on('brushstart', function brushstart (){
                                scope.oldRangeLimits = scope.brush.extent();
                            })

                            //It will update the min and max inputs, and create a brush on a single value when the user clics on the slider without making a drag( the created brush will be empty )
                            .on('brush', function brushmove() {
                                var newExtent = fillInputs();
                                if (scope.brush.empty()) {
                                    var exp = '1e-' + (nbDecimals + 1);
                                    svg.select('.brush').call(scope.brush.clear().extent([newExtent[0], newExtent[1] + Number(exp)]));
                                }
                            })

                            //It will propagate the new filter limits to the rest of the app, it's triggered when the user finishes a brush
                            .on('brushend', function brushend() {
                                if(scope.oldRangeLimits[0] !== scope.brush.extent()[0] || scope.oldRangeLimits[1] !== scope.brush.extent()[1]){
                                    //trigger filter process in the datagrid
                                    scope.onBrushEnd()(scope.brush.extent().map(function (n) {
                                        return +n.toFixed(nbDecimals);
                                    }));
                                }
                            });
                    }

                    //--------------------------------------------------------------------------------------------------
                    //----------------------------------------------X AXIS----------------------------------------------
                    //--------------------------------------------------------------------------------------------------
                    svg.append('g')
                        .attr('class', 'x axis')
                        .attr('transform', 'translate(0,' + (margin.top + 20) + ')')
                        .call(d3.svg.axis().scale(scale).orient('bottom').ticks(Math.abs(scale.range()[1] - scale.range()[0]) / 50)
                            .tickFormat(function (d) {
                                if (d > 1e4 || d < -1e4) {
                                    return d3.format('e')(d);
                                }
                                else {
                                    return d3.format(',')(d);
                                }
                            })
                        )
                        .selectAll('text').attr('y', 13);

                    svg.append('g').append('text')
                        .attr('class', 'the-minimum-label')
                        .attr('x', -10)
                        .attr('y', margin.top + 5)
                        .attr('text-anchor', 'start')
                        .attr('fill', 'grey')
                        .text(minimum);

                    svg.append('g').append('text')
                        .attr('class', 'the-maximum-label')
                        .attr('x', width+10)
                        .attr('y', margin.top + 5)
                        .attr('text-anchor', 'end')
                        .attr('fill', 'grey')
                        .text(maximum);
                    //--------------------------------------------------------------------------------------------------
                    //--------------------------------------------ERROR TEXT--------------------------------------------
                    //--------------------------------------------------------------------------------------------------
                    svg.append('g').append('text')
                        .attr('class', 'invalid-value-msg')
                        .attr('x', width / 2)
                        .attr('y', height)
                        .attr('text-anchor', 'middle')
                        .attr('fill', 'red');

                    //shows the message Error with details on comma existence
                    function showMsgErr() {
                        var msgErr = 'Invalid Entered Value';
                        var minMaxStr = document.getElementsByName('minRange')[0].value + document.getElementsByName('maxRange')[0].value;
                        var finalMsgErr = ctrl.checkCommaExistence(minMaxStr) ? msgErr + ': Use "." instead of ","' : msgErr;
                        d3.select('text.invalid-value-msg').text(finalMsgErr);
                    }

                    //hides the message Error
                    function hideMsgErr() {
                        d3.select('text.invalid-value-msg').text('');
                    }

                    //--------------------------------------------------------------------------------------------------
                    //-----------------------------------------------INIT-----------------------------------------------
                    //--------------------------------------------------------------------------------------------------
                    fillInputs();
                    initRangeInputsListeners();
                    initBrushListeners();

                    //In case of a single value filter
                    if (scope.brush.empty()) {
                        var exp = '1e-' + (nbDecimals + 1);
                        svg.select('.brush').call(scope.brush.clear().extent([minBrush, maxBrush + Number(exp)]));

                        //the case where there is a filter not responding to the range limits -- caused by sample size change
                        if (scope.rangeLimits.minFilterVal && scope.rangeLimits.maxFilterVal){
                            document.getElementsByName('minRange')[0].value = scope.rangeLimits.minFilterVal;
                            document.getElementsByName('maxRange')[0].value = scope.rangeLimits.maxFilterVal;
                        }
                    }
                }

                scope.$watch('rangeLimits',
                    function (rangeLimits) {
                        element.empty();
                        if ((rangeLimits) && (rangeLimits.min !== rangeLimits.max)) {
                            clearTimeout(renderTimeout);
                            renderTimeout = setTimeout(renderRangerSlider, 100);
                        }
                    });
            }
        };
    }

    angular.module('talend.widget')
        .directive('rangeSlider', RangeSlider);
})();<commit_msg>TDP-392 WIP<commit_after>(function () {
    'use strict';

    /**
     * @ngdoc directive
     * @name talend.widget.directive:rangeSlider
     * @description This directive renders the rangeSlider.
     * @restrict E
     * @usage
     *     <range-slider
     *             id="domId"
     *             width="250"
     *             height="100"
     *             range-limits="statsDetailsCtrl.rangeLimits"
     *             on-brush-end="statsDetailsCtrl.onBrushEndFn"
     *         ></range-slider>
     * @param {string} id The element id
     * @param {number} width The width of the slider
     * @param {number} height The height of the slider
     * @param {object} rangeLimits {min: number, max: number} The limits of the slider
     * @param {function} onBrushEnd The callback on slider move
     * */

    function RangeSlider() {
        return {
            restrict: 'E',
            scope: {
                rangeLimits: '=',
                onBrushEnd: '&'
            },
            controller: 'RangeSliderCtrl',
            controllerAs: 'rangeSliderCtrl',
            link: function (scope, element, attrs, ctrl) {
                var renderTimeout;
                var container = attrs.id;

                var margin = {top: 5, right: 25, bottom: 10, left: 10};
                var width = attrs.width - margin.left - margin.right;
                var height = attrs.height - margin.top - margin.bottom;

                /**
                 * @ngdoc method
                 * @name renderRangerSlider
                 * @methodOf data-prep.rangeSlider.directive:RangeSlider
                 * @description Render the slider and attach the actions listeners
                 **/
                function renderRangerSlider() {
                    var rangeLimits = scope.rangeLimits;

                    var minimum = rangeLimits.min;
                    var maximum = rangeLimits.max;

                    var minBrush = typeof rangeLimits.minBrush !== 'undefined' ? rangeLimits.minBrush : minimum;
                    var maxBrush = typeof rangeLimits.maxBrush !== 'undefined' ? rangeLimits.maxBrush : maximum;

                    var nbDecimals = d3.max([ctrl.decimalPlaces(minBrush), ctrl.decimalPlaces(maxBrush)]);
                    var centerValue = (minBrush + maxBrush) / 2;

                    //--------------------------------------------------------------------------------------------------
                    //----------------------------------------------BASE------------------------------------------------
                    //--------------------------------------------------------------------------------------------------
                    var scale = d3.scale.linear()
                        .domain([minimum, maximum])
                        .range([0, width]);

                    var svg = d3.select('#' + container).append('svg')
                        .attr('width', width + margin.left + margin.right)
                        .attr('height', height + margin.top + margin.bottom)
                        .attr('class', 'range-slider-cls')
                        .append('g')
                        .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');

                    //--------------------------------------------------------------------------------------------------
                    //----------------------------------------------INPUTS----------------------------------------------
                    //--------------------------------------------------------------------------------------------------
                    svg.append('g').append('foreignObject')
                        .attr('width', width)
                        .attr('height', 30)
                        .attr('transform', 'translate(0,' + (height - 45) + ')')
                        .append('xhtml:div')
                        .html('Min <input type="text" name="minRange"> Max  <input type="text" name="maxRange"/>');

                    //It will update Min and Max inputs with the correspondent brush extents
                    function fillInputs() {
                        hideMsgErr();
                        var s = scope.brush.extent();
                        document.getElementsByName('minRange')[0].value = s[0] > 1e4 || s[0] < -1e4 ? d3.format('e')(s[0].toFixed(nbDecimals)) : s[0].toFixed(nbDecimals);
                        document.getElementsByName('maxRange')[0].value = s[1] > 1e4 || s[1] < -1e4 ? d3.format('e')(s[1].toFixed(nbDecimals)) : s[1].toFixed(nbDecimals);
                        return s;
                    }

                    //stop event propagation
                    function stopPropagation(e) {
                        e.stopPropagation();
                    }

                    //create a key listener closure
                    var minCorrectness, maxCorrectness;
                    function handleKey(rangeType) {
                        return function (e) {
                            switch (e.keyCode) {
                                case 9:
                                case 13:
                                    if (minCorrectness !== null && maxCorrectness !== null) {
                                        adjustBrush();
                                    }
                                    else {
                                        fillInputs();
                                        hideMsgErr();
                                    }
                                    break;
                                case 27:
                                    fillInputs();
                                    break;
                                default:
                                    minCorrectness = rangeType === 'min' ? ctrl.toNumber(this.value) : minCorrectness;
                                    maxCorrectness = rangeType === 'max' ? ctrl.toNumber(this.value) : maxCorrectness;

                                    if (minCorrectness === null || maxCorrectness === null) {
                                        showMsgErr();
                                    }
                                    else {
                                        hideMsgErr();
                                    }
                            }
                        };
                    }

                    //attach events on inputs
                    function initRangeInputsListeners() {
                        var minRange = document.getElementsByName('minRange')[0];
                        var maxRange = document.getElementsByName('maxRange')[0];

                        minRange.onblur = adjustBrush;
                        minRange.onkeydown = stopPropagation;
                        minRange.onkeyup = handleKey('min');

                        maxRange.onblur = adjustBrush;
                        maxRange.onkeydown = stopPropagation;
                        maxRange.onkeyup = handleKey('max');
                    }

                    //--------------------------------------------------------------------------------------------------
                    //----------------------------------------------BRUSH-----------------------------------------------
                    //--------------------------------------------------------------------------------------------------
                    scope.brush = d3.svg.brush()
                        .x(scale)
                        .extent([centerValue, centerValue]);

                    var brushg = svg.append('g')
                        .attr('transform', 'translate(0,' + (margin.top + 10) + ')')
                        .attr('class', 'brush')
                        .call(scope.brush);

                    brushg.call(scope.brush.event)
                        .transition().duration(400)
                        .call(scope.brush.extent([minBrush, maxBrush]));

                    brushg.selectAll('.resize')
                        .append('rect')
                        .attr('transform', function (d, i) {
                            return i ? 'translate(-10, 0)' : 'translate(0,0)';
                        })
                        .attr('width', 10);

                    brushg.selectAll('rect')
                        .attr('height', 20);

                    brushg.select('.extent')
                        .attr('height', 14)
                        .attr('transform', 'translate(0,3)');

                    //It will update the brush handlers position and propagate the filter
                    function adjustBrush() {
                        scope.oldRangeLimits = scope.brush.extent();
                        var enteredMin = ctrl.toNumber(document.getElementsByName('minRange')[0].value);
                        var enteredMax = ctrl.toNumber(document.getElementsByName('maxRange')[0].value);

                        if (enteredMax === null || enteredMin === null) {
                            return;
                        }

                        nbDecimals = d3.max([ctrl.decimalPlaces(enteredMin), ctrl.decimalPlaces(enteredMax)]);
                        var finalExtent = ctrl.adaptRangeValues(enteredMin, enteredMax, minimum, maximum, nbDecimals);

                        brushg.transition()
                            .call(scope.brush.extent(finalExtent));

                        fillInputs(); //should be after brush update

                        if(scope.oldRangeLimits[0] !== scope.brush.extent()[0] || scope.oldRangeLimits[1] !== scope.brush.extent()[1]) {
                            //trigger brush end callback
                            scope.onBrushEnd()(scope.brush.extent().map(function (n) {
                                return +n.toFixed(nbDecimals);
                            }));
                        }
                    }

                    //attach brush listeners
                    function initBrushListeners() {
                        scope.brush
                            //Will memorize the ancient extent
                            .on('brushstart', function brushstart (){
                                scope.oldRangeLimits = scope.brush.extent();
                            })

                            //It will update the min and max inputs, and create a brush on a single value when the user clics on the slider without making a drag( the created brush will be empty )
                            .on('brush', function brushmove() {
                                var newExtent = fillInputs();
                                if (scope.brush.empty()) {
                                    var exp = '1e-' + (nbDecimals + 1);
                                    svg.select('.brush').call(scope.brush.clear().extent([newExtent[0], newExtent[1] + Number(exp)]));
                                }
                            })

                            //It will propagate the new filter limits to the rest of the app, it's triggered when the user finishes a brush
                            .on('brushend', function brushend() {
                                if(scope.oldRangeLimits[0] !== scope.brush.extent()[0] || scope.oldRangeLimits[1] !== scope.brush.extent()[1]){
                                    //trigger filter process in the datagrid
                                    scope.onBrushEnd()(scope.brush.extent().map(function (n) {
                                        return +n.toFixed(nbDecimals);
                                    }));
                                }
                            });
                    }

                    //--------------------------------------------------------------------------------------------------
                    //----------------------------------------------X AXIS----------------------------------------------
                    //--------------------------------------------------------------------------------------------------
                    svg.append('g')
                        .attr('class', 'x axis')
                        .attr('transform', 'translate(0,' + (margin.top + 20) + ')')
                        .call(d3.svg.axis().scale(scale).orient('bottom').ticks(Math.abs(scale.range()[1] - scale.range()[0]) / 50)
                            .tickFormat(function (d) {
                                if (d > 1e4 || d < -1e4) {
                                    return d3.format('e')(d);
                                }
                                else {
                                    return d3.format(',')(d);
                                }
                            })
                        )
                        .selectAll('text').attr('y', 13);

                    svg.append('g').append('text')
                        .attr('class', 'the-minimum-label')
                        .attr('x', -10)
                        .attr('y', margin.top + 5)
                        .attr('text-anchor', 'start')
                        .attr('fill', 'grey')
                        .text(function(){
                            return minimum < -1e4 || minimum > 1e4 ? d3.format('e')(minimum): d3.format(',')(minimum);
                        });

                    svg.append('g').append('text')
                        .attr('class', 'the-maximum-label')
                        .attr('x', width+10)
                        .attr('y', margin.top + 5)
                        .attr('text-anchor', 'end')
                        .attr('fill', 'grey')
                        .text(function(){
                            return maximum < -1e4 || maximum > 1e4 ? d3.format('e')(maximum): d3.format(',')(maximum);
                        });
                    //--------------------------------------------------------------------------------------------------
                    //--------------------------------------------ERROR TEXT--------------------------------------------
                    //--------------------------------------------------------------------------------------------------
                    svg.append('g').append('text')
                        .attr('class', 'invalid-value-msg')
                        .attr('x', width / 2)
                        .attr('y', height)
                        .attr('text-anchor', 'middle')
                        .attr('fill', 'red');

                    //shows the message Error with details on comma existence
                    function showMsgErr() {
                        var msgErr = 'Invalid Entered Value';
                        var minMaxStr = document.getElementsByName('minRange')[0].value + document.getElementsByName('maxRange')[0].value;
                        var finalMsgErr = ctrl.checkCommaExistence(minMaxStr) ? msgErr + ': Use "." instead of ","' : msgErr;
                        d3.select('text.invalid-value-msg').text(finalMsgErr);
                    }

                    //hides the message Error
                    function hideMsgErr() {
                        d3.select('text.invalid-value-msg').text('');
                    }

                    //--------------------------------------------------------------------------------------------------
                    //-----------------------------------------------INIT-----------------------------------------------
                    //--------------------------------------------------------------------------------------------------
                    fillInputs();
                    initRangeInputsListeners();
                    initBrushListeners();

                    //In case of a single value filter
                    if (scope.brush.empty()) {
                        var exp = '1e-' + (nbDecimals + 1);
                        svg.select('.brush').call(scope.brush.clear().extent([minBrush, maxBrush + Number(exp)]));

                        //the case where there is a filter not responding to the range limits -- caused by sample size change
                        if (scope.rangeLimits.minFilterVal && scope.rangeLimits.maxFilterVal){
                            document.getElementsByName('minRange')[0].value = scope.rangeLimits.minFilterVal;
                            document.getElementsByName('maxRange')[0].value = scope.rangeLimits.maxFilterVal;
                        }
                    }
                }

                scope.$watch('rangeLimits',
                    function (rangeLimits) {
                        element.empty();
                        if ((rangeLimits) && (rangeLimits.min !== rangeLimits.max)) {
                            clearTimeout(renderTimeout);
                            renderTimeout = setTimeout(renderRangerSlider, 100);
                        }
                    });
            }
        };
    }

    angular.module('talend.widget')
        .directive('rangeSlider', RangeSlider);
})();<|endoftext|>


<commit_before>'use strict';

define(['app', 'RegulatoryDocuments'], function(app, RegulatoryDocuments) {
    app.controller('RegulatoryDocumentsList', [
        '$scope',
        '$location',
        'RegulatoryDocsFactory',
        function($scope, $location, RegulatoryDocsFactory) {
            $scope.all = function() {
                RegulatoryDocsFactory.query(function(regulatories) {
                    $scope.regulatories = regulatories;
                });
            };
        }
    ]);
});
<commit_msg>applied column filter, removed unused vars<commit_after>'use strict';

define(['app', 'RegulatoryDocuments'], function(app) {
    app.controller('RegulatoryDocumentsList', [
        '$scope',
        '$location',
        'RegulatoryDocsFactory',
        function($scope, $location, RegulatoryDocsFactory) {
            $scope.all = function() {
                RegulatoryDocsFactory.query({
                    select: "name,valid_until,document_no,date_issued"
                },function(regulatories) {
                    $scope.regulatories = regulatories;
                });
            };
        }
    ]);
});
<|endoftext|>


<commit_before>var Backbone = require('backbone');
var ConfigModel = require('../../../../javascripts/cartodb3/data/config-model');
var LayerDefinitionsCollection = require('../../../../javascripts/cartodb3/data/layer-definitions-collection');
var LayerDefinitionModel = require('../../../../javascripts/cartodb3/data/layer-definition-model');
var AnalysisDefinitionNodesCollection = require('../../../../javascripts/cartodb3/data/analysis-definition-nodes-collection');

describe('cartodb3/data/layer-definition-model', function () {
  beforeEach(function () {
    this.configModel = new ConfigModel({
      base_url: '/u/pepe'
    });

    this.analysisDefinitionNodesCollection = new AnalysisDefinitionNodesCollection(null, {
      configModel: this.configModel
    });

    this.collection = new LayerDefinitionsCollection(null, {
      configModel: this.configModel,
      analysisDefinitionNodesCollection: this.analysisDefinitionNodesCollection,
      mapId: 'm123'
    });
    this.model = new LayerDefinitionModel({
      id: 'abc-123',
      kind: 'carto',
      options: {
        type: 'CartoDB',
        color: '#FABADA',
        table_name: 'foo',
        query: 'SELECT * FROM foo',
        tile_style: 'asdasd',
        visible: true
      }
    }, {
      parse: true,
      configModel: this.configModel,
      collection: this.collection
    });
  });

  it('should transform some attrs to be compatible with cartodb.js', function () {
    expect(this.model.get('cartocss')).toEqual('asdasd');
    expect(this.model.get('tile_style')).toBeUndefined();

    expect(this.model.get('sql')).toContain('SELECT');
    expect(this.model.get('query')).toBeUndefined();
  });

  describe('.toJSON', function () {
    it('should return the original data', function () {
      expect(this.model.toJSON()).toEqual({
        id: 'abc-123',
        kind: 'carto',
        options: {
          type: 'CartoDB',
          visible: true,
          color: '#FABADA',
          table_name: 'foo',
          query: 'SELECT * FROM foo',
          tile_style: 'asdasd',
          style_properties: jasmine.any(Object),
          cartocss_history: jasmine.any(Array),
          sql_history: jasmine.any(Array)
        }
      });
    });
  });

  describe('styleModel', function () {
    it('should create style model if table name attribute exists', function () {
      expect(this.model.styleModel).toBeDefined();
    });

    it('should not add style model if table name doesn\'t exist', function () {
      var mdl = new LayerDefinitionModel({
        id: 'other',
        options: {
          type: 'tiled',
          tile_style: 'asdasd'
        }
      }, {
        parse: true,
        configModel: this.configModel
      });

      expect(mdl.styleModel).toBeUndefined();
    });

    describe('.toJSON', function () {
      it('should provide style model definition', function () {
        spyOn(this.model.styleModel, 'isAutogenerated').and.returnValue(false);
        var data = this.model.toJSON();
        expect(data.options.style_properties).toBeDefined();
      });

      it('should not provide style definition if autogenerated option is true', function () {
        spyOn(this.model.styleModel, 'isAutogenerated').and.returnValue(true);
        var data = this.model.toJSON();
        expect(data.options.style_properties).not.toBeDefined();
      });
    });
  });

  describe('.isOwnerOfAnalysisNode', function () {
    beforeEach(function () {
      this.nodeModel = this.analysisDefinitionNodesCollection.add({
        id: 'b3',
        type: 'source',
        params: {
          query: 'SELECT * FROM somewhere'
        }
      });
    });

    it('should return true if given layer definition model is considered owning it', function () {
      expect(this.model.isOwnerOfAnalysisNode(this.nodeModel)).toBe(false);
      this.model.set('letter', 'b');
      expect(this.model.isOwnerOfAnalysisNode(this.nodeModel)).toBe(true);
    });
  });

  describe('for a layer with an analysis source', function () {
    beforeEach(function () {
      this.model = new LayerDefinitionModel({
        id: 'abc-123',
        kind: 'carto',
        options: {
          type: 'CartoDB',
          color: '#FABADA',
          table_name: 'foo_table',
          source: 'a1'
        }
      }, {
        parse: true,
        configModel: this.configModel
      });
    });

    it('should have a source set', function () {
      expect(this.model.get('source')).toEqual('a1');
    });

    describe('.toJSON', function () {
      it('should return the original data', function () {
        expect(this.model.toJSON()).toEqual({
          id: 'abc-123',
          kind: 'carto',
          options: {
            type: 'CartoDB',
            color: '#FABADA',
            table_name: 'foo_table',
            source: 'a1',
            style_properties: jasmine.any(Object),
            cartocss_history: jasmine.any(Array),
            sql_history: jasmine.any(Array)
          }
        });
      });
    });
  });

  describe('for a layer with an infowindow', function () {
    beforeEach(function () {
      this.configModel = new ConfigModel({
        base_url: '/u/pepe'
      });

      this.model = new LayerDefinitionModel({
        id: 'abc-123',
        kind: 'carto',
        options: {
          type: 'CartoDB',
          color: '#FABADA',
          table_name: 'foo_table',
          source: 'a1',
          style_properties: jasmine.any(Object),
          cartocss_history: jasmine.any(Array),
          sql_history: jasmine.any(Array)
        },
        infowindow: {
          template_name: 'infowindow_light',
          latlng: [0, 0],
          offset: [28, 0],
          maxHeight: 180,
          autoPan: true,
          template: '',
          content: '',
          visibility: false,
          alternative_names: {},
          fields: [
            {
              name: 'description',
              title: true,
              position: 0
            },
            {
              name: 'name',
              title: true,
              position: 1
            }
          ],
          width: 226,
          headerColor: {
            color: { fixed: '#35AAE5;', opacity: 1 }
          }
        }
      }, {
        parse: true,
        configModel: this.configModel
      });
      this.infowindowModel = this.model.infowindowModel;
    });

    it('should have an infowindow model', function () {
      expect(this.infowindowModel).toBeDefined();
      expect(this.infowindowModel.get('fields').length).toEqual(2);
    });

    describe('infowindow/tooltip reset', function () {
      beforeEach(function () {
        this.model.tooltipModel = new Backbone.Model();
        this.model.tooltipModel.unsetTemplate = function () {};
        this.model.infowindowModel = new Backbone.Model();
        this.model.infowindowModel.unsetTemplate = function () {};

        spyOn(this.model.tooltipModel, 'unsetTemplate');
        spyOn(this.model.infowindowModel, 'unsetTemplate');
        this.styleModel = this.model.styleModel;
      });

      it('should reset infowindow/tooltip template and fields if style model type is aggregated', function () {
        this.styleModel.set('type', 'heatmap');
        expect(this.model.infowindowModel.unsetTemplate).not.toHaveBeenCalled();
        expect(this.model.tooltipModel.unsetTemplate).not.toHaveBeenCalled();
        this.styleModel.set('type', 'squares');
        expect(this.model.infowindowModel.unsetTemplate).toHaveBeenCalled();
        expect(this.model.tooltipModel.unsetTemplate).toHaveBeenCalled();
      });

      it('should reset infowindow/tooltip template and fields if style model has animated enabled', function () {
        this.styleModel.set('type', 'simple');
        expect(this.model.infowindowModel.unsetTemplate).not.toHaveBeenCalled();
        expect(this.model.tooltipModel.unsetTemplate).not.toHaveBeenCalled();
        this.styleModel.set('type', 'animation');
        expect(this.model.infowindowModel.unsetTemplate).toHaveBeenCalled();
        expect(this.model.tooltipModel.unsetTemplate).toHaveBeenCalled();
      });
    });

    it('should not save the model if infowindow changes', function () {
      spyOn(this.model, 'save');
      this.infowindowModel.set('template_name', 'infowindow_dark');
      expect(this.model.save).not.toHaveBeenCalled();
    });

    describe('.toJSON', function () {
      it('should modify infowindow attribute', function () {
        this.infowindowModel.setTemplate('testing');
        var data = this.model.toJSON();
        expect(data.infowindow.template_name).toEqual('testing');
      });

      it('should return the original data', function () {
        expect(this.model.toJSON()).toEqual({
          id: 'abc-123',
          kind: 'carto',
          options: {
            type: 'CartoDB',
            color: '#FABADA',
            table_name: 'foo_table',
            source: 'a1',
            style_properties: jasmine.any(Object),
            cartocss_history: jasmine.any(Array),
            sql_history: jasmine.any(Array)
          },
          infowindow: {
            template_name: 'infowindow_light',
            latlng: [0, 0],
            offset: [28, 0],
            maxHeight: 180,
            autoPan: true,
            template: '',
            content: '',
            visibility: false,
            alternative_names: {},
            fields: [
              {
                name: 'description',
                title: true,
                position: 0
              },
              {
                name: 'name',
                title: true,
                position: 1
              }
            ],
            width: 226,
            headerColor: {
              color: { fixed: '#35AAE5;', opacity: 1 }
            }
          }
        });
      });

      it('should not provide infowindow data if model is empty', function () {
        this.infowindowModel.clear();

        expect(this.model.toJSON()).toEqual({
          id: 'abc-123',
          kind: 'carto',
          options: {
            type: 'CartoDB',
            color: '#FABADA',
            table_name: 'foo_table',
            source: 'a1',
            style_properties: jasmine.any(Object),
            cartocss_history: jasmine.any(Array),
            sql_history: jasmine.any(Array)
          }
        });
      });
    });
  });

  describe('.getAnalysisDefinitionNodeModel', function () {
    beforeEach(function () {
      this.a1 = {};
      spyOn(this.model, 'findAnalysisDefinitionNodeModel').and.returnValue(this.a1);
      this.model.set('source', 'a1');
      this.model.getAnalysisDefinitionNodeModel();
    });

    it('should return the current analysis model of layer', function () {
      expect(this.model.findAnalysisDefinitionNodeModel).toHaveBeenCalledWith('a1');
      expect(this.model.getAnalysisDefinitionNodeModel()).toBe(this.a1);
    });
  });

  describe('.findAnalysisDefinitionNodeModel', function () {
    beforeEach(function () {
      this.b1 = {id: 'b1'};
      spyOn(this.collection, 'findAnalysisDefinitionNodeModel');
      this.model.findAnalysisDefinitionNodeModel('b1');
    });

    it('should return the node for given id', function () {
      this.collection.findAnalysisDefinitionNodeModel.and.returnValue(this.b1);
      expect(this.model.findAnalysisDefinitionNodeModel('b1')).toBe(this.b1);
    });

    it('should return nothing given an id of a non-existing node', function () {
      expect(this.model.findAnalysisDefinitionNodeModel('x1')).toBeUndefined();
    });
  });

  describe('.toggleVisible', function () {
    it('should toggle the visible attr', function () {
      this.model.toggleVisible();
      expect(this.model.get('visible')).toBe(false);

      this.model.toggleVisible();
      expect(this.model.get('visible')).toBe(true);

      this.model.toggleVisible();
      expect(this.model.get('visible')).toBe(false);
    });
  });

  describe('.getNumberOfAnalyses', function () {
    beforeEach(function () {
      this.model.set('letter', 'a');
    });

    it("should return 0 if layer doesn't have a source node", function () {
      expect(this.model.getNumberOfAnalyses()).toEqual(0);
    });

    it('should return the number of analyses owned by the layer', function () {
      this.analysisDefinitionNodesCollection.add([{
        id: 'a0',
        type: 'source',
        params: {
          query: 'SELECT * FROM foo'
        }
      }, {
        id: 'a1',
        type: 'buffer',
        radio: 300,
        distance: 'meters',
        source: 'a0'
      }, {
        id: 'a2',
        type: 'buffer',
        radio: 600,
        distance: 'meters',
        source: 'a1'
      }, {
        id: 'b0',
        type: 'source',
        params: {
          query: 'SELECT * FROM bar'
        }
      }, {
        id: 'b1',
        type: 'buffer',
        radio: 300,
        distance: 'meters',
        source: 'b0'
      }]);

      this.model.set('source', 'a2');

      expect(this.model.getNumberOfAnalyses()).toEqual(3);
    });
  });

  describe('.containsNode', function () {
    beforeEach(function () {
      this.analysisDefinitionNodesCollection.add([{
        id: 'a0',
        type: 'source',
        params: {
          query: 'SELECT * FROM foo'
        }
      }, {
        id: 'a1',
        type: 'buffer',
        radio: 300,
        distance: 'meters',
        source: 'a0'
      }, {
        id: 'b1',
        type: 'buffer',
        radio: 300,
        distance: 'meters',
        source: 'a0'
      }]);
      this.model.set('source', 'a1');
    });

    it('should return true if layer contains given node', function () {
      expect(this.model.containsNode(this.analysisDefinitionNodesCollection.get('a1'))).toBe(true);
      expect(this.model.containsNode(this.analysisDefinitionNodesCollection.get('a0'))).toBe(true);

      expect(this.model.containsNode(this.analysisDefinitionNodesCollection.get('b1'))).toBe(false);
      expect(this.model.containsNode(false)).toBe(false);
    });
  });

  describe('.canBeDeletedByUser', function () {
    it('should return true as long as it is not the only data layer and not all the other layers depend on it', function () {
      expect(this.model.canBeDeletedByUser()).toBe(false);

      spyOn(this.collection, 'getNumberOfDataLayers').and.returnValue(3);
      expect(this.model.canBeDeletedByUser()).toBe(true);
    });
  });
});
<commit_msg>getAllDependentLayers layerDefinitionModel tests<commit_after>var Backbone = require('backbone');
var ConfigModel = require('../../../../javascripts/cartodb3/data/config-model');
var LayerDefinitionsCollection = require('../../../../javascripts/cartodb3/data/layer-definitions-collection');
var LayerDefinitionModel = require('../../../../javascripts/cartodb3/data/layer-definition-model');
var AnalysisDefinitionNodesCollection = require('../../../../javascripts/cartodb3/data/analysis-definition-nodes-collection');

describe('cartodb3/data/layer-definition-model', function () {
  beforeEach(function () {
    this.configModel = new ConfigModel({
      base_url: '/u/pepe'
    });

    this.analysisDefinitionNodesCollection = new AnalysisDefinitionNodesCollection(null, {
      configModel: this.configModel
    });

    this.collection = new LayerDefinitionsCollection(null, {
      configModel: this.configModel,
      analysisDefinitionNodesCollection: this.analysisDefinitionNodesCollection,
      mapId: 'm123'
    });
    this.model = new LayerDefinitionModel({
      id: 'abc-123',
      kind: 'carto',
      options: {
        type: 'CartoDB',
        color: '#FABADA',
        table_name: 'foo',
        query: 'SELECT * FROM foo',
        tile_style: 'asdasd',
        visible: true
      }
    }, {
      parse: true,
      configModel: this.configModel,
      collection: this.collection
    });
  });

  it('should transform some attrs to be compatible with cartodb.js', function () {
    expect(this.model.get('cartocss')).toEqual('asdasd');
    expect(this.model.get('tile_style')).toBeUndefined();

    expect(this.model.get('sql')).toContain('SELECT');
    expect(this.model.get('query')).toBeUndefined();
  });

  describe('.toJSON', function () {
    it('should return the original data', function () {
      expect(this.model.toJSON()).toEqual({
        id: 'abc-123',
        kind: 'carto',
        options: {
          type: 'CartoDB',
          visible: true,
          color: '#FABADA',
          table_name: 'foo',
          query: 'SELECT * FROM foo',
          tile_style: 'asdasd',
          style_properties: jasmine.any(Object),
          cartocss_history: jasmine.any(Array),
          sql_history: jasmine.any(Array)
        }
      });
    });
  });

  describe('styleModel', function () {
    it('should create style model if table name attribute exists', function () {
      expect(this.model.styleModel).toBeDefined();
    });

    it('should not add style model if table name doesn\'t exist', function () {
      var mdl = new LayerDefinitionModel({
        id: 'other',
        options: {
          type: 'tiled',
          tile_style: 'asdasd'
        }
      }, {
        parse: true,
        configModel: this.configModel
      });

      expect(mdl.styleModel).toBeUndefined();
    });

    describe('.toJSON', function () {
      it('should provide style model definition', function () {
        spyOn(this.model.styleModel, 'isAutogenerated').and.returnValue(false);
        var data = this.model.toJSON();
        expect(data.options.style_properties).toBeDefined();
      });

      it('should not provide style definition if autogenerated option is true', function () {
        spyOn(this.model.styleModel, 'isAutogenerated').and.returnValue(true);
        var data = this.model.toJSON();
        expect(data.options.style_properties).not.toBeDefined();
      });
    });
  });

  describe('.isOwnerOfAnalysisNode', function () {
    beforeEach(function () {
      this.nodeModel = this.analysisDefinitionNodesCollection.add({
        id: 'b3',
        type: 'source',
        params: {
          query: 'SELECT * FROM somewhere'
        }
      });
    });

    it('should return true if given layer definition model is considered owning it', function () {
      expect(this.model.isOwnerOfAnalysisNode(this.nodeModel)).toBe(false);
      this.model.set('letter', 'b');
      expect(this.model.isOwnerOfAnalysisNode(this.nodeModel)).toBe(true);
    });
  });

  describe('for a layer with an analysis source', function () {
    beforeEach(function () {
      this.model = new LayerDefinitionModel({
        id: 'abc-123',
        kind: 'carto',
        options: {
          type: 'CartoDB',
          color: '#FABADA',
          table_name: 'foo_table',
          source: 'a1'
        }
      }, {
        parse: true,
        configModel: this.configModel
      });
    });

    it('should have a source set', function () {
      expect(this.model.get('source')).toEqual('a1');
    });

    describe('.toJSON', function () {
      it('should return the original data', function () {
        expect(this.model.toJSON()).toEqual({
          id: 'abc-123',
          kind: 'carto',
          options: {
            type: 'CartoDB',
            color: '#FABADA',
            table_name: 'foo_table',
            source: 'a1',
            style_properties: jasmine.any(Object),
            cartocss_history: jasmine.any(Array),
            sql_history: jasmine.any(Array)
          }
        });
      });
    });
  });

  describe('for a layer with an infowindow', function () {
    beforeEach(function () {
      this.configModel = new ConfigModel({
        base_url: '/u/pepe'
      });

      this.model = new LayerDefinitionModel({
        id: 'abc-123',
        kind: 'carto',
        options: {
          type: 'CartoDB',
          color: '#FABADA',
          table_name: 'foo_table',
          source: 'a1',
          style_properties: jasmine.any(Object),
          cartocss_history: jasmine.any(Array),
          sql_history: jasmine.any(Array)
        },
        infowindow: {
          template_name: 'infowindow_light',
          latlng: [0, 0],
          offset: [28, 0],
          maxHeight: 180,
          autoPan: true,
          template: '',
          content: '',
          visibility: false,
          alternative_names: {},
          fields: [
            {
              name: 'description',
              title: true,
              position: 0
            },
            {
              name: 'name',
              title: true,
              position: 1
            }
          ],
          width: 226,
          headerColor: {
            color: { fixed: '#35AAE5;', opacity: 1 }
          }
        }
      }, {
        parse: true,
        configModel: this.configModel
      });
      this.infowindowModel = this.model.infowindowModel;
    });

    it('should have an infowindow model', function () {
      expect(this.infowindowModel).toBeDefined();
      expect(this.infowindowModel.get('fields').length).toEqual(2);
    });

    describe('infowindow/tooltip reset', function () {
      beforeEach(function () {
        this.model.tooltipModel = new Backbone.Model();
        this.model.tooltipModel.unsetTemplate = function () {};
        this.model.infowindowModel = new Backbone.Model();
        this.model.infowindowModel.unsetTemplate = function () {};

        spyOn(this.model.tooltipModel, 'unsetTemplate');
        spyOn(this.model.infowindowModel, 'unsetTemplate');
        this.styleModel = this.model.styleModel;
      });

      it('should reset infowindow/tooltip template and fields if style model type is aggregated', function () {
        this.styleModel.set('type', 'heatmap');
        expect(this.model.infowindowModel.unsetTemplate).not.toHaveBeenCalled();
        expect(this.model.tooltipModel.unsetTemplate).not.toHaveBeenCalled();
        this.styleModel.set('type', 'squares');
        expect(this.model.infowindowModel.unsetTemplate).toHaveBeenCalled();
        expect(this.model.tooltipModel.unsetTemplate).toHaveBeenCalled();
      });

      it('should reset infowindow/tooltip template and fields if style model has animated enabled', function () {
        this.styleModel.set('type', 'simple');
        expect(this.model.infowindowModel.unsetTemplate).not.toHaveBeenCalled();
        expect(this.model.tooltipModel.unsetTemplate).not.toHaveBeenCalled();
        this.styleModel.set('type', 'animation');
        expect(this.model.infowindowModel.unsetTemplate).toHaveBeenCalled();
        expect(this.model.tooltipModel.unsetTemplate).toHaveBeenCalled();
      });
    });

    it('should not save the model if infowindow changes', function () {
      spyOn(this.model, 'save');
      this.infowindowModel.set('template_name', 'infowindow_dark');
      expect(this.model.save).not.toHaveBeenCalled();
    });

    describe('.toJSON', function () {
      it('should modify infowindow attribute', function () {
        this.infowindowModel.setTemplate('testing');
        var data = this.model.toJSON();
        expect(data.infowindow.template_name).toEqual('testing');
      });

      it('should return the original data', function () {
        expect(this.model.toJSON()).toEqual({
          id: 'abc-123',
          kind: 'carto',
          options: {
            type: 'CartoDB',
            color: '#FABADA',
            table_name: 'foo_table',
            source: 'a1',
            style_properties: jasmine.any(Object),
            cartocss_history: jasmine.any(Array),
            sql_history: jasmine.any(Array)
          },
          infowindow: {
            template_name: 'infowindow_light',
            latlng: [0, 0],
            offset: [28, 0],
            maxHeight: 180,
            autoPan: true,
            template: '',
            content: '',
            visibility: false,
            alternative_names: {},
            fields: [
              {
                name: 'description',
                title: true,
                position: 0
              },
              {
                name: 'name',
                title: true,
                position: 1
              }
            ],
            width: 226,
            headerColor: {
              color: { fixed: '#35AAE5;', opacity: 1 }
            }
          }
        });
      });

      it('should not provide infowindow data if model is empty', function () {
        this.infowindowModel.clear();

        expect(this.model.toJSON()).toEqual({
          id: 'abc-123',
          kind: 'carto',
          options: {
            type: 'CartoDB',
            color: '#FABADA',
            table_name: 'foo_table',
            source: 'a1',
            style_properties: jasmine.any(Object),
            cartocss_history: jasmine.any(Array),
            sql_history: jasmine.any(Array)
          }
        });
      });
    });
  });

  describe('.getAnalysisDefinitionNodeModel', function () {
    beforeEach(function () {
      this.a1 = {};
      spyOn(this.model, 'findAnalysisDefinitionNodeModel').and.returnValue(this.a1);
      this.model.set('source', 'a1');
      this.model.getAnalysisDefinitionNodeModel();
    });

    it('should return the current analysis model of layer', function () {
      expect(this.model.findAnalysisDefinitionNodeModel).toHaveBeenCalledWith('a1');
      expect(this.model.getAnalysisDefinitionNodeModel()).toBe(this.a1);
    });
  });

  describe('.findAnalysisDefinitionNodeModel', function () {
    beforeEach(function () {
      this.b1 = {id: 'b1'};
      spyOn(this.collection, 'findAnalysisDefinitionNodeModel');
      this.model.findAnalysisDefinitionNodeModel('b1');
    });

    it('should return the node for given id', function () {
      this.collection.findAnalysisDefinitionNodeModel.and.returnValue(this.b1);
      expect(this.model.findAnalysisDefinitionNodeModel('b1')).toBe(this.b1);
    });

    it('should return nothing given an id of a non-existing node', function () {
      expect(this.model.findAnalysisDefinitionNodeModel('x1')).toBeUndefined();
    });
  });

  describe('.toggleVisible', function () {
    it('should toggle the visible attr', function () {
      this.model.toggleVisible();
      expect(this.model.get('visible')).toBe(false);

      this.model.toggleVisible();
      expect(this.model.get('visible')).toBe(true);

      this.model.toggleVisible();
      expect(this.model.get('visible')).toBe(false);
    });
  });

  describe('.getNumberOfAnalyses', function () {
    beforeEach(function () {
      this.model.set('letter', 'a');
    });

    it("should return 0 if layer doesn't have a source node", function () {
      expect(this.model.getNumberOfAnalyses()).toEqual(0);
    });

    it('should return the number of analyses owned by the layer', function () {
      this.analysisDefinitionNodesCollection.add([{
        id: 'a0',
        type: 'source',
        params: {
          query: 'SELECT * FROM foo'
        }
      }, {
        id: 'a1',
        type: 'buffer',
        radio: 300,
        distance: 'meters',
        source: 'a0'
      }, {
        id: 'a2',
        type: 'buffer',
        radio: 600,
        distance: 'meters',
        source: 'a1'
      }, {
        id: 'b0',
        type: 'source',
        params: {
          query: 'SELECT * FROM bar'
        }
      }, {
        id: 'b1',
        type: 'buffer',
        radio: 300,
        distance: 'meters',
        source: 'b0'
      }]);

      this.model.set('source', 'a2');

      expect(this.model.getNumberOfAnalyses()).toEqual(3);
    });
  });

  describe('.containsNode', function () {
    beforeEach(function () {
      this.analysisDefinitionNodesCollection.add([{
        id: 'a0',
        type: 'source',
        params: {
          query: 'SELECT * FROM foo'
        }
      }, {
        id: 'a1',
        type: 'buffer',
        radio: 300,
        distance: 'meters',
        source: 'a0'
      }, {
        id: 'b1',
        type: 'buffer',
        radio: 300,
        distance: 'meters',
        source: 'a0'
      }]);
      this.model.set('source', 'a1');
    });

    it('should return true if layer contains given node', function () {
      expect(this.model.containsNode(this.analysisDefinitionNodesCollection.get('a1'))).toBe(true);
      expect(this.model.containsNode(this.analysisDefinitionNodesCollection.get('a0'))).toBe(true);

      expect(this.model.containsNode(this.analysisDefinitionNodesCollection.get('b1'))).toBe(false);
      expect(this.model.containsNode(false)).toBe(false);
    });
  });

  describe('.canBeDeletedByUser', function () {
    it('should return true as long as it is not the only data layer and not all the other layers depend on it', function () {
      expect(this.model.canBeDeletedByUser()).toBe(false);

      spyOn(this.collection, 'getNumberOfDataLayers').and.returnValue(3);
      expect(this.model.canBeDeletedByUser()).toBe(true);
    });
  });

  describe('.getAllDependentLayers', function () {
    beforeEach(function () {
      /* ************************************ */
      /* Expected layers schema result:       */
      /*  _________    _________    _________ */
      /* | A      |   | B      |   | E      | */
      /* |  - a0  |   | - a1   |   | - a0   | */
      /* |  - a1  |   | - b1   |   |        | */
      /*  _________    _________    _________ */
      /* ************************************ */

      this.model.set('letter', 'a');
      this.model.set('source', 'a0');

      this.collection.add([{
        id: '323',
        kind: 'carto',
        letter: 'b',
        options: {
          table_name: 'alice',
          source: 'a0'
        }
      }, {
        id: '454',
        kind: 'carto',
        letter: 'e',
        options: {
          table_name: 'robin',
          source: 'b1'
        }
      }]);

      this.analysisDefinitionNodesCollection.add([{
        id: 'a0',
        type: 'source',
        params: {
          query: 'SELECT * FROM foo'
        }
      }, {
        id: 'a1',
        type: 'buffer',
        radio: 300,
        distance: 'meters',
        source: 'a0'
      }, {
        id: 'b1',
        type: 'buffer',
        radio: 300,
        distance: 'meters',
        source: 'a1'
      }, {
        id: 'e1',
        type: 'buffer',
        radio: 300,
        distance: 'meters',
        source: 'a0'
      }]);
    });

    it('should return the number of layers depending on it', function () {
      expect(this.model.getAllDependentLayers()).toBe(2);
    });
  });
});
<|endoftext|>


<commit_before>'use strict';
const path = require('path');
const fs = require('fs');

const BroccoliPlugin = require('broccoli-writer');
const BroccoliTypescript = require('./broccoli-typescript');
const BundlePlugin = require('./angular-broccoli-bundle');
const BroccoliFunnel = require('broccoli-funnel');
const BroccoliMergeTrees = require('broccoli-merge-trees');
const BroccoliSource = require('broccoli-source');
const UnwatchedDir = BroccoliSource.UnwatchedDir;
const Project = require('ember-cli/lib/models/project');
const HandlebarReplace = require('./broccoli-handlebars');
const config = require('../../addon/ng2/models/config');
const loadEnvironment = require('./environment');
const concat = require('broccoli-concat');
const uglify = require('broccoli-uglify-js');

class Angular2App extends BroccoliPlugin {
  constructor(project, inputNode, options) {
    super();
    this.ngConfig = config.CliConfig.fromProject();

    if (!options) {
      options = inputNode;
      inputNode = null;
    }

    options = options || {};

    this._options = options;
    this._sourceDir = options.sourceDir
                   || (this.ngConfig.defaults && this.ngConfig.defaults.sourceDir)
                   || 'src';
    this._options.polyfills = this._options.polyfills || [
      'vendor/es6-shim/es6-shim.js',
      'vendor/reflect-metadata/Reflect.js',
      'vendor/systemjs/dist/system.src.js',
      'vendor/zone.js/dist/zone.js'
    ];

    this._destDir = options.destDir || '';

    // By default, add all spec files to the tsCompiler.
    this._tsCompiler = options.tsCompiler || {
      additionalFiles: ['**/*.spec.ts']
    };

    this._initProject();
    this._notifyAddonIncluded();
    this._inputNode = inputNode || this._buildInputTree();

    this._tree = this._buildTree();
  }

  /**
   * For backward compatibility.
   * @public
   * @method toTree
   * @return {BroccoliPlugin} A broccoli plugin.
   */
  toTree() {
    // eslint-disable-next-line no-console
    console.warn('Angular2App is now a broccoli plugin. Calling toTree() is deprecated.');
    return this;
  }

  /**
   * @override
   */
  read(readTree) {
    return this._tree.read(readTree);
  }

  /**
   * @override
   */
  cleanup() {
    return this._tree.cleanup();
  }

  _buildInputTree() {
    const inputTreeArray = [
      new BroccoliFunnel(this._sourceDir, { destDir: this._sourceDir }),
      new BroccoliFunnel('typings', { destDir: 'typings' }),
      this._getConfigTree()
    ];

    if (fs.existsSync('public')) {
      inputTreeArray.push(new BroccoliFunnel('public', { destDir: 'public' }));
    }

    if (fs.existsSync('icons')) {
      inputTreeArray.push(new BroccoliFunnel('icons', { destDir: 'icons' }));
    }

    return new BroccoliMergeTrees(inputTreeArray, { overwrite: true });
  }

  /**
   * Create and return the app build system tree that:
   * - Get the `assets` tree
   * - Get the TS tree
   * - Get the TS src tree
   * - Get the index.html tree
   * - Get the NPM modules tree
   * - Apply/remove stuff based on the environment (dev|prod)
   * - Return the app trees to be extended
   *
   * @private
   * @method _buildTree
   * @return {BroccoliFunnel} The app trees that can be used to extend the build.
   */
  _buildTree() {
    var assetTree = this._getAssetsTree();
    var tsTree = this._getTsTree();
    var indexTree = this._getIndexTree();
    var vendorNpmTree = this._getVendorNpmTree();

    var buildTrees = [assetTree, tsTree, indexTree, vendorNpmTree];

    // Add available and supported CSS plugins.
    for (const suffix of ['sass', 'less', 'stylus', 'compass']) {
      const plugin = require(`./angular-broccoli-${suffix}`);
      const tree = plugin.makeBroccoliTree(this._inputNode, this._options[`${suffix}Compiler`]);

      if (!tree) {
        continue;
      }

      buildTrees.push(new BroccoliFunnel(tree, {
        include: ['**/*'],
        getDestinationPath: (n) => {
          if (n.startsWith(this._sourceDir)) {
            return n.substr(this._sourceDir.length);
          }
          return n;
        }
      }));
    }

    // Add the public folder in.
    buildTrees.push(new BroccoliFunnel(this._inputNode, {
      allowEmpty: true,
      srcDir: 'public',
      name: 'PublicFolderFunnel'
    }));

    var merged = new BroccoliMergeTrees(buildTrees, { overwrite: true });

    if (this.ngConfig.apps[0].mobile) {
      let AppShellPlugin = require('angular2-broccoli-prerender').AppShellPlugin;
      merged = new BroccoliMergeTrees([merged, new AppShellPlugin(merged, 'index.html', 'main-app-shell')], {
        overwrite: true
      });
    }

    if (loadEnvironment(this.project).production) {
      merged = this._getBundleTree(merged);
    }

    if (this.ngConfig.apps[0].mobile) {
      var ServiceWorkerPlugin = require('angular2-service-worker').ServiceWorkerPlugin;
      var swTree = new ServiceWorkerPlugin(merged);
      merged = BroccoliMergeTrees([merged, swTree], {
        overwrite: true
      });
    }

    return new BroccoliFunnel(merged, {
      destDir: this._destDir,
      overwrite: true
    });
  }

  /**
   * @private
   * @method _initProject
   * @param {Object} options
   */
  _initProject() {
    this.project = Project.closestSync(process.cwd());

    // project root dir env used on angular-cli side for including packages from project
    process.env.PROJECT_ROOT = process.env.PROJECT_ROOT || this.project.root;
  }

  /**
   * @private
   * @method _notifyAddonIncluded
   */
  _notifyAddonIncluded() {
    this.initializeAddons();
    this.project.addons = this.project.addons.filter(function (addon) {
      addon.app = this;

      if (!addon.isEnabled || addon.isEnabled()) {
        if (addon.included) {
          addon.included(this);
        }

        return addon;
      }
    }, this);
  }

  /**
   * Loads and initializes addons for this project.
   * Calls initializeAddons on the Project.
   *
   * @private
   * @method initializeAddons
   */
  initializeAddons() {
    this.project.initializeAddons();
  }

  /**
   * Returns the content for a specific type (section) for index.html.
   *
   * Currently supported types:
   * - 'head'
   * //- 'config-module'
   * //- 'app'
   * //- 'head-footer'
   * //- 'test-header-footer'
   * //- 'body-footer'
   * //- 'test-body-footer'
   *
   * Addons can also implement this method and could also define additional
   * types (eg. 'some-addon-section').
   *
   * @private
   * @method _contentFor
   * @param  {RegExP} match  Regular expression to match against
   * @param  {String} type   Type of content
   * @return {String}        The content.
   */
  _contentFor(match, type) {
    var content = [];

    /*switch (type) {
     case 'head':          this._contentForHead(content, config);         break;
     case 'config-module': this._contentForConfigModule(content, config); break;
     case 'app-boot':      this._contentForAppBoot(content, config);      break;
     }*/
    content = this.project.addons.reduce(function (content, addon) {
      var addonContent = addon.contentFor ? addon.contentFor(type) : null;
      if (addonContent) {
        return content.concat(addonContent);
      }

      return content;
    }, content);

    return content.join('\n');
  }

  /**
   * Returns the tree for app/index.html.
   *
   * @private
   * @method _getIndexTree
   * @return {Tree} Tree for app/index.html.
   */
  _getIndexTree() {
    var files = [
      'index.html'
    ];
    var mobile;

    let indexTree = new BroccoliFunnel(this._inputNode, {
      include: files.map(name => path.join(this._sourceDir, name)),
      getDestinationPath: (n) => {
        if (n.startsWith(this._sourceDir)) {
          return n.substr(this._sourceDir.length);
        }
        return n;
      }
    });

    if (this.ngConfig.apps[0].mobile) {
      mobile = {
        icons: [
          { rel: 'apple-touch-icon', href: '/icons/apple-touch-icon.png' },
          { rel: 'apple-touch-icon', sizes: '57x57', href: '/icons/apple-touch-icon-57x57.png' },
          { rel: 'apple-touch-icon', sizes: '60x60', href: '/icons/apple-touch-icon-60x60.png' },
          { rel: 'apple-touch-icon', sizes: '72x72', href: '/icons/apple-touch-icon-72x72.png' },
          { rel: 'apple-touch-icon', sizes: '76x76', href: '/icons/apple-touch-icon-76x76.png' },
          { rel: 'apple-touch-icon', sizes: '114x114', href: '/icons/apple-touch-icon-114x114.png' },
          { rel: 'apple-touch-icon', sizes: '120x120', href: '/icons/apple-touch-icon-120x120.png' },
          { rel: 'apple-touch-icon', sizes: '144x144', href: '/icons/apple-touch-icon-144x144.png' },
          { rel: 'apple-touch-icon', sizes: '152x152', href: '/icons/apple-touch-icon-152x152.png' },
          { rel: 'apple-touch-icon', sizes: '180x180', href: '/icons/apple-touch-icon-180x180.png' },
          { rel: 'apple-touch-startup-image', href: '/icons/apple-touch-icon-180x180.png' }
        ]
      }
    }

    return new HandlebarReplace(indexTree, {
      config: this.ngConfig,
      environment: loadEnvironment(this.project),
      scripts: {
        polyfills: this._options.polyfills
      },
      mobile: mobile
    }, {
      helpers: {
        'content-for': (name) => {
          // TODO: remove content-for.
          // eslint-disable-next-line no-console
          console.warn('"{{content-for}}" has been deprecated and will be removed before RC.');
          return this._contentFor(null, name);
        }
      }
    });
  }

  /**
   * Returns the TS tree.
   *
   * @private
   * @method _getTsTree
   * @return {Tree} Tree for TypeScript files.
   */
  _getTsTree() {
    var tsConfigPath = path.join(this._sourceDir, 'tsconfig.json');
    var tsTree = new BroccoliTypescript(this._inputNode, tsConfigPath, this._tsCompiler);

    var tsTreeExcludes = ['*.d.ts', 'tsconfig.json'];
    var excludeSpecFiles = '**/*.spec.*';

    if (loadEnvironment(this.project).production) {
      tsTreeExcludes.push(excludeSpecFiles);
    }

    tsTree = new BroccoliFunnel(tsTree, {
      srcDir: this._sourceDir,
      exclude: tsTreeExcludes
    });

    return tsTree;
  }

  /**
   * Returns the `vendorNpm` tree by merging the CLI dependencies plus the ones
   * passed by the user.
   *
   * @private
   * @method _getVendorNpmTree
   * @return {Tree} The NPM tree.
   */
  _getVendorNpmTree() {
    var vendorNpmFiles = [
    ];

    if (this.ngConfig.apps[0].mobile) {
      vendorNpmFiles.push('angular2-service-worker/dist/worker.js')
    }

    if (this._options.vendorNpmFiles) {
      vendorNpmFiles = vendorNpmFiles.concat(this._options.vendorNpmFiles);
    }

    return new BroccoliFunnel(new UnwatchedDir('node_modules'), {
      include: vendorNpmFiles,
      destDir: 'vendor',
      name: 'vendor'
    });
  }

  /**
   * Returns the `assets` tree.
   *
   * @private
   * @method _getAssetsTree
   * @return {Tree} The assets tree.
   */
  _getAssetsTree() {
    return new BroccoliFunnel(this._inputNode, {
      srcDir: this._sourceDir,
      exclude: [
        '**/*.ts',
        '**/*.scss',
        '**/*.sass',
        '**/*.less',
        '**/*.styl',
        '**/tsconfig.json'
      ],
      allowEmpty: true
    });
  }

  /**
   * Returns the config files tree.
   *
   * @private
   * @method _getConfigTree
   * @return {Tree} The config files tree.
   */
  _getConfigTree() {
    const isProduction = loadEnvironment(this.project).production;
    var envConfigFile = isProduction ? 'environment.prod.ts' : 'environment.dev.ts';

    return new BroccoliFunnel('config', {
      include: [envConfigFile],
      destDir: `${this._sourceDir}/app`,
      getDestinationPath: () => 'environment.ts'
    });
  }

  _getBundleTree(preBundleTree){
    var indexFile = path.join(this._sourceDir, 'index.html');
    var indexContent = fs.readFileSync(indexFile, 'utf8');
    var scriptTagVendorFiles = indexContent.match(/vendor\/[^"']*\.js/gi);

    var scriptTree = new BroccoliFunnel(preBundleTree, {
      include: scriptTagVendorFiles
    });

    var nonJsTree = new BroccoliFunnel(preBundleTree, {
      exclude: ['**/*.js', '**/*.js.map']
    });
    var jsTree = new BroccoliFunnel(preBundleTree, {
      include: ['**/*.js']
    });

    var bundleTree = new BundlePlugin([jsTree]);

    if (this.ngConfig.apps[0].mobile) {
      bundleTree = concat(BroccoliMergeTrees([jsTree, scriptTree, bundleTree], {
        overwrite: true
      }), {
        headerFiles: this._options.polyfills.concat([
          'system-config.js',
          'main.js',
          'app/index.js'
        ]),
        inputFiles: [
          'system-import.js'
        ],
        header: ';(function() {',
        footer: '}());',
        sourceMapConfig: { enabled: true },
        allowNone: false,
        outputFile: '/app-concat.js'
      });

      bundleTree = uglify(bundleTree, {
        mangle: false
      });
    }

    return BroccoliMergeTrees([nonJsTree, scriptTree, bundleTree], { overwrite: true });
  }
}

module.exports = Angular2App;
<commit_msg>fix(mobile): update path to reflect updated service worker package (#746)<commit_after>'use strict';
const path = require('path');
const fs = require('fs');

const BroccoliPlugin = require('broccoli-writer');
const BroccoliTypescript = require('./broccoli-typescript');
const BundlePlugin = require('./angular-broccoli-bundle');
const BroccoliFunnel = require('broccoli-funnel');
const BroccoliMergeTrees = require('broccoli-merge-trees');
const BroccoliSource = require('broccoli-source');
const UnwatchedDir = BroccoliSource.UnwatchedDir;
const Project = require('ember-cli/lib/models/project');
const HandlebarReplace = require('./broccoli-handlebars');
const config = require('../../addon/ng2/models/config');
const loadEnvironment = require('./environment');
const concat = require('broccoli-concat');
const uglify = require('broccoli-uglify-js');

class Angular2App extends BroccoliPlugin {
  constructor(project, inputNode, options) {
    super();
    this.ngConfig = config.CliConfig.fromProject();

    if (!options) {
      options = inputNode;
      inputNode = null;
    }

    options = options || {};

    this._options = options;
    this._sourceDir = options.sourceDir
                   || (this.ngConfig.defaults && this.ngConfig.defaults.sourceDir)
                   || 'src';
    this._options.polyfills = this._options.polyfills || [
      'vendor/es6-shim/es6-shim.js',
      'vendor/reflect-metadata/Reflect.js',
      'vendor/systemjs/dist/system.src.js',
      'vendor/zone.js/dist/zone.js'
    ];

    this._destDir = options.destDir || '';

    // By default, add all spec files to the tsCompiler.
    this._tsCompiler = options.tsCompiler || {
      additionalFiles: ['**/*.spec.ts']
    };

    this._initProject();
    this._notifyAddonIncluded();
    this._inputNode = inputNode || this._buildInputTree();

    this._tree = this._buildTree();
  }

  /**
   * For backward compatibility.
   * @public
   * @method toTree
   * @return {BroccoliPlugin} A broccoli plugin.
   */
  toTree() {
    // eslint-disable-next-line no-console
    console.warn('Angular2App is now a broccoli plugin. Calling toTree() is deprecated.');
    return this;
  }

  /**
   * @override
   */
  read(readTree) {
    return this._tree.read(readTree);
  }

  /**
   * @override
   */
  cleanup() {
    return this._tree.cleanup();
  }

  _buildInputTree() {
    const inputTreeArray = [
      new BroccoliFunnel(this._sourceDir, { destDir: this._sourceDir }),
      new BroccoliFunnel('typings', { destDir: 'typings' }),
      this._getConfigTree()
    ];

    if (fs.existsSync('public')) {
      inputTreeArray.push(new BroccoliFunnel('public', { destDir: 'public' }));
    }

    if (fs.existsSync('icons')) {
      inputTreeArray.push(new BroccoliFunnel('icons', { destDir: 'icons' }));
    }

    return new BroccoliMergeTrees(inputTreeArray, { overwrite: true });
  }

  /**
   * Create and return the app build system tree that:
   * - Get the `assets` tree
   * - Get the TS tree
   * - Get the TS src tree
   * - Get the index.html tree
   * - Get the NPM modules tree
   * - Apply/remove stuff based on the environment (dev|prod)
   * - Return the app trees to be extended
   *
   * @private
   * @method _buildTree
   * @return {BroccoliFunnel} The app trees that can be used to extend the build.
   */
  _buildTree() {
    var assetTree = this._getAssetsTree();
    var tsTree = this._getTsTree();
    var indexTree = this._getIndexTree();
    var vendorNpmTree = this._getVendorNpmTree();

    var buildTrees = [assetTree, tsTree, indexTree, vendorNpmTree];

    // Add available and supported CSS plugins.
    for (const suffix of ['sass', 'less', 'stylus', 'compass']) {
      const plugin = require(`./angular-broccoli-${suffix}`);
      const tree = plugin.makeBroccoliTree(this._inputNode, this._options[`${suffix}Compiler`]);

      if (!tree) {
        continue;
      }

      buildTrees.push(new BroccoliFunnel(tree, {
        include: ['**/*'],
        getDestinationPath: (n) => {
          if (n.startsWith(this._sourceDir)) {
            return n.substr(this._sourceDir.length);
          }
          return n;
        }
      }));
    }

    // Add the public folder in.
    buildTrees.push(new BroccoliFunnel(this._inputNode, {
      allowEmpty: true,
      srcDir: 'public',
      name: 'PublicFolderFunnel'
    }));

    var merged = new BroccoliMergeTrees(buildTrees, { overwrite: true });

    if (this.ngConfig.apps[0].mobile) {
      let AppShellPlugin = require('angular2-broccoli-prerender').AppShellPlugin;
      merged = new BroccoliMergeTrees([merged, new AppShellPlugin(merged, 'index.html', 'main-app-shell')], {
        overwrite: true
      });
    }

    if (loadEnvironment(this.project).production) {
      merged = this._getBundleTree(merged);
    }

    if (this.ngConfig.apps[0].mobile) {
      var ServiceWorkerPlugin = require('@angular/service-worker').ServiceWorkerPlugin;
      var swTree = new ServiceWorkerPlugin(merged);
      merged = BroccoliMergeTrees([merged, swTree], {
        overwrite: true
      });
    }

    return new BroccoliFunnel(merged, {
      destDir: this._destDir,
      overwrite: true
    });
  }

  /**
   * @private
   * @method _initProject
   * @param {Object} options
   */
  _initProject() {
    this.project = Project.closestSync(process.cwd());

    // project root dir env used on angular-cli side for including packages from project
    process.env.PROJECT_ROOT = process.env.PROJECT_ROOT || this.project.root;
  }

  /**
   * @private
   * @method _notifyAddonIncluded
   */
  _notifyAddonIncluded() {
    this.initializeAddons();
    this.project.addons = this.project.addons.filter(function (addon) {
      addon.app = this;

      if (!addon.isEnabled || addon.isEnabled()) {
        if (addon.included) {
          addon.included(this);
        }

        return addon;
      }
    }, this);
  }

  /**
   * Loads and initializes addons for this project.
   * Calls initializeAddons on the Project.
   *
   * @private
   * @method initializeAddons
   */
  initializeAddons() {
    this.project.initializeAddons();
  }

  /**
   * Returns the content for a specific type (section) for index.html.
   *
   * Currently supported types:
   * - 'head'
   * //- 'config-module'
   * //- 'app'
   * //- 'head-footer'
   * //- 'test-header-footer'
   * //- 'body-footer'
   * //- 'test-body-footer'
   *
   * Addons can also implement this method and could also define additional
   * types (eg. 'some-addon-section').
   *
   * @private
   * @method _contentFor
   * @param  {RegExP} match  Regular expression to match against
   * @param  {String} type   Type of content
   * @return {String}        The content.
   */
  _contentFor(match, type) {
    var content = [];

    /*switch (type) {
     case 'head':          this._contentForHead(content, config);         break;
     case 'config-module': this._contentForConfigModule(content, config); break;
     case 'app-boot':      this._contentForAppBoot(content, config);      break;
     }*/
    content = this.project.addons.reduce(function (content, addon) {
      var addonContent = addon.contentFor ? addon.contentFor(type) : null;
      if (addonContent) {
        return content.concat(addonContent);
      }

      return content;
    }, content);

    return content.join('\n');
  }

  /**
   * Returns the tree for app/index.html.
   *
   * @private
   * @method _getIndexTree
   * @return {Tree} Tree for app/index.html.
   */
  _getIndexTree() {
    var files = [
      'index.html'
    ];
    var mobile;

    let indexTree = new BroccoliFunnel(this._inputNode, {
      include: files.map(name => path.join(this._sourceDir, name)),
      getDestinationPath: (n) => {
        if (n.startsWith(this._sourceDir)) {
          return n.substr(this._sourceDir.length);
        }
        return n;
      }
    });

    if (this.ngConfig.apps[0].mobile) {
      mobile = {
        icons: [
          { rel: 'apple-touch-icon', href: '/icons/apple-touch-icon.png' },
          { rel: 'apple-touch-icon', sizes: '57x57', href: '/icons/apple-touch-icon-57x57.png' },
          { rel: 'apple-touch-icon', sizes: '60x60', href: '/icons/apple-touch-icon-60x60.png' },
          { rel: 'apple-touch-icon', sizes: '72x72', href: '/icons/apple-touch-icon-72x72.png' },
          { rel: 'apple-touch-icon', sizes: '76x76', href: '/icons/apple-touch-icon-76x76.png' },
          { rel: 'apple-touch-icon', sizes: '114x114', href: '/icons/apple-touch-icon-114x114.png' },
          { rel: 'apple-touch-icon', sizes: '120x120', href: '/icons/apple-touch-icon-120x120.png' },
          { rel: 'apple-touch-icon', sizes: '144x144', href: '/icons/apple-touch-icon-144x144.png' },
          { rel: 'apple-touch-icon', sizes: '152x152', href: '/icons/apple-touch-icon-152x152.png' },
          { rel: 'apple-touch-icon', sizes: '180x180', href: '/icons/apple-touch-icon-180x180.png' },
          { rel: 'apple-touch-startup-image', href: '/icons/apple-touch-icon-180x180.png' }
        ]
      }
    }

    return new HandlebarReplace(indexTree, {
      config: this.ngConfig,
      environment: loadEnvironment(this.project),
      scripts: {
        polyfills: this._options.polyfills
      },
      mobile: mobile
    }, {
      helpers: {
        'content-for': (name) => {
          // TODO: remove content-for.
          // eslint-disable-next-line no-console
          console.warn('"{{content-for}}" has been deprecated and will be removed before RC.');
          return this._contentFor(null, name);
        }
      }
    });
  }

  /**
   * Returns the TS tree.
   *
   * @private
   * @method _getTsTree
   * @return {Tree} Tree for TypeScript files.
   */
  _getTsTree() {
    var tsConfigPath = path.join(this._sourceDir, 'tsconfig.json');
    var tsTree = new BroccoliTypescript(this._inputNode, tsConfigPath, this._tsCompiler);

    var tsTreeExcludes = ['*.d.ts', 'tsconfig.json'];
    var excludeSpecFiles = '**/*.spec.*';

    if (loadEnvironment(this.project).production) {
      tsTreeExcludes.push(excludeSpecFiles);
    }

    tsTree = new BroccoliFunnel(tsTree, {
      srcDir: this._sourceDir,
      exclude: tsTreeExcludes
    });

    return tsTree;
  }

  /**
   * Returns the `vendorNpm` tree by merging the CLI dependencies plus the ones
   * passed by the user.
   *
   * @private
   * @method _getVendorNpmTree
   * @return {Tree} The NPM tree.
   */
  _getVendorNpmTree() {
    var vendorNpmFiles = [
    ];

    if (this.ngConfig.apps[0].mobile) {
      vendorNpmFiles.push('@angular/service-worker/dist/worker.js')
    }

    if (this._options.vendorNpmFiles) {
      vendorNpmFiles = vendorNpmFiles.concat(this._options.vendorNpmFiles);
    }

    return new BroccoliFunnel(new UnwatchedDir('node_modules'), {
      include: vendorNpmFiles,
      destDir: 'vendor',
      name: 'vendor'
    });
  }

  /**
   * Returns the `assets` tree.
   *
   * @private
   * @method _getAssetsTree
   * @return {Tree} The assets tree.
   */
  _getAssetsTree() {
    return new BroccoliFunnel(this._inputNode, {
      srcDir: this._sourceDir,
      exclude: [
        '**/*.ts',
        '**/*.scss',
        '**/*.sass',
        '**/*.less',
        '**/*.styl',
        '**/tsconfig.json'
      ],
      allowEmpty: true
    });
  }

  /**
   * Returns the config files tree.
   *
   * @private
   * @method _getConfigTree
   * @return {Tree} The config files tree.
   */
  _getConfigTree() {
    const isProduction = loadEnvironment(this.project).production;
    var envConfigFile = isProduction ? 'environment.prod.ts' : 'environment.dev.ts';

    return new BroccoliFunnel('config', {
      include: [envConfigFile],
      destDir: `${this._sourceDir}/app`,
      getDestinationPath: () => 'environment.ts'
    });
  }

  _getBundleTree(preBundleTree){
    var indexFile = path.join(this._sourceDir, 'index.html');
    var indexContent = fs.readFileSync(indexFile, 'utf8');
    var scriptTagVendorFiles = indexContent.match(/vendor\/[^"']*\.js/gi);

    var scriptTree = new BroccoliFunnel(preBundleTree, {
      include: scriptTagVendorFiles
    });

    var nonJsTree = new BroccoliFunnel(preBundleTree, {
      exclude: ['**/*.js', '**/*.js.map']
    });
    var jsTree = new BroccoliFunnel(preBundleTree, {
      include: ['**/*.js']
    });

    var bundleTree = new BundlePlugin([jsTree]);

    if (this.ngConfig.apps[0].mobile) {
      bundleTree = concat(BroccoliMergeTrees([jsTree, scriptTree, bundleTree], {
        overwrite: true
      }), {
        headerFiles: this._options.polyfills.concat([
          'system-config.js',
          'main.js',
          'app/index.js'
        ]),
        inputFiles: [
          'system-import.js'
        ],
        header: ';(function() {',
        footer: '}());',
        sourceMapConfig: { enabled: true },
        allowNone: false,
        outputFile: '/app-concat.js'
      });

      bundleTree = uglify(bundleTree, {
        mangle: false
      });
    }

    return BroccoliMergeTrees([nonJsTree, scriptTree, bundleTree], { overwrite: true });
  }
}

module.exports = Angular2App;
<|endoftext|>