<commit_before>/**
 * Module dependencies.
 */

var leo_gen = require('./leonardo_numbers');

/**
 * Implementations
 */

var TreeNode = function(p_data){
    this.m_left_child = null;
    this.m_right_child = null;
    this.m_parent = null;
    this.m_data = p_data;
    this.m_full = false;
}

/* Start LeonardoTree class */

function LeonardoTree(p_n){
    this.setup(p_n);
}

LeonardoTree.prototype = (function(){
    var createTree = function(p_n){
        var current_node = new TreeNode(null);

        try{
            if(1 <= m_leonardo_number.get(p_n - 2)){
                current_node.m_left_child = createTree(p_n - 2);
                current_node.m_left_child.m_parent = current_node;
            }

            if(1 <= m_leonardo_number.get(p_n - 1)){
                current_node.m_right_child = createTree(p_n - 1);
                current_node.m_right_child.m_parent = current_node;
            }
        }catch(e){}

        return current_node;
    };

    var swap = function(p_node, p_detached_data){
        var temp_data = p_node.m_data;
        p_node.m_data = p_detached_data;

        return temp_data;
    };

    var rotateRight = function(p_node, p_data){
        if(null == p_node)
            return;

        p_data = swap(p_node, p_data);
        p_node = p_node.m_right_child;

        if(null == p_node)
            return;

        if(null == p_node.m_data){
            store(p_node, p_data);
            p_data = null;
            return;
        }

        if(isLeafNode(p_node)){
            if(p_node.m_data < p_data)
                p_data = swap(p_node, p_data);

            p_node = getLesserParent(p_node, p_data);
            rotateRight(p_node, p_data);
        }
        else
            insertH(p_node, p_data);
    };

    var rotateLeft = function(p_node, p_data){
        if(null == p_node)
            return;

        p_data = swap(p_node, p_data);
        p_node = p_node.m_left_child;

        if(null == p_node)
            return;

        if(null == p_node.m_data){
            store(p_node, p_data);
            p_data = null;
            return;
        }

        if(isLeafNode(p_node)){
            if(p_node.m_data > p_data)
                p_data = swap(p_node, p_data);

            p_node = getGreaterParent(p_node, p_data);
            rotateLeft(p_node, p_data);
        }
        else
            insertH(p_node, p_data);
    };

    var isLeafNode = function(p_node){
        return null == p_node.m_left_child && null == p_node.m_right_child;
    };

    var store = function(p_node, p_data){
        p_node.m_data = p_data;

        if(isLeafNode(p_node)){
            p_node.m_full = true;
            reportFullToParent(p_node.m_parent);
        }
    };

    var reportFullToParent = function(p_node){
        do{
            p_node.m_full = (p_node.m_left_child.m_full && p_node.m_right_child.m_full);

            p_node = (p_node.m_full)? p_node.m_parent: null;
        }while(null != p_node);
    };

    var getLesserParent = function(p_node, p_data){
        while(null != p_node && p_node.m_data > p_data)
            p_node = p_node.m_parent;

        return p_node;
    };

    var getGreaterParent = function(p_node, p_data){
        while(null != p_node && p_node.m_data < p_data)
            p_node = p_node.m_parent;

        return p_node;
    };

    var insertH = function(p_node, p_data){
        if(null == p_node)
            return;

        var nearest_not_full;

        do{
            if(null == p_node.m_data){
                store(p_node, p_data);
                break;
            }

            if(isLeafNode(p_node)){
                var parent_node = p_node.m_parent;

                if(null == parent_node)
                    break;

                if(((null == nearest_not_full || nearest_not_full == 'right') && p_node.m_data < p_data) ||
                    (nearest_not_full == 'left' && p_node.m_data > p_data))
                        p_data = swap(p_node, p_data);

                if(p_node == parent_node.m_left_child)
                    p_node = parent_node;
                else if(null == nearest_not_full || nearest_not_full == 'right')
                    p_node = getLesserParent(p_node, p_data);
                else
                    p_node = getGreaterParent(p_node, p_data);

                if(null == nearest_not_full || nearest_not_full == 'right')
                    rotateRight(p_node, p_data);
                else
                    rotateLeft(p_node, p_data);

                break;
            }

            if(!p_node.m_left_child.m_full)
                nearest_not_full = 'left';
            if(!p_node.m_right_child.m_full)
                nearest_not_full = 'right';

            if(p_node.m_data > p_data)
                p_node = p_node.m_right_child;
            else
                p_node = p_node.m_left_child;
        }while(null != p_node);
    };

    var findH = function(p_data){
        var node = m_root;

        while(null != node){

            if(null == node.m_data){
                node = null;
                break;
            }

            if(p_data == node.m_data)
                break;

            if(node.m_data > p_data)
                node = node.m_right_child;
            else
                node = node.m_left_child;
        }

        return node;
    };

    var removeH = function(p_treenode){
        if(isLeafNode(p_treenode))
            return;

        var runner_node = p_treenode.m_right_child;

        while(!isLeafNode(runner_node))
            runner_node = runner_node.m_left_child;

        p_treenode.m_data = runner_node.m_data;
        runner_node.m_data = null;
        runner_node.m_full = false;

        reportFullToParent(runner_node.m_parent);

        if(null == p_treenode.m_data)
            reportFullToParent(p_treenode.m_parent);

        removeH(runner_node);
    }

    var setupH = function(p_n){
        if(0 > p_n)
            return null;

        var counter = 1;

        if(1 == p_n){
            m_size = 1;
        }
        else{
            do{
                m_size = m_leonardo_number.get(++counter);
            }while(m_size < p_n);
        }

        m_root = createTree(counter);
    };

    var m_leonardo_number = new leo_gen.LeonardoNumberSolver();
    var m_root = null;
    var m_size = 0;
    var m_cur_size = 0;

    return {
        constructor: LeonardoTree,
        setup: function(p_n){
            setupH(p_n);
        },
        find: function(p_data){
            var data = findH(p_data);

            if(null == data)
                return null;

            return data.m_data;
        },
        insert: function(p_data){
            if(m_cur_size >= m_size)
                return false;

            insertH(m_root, p_data);
            m_cur_size++;

            return true;
        },
        remove: function(p_data){
            var treenode = findH(p_data);

            if(null == treenode)
                return;

            m_cur_size--;
            treenode.m_data = null;
            removeH(treenode);
        },
        getSize: function(){
            return m_cur_size;
        },
        getMaxSize: function(){
            return m_size;
        },
        getRoot: function(){
            return m_root;
        },
        getLeonardoNumberSolver: function(){
            return m_leonardo_number;
        }
    }
})();

/**
 * Exports
 */

var exports = module.exports = {}

exports.LeonardoTree = LeonardoTree;
<commit_msg>Exports the LeonardoTree reference rather than an object that contains it<commit_after>/**
 * Module dependencies.
 */

var leo_gen = require('./leonardo_numbers');

/**
 * Implementations
 */

var TreeNode = function(p_data){
    this.m_left_child = null;
    this.m_right_child = null;
    this.m_parent = null;
    this.m_data = p_data;
    this.m_full = false;
}

/* Start LeonardoTree class */

function LeonardoTree(p_n){
    this.setup(p_n);
}

LeonardoTree.prototype = (function(){
    var createTree = function(p_n){
        var current_node = new TreeNode(null);

        try{
            if(1 <= m_leonardo_number.get(p_n - 2)){
                current_node.m_left_child = createTree(p_n - 2);
                current_node.m_left_child.m_parent = current_node;
            }

            if(1 <= m_leonardo_number.get(p_n - 1)){
                current_node.m_right_child = createTree(p_n - 1);
                current_node.m_right_child.m_parent = current_node;
            }
        }catch(e){}

        return current_node;
    };

    var swap = function(p_node, p_detached_data){
        var temp_data = p_node.m_data;
        p_node.m_data = p_detached_data;

        return temp_data;
    };

    var rotateRight = function(p_node, p_data){
        if(null == p_node)
            return;

        p_data = swap(p_node, p_data);
        p_node = p_node.m_right_child;

        if(null == p_node)
            return;

        if(null == p_node.m_data){
            store(p_node, p_data);
            p_data = null;
            return;
        }

        if(isLeafNode(p_node)){
            if(p_node.m_data < p_data)
                p_data = swap(p_node, p_data);

            p_node = getLesserParent(p_node, p_data);
            rotateRight(p_node, p_data);
        }
        else
            insertH(p_node, p_data);
    };

    var rotateLeft = function(p_node, p_data){
        if(null == p_node)
            return;

        p_data = swap(p_node, p_data);
        p_node = p_node.m_left_child;

        if(null == p_node)
            return;

        if(null == p_node.m_data){
            store(p_node, p_data);
            p_data = null;
            return;
        }

        if(isLeafNode(p_node)){
            if(p_node.m_data > p_data)
                p_data = swap(p_node, p_data);

            p_node = getGreaterParent(p_node, p_data);
            rotateLeft(p_node, p_data);
        }
        else
            insertH(p_node, p_data);
    };

    var isLeafNode = function(p_node){
        return null == p_node.m_left_child && null == p_node.m_right_child;
    };

    var store = function(p_node, p_data){
        p_node.m_data = p_data;

        if(isLeafNode(p_node)){
            p_node.m_full = true;
            reportFullToParent(p_node.m_parent);
        }
    };

    var reportFullToParent = function(p_node){
        do{
            p_node.m_full = (p_node.m_left_child.m_full && p_node.m_right_child.m_full);

            p_node = (p_node.m_full)? p_node.m_parent: null;
        }while(null != p_node);
    };

    var getLesserParent = function(p_node, p_data){
        while(null != p_node && p_node.m_data > p_data)
            p_node = p_node.m_parent;

        return p_node;
    };

    var getGreaterParent = function(p_node, p_data){
        while(null != p_node && p_node.m_data < p_data)
            p_node = p_node.m_parent;

        return p_node;
    };

    var insertH = function(p_node, p_data){
        if(null == p_node)
            return;

        var nearest_not_full;

        do{
            if(null == p_node.m_data){
                store(p_node, p_data);
                break;
            }

            if(isLeafNode(p_node)){
                var parent_node = p_node.m_parent;

                if(null == parent_node)
                    break;

                if(((null == nearest_not_full || nearest_not_full == 'right') && p_node.m_data < p_data) ||
                    (nearest_not_full == 'left' && p_node.m_data > p_data))
                        p_data = swap(p_node, p_data);

                if(p_node == parent_node.m_left_child)
                    p_node = parent_node;
                else if(null == nearest_not_full || nearest_not_full == 'right')
                    p_node = getLesserParent(p_node, p_data);
                else
                    p_node = getGreaterParent(p_node, p_data);

                if(null == nearest_not_full || nearest_not_full == 'right')
                    rotateRight(p_node, p_data);
                else
                    rotateLeft(p_node, p_data);

                break;
            }

            if(!p_node.m_left_child.m_full)
                nearest_not_full = 'left';
            if(!p_node.m_right_child.m_full)
                nearest_not_full = 'right';

            if(p_node.m_data > p_data)
                p_node = p_node.m_right_child;
            else
                p_node = p_node.m_left_child;
        }while(null != p_node);
    };

    var findH = function(p_data){
        var node = m_root;

        while(null != node){

            if(null == node.m_data){
                node = null;
                break;
            }

            if(p_data == node.m_data)
                break;

            if(node.m_data > p_data)
                node = node.m_right_child;
            else
                node = node.m_left_child;
        }

        return node;
    };

    var removeH = function(p_treenode){
        if(isLeafNode(p_treenode))
            return;

        var runner_node = p_treenode.m_right_child;

        while(!isLeafNode(runner_node))
            runner_node = runner_node.m_left_child;

        p_treenode.m_data = runner_node.m_data;
        runner_node.m_data = null;
        runner_node.m_full = false;

        reportFullToParent(runner_node.m_parent);

        if(null == p_treenode.m_data)
            reportFullToParent(p_treenode.m_parent);

        removeH(runner_node);
    }

    var setupH = function(p_n){
        if(0 > p_n)
            return null;

        var counter = 1;

        if(1 == p_n){
            m_size = 1;
        }
        else{
            do{
                m_size = m_leonardo_number.get(++counter);
            }while(m_size < p_n);
        }

        m_root = createTree(counter);
    };

    var m_leonardo_number = new leo_gen.LeonardoNumberSolver();
    var m_root = null;
    var m_size = 0;
    var m_cur_size = 0;

    return {
        constructor: LeonardoTree,
        setup: function(p_n){
            setupH(p_n);
        },
        find: function(p_data){
            var data = findH(p_data);

            if(null == data)
                return null;

            return data.m_data;
        },
        insert: function(p_data){
            if(m_cur_size >= m_size)
                return false;

            insertH(m_root, p_data);
            m_cur_size++;

            return true;
        },
        remove: function(p_data){
            var treenode = findH(p_data);

            if(null == treenode)
                return;

            m_cur_size--;
            treenode.m_data = null;
            removeH(treenode);
        },
        getSize: function(){
            return m_cur_size;
        },
        getMaxSize: function(){
            return m_size;
        },
        getRoot: function(){
            return m_root;
        },
        getLeonardoNumberSolver: function(){
            return m_leonardo_number;
        }
    }
})();

/**
 * Exports
 */

var exports = module.exports = LeonardoTree;
<|endoftext|>


<commit_before>import d3 from 'd3'
import {colors, colorTextures} from '../../data/colorPalette'
import {unitFormatter as _unitFormatter} from '../../lib/unitFormatter'

const showMargins = false

class Chart {
  constructor(el, props, state, functions, config) {
    // _svg is the actual SVG element
    this._svg = null
    // svg is a translated 'g' within _svg that all graphs draw to
    this.svg = null

    this.props = props
    this.colors = d3.scale.ordinal().range(colors)
    this.eventDispatcher = config.eventEmitter

    if (functions) {
      if (functions.hasOwnProperty('drawPoints')) {
        this._drawPoints = functions.drawPoints
      }
      if (functions.hasOwnProperty('calculateHeight')) {
        this._calculateHeight = functions.calculateHeight
      }
    }

    this.update(el, state, config)
  }

  _drawPoints(el, data) {}

  _calculateMargins(data) {
    if (!data) {
      return null
    }

    if (!data.preparedData) {
      return {}
    }

    // LEFT MARGIN
    // Need to add a Y axis and see how wide the largest label is
    const yc = this.configureYscale(data.preparedData.extent, data.unit, 100)
    const testSVG = d3.select('body').append('svg').style('display', 'hidden')
    const yAxis = d3.svg.axis().scale(yc.scale).orient('left').tickFormat(yc.axisFormat)
    testSVG.append('g')
    .attr('class', 'axis')
    .call(yAxis)

    // Find the longest text string on this axis
    let axislabelLength = 0
    let axislabelHeight = 0
    testSVG.selectAll('text').each(function () {
      const height = d3.select(this).node().getBBox().height
      if (height > axislabelHeight) {
        axislabelHeight = height
      }
      const len = this.getComputedTextLength()
      if (len > axislabelLength) {
        axislabelLength = len
      }
    })

    testSVG.remove()

    const result = {
      left: axislabelLength + 10, // Add some space for the actual axis and tick marks
      top: axislabelHeight,
      right: 15
    }
    return result
  }

  _calculateHeight(data) {
    return 400
  }

  update(el, state, config) {
    // We don't support redrawing on top of old graphs, so just remove any
    // previous presentation
    if (this._svg) {
      this._svg.remove()
    }

    // Our width is determined by our element width
    this.fullWidth = el.offsetWidth
    const defaultMargins = {left: 0, top: 0, right: 0, bottom: 0}
    this.margins = defaultMargins
    if (config.shouldCalculateMargins) {
      this.margins = Object.assign(defaultMargins, this._calculateMargins(state.data))
    }
    this.fullHeight = this._calculateHeight() + this.margins.top + this.margins.bottom

    this.size = {
      width: this.fullWidth - this.margins.left - this.margins.right,
      height: this.fullHeight - this.margins.top - this.margins.bottom
    }

    // TODO: https://css-tricks.com/scale-svg/
    // http://stackoverflow.com/a/9539361/194404
    this._svg = d3.select(el).append('svg')
      .attr('class', 'd3')
      .attr('width', this.fullWidth)
      .attr('height', this.fullHeight)

    if (showMargins) {
      // Visualize SVG
      this._svg.append('rect')
      .attr('x', 0)
      .attr('y', 0)
      .attr('width', this.fullWidth)
      .attr('height', this.fullHeight)
      .style('fill', '#ccc')
      // TODO: Make vertical space for potential X axis labels (might line break)
      // TODO: Make horizontal space for potential Y axis with formatted labels
    }

    // Now that we have a SVG element, we can create textures
    // They are added as <defs><pattern> nodes to the SVG and
    // referred to by an url(). We can then use it as a fill
    const textureFills = []
    const textureColors = Object.keys(colorTextures)
    textureColors.forEach(color => {
      let fill = color
      const textureFunc = colorTextures[color]
      if (textureFunc) {
        const tx = textureFunc()
        this._svg.call(tx) // eslint-disable-line prefer-reflect
        fill = tx.url()
      }
      textureFills.push(fill)
    })
    this.textures = d3.scale.ordinal().range(textureFills).domain(textureColors)

    // Conventional margins (http://bl.ocks.org/mbostock/3019563)
    // Translating an outer 'g' so we dont have to consider margins in the rest
    // of the code
    this.svg = this._svg.append('g')
      .attr('class', 'd3-points')
      .attr('transform', this.translation(this.margins.left, this.margins.top))

    // Visualize svg with margins
    if (showMargins) {
      this.svg.append('rect')
      .attr('x', 0)
      .attr('y', 0)
      .attr('width', this.size.width)
      .attr('height', this.size.height)
      .style('fill', '#655959')
    }

    this._drawPoints(el, state.data)
  }

  destroy(el) {
  }

  translation(x, y) {
    return `translate(${x},${y})`
  }

  unitFormatter(unit) {
    return _unitFormatter(unit)
  }

  limitScaleRangeBand(scale, limit) {
    const domain = scale.domain()
    while (scale.rangeBand() > limit) {
      domain.push(Math.random())
      domain.unshift(Math.random())
      scale.domain(domain)
    }
    return scale
  }

  // Returns {scale: d3.scale, format: tickFormat}
  configureYscale(extent, unit, height = this.size.height) {
    const maxValue = extent[1] < 0 ? 0 : extent[1]
    const y = d3.scale.linear().range([height, 0])
    switch (unit) {
      case 'prosent': {
        // Always 0 -> (100% or > 100%)
        y.domain([0, Math.max(1, maxValue)])
        break
      }
      case 'promille': {
        // Always 0 -> max
        y.domain([0, maxValue])
        break
      }
      default: {
        // Always 0 or < 0 -> max
        y.domain([Math.min(0, extent[0]), maxValue])
      }
    }
    return Object.assign({
      scale: y
    }, this.unitFormatter(unit))
  }

  addYAxis(scale, format) {
    scale.nice()

    const yAxis = d3.svg.axis().scale(scale).orient('left')
    yAxis.tickFormat(format)

    /* eslint-disable prefer-reflect */
    this.svg.append('g')
    .attr('class', 'axis')
    .call(yAxis)
    .select('path').remove()
    /* eslint-enable prefer-reflect */

    // Draw horizontal background lines where the tick marks are
    this.svg.selectAll('.axis .tick')
    .append('line')
    .attr('class', 'benchmark--line')
    .attr('x1', -this.margins.left)
    .attr('x2', this.size.width)
    .attr('y1', 0)
    .attr('y2', 0)

    // Translate the text up by half font size to make the text rest on top
    // of the background lines
    this.svg.selectAll('.axis .tick text')
    .attr('transform', function () {
      return `translate(0, ${-this.getBBox().height / 2})`
    })
    .attr('class', 'benchmark--text')
  }

  legend() {
    let color = d3.scale.category20()
    let height = 0
    const attr = {
      width: (item, idx) => 25,
      height: (item, idx) => 25,
      fontSize: (item, idx) => 15
    }
    const dispatch = d3.dispatch('legendClick', 'legendMouseover', 'legendMouseout')
    const wrapper = this.wrapTextNode

    function chart(selection) {
      selection.each(function (data, idx) {
        if (data.length < 1) {
          // Dont show single legends or none
          return
        }

        const wrap = d3.select(this).selectAll('g.legend').data(data)
        let legend = null
        legend = wrap.enter()
          .append('g').attr('class', 'legend')
          .attr('transform', `translate(0,0)`)
          .append('g')
          .on('click', (item, index) => {
            dispatch.legendClick(item, index)
          })
          .on('mouseover', (item, index) => {
            dispatch.legendMouseover(item, index)
          })
          .on('mouseout', (item, index) => {
            dispatch.legendMouseout(item, index)
          })

        legend.append('rect')
          .attr('x', (dataItem, index) => {
            return 0
          })
          .attr('y', 0)
          .attr('width', (item, i) => attr.width(item, i))
          .attr('height', (item, i) => attr.height(item, i))
          .style('fill', dataItem => color(dataItem))

        const textX = attr.width() + attr.fontSize() / 2
        const parent = d3.select(wrap.node().parentNode)
        const maxWidth = parent.attr('width') - textX

        legend
          .append('text')
          .text(dataItem => dataItem)
          .attr('y', () => (attr.height() - attr.fontSize()) / 2)
          .attr('dy', () => 0)
          .attr('x', 0)
          .attr('dx', textX)
          .attr('text-anchor', 'start')
          .attr('font-size', () => attr.fontSize())
          .attr('dominant-baseline', 'text-before-edge')

        // Wrap lines
        legend.selectAll('text').call(wrapper, maxWidth)

        let x = 0
        let y = 0
        let previousHeight = 0
        wrap.selectAll('g').each(function () {
          const el = d3.select(this)
          const bbox = el.node().getBBox()
          const width = bbox.width + attr.width()
          const newX = x + width
          if (newX > maxWidth) {
            x = width
            y += previousHeight + 5 // 5 pixels padding
          } else {
            x += width
          }
          el.attr('transform', `translate(${x - width}, ${y})`)
          previousHeight = bbox.height
        })
        height = this.getBBox().height
      })
      return chart
    }

    chart.dispatch = dispatch

    chart.height = function () {
      return height
    }

    chart.color = function (newColor) {
      if (!arguments.length) {
        return color
      }
      color = newColor
      return chart
    }

    chart.attr = function (key, newValue) {
      if (!arguments.length) {
        return attr
      }
      if (arguments.length == 1) {
        const value = attr[key]
        if (typeof value === 'function') {
          return value()
        }
        return value
      }
      attr[key] = newValue
      return chart
    }

    return chart
  }

  // Wrapping text nodes
  // https://gist.github.com/mbostock/7555321
  wrapTextNode(text, width) {
    text.each(function () {
      const txt = d3.select(this)
      const words = txt.text()
      .split(/\s+/)
      .reverse()
      const lineHeight = 1.1 // ems
      const y = txt.attr('y')
      const x = txt.attr('x')
      const dy = parseFloat(txt.attr('dy'))
      let dx = parseFloat(txt.attr('dx'))
      if (isNaN(dx)) {
        dx = 0
      }
      let word = null
      let line = []
      let lineNumber = 0
      let tspan = txt.text(null)
      .append('tspan')
      .attr('x', x)
      .attr('y', y)
      .attr('dy', `${dy}em`)
      word = words.pop()
      while (word) {
        line.push(word)
        tspan.text(line.join(' '))
        if (tspan.node().getComputedTextLength() > width) {
          line.pop()
          tspan.text(line.join(' '))
          line = [word]
          tspan = txt.append('tspan')
          .attr('x', 0)
          .attr('dx', dx)
          .attr('y', y)
          .attr('dy', `${++lineNumber * lineHeight + dy}em`)
          .text(word)
        }
        word = words.pop()
      }
    })
  }
}

export default Chart
<commit_msg>Draw background line the full width including any margins<commit_after>import d3 from 'd3'
import {colors, colorTextures} from '../../data/colorPalette'
import {unitFormatter as _unitFormatter} from '../../lib/unitFormatter'

const showMargins = false

class Chart {
  constructor(el, props, state, functions, config) {
    // _svg is the actual SVG element
    this._svg = null
    // svg is a translated 'g' within _svg that all graphs draw to
    this.svg = null

    this.props = props
    this.colors = d3.scale.ordinal().range(colors)
    this.eventDispatcher = config.eventEmitter

    if (functions) {
      if (functions.hasOwnProperty('drawPoints')) {
        this._drawPoints = functions.drawPoints
      }
      if (functions.hasOwnProperty('calculateHeight')) {
        this._calculateHeight = functions.calculateHeight
      }
    }

    this.update(el, state, config)
  }

  _drawPoints(el, data) {}

  _calculateMargins(data) {
    if (!data) {
      return null
    }

    if (!data.preparedData) {
      return {}
    }

    // LEFT MARGIN
    // Need to add a Y axis and see how wide the largest label is
    const yc = this.configureYscale(data.preparedData.extent, data.unit, 100)
    const testSVG = d3.select('body').append('svg').style('display', 'hidden')
    const yAxis = d3.svg.axis().scale(yc.scale).orient('left').tickFormat(yc.axisFormat)
    testSVG.append('g')
    .attr('class', 'axis')
    .call(yAxis)

    // Find the longest text string on this axis
    let axislabelLength = 0
    let axislabelHeight = 0
    testSVG.selectAll('text').each(function () {
      const height = d3.select(this).node().getBBox().height
      if (height > axislabelHeight) {
        axislabelHeight = height
      }
      const len = this.getComputedTextLength()
      if (len > axislabelLength) {
        axislabelLength = len
      }
    })

    testSVG.remove()

    const result = {
      left: axislabelLength + 10, // Add some space for the actual axis and tick marks
      top: axislabelHeight,
      right: 15
    }
    return result
  }

  _calculateHeight(data) {
    return 400
  }

  update(el, state, config) {
    // We don't support redrawing on top of old graphs, so just remove any
    // previous presentation
    if (this._svg) {
      this._svg.remove()
    }

    // Our width is determined by our element width
    this.fullWidth = el.offsetWidth
    const defaultMargins = {left: 0, top: 0, right: 0, bottom: 0}
    this.margins = defaultMargins
    if (config.shouldCalculateMargins) {
      this.margins = Object.assign(defaultMargins, this._calculateMargins(state.data))
    }
    this.fullHeight = this._calculateHeight() + this.margins.top + this.margins.bottom

    this.size = {
      width: this.fullWidth - this.margins.left - this.margins.right,
      height: this.fullHeight - this.margins.top - this.margins.bottom
    }

    // TODO: https://css-tricks.com/scale-svg/
    // http://stackoverflow.com/a/9539361/194404
    this._svg = d3.select(el).append('svg')
      .attr('class', 'd3')
      .attr('width', this.fullWidth)
      .attr('height', this.fullHeight)

    if (showMargins) {
      // Visualize SVG
      this._svg.append('rect')
      .attr('x', 0)
      .attr('y', 0)
      .attr('width', this.fullWidth)
      .attr('height', this.fullHeight)
      .style('fill', '#ccc')
      // TODO: Make vertical space for potential X axis labels (might line break)
      // TODO: Make horizontal space for potential Y axis with formatted labels
    }

    // Now that we have a SVG element, we can create textures
    // They are added as <defs><pattern> nodes to the SVG and
    // referred to by an url(). We can then use it as a fill
    const textureFills = []
    const textureColors = Object.keys(colorTextures)
    textureColors.forEach(color => {
      let fill = color
      const textureFunc = colorTextures[color]
      if (textureFunc) {
        const tx = textureFunc()
        this._svg.call(tx) // eslint-disable-line prefer-reflect
        fill = tx.url()
      }
      textureFills.push(fill)
    })
    this.textures = d3.scale.ordinal().range(textureFills).domain(textureColors)

    // Conventional margins (http://bl.ocks.org/mbostock/3019563)
    // Translating an outer 'g' so we dont have to consider margins in the rest
    // of the code
    this.svg = this._svg.append('g')
      .attr('class', 'd3-points')
      .attr('transform', this.translation(this.margins.left, this.margins.top))

    // Visualize svg with margins
    if (showMargins) {
      this.svg.append('rect')
      .attr('x', 0)
      .attr('y', 0)
      .attr('width', this.size.width)
      .attr('height', this.size.height)
      .style('fill', '#655959')
    }

    this._drawPoints(el, state.data)
  }

  destroy(el) {
  }

  translation(x, y) {
    return `translate(${x},${y})`
  }

  unitFormatter(unit) {
    return _unitFormatter(unit)
  }

  limitScaleRangeBand(scale, limit) {
    const domain = scale.domain()
    while (scale.rangeBand() > limit) {
      domain.push(Math.random())
      domain.unshift(Math.random())
      scale.domain(domain)
    }
    return scale
  }

  // Returns {scale: d3.scale, format: tickFormat}
  configureYscale(extent, unit, height = this.size.height) {
    const maxValue = extent[1] < 0 ? 0 : extent[1]
    const y = d3.scale.linear().range([height, 0])
    switch (unit) {
      case 'prosent': {
        // Always 0 -> (100% or > 100%)
        y.domain([0, Math.max(1, maxValue)])
        break
      }
      case 'promille': {
        // Always 0 -> max
        y.domain([0, maxValue])
        break
      }
      default: {
        // Always 0 or < 0 -> max
        y.domain([Math.min(0, extent[0]), maxValue])
      }
    }
    return Object.assign({
      scale: y
    }, this.unitFormatter(unit))
  }

  addYAxis(scale, format) {
    scale.nice()

    const yAxis = d3.svg.axis().scale(scale).orient('left')
    yAxis.tickFormat(format)

    /* eslint-disable prefer-reflect */
    this.svg.append('g')
    .attr('class', 'axis')
    .call(yAxis)
    .select('path').remove()
    /* eslint-enable prefer-reflect */

    // Draw horizontal background lines where the tick marks are
    this.svg.selectAll('.axis .tick')
    .append('line')
    .attr('class', 'benchmark--line')
    .attr('x1', -this.margins.left)
    .attr('x2', this.fullWidth)
    .attr('y1', 0)
    .attr('y2', 0)

    // Translate the text up by half font size to make the text rest on top
    // of the background lines
    this.svg.selectAll('.axis .tick text')
    .attr('transform', function () {
      return `translate(0, ${-this.getBBox().height / 2})`
    })
    .attr('class', 'benchmark--text')
  }

  legend() {
    let color = d3.scale.category20()
    let height = 0
    const attr = {
      width: (item, idx) => 25,
      height: (item, idx) => 25,
      fontSize: (item, idx) => 15
    }
    const dispatch = d3.dispatch('legendClick', 'legendMouseover', 'legendMouseout')
    const wrapper = this.wrapTextNode

    function chart(selection) {
      selection.each(function (data, idx) {
        if (data.length < 1) {
          // Dont show single legends or none
          return
        }

        const wrap = d3.select(this).selectAll('g.legend').data(data)
        let legend = null
        legend = wrap.enter()
          .append('g').attr('class', 'legend')
          .attr('transform', `translate(0,0)`)
          .append('g')
          .on('click', (item, index) => {
            dispatch.legendClick(item, index)
          })
          .on('mouseover', (item, index) => {
            dispatch.legendMouseover(item, index)
          })
          .on('mouseout', (item, index) => {
            dispatch.legendMouseout(item, index)
          })

        legend.append('rect')
          .attr('x', (dataItem, index) => {
            return 0
          })
          .attr('y', 0)
          .attr('width', (item, i) => attr.width(item, i))
          .attr('height', (item, i) => attr.height(item, i))
          .style('fill', dataItem => color(dataItem))

        const textX = attr.width() + attr.fontSize() / 2
        const parent = d3.select(wrap.node().parentNode)
        const maxWidth = parent.attr('width') - textX

        legend
          .append('text')
          .text(dataItem => dataItem)
          .attr('y', () => (attr.height() - attr.fontSize()) / 2)
          .attr('dy', () => 0)
          .attr('x', 0)
          .attr('dx', textX)
          .attr('text-anchor', 'start')
          .attr('font-size', () => attr.fontSize())
          .attr('dominant-baseline', 'text-before-edge')

        // Wrap lines
        legend.selectAll('text').call(wrapper, maxWidth)

        let x = 0
        let y = 0
        let previousHeight = 0
        wrap.selectAll('g').each(function () {
          const el = d3.select(this)
          const bbox = el.node().getBBox()
          const width = bbox.width + attr.width()
          const newX = x + width
          if (newX > maxWidth) {
            x = width
            y += previousHeight + 5 // 5 pixels padding
          } else {
            x += width
          }
          el.attr('transform', `translate(${x - width}, ${y})`)
          previousHeight = bbox.height
        })
        height = this.getBBox().height
      })
      return chart
    }

    chart.dispatch = dispatch

    chart.height = function () {
      return height
    }

    chart.color = function (newColor) {
      if (!arguments.length) {
        return color
      }
      color = newColor
      return chart
    }

    chart.attr = function (key, newValue) {
      if (!arguments.length) {
        return attr
      }
      if (arguments.length == 1) {
        const value = attr[key]
        if (typeof value === 'function') {
          return value()
        }
        return value
      }
      attr[key] = newValue
      return chart
    }

    return chart
  }

  // Wrapping text nodes
  // https://gist.github.com/mbostock/7555321
  wrapTextNode(text, width) {
    text.each(function () {
      const txt = d3.select(this)
      const words = txt.text()
      .split(/\s+/)
      .reverse()
      const lineHeight = 1.1 // ems
      const y = txt.attr('y')
      const x = txt.attr('x')
      const dy = parseFloat(txt.attr('dy'))
      let dx = parseFloat(txt.attr('dx'))
      if (isNaN(dx)) {
        dx = 0
      }
      let word = null
      let line = []
      let lineNumber = 0
      let tspan = txt.text(null)
      .append('tspan')
      .attr('x', x)
      .attr('y', y)
      .attr('dy', `${dy}em`)
      word = words.pop()
      while (word) {
        line.push(word)
        tspan.text(line.join(' '))
        if (tspan.node().getComputedTextLength() > width) {
          line.pop()
          tspan.text(line.join(' '))
          line = [word]
          tspan = txt.append('tspan')
          .attr('x', 0)
          .attr('dx', dx)
          .attr('y', y)
          .attr('dy', `${++lineNumber * lineHeight + dy}em`)
          .text(word)
        }
        word = words.pop()
      }
    })
  }
}

export default Chart
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import {FormGroup,
        Button,
        Col,
        Alert,
        Form,
        FormControl,
        ControlLabel} from 'react-bootstrap';
const request = require('superagent');
const FontAwesome = require('react-fontawesome');

class AddBook extends Component {
  constructor(props) {
    super(props);
    let room_id = localStorage.getItem('selected_room');
    if (room_id != null) {
      this.setState({room_id: room_id});
    }
    this.state = {
      loadingBookData: false,
      errorMsg: null,
      infoMsg: null,
      isbn: null,
      tag: null,
      title: null,
      author: null,
      description: null,
      format: null,
      pages: null,
      publication_date: null,
      thumbnail: 'https://images.gr-assets.com/books/1419180921s/23232941.jpg'
    };
  }
  onFormInput(e) {
    this.setState({[e.target.id]: e.target.value});
  }
  onIsbnChange(e) {
    let isbn = e.target.value;
    if (isbn.length === 13) {
      this.getBookData(isbn);
    }
  }
  getBookData(isbn) {
    let url = `${window.__appUrl}/api/books/goodreads/${isbn}`;
    this.setState({loadingBookData: true});
    request
      .get(url)
      .type('application/json')
      .on('error', (err) => {
        this.setState({
          errorMsg: `Unable to fetch book info for ${isbn}`,
          loadingBookData: false
        });
      })
      .end((err, res) => {
        if (err) {
          return;
        }
        this.setState({loadingBookData: false});
        this.setState({errorMsg: null});
        let state = Object.assign({}, res.body);
        state.pages = state.num_pages;
        state.isbn = isbn;
        delete state.num_pages;
        delete state.errorMsg;
        this.setState(state);
      });
  }

  reset(e) {
    this.setState({
        loadingBookData: false,
        errorMsg: null,
        infoMsg: null,
        isbn: '',
        title: '',
        tag: '',
        author: '',
        description: '',
        format: '',
        pages: '',
        publication_date: ''
     });
  }

  submitBook() {
    let url = `${window.__appUrl}/api/books/${this.state.tag}`;
    request
      .put(url)
      .send(this.state)
      .type('application/json')
      .on('error', (err) => {
        this.setState({errorMsg: `Unable to post new book: ${err.response.text}`});
      })
      .end((err, res) => {
        if (!err) {
          this.setState({
              errorMsg: null,
              infoMsg: `New book with tag ${res.body.tag} added!`,
            });
        }
      });
  }
  onRoomChange(e) {
    this.setState({room_id: e.target.value});
    localStorage.setItem('selected_room', e.target.value);
  }
  render() {
    const ErrAlert = this.state.errorMsg ? <Alert bsStyle="danger">{this.state.errorMsg}</Alert> : null;
    const InfoAlert = this.state.infoMsg ? <Alert bsStyle="success">{this.state.infoMsg}</Alert> : null;
	let rooms = []
	this.props.sites.map((site) => {
		site.rooms.map((room) => {
			rooms.push({name: `${site.name}-${room.name}`, id: room.id});
		})
	});
    return (

Add book

          {ErrAlert}
          {InfoAlert}
          <Form horizontal>
            <FormGroup controlId="isbn">
              <Col
                componentClass={ControlLabel}
                sm={2}>
                ISBN-13
              </Col>
              <Col sm={4}>
                <FormControl
                  value={this.state.isbn}
                  onChange={this.onIsbnChange.bind(this)}
                  type="text"
                  autoComplete="off"
                  placeholder=""/>
              </Col>
              <Col sm={1}>
                {this.state.loadingBookData ? <FontAwesome name='spinner' size='2x'spin/> : null}
              </Col>
            </FormGroup>
              <FormGroup controlId="tag">
              <Col componentClass={ControlLabel} sm={2}>
                TAG
              </Col>
              <Col sm={5}>
                <FormControl
                  value={this.state.tag}
                  onChange={this.onFormInput.bind(this)}
                  type="text"
                  autoComplete="off"
                  placeholder=""/>
              </Col>
            </FormGroup>
            <FormGroup controlId="title">
              <Col componentClass={ControlLabel} sm={2}>
                Title
              </Col>
              <Col sm={7}>
                <FormControl
                  value={this.state.title}
                  onChange={this.onFormInput.bind(this)}
                  type="text"
                  autoComplete="off"
                  placeholder=""/>
              </Col>
            </FormGroup>
            <FormGroup controlId="room_id">
              <Col componentClass={ControlLabel} sm={2}>
                Room
              </Col>
              <Col sm={7}>
                <FormControl
                  componentClass="select"
		          defaultValue={localStorage.getItem('selected_room')}
                  onChange={this.onRoomChange.bind(this)}
                  placeholder="select">
                    <option />
                    {
                      rooms.map((room) => {
                        return <option
                                 key={room.id}
                                 value={room.id}>{room.name}</option>
				      })
                    }
                </FormControl>
              </Col>
            </FormGroup>
            <FormGroup controlId="author">
              <Col componentClass={ControlLabel} sm={2}>
                Author
              </Col>
              <Col sm={7}>
                <FormControl
                  value={this.state.author}
                  onChange={this.onFormInput.bind(this)}
                  type="text"
                  autoComplete="off"
                  placeholder=""/>
              </Col>
            </FormGroup>
            <FormGroup controlId="description">
              <Col componentClass={ControlLabel} sm={2}>
                Description
              </Col>
              <Col sm={10}>
                <FormControl
                  value={this.state.description}
                  onChange={this.onFormInput.bind(this)}
                  style={{height: '150px'}}
                  componentClass="textarea"
                  autoComplete="off"
                  placeholder=""/>
              </Col>
            </FormGroup>
            <FormGroup controlId="format">
              <Col componentClass={ControlLabel} sm={2}>
                Format
              </Col>
              <Col sm={2}>
                <FormControl
                  value={this.state.format}
                  onChange={this.onFormInput.bind(this)}
                  autoComplete="off"
                  placeholder=""/>
              </Col>
            </FormGroup>
            <FormGroup controlId="pages">
              <Col componentClass={ControlLabel} sm={2}>
                Pages
              </Col>
              <Col sm={2}>
                <FormControl
                  value={this.state.pages}
                  onChange={this.onFormInput.bind(this)}
                  autoComplete="off"
                  placeholder=""/>
              </Col>
            </FormGroup>
            <FormGroup controlId="publication_date">
              <Col componentClass={ControlLabel} sm={2}>
                Publication date
              </Col>
              <Col sm={2}>
                <FormControl
                  value={this.state.publication_date}
                  onChange={this.onFormInput.bind(this)}
                  autoComplete="off"
                  placeholder=""/>

              </Col>
            </FormGroup>
            <FormGroup controlId="thumbnail">
               <Col componentClass={ControlLabel} sm={2}>
                Thumbnail
              </Col>
              <Col xs={6} md={3}>

              </Col>
            </FormGroup>
            <FormGroup>
              <Col smOffset={2} sm={1}>
                <Button
                  disabled={!this.state.isbn && !this.state.tag}
                  onClick={this.submitBook.bind(this)}>
                  Submit book
                </Button>
              </Col>
              <Col smOffset={2} sm={1}>
                <Button
                  onClick={this.reset.bind(this)}>
                  Reset
                </Button>
              </Col>
            </FormGroup>
          </Form>

    );
  }
}

export default AddBook;
<commit_msg>Fix #9 addBook.js Compiler warnings "Expected to return a value in arrow function"<commit_after>import React, { Component } from 'react';
import {FormGroup,
        Button,
        Col,
        Alert,
        Form,
        FormControl,
        ControlLabel} from 'react-bootstrap';
const request = require('superagent');
const FontAwesome = require('react-fontawesome');

class AddBook extends Component {
  constructor(props) {
    super(props);
    let room_id = localStorage.getItem('selected_room');
    if (room_id != null) {
      this.setState({room_id: room_id});
    }
    this.state = {
      loadingBookData: false,
      errorMsg: null,
      infoMsg: null,
      isbn: null,
      tag: null,
      title: null,
      author: null,
      description: null,
      format: null,
      pages: null,
      publication_date: null,
      thumbnail: 'https://images.gr-assets.com/books/1419180921s/23232941.jpg'
    };
  }
  onFormInput(e) {
    this.setState({[e.target.id]: e.target.value});
  }
  onIsbnChange(e) {
    let isbn = e.target.value;
    if (isbn.length === 13) {
      this.getBookData(isbn);
    }
  }
  getBookData(isbn) {
    let url = `${window.__appUrl}/api/books/goodreads/${isbn}`;
    this.setState({loadingBookData: true});
    request
      .get(url)
      .type('application/json')
      .on('error', (err) => {
        this.setState({
          errorMsg: `Unable to fetch book info for ${isbn}`,
          loadingBookData: false
        });
      })
      .end((err, res) => {
        if (err) {
          return;
        }
        this.setState({loadingBookData: false});
        this.setState({errorMsg: null});
        let state = Object.assign({}, res.body);
        state.pages = state.num_pages;
        state.isbn = isbn;
        delete state.num_pages;
        delete state.errorMsg;
        this.setState(state);
      });
  }

  reset(e) {
    this.setState({
        loadingBookData: false,
        errorMsg: null,
        infoMsg: null,
        isbn: '',
        title: '',
        tag: '',
        author: '',
        description: '',
        format: '',
        pages: '',
        publication_date: ''
     });
  }

  submitBook() {
    let url = `${window.__appUrl}/api/books/${this.state.tag}`;
    request
      .put(url)
      .send(this.state)
      .type('application/json')
      .on('error', (err) => {
        this.setState({errorMsg: `Unable to post new book: ${err.response.text}`});
      })
      .end((err, res) => {
        if (!err) {
          this.setState({
              errorMsg: null,
              infoMsg: `New book with tag ${res.body.tag} added!`,
            });
        }
      });
  }
  onRoomChange(e) {
    this.setState({room_id: e.target.value});
    localStorage.setItem('selected_room', e.target.value);
  }
  render() {
    const ErrAlert = this.state.errorMsg ? <Alert bsStyle="danger">{this.state.errorMsg}</Alert> : null;
    const InfoAlert = this.state.infoMsg ? <Alert bsStyle="success">{this.state.infoMsg}</Alert> : null;
	let rooms = []
	this.props.sites.map((site) =>
		site.rooms.map((room) =>
		    rooms.push({name: `${site.name}-${room.name}`, id: room.id})
		)
	);

    return (

Add book

          {ErrAlert}
          {InfoAlert}
          <Form horizontal>
            <FormGroup controlId="isbn">
              <Col
                componentClass={ControlLabel}
                sm={2}>
                ISBN-13
              </Col>
              <Col sm={4}>
                <FormControl
                  value={this.state.isbn}
                  onChange={this.onIsbnChange.bind(this)}
                  type="text"
                  autoComplete="off"
                  placeholder=""/>
              </Col>
              <Col sm={1}>
                {this.state.loadingBookData ? <FontAwesome name='spinner' size='2x'spin/> : null}
              </Col>
            </FormGroup>
              <FormGroup controlId="tag">
              <Col componentClass={ControlLabel} sm={2}>
                TAG
              </Col>
              <Col sm={5}>
                <FormControl
                  value={this.state.tag}
                  onChange={this.onFormInput.bind(this)}
                  type="text"
                  autoComplete="off"
                  placeholder=""/>
              </Col>
            </FormGroup>
            <FormGroup controlId="title">
              <Col componentClass={ControlLabel} sm={2}>
                Title
              </Col>
              <Col sm={7}>
                <FormControl
                  value={this.state.title}
                  onChange={this.onFormInput.bind(this)}
                  type="text"
                  autoComplete="off"
                  placeholder=""/>
              </Col>
            </FormGroup>
            <FormGroup controlId="room_id">
              <Col componentClass={ControlLabel} sm={2}>
                Room
              </Col>
              <Col sm={7}>
                <FormControl
                  componentClass="select"
		          defaultValue={localStorage.getItem('selected_room')}
                  onChange={this.onRoomChange.bind(this)}
                  placeholder="select">
                    <option />
                    {
                      rooms.map((room) => {
                        return <option
                                 key={room.id}
                                 value={room.id}>{room.name}</option>
				      })
                    }
                </FormControl>
              </Col>
            </FormGroup>
            <FormGroup controlId="author">
              <Col componentClass={ControlLabel} sm={2}>
                Author
              </Col>
              <Col sm={7}>
                <FormControl
                  value={this.state.author}
                  onChange={this.onFormInput.bind(this)}
                  type="text"
                  autoComplete="off"
                  placeholder=""/>
              </Col>
            </FormGroup>
            <FormGroup controlId="description">
              <Col componentClass={ControlLabel} sm={2}>
                Description
              </Col>
              <Col sm={10}>
                <FormControl
                  value={this.state.description}
                  onChange={this.onFormInput.bind(this)}
                  style={{height: '150px'}}
                  componentClass="textarea"
                  autoComplete="off"
                  placeholder=""/>
              </Col>
            </FormGroup>
            <FormGroup controlId="format">
              <Col componentClass={ControlLabel} sm={2}>
                Format
              </Col>
              <Col sm={2}>
                <FormControl
                  value={this.state.format}
                  onChange={this.onFormInput.bind(this)}
                  autoComplete="off"
                  placeholder=""/>
              </Col>
            </FormGroup>
            <FormGroup controlId="pages">
              <Col componentClass={ControlLabel} sm={2}>
                Pages
              </Col>
              <Col sm={2}>
                <FormControl
                  value={this.state.pages}
                  onChange={this.onFormInput.bind(this)}
                  autoComplete="off"
                  placeholder=""/>
              </Col>
            </FormGroup>
            <FormGroup controlId="publication_date">
              <Col componentClass={ControlLabel} sm={2}>
                Publication date
              </Col>
              <Col sm={2}>
                <FormControl
                  value={this.state.publication_date}
                  onChange={this.onFormInput.bind(this)}
                  autoComplete="off"
                  placeholder=""/>

              </Col>
            </FormGroup>
            <FormGroup controlId="thumbnail">
               <Col componentClass={ControlLabel} sm={2}>
                Thumbnail
              </Col>
              <Col xs={6} md={3}>

              </Col>
            </FormGroup>
            <FormGroup>
              <Col smOffset={2} sm={1}>
                <Button
                  disabled={!this.state.isbn && !this.state.tag}
                  onClick={this.submitBook.bind(this)}>
                  Submit book
                </Button>
              </Col>
              <Col smOffset={2} sm={1}>
                <Button
                  onClick={this.reset.bind(this)}>
                  Reset
                </Button>
              </Col>
            </FormGroup>
          </Form>

    );
  }
}

export default AddBook;
<|endoftext|>


<commit_before>'use strict';

var debug = require('debug')('kiss.io:router');

function Router(opts)
{
  if(!(this instanceof Router))
  {
    return new Router();
  }

  this.opts = opts || {};
  this.routes = {};
}

Router.Route = Route;

/**
 * @param event
 * @param {Function} handler
 * @ret Route
 */
Router.prototype.on =
Router.prototype.event = function(event, handler)
{
  var route = this.routes[event];

  if(!route)
  {
    route = new Route(event);
    this.routes[event] = route;
  }

  route.use(handler);

  return route;
};

/**
 * @param {Router|string} item - an event+handler or Router to use with this router.
 */
Router.prototype.use = function(item)
{
  if(typeof item == 'string')
  {
    this.on.apply(this, arguments);
  }
  else if(item instanceof Route)
  {
    this.routes[item.event] = item;
  }
  else if(item instanceof Router)
  {
    Object.assign(this.routes, item.routes);
  }

  return this;
};

/**
 *
 * @param event
 * @param handler
 * @returns {Route}
 * @constructor
 */
function Route(event, handler)
{
  if(!(this instanceof Route))
  {
    return new Route(event);
  }

  this.event = event;
  this.args = [];
  this.handler = function noop() {};

  this.use(handler);
}

/**
 * Interface for Router.event
 *
 * @param {Function} handler
 */
Route.prototype.do =
Route.prototype.use = function(handler)
{
  if(typeof handler == 'function')
  {
    this.handler = handler;
  }

  return this;
};

/**
 * For now, just an helper function
 * @param args
 * @returns {Route}
 */
Route.prototype.expect = function(args)
{
  if(Array.isArray(args))
  {
    this.args = args;
  }

  return this;
};

module.exports = Router;<commit_msg>Added option to register multiple handlers to route<commit_after>'use strict';

var debug = require('debug')('kiss.io:router');

function Router(opts)
{
  if(!(this instanceof Router))
  {
    return new Router();
  }

  this.opts = opts || {};
  this.routes = {};
}

Router.Route = Route;

/**
 * @param event
 * @param {Function} handler
 * @ret Route
 */
Router.prototype.on =
Router.prototype.event = function(event, handlers)
{
  var route = this.routes[event];

  if(!route)
  {
    route = new Route(event);
    this.routes[event] = route;
  }

  route.use(handlers);

  return route;
};

/**
 * @param {Router|string} item - an event+handler or Router to use with this router.
 */
Router.prototype.use = function(item, fns)
{
  if(typeof item == 'string')
  {
    this.on.apply(this, arguments);
  }
  else if(item instanceof Route)
  {
    this.routes[item.event] = item;
  }
  else if(item instanceof Router)
  {
    Object.assign(this.routes, item.routes);
  }

  return this;
};

/**
 *
 * @param event
 * @param handler
 * @returns {Route}
 * @constructor
 */
function Route(event, handlers)
{
  if(!(this instanceof Route))
  {
    return new Route(event, handlers);
  }

  this.event = event;
  this.params = [];
  this.handlers = [];

  this.use(handlers);
}

/**
 * Interface for Router.event
 *
 * @param {Function} handler
 */
Route.prototype.do =
Route.prototype.use = function(handlers)
{
  if(!Array.isArray(handlers))
  {
    handlers = [handlers];
  }

  for(let handler of handlers)
  {
    if(typeof handler == 'function')
    {
      this.handlers.push(handler);
    }
  }

  return this;
};

/**
 * For now, just an helper function
 * @param args
 * @returns {Route}
 */
Route.prototype.expect = function(args)
{
  if(Array.isArray(args))
  {
    this.args = args;
  }

  return this;
};

module.exports = Router;<|endoftext|>


<commit_before>// Dependencies ---------------------------------------------------------------
// ----------------------------------------------------------------------------
var FileLinker = null,
    Macro = require('./Macro'),
    Errors = require('../Errors'),
    Token = require('../parser/Lexer').Token,
    Variable = require('../data/Variable'),
    Constant = require('../data/Constant'),
    Section = require('../data/Section'),
    Label = require('../data/Label'),
    Expression = require('../parser/Expression');

// Global Linking Logic -------------------------------------------------------
// ----------------------------------------------------------------------------
var Linker = {

    init: function(files) {

        // Order sections based on their offsets / banks
        var sections = getAllSections(files);
        sections.sort(function(a, b) {

            if (a.segment === b.segment) {

                if (a.bank === b.bank) {

                    if (a.hasCustomBaseOffset && b.hasCustomBaseOffset) {
                        return a.baseOffset - b.baseOffset;

                    } else if (a.hasCustomBaseOffset) {
                        return -1;

                    } else {
                        return 1;
                    }

                } else {
                    return a.bank - b.bank;
                }

            } else {
                return Section.Segments[a.segment].index
                     - Section.Segments[b.segment].index;
            }

        });

    },

    link: function(files) {

        // First calculate all initial addresses for all files
        files.forEach(FileLinker.init);

        // Now order and re-arrange the existing section and assign bases
        // addresses of sections without specific offset adresses
        var sections = getAllSections(files),
            sectionsLastAddresses = {};

        sections.forEach(function(section) {

            var id = section.segment + '#' + section.bank;

            // Place sections without a specified offset after other sections
            // in the machting segment / bank
            if (!section.hasCustomBaseOffset) {
                section.resolvedOffset = sectionsLastAddresses[id] || section.resolvedOffset;
                section.calculateOffsets();
            }

            sectionsLastAddresses[id] = section.resolvedOffset + section.size;

        });

        // Check for overlapping sections
        for(var i = 0; i < sections.length; i++) {
            for(var e = i + 1; e < sections.length; e++) {

                var b = sections[i],
                    a = sections[e];

                if (a.resolvedOffset >= b.resolvedOffset && a.resolvedOffset < b.resolvedOffset + b.size) {
                    new Errors.AddressError(a.file, 'Section overlaps with previously defined section ' + b.toString(), a.nameIndex);
                }

            }
        }

        // Link all files with the newly calculated addresses
        files.forEach(FileLinker.link);

    },

    // Name / Value / Expression Resolution -----------------------------------
    resolveValue: function(sourceFile, value, sourceOffset, sourceIndex, relativeOffset, stack) {

        // Check for circular references during value resolution
        if (stack.indexOf(value) === -1) {
            stack.push(value);

            switch(value.type) {
                case 'NUMBER':
                case 'STRING':
                    return value.value;

                case 'NAME':
                    return Linker.resolveNameValue(sourceFile, value, sourceOffset, sourceIndex, relativeOffset, stack);

                case 'LABEL_LOCAL_REF':
                    return Linker.resolveLocalLabel(sourceFile, value, relativeOffset, sourceOffset);

                case 'EXPRESSION':
                    var resolved = Linker.resolveExpression(
                        value.value, sourceFile, sourceOffset, sourceIndex,
                        relativeOffset, stack
                    );
                    return typeof resolved === 'number' ? resolved | 0 : resolved;

                case 'OFFSET':
                    return relativeOffset ? value.value : sourceOffset + value.value;

                default:
                    throw new TypeError('Unresolved ' + value.type + '(' + value.value + ')');
            }

        } else {

            // TODO more detailed error message (include locations of the other names)
            new Errors.ReferenceError(
                stack[0].file,
                'Circular reference of "'
                + stack[0].value
                + '" to itself via '
                + stack.slice(1).reverse().map(function(s) {
                    return s.value + ' in ' + s.file.getPath();

                }).join(' -> '),
                stack[0].index
            );

        }

    },

    resolveLocalLabel: function(sourceFile, value, relativeOffset, sourceOffset) {

        var resolved = FileLinker.resolveLocalLabel(sourceFile, value);
        if (resolved) {
            if (relativeOffset) {
                return resolved.offset - sourceOffset;

            } else {
                return resolved.offset;
            }

        } else {
            new Errors.ReferenceError(
                value.file,
                'Local label "' + value.value + '" not found in current scope',
                value.index
            );
        }

    },

    resolveNameValue: function(sourceFile, value, sourceOffset, sourceIndex, relativeOffset, stack) {

        var resolved = Linker.resolveName(value.value, sourceFile);
        if (resolved) {

            // Recursively resolve constants
            if (resolved instanceof Constant) {
                if (resolved.value instanceof Token) {
                    return Linker.resolveValue(
                        resolved.file, resolved.value,
                        sourceOffset, resolved.value.index,
                        relativeOffset, stack
                    );

                } else {
                    return resolved.value;
                }

            // Resolve Variable Values and Label Addresses
            } else if (resolved instanceof Variable || resolved instanceof Label) {
                if (relativeOffset) {
                    return resolved.offset - sourceOffset;

                } else {
                    return resolved.offset;
                }

            // Resolve macro handlers
            } else if (resolved instanceof Macro) {
                return resolved;

            // Resolve other pre-defined, built-in values
            } else {
                return resolved;
            }

        // Error on missing local names
        } else if (value.value.charCodeAt(0) === 95) {

            var resolvedGlobal = Linker.resolveName(
                value.value, sourceFile, true
            );

            if (resolvedGlobal) {
                new Errors.ReferenceError(
                    value.file,
                    'Local name "'
                    + value.value
                    + '" was not declared in current file, but found in '
                    + resolvedGlobal.file.getPath(resolvedGlobal.index, true),
                    value.index
                );

            } else {
                new Errors.ReferenceError(
                    value.file,
                    'Local name "' + value.value + '" was not declared',
                    value.index
                );
            }

        // Error on missing global names
        } else {
            // TODO Show the reference path
            new Errors.ReferenceError(
                value.file,
                '"' + value.value + '" was not declared',
                value.index
            );
        }

    },

    resolveName: function(name, file, global) {

        // Check if their is a macro with the specified name
        if (Macro.isDefined(name)) {
            return Macro.get(name);

        // Names prefixed with _ will only be looked up in their own file
        } else if (!global && name.charCodeAt(0) === 95) {
            return file.names[name];

        // All other names will be searched globally
        } else {

            var files = file.compiler.files;
            for(var i = 0, l = files.length; i < l; i++) {
                file = files[i];

                var value = file.names[name];
                if (value) {
                    return value;
                }
            }

            return null;

        }

    },

    resolveExpression: function(node, sourceFile, sourceOffset, sourceIndex, relativeOffset, stack) {

        // Binary Expressions
        if (node instanceof Expression.Node) {

            var left = Linker.resolveExpression(
                    node.left, sourceFile, sourceOffset, sourceIndex,
                    relativeOffset, stack
                );

            if (node.right) {

                var right = Linker.resolveExpression(
                    node.right, sourceFile, sourceOffset, sourceIndex,
                    relativeOffset, stack
                );

                if (typeof left !== typeof right) {
                    new Errors.ExpressionError(
                        sourceFile,
                        'Incompatible operand types '
                        + (typeof left).toUpperCase()
                        + ' and '
                        + (typeof right).toUpperCase()
                        + ' for binary operator ' + node.op.id,
                        node.op.index
                    );

                } else {
                    return Linker.evaluateBinaryOperator(node.op.id, left, right);
                }

            } else if (typeof left === 'number') {
                return Linker.evaluateUnaryOperator(node.op.id, left);

            } else{
                new Errors.ExpressionError(
                    sourceFile,
                    'Invalid operand type '
                    + (typeof left).toUpperCase()
                    + ' for unary operator ' + node.op.id,
                    node.left.value.index
                );
            }

        // Raw Values
        } else if (node instanceof Expression.Leaf) {
            return Linker.resolveValue(
                sourceFile, node.value, sourceOffset,
                node.value.index, false, stack
            );

        // Macro Calls
        } else if (node instanceof Expression.Call) {

            if (!Macro.isDefined(node.callee.value)) {
                new Errors.ExpressionError(
                    sourceFile,
                    'Call of undefined MACRO function "' + node.callee.value + '"',
                    node.callee.index
                );
            }

            var callee = Macro.get(node.callee.value);
            var args = node.args.map(function(arg, index) {

                var value = Linker.resolveValue(
                    sourceFile, arg.value, sourceOffset,
                    arg.value.index, false, stack
                );

                if (typeof value !== callee.args[index]) {
                    new Errors.ExpressionError(
                        'Invalid type for MACRO argument, '
                        + 'expected ' + callee.args[index].toUpperCase()
                        + ' but got ' + (typeof value).toUpperCase()
                        + ' instead',
                        sourceFile, arg.value.index
                    );

                } else {
                    return value;
                }

            });

            return callee.handler.apply(null, args);

        }

    },

    evaluateBinaryOperator: function(op, left, right) {
        switch(op) {

            // Binary
            case '&':
                return (left & right) | 0;

            case '|':
                return (left | right) | 0;

            case '^':
                return (left ^ right) | 0;

            // Math
            case '+':
                if (typeof left === 'string') {
                    return left + right;

                } else {
                    return left + right;
                }
                break;

            case '-':
                return left - right;

            case '*':
                return left * right;

            case '/':
                return left / right;

            case '%':
                return left % right;

            case '**':
                return Math.pow(left, right);

            // Shift
            case '>>':
                return (left >> right) | 0;

            case '<<':
                return (left << right) | 0;

            // Comparisons
            case '>':
                return left > right ? 1 : 0;

            case '>=':
                return left >= right ? 1 : 0;

            case '<':
                return left < right ? 1 : 0;

            case '<=':
                return left <= right ? 1 : 0;

            case '==':
                return left === right ? 1 : 0;

            case '!=':
                return left !== right ? 1 : 0;

            default:
                throw new TypeError('Unimplemented binary operator: ' + op);
        }
    },

    evaluateUnaryOperator: function(op, arg) {
        switch(op) {
            case '!':
                return !arg ? 1 : 0;

            case '-':
                return -arg;

            case '~':
                return (~arg) | 0;

            default:
                throw new TypeError('Unimplemented unary operator: ' + op);
        }
    },

    // Optimization -----------------------------------------------------------
    optimize: function(files) {

        // Optimize instructions
        files.forEach(FileLinker.optimize);

        // Now relink with the changed addresses
        Linker.link(files);

    }

};

// Helpers --------------------------------------------------------------------
function getAllSections(files) {

    var sections = [];
    files.forEach(function(file) {
        sections.push.apply(sections, file.sections);
    });

    return sections;

}

// Exports --------------------------------------------------------------------
module.exports = Linker;

// After Dependencies ---------------------------------------------------------
FileLinker = require('./FileLinker');

<commit_msg>Minor correction for size calculation during section overlap checks<commit_after>// Dependencies ---------------------------------------------------------------
// ----------------------------------------------------------------------------
var FileLinker = null,
    Macro = require('./Macro'),
    Errors = require('../Errors'),
    Token = require('../parser/Lexer').Token,
    Variable = require('../data/Variable'),
    Constant = require('../data/Constant'),
    Section = require('../data/Section'),
    Label = require('../data/Label'),
    Expression = require('../parser/Expression');

// Global Linking Logic -------------------------------------------------------
// ----------------------------------------------------------------------------
var Linker = {

    init: function(files) {

        // Order sections based on their offsets / banks
        var sections = getAllSections(files);
        sections.sort(function(a, b) {

            if (a.segment === b.segment) {

                if (a.bank === b.bank) {

                    if (a.hasCustomBaseOffset && b.hasCustomBaseOffset) {
                        return a.baseOffset - b.baseOffset;

                    } else if (a.hasCustomBaseOffset) {
                        return -1;

                    } else {
                        return 1;
                    }

                } else {
                    return a.bank - b.bank;
                }

            } else {
                return Section.Segments[a.segment].index
                     - Section.Segments[b.segment].index;
            }

        });

    },

    link: function(files) {

        // First calculate all initial addresses for all files
        files.forEach(FileLinker.init);

        // Now order and re-arrange the existing section and assign bases
        // addresses of sections without specific offset adresses
        var sections = getAllSections(files),
            sectionsLastAddresses = {};

        sections.forEach(function(section) {

            var id = section.segment + '#' + section.bank;

            // Place sections without a specified offset after other sections
            // in the machting segment / bank
            if (!section.hasCustomBaseOffset) {
                section.resolvedOffset = sectionsLastAddresses[id] || section.resolvedOffset;
                section.calculateOffsets();
            }

            sectionsLastAddresses[id] = section.resolvedOffset + section.size;

        });

        // Check for overlapping sections
        for(var i = 0; i < sections.length; i++) {
            for(var e = i + 1; e < sections.length; e++) {

                var b = sections[i],
                    a = sections[e];

                if (a.resolvedOffset >= b.resolvedOffset && a.resolvedOffset < b.resolvedOffset + b.size - 1) {
                    new Errors.AddressError(a.file, 'Section overlaps with previously defined section ' + b.toString(), a.nameIndex);
                }

            }
        }

        // Link all files with the newly calculated addresses
        files.forEach(FileLinker.link);

    },

    // Name / Value / Expression Resolution -----------------------------------
    resolveValue: function(sourceFile, value, sourceOffset, sourceIndex, relativeOffset, stack) {

        // Check for circular references during value resolution
        if (stack.indexOf(value) === -1) {
            stack.push(value);

            switch(value.type) {
                case 'NUMBER':
                case 'STRING':
                    return value.value;

                case 'NAME':
                    return Linker.resolveNameValue(sourceFile, value, sourceOffset, sourceIndex, relativeOffset, stack);

                case 'LABEL_LOCAL_REF':
                    return Linker.resolveLocalLabel(sourceFile, value, relativeOffset, sourceOffset);

                case 'EXPRESSION':
                    var resolved = Linker.resolveExpression(
                        value.value, sourceFile, sourceOffset, sourceIndex,
                        relativeOffset, stack
                    );
                    return typeof resolved === 'number' ? resolved | 0 : resolved;

                case 'OFFSET':
                    return relativeOffset ? value.value : sourceOffset + value.value;

                default:
                    throw new TypeError('Unresolved ' + value.type + '(' + value.value + ')');
            }

        } else {

            // TODO more detailed error message (include locations of the other names)
            new Errors.ReferenceError(
                stack[0].file,
                'Circular reference of "'
                + stack[0].value
                + '" to itself via '
                + stack.slice(1).reverse().map(function(s) {
                    return s.value + ' in ' + s.file.getPath();

                }).join(' -> '),
                stack[0].index
            );

        }

    },

    resolveLocalLabel: function(sourceFile, value, relativeOffset, sourceOffset) {

        var resolved = FileLinker.resolveLocalLabel(sourceFile, value);
        if (resolved) {
            if (relativeOffset) {
                return resolved.offset - sourceOffset;

            } else {
                return resolved.offset;
            }

        } else {
            new Errors.ReferenceError(
                value.file,
                'Local label "' + value.value + '" not found in current scope',
                value.index
            );
        }

    },

    resolveNameValue: function(sourceFile, value, sourceOffset, sourceIndex, relativeOffset, stack) {

        var resolved = Linker.resolveName(value.value, sourceFile);
        if (resolved) {

            // Recursively resolve constants
            if (resolved instanceof Constant) {
                if (resolved.value instanceof Token) {
                    return Linker.resolveValue(
                        resolved.file, resolved.value,
                        sourceOffset, resolved.value.index,
                        relativeOffset, stack
                    );

                } else {
                    return resolved.value;
                }

            // Resolve Variable Values and Label Addresses
            } else if (resolved instanceof Variable || resolved instanceof Label) {
                if (relativeOffset) {
                    return resolved.offset - sourceOffset;

                } else {
                    return resolved.offset;
                }

            // Resolve macro handlers
            } else if (resolved instanceof Macro) {
                return resolved;

            // Resolve other pre-defined, built-in values
            } else {
                return resolved;
            }

        // Error on missing local names
        } else if (value.value.charCodeAt(0) === 95) {

            var resolvedGlobal = Linker.resolveName(
                value.value, sourceFile, true
            );

            if (resolvedGlobal) {
                new Errors.ReferenceError(
                    value.file,
                    'Local name "'
                    + value.value
                    + '" was not declared in current file, but found in '
                    + resolvedGlobal.file.getPath(resolvedGlobal.index, true),
                    value.index
                );

            } else {
                new Errors.ReferenceError(
                    value.file,
                    'Local name "' + value.value + '" was not declared',
                    value.index
                );
            }

        // Error on missing global names
        } else {
            // TODO Show the reference path
            new Errors.ReferenceError(
                value.file,
                '"' + value.value + '" was not declared',
                value.index
            );
        }

    },

    resolveName: function(name, file, global) {

        // Check if their is a macro with the specified name
        if (Macro.isDefined(name)) {
            return Macro.get(name);

        // Names prefixed with _ will only be looked up in their own file
        } else if (!global && name.charCodeAt(0) === 95) {
            return file.names[name];

        // All other names will be searched globally
        } else {

            var files = file.compiler.files;
            for(var i = 0, l = files.length; i < l; i++) {
                file = files[i];

                var value = file.names[name];
                if (value) {
                    return value;
                }
            }

            return null;

        }

    },

    resolveExpression: function(node, sourceFile, sourceOffset, sourceIndex, relativeOffset, stack) {

        // Binary Expressions
        if (node instanceof Expression.Node) {

            var left = Linker.resolveExpression(
                    node.left, sourceFile, sourceOffset, sourceIndex,
                    relativeOffset, stack
                );

            if (node.right) {

                var right = Linker.resolveExpression(
                    node.right, sourceFile, sourceOffset, sourceIndex,
                    relativeOffset, stack
                );

                if (typeof left !== typeof right) {
                    new Errors.ExpressionError(
                        sourceFile,
                        'Incompatible operand types '
                        + (typeof left).toUpperCase()
                        + ' and '
                        + (typeof right).toUpperCase()
                        + ' for binary operator ' + node.op.id,
                        node.op.index
                    );

                } else {
                    return Linker.evaluateBinaryOperator(node.op.id, left, right);
                }

            } else if (typeof left === 'number') {
                return Linker.evaluateUnaryOperator(node.op.id, left);

            } else{
                new Errors.ExpressionError(
                    sourceFile,
                    'Invalid operand type '
                    + (typeof left).toUpperCase()
                    + ' for unary operator ' + node.op.id,
                    node.left.value.index
                );
            }

        // Raw Values
        } else if (node instanceof Expression.Leaf) {
            return Linker.resolveValue(
                sourceFile, node.value, sourceOffset,
                node.value.index, false, stack
            );

        // Macro Calls
        } else if (node instanceof Expression.Call) {

            if (!Macro.isDefined(node.callee.value)) {
                new Errors.ExpressionError(
                    sourceFile,
                    'Call of undefined MACRO function "' + node.callee.value + '"',
                    node.callee.index
                );
            }

            var callee = Macro.get(node.callee.value);
            var args = node.args.map(function(arg, index) {

                var value = Linker.resolveValue(
                    sourceFile, arg.value, sourceOffset,
                    arg.value.index, false, stack
                );

                if (typeof value !== callee.args[index]) {
                    new Errors.ExpressionError(
                        'Invalid type for MACRO argument, '
                        + 'expected ' + callee.args[index].toUpperCase()
                        + ' but got ' + (typeof value).toUpperCase()
                        + ' instead',
                        sourceFile, arg.value.index
                    );

                } else {
                    return value;
                }

            });

            return callee.handler.apply(null, args);

        }

    },

    evaluateBinaryOperator: function(op, left, right) {
        switch(op) {

            // Binary
            case '&':
                return (left & right) | 0;

            case '|':
                return (left | right) | 0;

            case '^':
                return (left ^ right) | 0;

            // Math
            case '+':
                if (typeof left === 'string') {
                    return left + right;

                } else {
                    return left + right;
                }
                break;

            case '-':
                return left - right;

            case '*':
                return left * right;

            case '/':
                return left / right;

            case '%':
                return left % right;

            case '**':
                return Math.pow(left, right);

            // Shift
            case '>>':
                return (left >> right) | 0;

            case '<<':
                return (left << right) | 0;

            // Comparisons
            case '>':
                return left > right ? 1 : 0;

            case '>=':
                return left >= right ? 1 : 0;

            case '<':
                return left < right ? 1 : 0;

            case '<=':
                return left <= right ? 1 : 0;

            case '==':
                return left === right ? 1 : 0;

            case '!=':
                return left !== right ? 1 : 0;

            default:
                throw new TypeError('Unimplemented binary operator: ' + op);
        }
    },

    evaluateUnaryOperator: function(op, arg) {
        switch(op) {
            case '!':
                return !arg ? 1 : 0;

            case '-':
                return -arg;

            case '~':
                return (~arg) | 0;

            default:
                throw new TypeError('Unimplemented unary operator: ' + op);
        }
    },

    // Optimization -----------------------------------------------------------
    optimize: function(files) {

        // Optimize instructions
        files.forEach(FileLinker.optimize);

        // Now relink with the changed addresses
        Linker.link(files);

    }

};

// Helpers --------------------------------------------------------------------
function getAllSections(files) {

    var sections = [];
    files.forEach(function(file) {
        sections.push.apply(sections, file.sections);
    });

    return sections;

}

// Exports --------------------------------------------------------------------
module.exports = Linker;

// After Dependencies ---------------------------------------------------------
FileLinker = require('./FileLinker');

<|endoftext|>


<commit_before>//controller and any services relating to the main boaard
plugin.controller("BoardController", function ($scope, autoRefresh, $rootScope) {
	$rootScope.projectName = "cdpipeline";
	$scope.results = autoRefresh.data;
	$rootScope.dataLoaded = false;

	//scroll for new data
	$scope.totalDisplayed = 15;
	$scope.loadMore = function() {
		$scope.totalDisplayed += 5;
	};
});

//polls a REST endpoint every five seconds and automatically refreshes
plugin.factory('autoRefresh', function ($http, $timeout, $rootScope) {
	var data = { resp: {}};
	var poller = function() {
		$http.get('?data=all').then( function(r) {
			data.resp = r.data;
			$rootScope.dataLoaded = true;
			$timeout(poller, 5000);
		});
	};
	poller();

	return {
		data: data
	};
});

// filter that puts element with key == null to the end of the list
plugin.filter("emptyToEnd", function () {
	return function (array, key) {
		if(!angular.isArray(array)) return;
        var present = array.filter(function (item) {
            return item[key]["lastUpdateTime"];
        });
        var empty = array.filter(function (item) {
            return !item[key]["lastUpdateTime"];
        });
		return present.concat(empty);
	};
});

// filter that puts in progress and queued projects to the top
plugin.filter("progressToFront", function () {
	return function (array, key) {
		if(!angular.isArray(array)) return;
        var inProgress = array.filter(function (item) {
            return item[key]["currentBuild"]["cdpipelineState"] == "CD_IN_PROGRESS";
        });
        var queued = array.filter(function (item) {
        	return item[key]["currentBuild"]["cdpipelineState"] == "CD_QUEUED";
        });
        var finished = array.filter(function (item) {
            return item[key]["currentBuild"]["cdpipelineState"] != "CD_IN_PROGRESS" && item[key]["currentBuild"]["cdpipelineState"] != "CD_QUEUED";
        });
		return inProgress.concat(queued).concat(finished);
	};
});

// The search button on the menu bar
plugin.filter('searchFor', keywordSearch);

// Helper method for the search button on the menu bar
function keywordSearch(){

	// All filters must return a function. The first parameter
	// is the data that is to be filtered, and the second is an
	// argument that may be passed with a colon (searchFor:searchString)

	return function(arr, searchString){

		if(!searchString){
			return arr;
		}
		var result = [];
		searchString = searchString.toLowerCase();
		// Using the forEach helper method to loop through the array
		angular.forEach(arr, function(item){
            if(item.cdresult.projectName.toLowerCase().indexOf(searchString) !== -1 |
                item.cdresult.planName.toLowerCase().indexOf(searchString) !== -1) {
                    result.push(item);
            }
            for (i = 0; i < item.cdresult.contributors.length; i++) {
                if (item.cdresult.contributors[i].username.toLowerCase().indexOf(searchString) !== -1 |
                     item.cdresult.contributors[i].fullname.toLowerCase().indexOf(searchString) !== -1) {
                    result.push(item);
                }
            }
        });

		return result;
	};
}

// Filter that deals with edge case of the pipeline
// (when there's only one stage)
plugin.filter('pipelineWidth', function(){
  return function(input){
    if(input > 1) {
      return (1 / (input - 1));
    } else {
      return 0;
    }
  };
});

plugin.filter('percentageLimit', function(){
	return function(input){
		if(input <= 100) {
			return input;
		} else {
			return 100;
		}
	};
});

// Filter that strips the '+' or '-' of letter grades.
// E.g. from B+ to B.
plugin.filter('letterOnly', function() {
	return function(input){
		return input.substring(0, 1);
	};
});<commit_msg>Fixing issue with search<commit_after>//controller and any services relating to the main boaard
plugin.controller("BoardController", function ($scope, autoRefresh, $rootScope) {
	$rootScope.projectName = "cdpipeline";
	$scope.results = autoRefresh.data;
	$rootScope.dataLoaded = false;

	//scroll for new data
	$scope.totalDisplayed = 15;
	$scope.loadMore = function() {
		$scope.totalDisplayed += 5;
	};
});

//polls a REST endpoint every five seconds and automatically refreshes
plugin.factory('autoRefresh', function ($http, $timeout, $rootScope) {
	var data = { resp: {}};
	var poller = function() {
		$http.get('?data=all').then( function(r) {
			data.resp = r.data;
			$rootScope.dataLoaded = true;
			$timeout(poller, 5000);
		});
	};
	poller();

	return {
		data: data
	};
});

// filter that puts element with key == null to the end of the list
plugin.filter("emptyToEnd", function () {
	return function (array, key) {
		if(!angular.isArray(array)) return;
        var present = array.filter(function (item) {
            return item[key]["lastUpdateTime"];
        });
        var empty = array.filter(function (item) {
            return !item[key]["lastUpdateTime"];
        });
		return present.concat(empty);
	};
});

// filter that puts in progress and queued projects to the top
plugin.filter("progressToFront", function () {
	return function (array, key) {
		if(!angular.isArray(array)) return;
        var inProgress = array.filter(function (item) {
            return item[key]["currentBuild"]["cdpipelineState"] == "CD_IN_PROGRESS";
        });
        var queued = array.filter(function (item) {
        	return item[key]["currentBuild"]["cdpipelineState"] == "CD_QUEUED";
        });
        var finished = array.filter(function (item) {
            return item[key]["currentBuild"]["cdpipelineState"] != "CD_IN_PROGRESS" && item[key]["currentBuild"]["cdpipelineState"] != "CD_QUEUED";
        });
		return inProgress.concat(queued).concat(finished);
	};
});

// The search button on the menu bar
plugin.filter('searchFor', keywordSearch);

// Helper method for the search button on the menu bar
function keywordSearch(){

	// All filters must return a function. The first parameter
	// is the data that is to be filtered, and the second is an
	// argument that may be passed with a colon (searchFor:searchString)

	return function(arr, searchString){

		if(!searchString){
			return arr;
		}
		var result = [];
		searchString = searchString.toLowerCase();
		// Using the forEach helper method to loop through the array
		angular.forEach(arr, function(item){
            if(item.cdresult.projectName.toLowerCase().indexOf(searchString) !== -1 |
                item.cdresult.planName.toLowerCase().indexOf(searchString) !== -1) {
                    result.push(item);
            }
        });

		return result;
	};
}

// Filter that deals with edge case of the pipeline
// (when there's only one stage)
plugin.filter('pipelineWidth', function(){
  return function(input){
    if(input > 1) {
      return (1 / (input - 1));
    } else {
      return 0;
    }
  };
});

plugin.filter('percentageLimit', function(){
	return function(input){
		if(input <= 100) {
			return input;
		} else {
			return 100;
		}
	};
});

// Filter that strips the '+' or '-' of letter grades.
// E.g. from B+ to B.
plugin.filter('letterOnly', function() {
	return function(input){
		return input.substring(0, 1);
	};
});<|endoftext|>


<commit_before>/**
 * @file TableBody component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';

import TableRow from '../_TableRow';

import SelectMode from '../_statics/SelectMode';

import Util from '../_vendors/Util';

class TableBody extends Component {

    static SelectMode = SelectMode;

    constructor(props, ...restArgs) {
        super(props, ...restArgs);
    }

    isItemChecked = rowData => {

        const {selectMode, idProp, value} = this.props;

        if (!rowData || !value) {
            return false;
        }

        switch (selectMode) {
            case SelectMode.MULTI_SELECT:
                return value.findIndex(item => item[idProp] === rowData[idProp]) !== -1;
            case SelectMode.SINGLE_SELECT:
                return value[idProp] === rowData[idProp];
        }

    };

    render() {

        const {
            columns, data, startIndex, idProp, disabled,
            onRowClick, onCellClick
        } = this.props;

        return (
            <tbody className="table-body">
                {
                    data.map((row, rowIndex) => row ?
                        <TableRow key={idProp && idProp in row ? row[idProp] : rowIndex}
                                  rowIndex={startIndex + rowIndex}
                                  columns={columns}
                                  data={row}
                                  isChecked={this.isItemChecked(row)}
                                  disabled={disabled || row.disabled}
                                  onRowClick={onRowClick}
                                  onCellClick={onCellClick}/>
                        :
                        null
                    )
                }
            </tbody>
        );

    }
}

TableBody.propTypes = {

    columns: PropTypes.array,
    data: PropTypes.array,
    startIndex: PropTypes.number,
    idProp: PropTypes.string,
    selectMode: PropTypes.oneOf(Util.enumerateValue(SelectMode)),
    disabled: PropTypes.bool,

    isItemChecked: PropTypes.func,

    onRowClick: PropTypes.func,
    onCellClick: PropTypes.func

};

TableBody.defaultProps = {
    data: [],
    columns: [],
    startIndex: 0,
    idProp: 'id',
    selectMode: SelectMode.SINGLE_SELECT,
    disabled: false
};

export default TableBody;<commit_msg>optimize TableBody<commit_after>/**
 * @file TableBody component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';

import TableRow from '../_TableRow';

import SelectMode from '../_statics/SelectMode';

import Util from '../_vendors/Util';

class TableBody extends Component {

    static SelectMode = SelectMode;

    constructor(props, ...restArgs) {
        super(props, ...restArgs);
    }

    isItemChecked = rowData => {

        const {selectMode, idProp, value} = this.props;

        if (!rowData || !value) {
            return false;
        }

        switch (selectMode) {
            case SelectMode.MULTI_SELECT:
                return value.findIndex(item => item[idProp] === rowData[idProp]) !== -1;
            case SelectMode.SINGLE_SELECT:
                return value[idProp] === rowData[idProp];
        }

    };

    render() {

        const {
            columns, data, startIndex, idProp, disabled,
            onRowClick, onCellClick
        } = this.props;

        return (
            <tbody className="table-body">
                {
                    data && data.map((row, rowIndex) => row ?
                        <TableRow key={idProp && idProp in row ? row[idProp] : rowIndex}
                                  rowIndex={startIndex + rowIndex}
                                  columns={columns}
                                  data={row}
                                  isChecked={this.isItemChecked(row)}
                                  disabled={disabled || row.disabled}
                                  onRowClick={onRowClick}
                                  onCellClick={onCellClick}/>
                        :
                        null
                    )
                }
            </tbody>
        );

    }
}

TableBody.propTypes = {

    columns: PropTypes.array,
    data: PropTypes.array,
    startIndex: PropTypes.number,
    idProp: PropTypes.string,
    selectMode: PropTypes.oneOf(Util.enumerateValue(SelectMode)),
    disabled: PropTypes.bool,

    isItemChecked: PropTypes.func,

    onRowClick: PropTypes.func,
    onCellClick: PropTypes.func

};

TableBody.defaultProps = {
    data: [],
    columns: [],
    startIndex: 0,
    idProp: 'id',
    selectMode: SelectMode.SINGLE_SELECT,
    disabled: false
};

export default TableBody;<|endoftext|>


<commit_before>'use strict'

const morgan = require('morgan')

// This adds a very simple log message for each request. Morgan is a package
// from express to do this, and we use tiny so we don't record any user
// information to logs in production.
//
// TODO: Add a different log format for dev & staging that provides more
// information for debugging.
module.exports = morgan(':date[iso] :method :url :status :res[content-length] - :response-time ms')
<commit_msg>Update documentation<commit_after>'use strict';

const morgan = require('morgan');

// This adds a very simple log message for each request. Morgan is a package
// from express to do this, and we don't want to record any user information to
// logs in production.
//
// TODO: Add a different log format for dev & staging that provides more
// information for debugging.
//
// Usage:
//   import logMiddleware from 'react-scripts/lib/logMiddleware';
//   app.use(logMiddleware);
module.exports = morgan(':date[iso] :method :url :status :res[content-length] - :response-time ms');
<|endoftext|>