<commit_before>//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// Draws the appropriate shape based on the data
//------------------------------------------------------------------------------
d3plus.shape.draw = function(vars,data) {

  //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  // Match vars.shape types to their respective d3plus.shape functions. For
  // example, both "square", and "circle" shapes use "rect" as their drawing
  // class.
  //----------------------------------------------------------------------------
  var shape_lookup = {
    "area": "area",
    "circle": "rect",
    "donut": "donut",
    "line": "line",
    "square": "rect",
    "coordinates": "coordinates"
  }

  //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  // Split the data by each shape type in the data.
  //----------------------------------------------------------------------------
  var shapes = {}
  data.forEach(function(d){
    if (!d.d3plus) {
      var s = shape_lookup[vars.shape.value]
    }
    else if (!d.d3plus.shape) {
      var s = shape_lookup[vars.shape.value]
      d.d3plus.shapeType = s
    }
    else {
      var s = d.d3plus.shape
      d.d3plus.shapeType = s
    }
    if (!shapes[s]) {
      shapes[s] = []
    }
    shapes[s].push(d)
  })

  //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  // Resets the "id" of each data point to use with matching.
  //----------------------------------------------------------------------------
  function id(d) {

    var depth = d.d3plus.depth ? d.d3plus.depth : vars.depth.value

    d.d3plus.id = ""

    vars.id.nesting.forEach(function(n,i){
      if (i <= depth) {
        d.d3plus.id += d3plus.variable.value(vars,d,n)
        d.d3plus.id += "_"
      }
    })

    d.d3plus.id += depth+"_"+shape

    vars.axes.values.forEach(function(axis){
      if (vars[axis].scale.value == "continuous") {
        d.d3plus.id += "_"+d3plus.variable.value(vars,d,vars[axis].key)
      }
    })

    d.d3plus.id = d3plus.utils.strip(d.d3plus.id)

    return d
  }

  //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  // Transforms the positions and scale of each group.
  //----------------------------------------------------------------------------
  function transform(g,grow) {

    var scales = d3plus.apps[vars.type.value].scale
    if (grow && scales && scales[vars.shape.value]) {
       var scale = scales[vars.shape.value]
    }
    else if (grow && scales && typeof scales == "number") {
      var scale = scales
    }
    else {
      var scale = 1
    }

    g
      .attr("transform",function(d){
        if (["line","area","coordinates"].indexOf(shape) < 0) {
          return "translate("+d.d3plus.x+","+d.d3plus.y+")scale("+scale+")"
        }
        else {
          return "scale("+scale+")"
        }
      })

  }

  //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  // Remove old groups
  //----------------------------------------------------------------------------
  for (shape in shape_lookup) {
    if (!(shape_lookup[shape] in shapes)) {
      vars.g.data.selectAll("g."+shape_lookup[shape])
        .transition().duration(vars.style.timing.transitions)
        .attr("opacity",0)
        .remove()
    }
  }

  //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  // Initialize arrays for labels and sizes
  //----------------------------------------------------------------------------
  var labels = [],
      shares = []

  //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  // Create groups by shape, apply data, and call specific shape drawing class.
  //----------------------------------------------------------------------------
  for (shape in shapes) {

    if (vars.dev.value) d3plus.console.group("Drawing \"" + shape + "\" groups")

    //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    // Filter out too small shapes
    //--------------------------------------------------------------------------
    if (vars.dev.value) d3plus.console.time("filtering out small shapes")
    var filtered_shapes = shapes[shape].filter(function(s){
      if (s.d3plus) {
        if ("width" in s.d3plus && s.d3plus.width < 1) {
          return false
        }
        if ("height" in s.d3plus && s.d3plus.height < 1) {
          return false
        }
        if ("r" in s.d3plus && s.d3plus.r < 0.5) {
          return false
        }
      }
      else if (s.values) {
        var small = true
        s.values.forEach(function(v){
          if (!("y0" in v.d3plus)) {
            small = false
          }
          else if (small && "y0" in v.d3plus && v.d3plus.y0-v.d3plus.y >= 1) {
            small = false
          }
        })
        if (small) {
          return false
        }
      }
      return true
    })

    if (vars.dev.value) {
      d3plus.console.timeEnd("filtering out small shapes")
      var removed = shapes[shape].length-filtered_shapes.length,
          percent = d3.round(removed/shapes[shape].length,2)
      console.log("removed "+removed+" out of "+shapes[shape].length+" shapes ("+percent*100+"% reduction)")
    }

    //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    // Bind Data to Groups
    //--------------------------------------------------------------------------
    var selection = vars.g.data.selectAll("g.d3plus_"+shape)
      .data(filtered_shapes,function(d){

        if (shape == "coordinates") {
          if (!d.d3plus) {
            d.d3plus = {}
          }
          return d.id
        }

        if (d.values) {
          d.values.forEach(function(v){
            v = id(v)
            v.d3plus.shapeType = "circle"
          })
        }
        else {

          d = id(d)

          if (!d.d3plus.a) {

            d.d3plus.a = {"donut": Math.PI*2}
            var active = vars.active.key ? d.d3plus[vars.active.key] : d.d3plus.active,
                temp = vars.temp.key ? d.d3plus[vars.temp.key] : d.d3plus.temp,
                total = vars.total.key ? d.d3plus[vars.total.key] : d.d3plus.total

            if (total) {
              if (active) {
                d.d3plus.a.active = (active/total) * (Math.PI * 2)
              }
              else {
                d.d3plus.a.active = 0
              }
              if (temp) {
                d.d3plus.a.temp = ((temp/total) * (Math.PI * 2)) + d.d3plus.a.active
              }
              else {
                d.d3plus.a.temp = 0
              }
            }

          }

        }

        return d.d3plus ? d.d3plus.id : d[vars.id.key];

      })

    //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    // Groups Enter
    //--------------------------------------------------------------------------
    var enter = selection.enter().append("g")
      .attr("class","d3plus_"+shape)
      .attr("opacity",0)
      .call(transform)

    //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    // Groups Update
    //--------------------------------------------------------------------------
    selection
      .order()
      .transition().duration(vars.style.timing.transitions)
      .call(transform)
      .attr("opacity",1)

    //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    // Groups Exit
    //--------------------------------------------------------------------------
    var exit = selection.exit()
      .transition().duration(vars.style.timing.transitions)
      .attr("opacity",0)
      .remove()

    //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    // Draw appropriate graphics inside of each group
    //--------------------------------------------------------------------------
    if (vars.dev.value) d3plus.console.time("shapes")
    d3plus.shape[shape](vars,selection,enter,exit,transform)
    if (vars.dev.value) d3plus.console.timeEnd("shapes")

    //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    // Check for active and temp fills for rects and donuts
    //--------------------------------------------------------------------------
    if (["rect","donut"].indexOf(shape) >= 0) {
      d3plus.shape.fill(vars,selection,enter,exit,transform)
    }

    //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    // Create labels
    //--------------------------------------------------------------------------
    if (vars.dev.value) d3plus.console.time("labels")
    d3plus.shape.labels(vars,selection)
    if (vars.dev.value) d3plus.console.timeEnd("labels")

    if (vars.dev.value) d3plus.console.groupEnd()

  }

  //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  // Function to Update Links
  //----------------------------------------------------------------------------
  function links(d) {

    var link_count = vars.g.links.selectAll("line, path").size()

    if (d) {

      vars.g.links.selectAll("line, path")
        .each(function(l){

          var id = d[vars.id.key],
              source = l.source[vars.id.key],
              target = l.target[vars.id.key]

          if (source == id || target == id) {
            vars.g.link_focus.node().appendChild(this.cloneNode(true))
          }

        })

      vars.g.link_focus
        .attr("opacity",0)
        .selectAll("line, path")
        .style("stroke",vars.style.highlight.primary)

      vars.g.link_focus
        .transition().duration(vars.style.timing.mouseevents)
        .attr("opacity",1)

      if (link_count < vars.links.large) {
        vars.g.links
          .transition().duration(vars.style.timing.mouseevents)
          .attr("opacity",0.5)
      }

    }
    else {

      vars.g.link_focus
        .transition().duration(vars.style.timing.mouseevents)
        .attr("opacity",0)
        .transition()
        .selectAll("*")
        .remove()

      if (link_count < vars.links.large) {
        vars.g.links
          .transition().duration(vars.style.timing.mouseevents)
          .attr("opacity",1)
      }

    }

  }

  vars.g.data.selectAll("g")
    .on(d3plus.evt.over,function(d){

      if (!vars.frozen && (!d.d3plus || !d.d3plus.static)) {

        d3.select(this).style("cursor","pointer")

        if (!vars.small) {

          vars.covered = false

          var tooltip_data = d.data ? d.data : d
          d3plus.tooltip.app({
            "vars": vars,
            "data": tooltip_data
          })

          if (typeof vars.mouse == "function") {
            vars.mouse(d)
          }
          else if (vars.mouse.over) {
            vars.mouse.over(d)
          }

        }

        links(d)

      }

    })
    .on(d3plus.evt.move,function(d){

      if (!vars.frozen && (!d.d3plus || !d.d3plus.static)) {

        if (!vars.small) {

          vars.covered = false

          if (["area","line"].indexOf(vars.shape.value) >= 0 || d3plus.apps[vars.type.value].tooltip == "follow") {

            var tooltip_data = d.data ? d.data : d
            d3plus.tooltip.app({
              "vars": vars,
              "data": tooltip_data
            })

          }

          if (typeof vars.mouse == "function") {
            vars.mouse(d)
          }
          else if (vars.mouse.move) {
            vars.mouse.move(d)
          }

        }

      }

    })
    .on(d3plus.evt.out,function(d){

      if (!vars.frozen && (!d.d3plus || !d.d3plus.static)) {

        if (!vars.small) {

          if (!vars.covered) {
            d3plus.tooltip.remove(vars.type.value)
          }

          if (typeof vars.mouse == "function") {
            vars.mouse(d)
          }
          else if (vars.mouse.out) {
            vars.mouse.out(d)
          }

        }

        links()

      }

    })
    .on(d3plus.evt.click,function(d){

      if (!vars.frozen && (!d.d3plus || !d.d3plus.static)) {

        if (!vars.small) {

          links()

          var tooltip_data = d.data ? d.data : d

          d3plus.tooltip.app({
            "vars": vars,
            "data": tooltip_data
          })

          if (typeof vars.mouse == "function") {
            vars.mouse(d)
          }
          else if (vars.mouse.click) {
            vars.mouse.click(d)
          }

        }

      }

    })

}
<commit_msg>updated link drawing to detect links inside of groups<commit_after>//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// Draws the appropriate shape based on the data
//------------------------------------------------------------------------------
d3plus.shape.draw = function(vars,data) {

  //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  // Match vars.shape types to their respective d3plus.shape functions. For
  // example, both "square", and "circle" shapes use "rect" as their drawing
  // class.
  //----------------------------------------------------------------------------
  var shape_lookup = {
    "area": "area",
    "circle": "rect",
    "donut": "donut",
    "line": "line",
    "square": "rect",
    "coordinates": "coordinates"
  }

  //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  // Split the data by each shape type in the data.
  //----------------------------------------------------------------------------
  var shapes = {}
  data.forEach(function(d){
    if (!d.d3plus) {
      var s = shape_lookup[vars.shape.value]
    }
    else if (!d.d3plus.shape) {
      var s = shape_lookup[vars.shape.value]
      d.d3plus.shapeType = s
    }
    else {
      var s = d.d3plus.shape
      d.d3plus.shapeType = s
    }
    if (!shapes[s]) {
      shapes[s] = []
    }
    shapes[s].push(d)
  })

  //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  // Resets the "id" of each data point to use with matching.
  //----------------------------------------------------------------------------
  function id(d) {

    var depth = d.d3plus.depth ? d.d3plus.depth : vars.depth.value

    d.d3plus.id = ""

    vars.id.nesting.forEach(function(n,i){
      if (i <= depth) {
        d.d3plus.id += d3plus.variable.value(vars,d,n)
        d.d3plus.id += "_"
      }
    })

    d.d3plus.id += depth+"_"+shape

    vars.axes.values.forEach(function(axis){
      if (vars[axis].scale.value == "continuous") {
        d.d3plus.id += "_"+d3plus.variable.value(vars,d,vars[axis].key)
      }
    })

    d.d3plus.id = d3plus.utils.strip(d.d3plus.id)

    return d
  }

  //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  // Transforms the positions and scale of each group.
  //----------------------------------------------------------------------------
  function transform(g,grow) {

    var scales = d3plus.apps[vars.type.value].scale
    if (grow && scales && scales[vars.shape.value]) {
       var scale = scales[vars.shape.value]
    }
    else if (grow && scales && typeof scales == "number") {
      var scale = scales
    }
    else {
      var scale = 1
    }

    g
      .attr("transform",function(d){
        if (["line","area","coordinates"].indexOf(shape) < 0) {
          return "translate("+d.d3plus.x+","+d.d3plus.y+")scale("+scale+")"
        }
        else {
          return "scale("+scale+")"
        }
      })

  }

  //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  // Remove old groups
  //----------------------------------------------------------------------------
  for (shape in shape_lookup) {
    if (!(shape_lookup[shape] in shapes)) {
      vars.g.data.selectAll("g."+shape_lookup[shape])
        .transition().duration(vars.style.timing.transitions)
        .attr("opacity",0)
        .remove()
    }
  }

  //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  // Initialize arrays for labels and sizes
  //----------------------------------------------------------------------------
  var labels = [],
      shares = []

  //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  // Create groups by shape, apply data, and call specific shape drawing class.
  //----------------------------------------------------------------------------
  for (shape in shapes) {

    if (vars.dev.value) d3plus.console.group("Drawing \"" + shape + "\" groups")

    //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    // Filter out too small shapes
    //--------------------------------------------------------------------------
    if (vars.dev.value) d3plus.console.time("filtering out small shapes")
    var filtered_shapes = shapes[shape].filter(function(s){
      if (s.d3plus) {
        if ("width" in s.d3plus && s.d3plus.width < 1) {
          return false
        }
        if ("height" in s.d3plus && s.d3plus.height < 1) {
          return false
        }
        if ("r" in s.d3plus && s.d3plus.r < 0.5) {
          return false
        }
      }
      else if (s.values) {
        var small = true
        s.values.forEach(function(v){
          if (!("y0" in v.d3plus)) {
            small = false
          }
          else if (small && "y0" in v.d3plus && v.d3plus.y0-v.d3plus.y >= 1) {
            small = false
          }
        })
        if (small) {
          return false
        }
      }
      return true
    })

    if (vars.dev.value) {
      d3plus.console.timeEnd("filtering out small shapes")
      var removed = shapes[shape].length-filtered_shapes.length,
          percent = d3.round(removed/shapes[shape].length,2)
      console.log("removed "+removed+" out of "+shapes[shape].length+" shapes ("+percent*100+"% reduction)")
    }

    //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    // Bind Data to Groups
    //--------------------------------------------------------------------------
    var selection = vars.g.data.selectAll("g.d3plus_"+shape)
      .data(filtered_shapes,function(d){

        if (shape == "coordinates") {
          if (!d.d3plus) {
            d.d3plus = {}
          }
          return d.id
        }

        if (d.values) {
          d.values.forEach(function(v){
            v = id(v)
            v.d3plus.shapeType = "circle"
          })
        }
        else {

          d = id(d)

          if (!d.d3plus.a) {

            d.d3plus.a = {"donut": Math.PI*2}
            var active = vars.active.key ? d.d3plus[vars.active.key] : d.d3plus.active,
                temp = vars.temp.key ? d.d3plus[vars.temp.key] : d.d3plus.temp,
                total = vars.total.key ? d.d3plus[vars.total.key] : d.d3plus.total

            if (total) {
              if (active) {
                d.d3plus.a.active = (active/total) * (Math.PI * 2)
              }
              else {
                d.d3plus.a.active = 0
              }
              if (temp) {
                d.d3plus.a.temp = ((temp/total) * (Math.PI * 2)) + d.d3plus.a.active
              }
              else {
                d.d3plus.a.temp = 0
              }
            }

          }

        }

        return d.d3plus ? d.d3plus.id : d[vars.id.key];

      })

    //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    // Groups Enter
    //--------------------------------------------------------------------------
    var enter = selection.enter().append("g")
      .attr("class","d3plus_"+shape)
      .attr("opacity",0)
      .call(transform)

    //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    // Groups Update
    //--------------------------------------------------------------------------
    selection
      .order()
      .transition().duration(vars.style.timing.transitions)
      .call(transform)
      .attr("opacity",1)

    //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    // Groups Exit
    //--------------------------------------------------------------------------
    var exit = selection.exit()
      .transition().duration(vars.style.timing.transitions)
      .attr("opacity",0)
      .remove()

    //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    // Draw appropriate graphics inside of each group
    //--------------------------------------------------------------------------
    if (vars.dev.value) d3plus.console.time("shapes")
    d3plus.shape[shape](vars,selection,enter,exit,transform)
    if (vars.dev.value) d3plus.console.timeEnd("shapes")

    //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    // Check for active and temp fills for rects and donuts
    //--------------------------------------------------------------------------
    if (["rect","donut"].indexOf(shape) >= 0) {
      d3plus.shape.fill(vars,selection,enter,exit,transform)
    }

    //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    // Create labels
    //--------------------------------------------------------------------------
    if (vars.dev.value) d3plus.console.time("labels")
    d3plus.shape.labels(vars,selection)
    if (vars.dev.value) d3plus.console.timeEnd("labels")

    if (vars.dev.value) d3plus.console.groupEnd()

  }

  //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  // Function to Update Links
  //----------------------------------------------------------------------------
  function links(d) {

    var link_count = vars.g.links.selectAll("line, path").size()

    if (d) {

      vars.g.links.selectAll("g")
        .each(function(l){

          var id = d[vars.id.key],
              source = l.source[vars.id.key],
              target = l.target[vars.id.key]

          if (source == id || target == id) {
            vars.g.link_focus.node().appendChild(this.cloneNode(true))
          }

        })

      vars.g.link_focus
        .attr("opacity",0)
        .selectAll("line, path")
        .style("stroke",vars.style.highlight.primary)

      vars.g.link_focus
        .transition().duration(vars.style.timing.mouseevents)
        .attr("opacity",1)

      if (link_count < vars.links.large) {
        vars.g.links
          .transition().duration(vars.style.timing.mouseevents)
          .attr("opacity",0.5)
      }

    }
    else {

      vars.g.link_focus
        .transition().duration(vars.style.timing.mouseevents)
        .attr("opacity",0)
        .transition()
        .selectAll("*")
        .remove()

      if (link_count < vars.links.large) {
        vars.g.links
          .transition().duration(vars.style.timing.mouseevents)
          .attr("opacity",1)
      }

    }

  }

  vars.g.data.selectAll("g")
    .on(d3plus.evt.over,function(d){

      if (!vars.frozen && (!d.d3plus || !d.d3plus.static)) {

        d3.select(this).style("cursor","pointer")

        if (!vars.small) {

          vars.covered = false

          var tooltip_data = d.data ? d.data : d
          d3plus.tooltip.app({
            "vars": vars,
            "data": tooltip_data
          })

          if (typeof vars.mouse == "function") {
            vars.mouse(d)
          }
          else if (vars.mouse.over) {
            vars.mouse.over(d)
          }

        }

        links(d)

      }

    })
    .on(d3plus.evt.move,function(d){

      if (!vars.frozen && (!d.d3plus || !d.d3plus.static)) {

        if (!vars.small) {

          vars.covered = false

          if (["area","line"].indexOf(vars.shape.value) >= 0 || d3plus.apps[vars.type.value].tooltip == "follow") {

            var tooltip_data = d.data ? d.data : d
            d3plus.tooltip.app({
              "vars": vars,
              "data": tooltip_data
            })

          }

          if (typeof vars.mouse == "function") {
            vars.mouse(d)
          }
          else if (vars.mouse.move) {
            vars.mouse.move(d)
          }

        }

      }

    })
    .on(d3plus.evt.out,function(d){

      if (!vars.frozen && (!d.d3plus || !d.d3plus.static)) {

        if (!vars.small) {

          if (!vars.covered) {
            d3plus.tooltip.remove(vars.type.value)
          }

          if (typeof vars.mouse == "function") {
            vars.mouse(d)
          }
          else if (vars.mouse.out) {
            vars.mouse.out(d)
          }

        }

        links()

      }

    })
    .on(d3plus.evt.click,function(d){

      if (!vars.frozen && (!d.d3plus || !d.d3plus.static)) {

        if (!vars.small) {

          links()

          var tooltip_data = d.data ? d.data : d

          d3plus.tooltip.app({
            "vars": vars,
            "data": tooltip_data
          })

          if (typeof vars.mouse == "function") {
            vars.mouse(d)
          }
          else if (vars.mouse.click) {
            vars.mouse.click(d)
          }

        }

      }

    })

}
<|endoftext|>


<commit_before>'use strict';

module.exports = (columns, column, done) => {
    // reset old classes
    columns.forEach(function (col) {
        col.headerClass = col.headerClass.replace('sort-asc', '');
        col.headerClass = col.headerClass.replace('sort-desc', '');
    });

    column.sort = column.sort === 'asc' ? 'desc' : 'asc';

    // push sorting hint
    column.headerClass += ' sort-' + column.sort;

    done({
        sortingColumn: column,
        columns: columns
    });
};

// sorter === lodash sortByOrder
// https://lodash.com/docs#sortByOrder
module.exports.sort = (data, column, sorter) => {
    if (!column) {
        return data;
    }

    return sorter(data, [column.property], [column.sort]);
};
<commit_msg>case-insensitive sort<commit_after>'use strict';

import {isString} from 'lodash';

module.exports = (columns, column, done) => {
    // reset old classes
    columns.forEach(function (col) {
        col.headerClass = col.headerClass.replace('sort-asc', '');
        col.headerClass = col.headerClass.replace('sort-desc', '');
    });

    column.sort = column.sort === 'asc' ? 'desc' : 'asc';

    // push sorting hint
    column.headerClass += ' sort-' + column.sort;

    done({
        sortingColumn: column,
        columns: columns
    });
};

// sorter === lodash sortByOrder
// https://lodash.com/docs#sortByOrder
module.exports.sort = (data, column, sorter) => {
    if (!column) {
        return data;
    }

    let sortFn = function (row) {
        let value = row[column.property];
        return isString(value) ? value.toLowerCase() : value;
    };

    return sorter(data, [sortFn], [column.sort]);
};<|endoftext|>


<commit_before>/**
 * StorageJar is a minimal API for interfacing with Local Storage.  In the event that
 * Local Store is not available, StorageJar will fallback to using Cookies.
 * @class
 **/
export default class StorageJar {
	/**
	 * This function determines if a key exists in either Cookies or Local Storage.
	 * @param {string} key the key of the item you are querying.
	 * @returns {boolean}.
	 * @function
	 * @name contains
	 * @static
	 * @public
	 *
	 * @example
	 * StorageJar.contains("hello");
	 **/
	static contains(key) {
		const item = this.read(key);
		if (item !== null)
			return true;

		return false;
	}
	/**
	 * This function converts the given number of days into milliseconds.
	 * @param {number} days the number of days to convert into milliseconds.
	 * @returns {number} the representation of days in milliseconds.
	 * @function
	 * @name convertDaysToMillis
	 * @static
	 * @private
	 **/
	static convertDaysToMillis(days) {
		return days * 24 * 60 * 60 * 1000;
	}
	/**
	 * This function will delete an item for either Cookies or Local Storage if it exists.
	 * @param {string} key the key of the item you are trying to delete.
	 * @returns {boolean}.
	 * @function
	 * @name delete
	 * @static
	 * @public
	 *
	 * @example
	 * StorageJar.delete("hello");
	 **/
	static delete(key) {
		if (! this.contains(key))
			return false;

		this.hasLocalStorage() ? window.localStorage.removeItem(key) : this.write(key, '', -1);
		return true;
	}
	/**
	 * This function is responsible for generating the value object of a Local Storage item.
	 * @param {value} value the value of the item you are trying to insert.
	 * @param {lifetime} lifetime the lifetime in days you wish to store the item for.
	 * @returns {object} the object to be stored as the item's value.
	 * @function
	 * @name generateStorageValue
	 * @static
	 * @private
	 **/
	static generateStorageValue(value, lifetime) {
		return {
			value,
			"timestamp": this.generateLifetimeTimestamp(lifetime)
		}
	}
	/**
	 * This function is responsible for generating the expiration timestamp of a stored item.
	 * @param {number} days the number of days you wish to store an item for.
	 * @returns {number} the expiration date in milliseconds.
	 * @function
	 * @name generateLifetimeTimestamp
	 * @static
	 * @private
	 **/
	static generateLifetimeTimestamp(days) {
		const currentTimestamp = new Date();
		return currentTimestamp.getTime() + this.convertDaysToMillis(days);
	}
	/**
	 * This function is responsible for generating the expiration timestamp of a stored item.
	 * @returns {localStorage|NULL} localStorage or null if localStorage is not available.
	 * @function
	 * @name hasLocalStorage
	 * @static
	 * @private
	 **/
	static hasLocalStorage() {
		return window.localStorage;
	}
	/**
	 * This function is responsible for determining if an item for Local Storage has expired.
	 * @param {number} timestamp the expiration timestamp of item in question.
	 * @returns {boolean}.
	 * @function
	 * @name isTimestampValid
	 * @static
	 * @private
	 **/
	static isTimestampValid(timestamp) {
		const currentTimestamp = new Date();
		return timestamp < currentTimestamp.getTime();
	}
	/**
	 * This function is responsible for fetching an item from either Cookies or Local Storage.
	 * @param {string} key the key of the item you are trying to fetch.
	 * @returns {object|NULL} the value of the item will be returned if found. if not, null will be returned.
	 * @function
	 * @name read
	 * @static
	 * @public
	 *
	 * @example
	 * StorageJar.read("hello");
	 **/
	static read(key) {
		if (this.hasLocalStorage()) {
			let item = JSON.parse(window.localStorage.getItem(key));
			if (this.isTimestampValid(item.timestamp))
				return item;
		} else {
		    let name = key + "=";
		    let cookies = document.cookie.split(';');
		    for(var i = 0; i < cookies.length; i++) {
		        let cookie = cookies[i];
		        while (cookie.charAt(0) == ' ') {
		            cookie = cookie.substring(1);
		        }
		        if (cookie.indexOf(name) == 0) {
		            return cookie.substring(name.length, cookie.length);
		        }
		    }
		}

		return null;
	}
	/**
	 * This function is responsible for writing an item to either Cookies or Local Storage.
	 * @param {string} key the key of the item you are trying to store.
	 * @param {string} value the value of the item you are trying to store.
 	 * @param {number} [lifetime=7] the length of time in days, you wish to store the item.
	 * @function
	 * @name write
	 * @static
	 * @public
	 *
	 * @example
	 * StorageJar.write("hello", "world", 5);
	 **/
	static write(key, value, lifetime = 7) {
		if ( this.hasLocalStorage() ) {
			const _value = this.generateStorageValue(value, lifetime);
			window.localStorage.setItem(key, JSON.stringify(_value));
		} else {
			let date = new Date();
			date.setTime(date.getTime() + (lifetime * 24 * 60 * 60 * 1000));
        	const expires = '; expires=' + date.toGMTString();
	        document.cookie = key + ' = ' + value + expires + '; path=/';
		}
	}
}
<commit_msg>resolve issue with reading items from local storage<commit_after>/**
 * StorageJar is a minimal API for interfacing with Local Storage.  In the event that
 * Local Store is not available, StorageJar will fallback to using Cookies.
 * @class
 **/
export default class StorageJar {
	/**
	 * This function determines if a key exists in either Cookies or Local Storage.
	 * @param {string} key the key of the item you are querying.
	 * @returns {boolean}.
	 * @function
	 * @name contains
	 * @static
	 * @public
	 *
	 * @example
	 * StorageJar.contains("hello");
	 **/
	static contains(key) {
		const item = this.read(key);
		if (item !== null)
			return true;

		return false;
	}
	/**
	 * This function converts the given number of days into milliseconds.
	 * @param {number} days the number of days to convert into milliseconds.
	 * @returns {number} the representation of days in milliseconds.
	 * @function
	 * @name convertDaysToMillis
	 * @static
	 * @private
	 **/
	static convertDaysToMillis(days) {
		return days * 24 * 60 * 60 * 1000;
	}
	/**
	 * This function will delete an item for either Cookies or Local Storage if it exists.
	 * @param {string} key the key of the item you are trying to delete.
	 * @returns {boolean}.
	 * @function
	 * @name delete
	 * @static
	 * @public
	 *
	 * @example
	 * StorageJar.delete("hello");
	 **/
	static delete(key) {
		if (! this.contains(key))
			return false;

		this.hasLocalStorage() ? window.localStorage.removeItem(key) : this.write(key, '', -1);
		return true;
	}
	/**
	 * This function is responsible for generating the value object of a Local Storage item.
	 * @param {value} value the value of the item you are trying to insert.
	 * @param {lifetime} lifetime the lifetime in days you wish to store the item for.
	 * @returns {object} the object to be stored as the item's value.
	 * @function
	 * @name generateStorageValue
	 * @static
	 * @private
	 **/
	static generateStorageValue(value, lifetime) {
		return {
			value,
			"timestamp": this.generateLifetimeTimestamp(lifetime)
		}
	}
	/**
	 * This function is responsible for generating the expiration timestamp of a stored item.
	 * @param {number} days the number of days you wish to store an item for.
	 * @returns {number} the expiration date in milliseconds.
	 * @function
	 * @name generateLifetimeTimestamp
	 * @static
	 * @private
	 **/
	static generateLifetimeTimestamp(days) {
		const currentTimestamp = new Date();
		return currentTimestamp.getTime() + this.convertDaysToMillis(days);
	}
	/**
	 * This function is responsible for generating the expiration timestamp of a stored item.
	 * @returns {localStorage|NULL} localStorage or null if localStorage is not available.
	 * @function
	 * @name hasLocalStorage
	 * @static
	 * @private
	 **/
	static hasLocalStorage() {
		return window.localStorage;
	}
	/**
	 * This function is responsible for determining if an item for Local Storage has expired.
	 * @param {number} timestamp the expiration timestamp of item in question.
	 * @returns {boolean}.
	 * @function
	 * @name isTimestampValid
	 * @static
	 * @private
	 **/
	static isTimestampValid(timestamp) {
		const currentTimestamp = new Date();
		return currentTimestamp.getTime() <= timestamp;
	}
	/**
	 * This function is responsible for fetching an item from either Cookies or Local Storage.
	 * @param {string} key the key of the item you are trying to fetch.
	 * @returns {object|NULL} the value of the item will be returned if found. if not, null will be returned.
	 * @function
	 * @name read
	 * @static
	 * @public
	 *
	 * @example
	 * StorageJar.read("hello");
	 **/
	static read(key) {
		if (this.hasLocalStorage()) {
			let item = JSON.parse(window.localStorage.getItem(key));
			if (this.isTimestampValid(item.timestamp))
				return item;
		} else {
		    let name = key + "=";
		    let cookies = document.cookie.split(';');
		    for(var i = 0; i < cookies.length; i++) {
		        let cookie = cookies[i];
		        while (cookie.charAt(0) == ' ') {
		            cookie = cookie.substring(1);
		        }
		        if (cookie.indexOf(name) == 0) {
		            return cookie.substring(name.length, cookie.length);
		        }
		    }
		}

		return null;
	}
	/**
	 * This function is responsible for writing an item to either Cookies or Local Storage.
	 * @param {string} key the key of the item you are trying to store.
	 * @param {string} value the value of the item you are trying to store.
 	 * @param {number} [lifetime=7] the length of time in days, you wish to store the item.
	 * @function
	 * @name write
	 * @static
	 * @public
	 *
	 * @example
	 * StorageJar.write("hello", "world", 5);
	 **/
	static write(key, value, lifetime = 7) {
		if ( this.hasLocalStorage() ) {
			const _value = this.generateStorageValue(value, lifetime);
			window.localStorage.setItem(key, JSON.stringify(_value));
		} else {
			let date = new Date();
			date.setTime(date.getTime() + (lifetime * 24 * 60 * 60 * 1000));
        	const expires = '; expires=' + date.toGMTString();
	        document.cookie = key + ' = ' + value + expires + '; path=/';
		}
	}
}
<|endoftext|>


<commit_before>import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

import axios from 'axios'
import storeConfig from './config.js'

export default new Vuex.Store({
    state: {
        apiUrl: "",
        token: null,
        reCaptchaSiteKey: "",
        loading: false,
        networkError: false,
        account: {
            username: ""
        },
        wallet: [
            {
                balance_pretty: ""
            }
        ],
        ticket: {},
        vps: {},
        profile: {},
        ticketMessages: {},
        services: {"paging": {"total_pages": 1}},
        pagination: {"paging": {"total_pages": 1}},
        count: 1,
        toolbarTitle: "",
        toolbarTitleArgs: {},
        version: "",
        language: "",
        meta: {},
        logout: false
    },
    actions: {
        boot ({dispatch, state, commit}) {
            commit('setVersion', "v" + storeConfig.version)
            commit('setReCaptchaSiteKey', storeConfig.reCaptchaSiteKey)
        },
        logOut ({dispatch, state, commit}) {
            localStorage.removeItem("token");
            commit('setAccount', {})
            commit('setWallet', [{balance_pretty: ""}])
            commit('setServices', [])
            commit('setToken', null)
            commit('setNetworkError', false)
            commit('setLogout', true)
        },
        handleError({dispatch, state, commit}, args) {
            let msg = args.error.message;
            console.log(msg + ' in ' + args.name + '()!');
            if (msg === 'Network Error') {
                commit('setNetworkError', true)
            } else if (msg === 'Request failed with status code 401' || msg === 'Request failed with status code 403') {
                dispatch('logOut')
            } else {
                //Other Error
            }
        },
        //return true if not authorized
        checkSession ({dispatch, state}) {
            state.apiUrl = storeConfig.apiUrl

            //logout, force login
            if (state.logout) {
                return true
            }
            //no token, force login
            if (localStorage.getItem("token") === null) {
                return true
            } else {
                dispatch('accountInfo').then((res) => {
                    //if no proper response then "log out" and force new login
                    if (res !== true) {
                        localStorage.removeItem("token");
                        return true
                    }
                })
                dispatch('walletInfo')
            }
        },
        accountLogin ({commit, dispatch, state}, args) {
            return axios.post(state.apiUrl + 'auth/login', args)
                .then(function (res) {
                    if (typeof res.data.token !== 'undefined') {
                        localStorage.setItem("token", res.data.token);
                        return true;
                    }
                }).catch(function (error) {
                    dispatch('handleError', {'name': 'accountLogin', 'error': error});
                })
        },
        accountInfo ({commit, dispatch, state}) {
            axios.defaults.headers.common['Authorization'] = 'Bearer ' + localStorage.getItem("token");
            return axios.get(state.apiUrl + 'me')
                .then(function (res) {
                    if (typeof res.data.username !== 'undefined') {
                        commit('setAccount', res.data)
                        return true
                    }
                }).catch(function (error) {
                    dispatch('handleError', 'accountInfo', error)
                })
        },
        walletInfo ({commit, dispatch, state}) {
            axios.defaults.headers.common['Authorization'] = 'Bearer ' + localStorage.getItem("token");
            return axios.get(state.apiUrl + 'me/balance')
                .then(function (res) {
                    if (typeof res.data[0].balance !== 'undefined') {
                        commit('setWallet', res.data)
                    }
                }).catch(function (error) {
                    dispatch('handleError', {'name': 'walletInfo', 'error': error})
                })
        },
        ticketInfo ({commit, dispatch, state}, args) {
            axios.defaults.headers.common['Authorization'] = 'Bearer ' + localStorage.getItem("token");
            return axios.get(state.apiUrl + 'help/ticket/' + args.id)
                .then(function (res) {
                    if (typeof res.data.id !== 'undefined') {
                        //http://stackoverflow.com/a/784547/1351857
                        res.data.message = res.data.message.replace(/(?:\r\n|\r|\n)/g, '
');
                        commit('setTicket', res.data)
                    }
                }).catch(function (error) {
                    dispatch('handleError', {'name': 'ticketInfo', 'error': error})
                })
        },
        ticketMessages ({commit, dispatch, state}, args) {
            axios.defaults.headers.common['Authorization'] = 'Bearer ' + localStorage.getItem("token");
            return axios.get(state.apiUrl + 'help/ticket/' + args.id + '/message')
                .then(function (res) {
                    for (let i = 0; i < res.data.messages.length; i++) {
                        res.data.messages[i].message = res.data.messages[i].message.replace(/(?:\r\n|\r|\n)/g, '
')
                    }
                    commit('setTicketMessages', res.data)
                }).catch(function (error) {
                    dispatch('handleError', {'name': 'ticketMessages', 'error': error})
                })
        },
        ticketAddMessage ({commit, dispatch, state}, args) {
            axios.defaults.headers.common['Authorization'] = 'Bearer ' + localStorage.getItem("token");
            return axios.post(state.apiUrl + 'help/ticket/' + args.id + '/message', args)
                .then(function (res) {
                    if (typeof res.data.error !== false) {
                        //commit('setTicketMessages', res.data)
                        return true
                    }
                }).catch(function (error) {
                    dispatch('handleError', {'name': 'ticketAddMessage', 'error': error})
                })
        },
        servicesList ({commit, dispatch, state}, args) {
            axios.defaults.headers.common['Authorization'] = 'Bearer ' + localStorage.getItem("token");
            return axios.get(state.apiUrl + 'vps?page=' + args.page + '&limit=' + args.limit)
                .then(function (res) {
                    if (typeof res.data !== 'undefined') {
                        commit('setServices', res.data)
                    }
                }).catch(function (error) {
                    dispatch('handleError', {'name': 'servicesList', 'error': error})
                })
        },
        paginationList ({commit, dispatch, state}, args) {
            axios.defaults.headers.common['Authorization'] = 'Bearer ' + localStorage.getItem("token");
            return axios.get(state.apiUrl + args.url + '?page=' + args.page + '&limit=' + args.limit)
                .then(function (res) {
                    if (typeof res.data !== 'undefined') {
                        commit('setPagination', res.data)
                    }
                }).catch(function (error) {
                    dispatch('handleError', {'name': 'paginationList', 'error': error})
                })
        },
        vpsInfo ({commit, dispatch, state}, args) {
            axios.defaults.headers.common['Authorization'] = 'Bearer ' + localStorage.getItem("token");
            return axios.get(state.apiUrl + 'vps/' + args.id)
                .then(function (res) {
                    if (typeof res.data.status !== 'undefined') {
                        if (typeof state.vps.status !== 'undefined' && state.vps.status === 'running') {
                            let s = res.data.uptime_s - state.vps.uptime_s;
                            let netInChange = res.data.net_in_b - state.vps.net_in_b;
                            let netOutChange = res.data.net_out_b - state.vps.net_out_b;
                            res.data.net_in_bps = netInChange / s;
                            res.data.net_out_bps = netOutChange / s;
                            if (state.vps.virt == 'kvm') {
                                let diskReadChange = res.data.disk_read_b - state.vps.disk_read_b;
                                let diskWriteChange = res.data.disk_write_b - state.vps.disk_write_b;
                                res.data.disk_read_bps = diskReadChange / s;
                                res.data.disk_write_bps = diskWriteChange / s;
                            }
                        } else {
                            res.data.net_in_bps = -1;
                            res.data.net_out_bps = -1;
                            res.data.disk_read_bps = -1;
                            res.data.disk_write_bps = -1;
                        }
                        commit('setVps', res.data)
                    }
                }).catch(function (error) {
                    dispatch('handleError', {'name': 'vpsInfo', 'error': error})
                })
        },
        profileInfo ({commit, dispatch, state}, args) {
            axios.defaults.headers.common['Authorization'] = 'Bearer ' + localStorage.getItem("token");
            return axios.get(state.apiUrl + 'me')
                .then(function (res) {
                    if (typeof res.data.id !== 'undefined') {
                        commit('setProfile', res.data)
                    }
                }).catch(function (error) {
                    dispatch('handleError', {'name': 'profileInfo', 'error': error})
                })
        }
    },
    mutations: {
        setVersion (state, newVersion) {
            state.version = newVersion;
        },
        setToken (state, newToken) {
            state.token = newToken;
        },
        setNetworkError (state, newErrorState) {
            state.networkError = newErrorState;
        },
        setReCaptchaSiteKey (state, newKey) {
            state.reCaptchaSiteKey = newKey;
        },
        setUsername (state, newUsername) {
            state.account.username = newUsername;
        },
        setAccount (state, newAccount) {
            state.account = newAccount;
        },
        setWallet (state, newWallet) {
            state.wallet = newWallet;
        },
        setTicket (state, newTicket) {
            state.ticket = newTicket;
        },
        setVps (state, newVps) {
            state.vps = newVps;
        },
        setProfile (state, newProfile) {
            state.profile = newProfile
        },
        setTicketMessages (state, newTicketMessages) {
            state.ticketMessages = newTicketMessages;
        },
        setServices (state, newServices) {
            state.services = newServices;
        },
        setPagination (state, newPagination) {
            state.pagination = newPagination;
        },
        setToolbarTitle (state, newTitle) {
            state.toolbarTitle = newTitle;
        },
        setToolbarTitleArgs (state, newTitleArgs) {
            state.toolbarTitleArgs = newTitleArgs;
        },
        setLanguage (state, newLanguage) {
            state.language = newLanguage;
            Vue.config.lang = newLanguage;
        },
        setMeta(state, newMeta) {
            state.meta = newMeta;
        },
        setLoading (state) {
            state.loading = true;
        },
        setLoaded (state) {
            state.loading = false;
        },
        setLogout(state, newLogout) {
            state.logout = newLogout;
        },
        setTitle (state, newTitle) {
            document.title = newTitle;
        },
        setDescription (state, newDescription) {
            document.head.querySelector('meta[name=description]').content = newDescription;
        },
        setKeywords (state, newKeywords) {
            document.head.querySelector('meta[name=keywords]').content = newKeywords;
        }
    }
})
<commit_msg>Don't show "session expired" toast after failed login attempt<commit_after>import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

import axios from 'axios'
import storeConfig from './config.js'

export default new Vuex.Store({
    state: {
        apiUrl: "",
        token: null,
        reCaptchaSiteKey: "",
        loading: false,
        networkError: false,
        account: {
            username: ""
        },
        wallet: [
            {
                balance_pretty: ""
            }
        ],
        ticket: {},
        vps: {},
        profile: {},
        ticketMessages: {},
        services: {"paging": {"total_pages": 1}},
        pagination: {"paging": {"total_pages": 1}},
        count: 1,
        toolbarTitle: "",
        toolbarTitleArgs: {},
        version: "",
        language: "",
        meta: {},
        logout: false
    },
    actions: {
        boot ({dispatch, state, commit}) {
            commit('setVersion', "v" + storeConfig.version)
            commit('setReCaptchaSiteKey', storeConfig.reCaptchaSiteKey)
        },
        logOut ({dispatch, state, commit}) {
            localStorage.removeItem("token");
            commit('setAccount', {})
            commit('setWallet', [{balance_pretty: ""}])
            commit('setServices', [])
            commit('setToken', null)
            commit('setNetworkError', false)
            commit('setLogout', true)
        },
        handleError({dispatch, state, commit}, args) {
            let msg = args.error.message, fn = args.name;
            console.log(msg + ' in ' + fn + '()!');
            if (msg === 'Network Error') {
                commit('setNetworkError', true)
            } else if (
                fn !== 'accountLogin' &&
                msg === 'Request failed with status code 401' || msg === 'Request failed with status code 403'
            ) {
                dispatch('logOut')
            } else {
                //Other Error
            }
        },
        //return true if not authorized
        checkSession ({dispatch, state}) {
            state.apiUrl = storeConfig.apiUrl

            //logout, force login
            if (state.logout) {
                return true
            }
            //no token, force login
            if (localStorage.getItem("token") === null) {
                return true
            } else {
                dispatch('accountInfo').then((res) => {
                    //if no proper response then "log out" and force new login
                    if (res !== true) {
                        localStorage.removeItem("token");
                        return true
                    }
                })
                dispatch('walletInfo')
            }
        },
        accountLogin ({commit, dispatch, state}, args) {
            return axios.post(state.apiUrl + 'auth/login', args)
                .then(function (res) {
                    if (typeof res.data.token !== 'undefined') {
                        localStorage.setItem("token", res.data.token);
                        return true;
                    }
                }).catch(function (error) {
                    dispatch('handleError', {'name': 'accountLogin', 'error': error});
                })
        },
        accountInfo ({commit, dispatch, state}) {
            axios.defaults.headers.common['Authorization'] = 'Bearer ' + localStorage.getItem("token");
            return axios.get(state.apiUrl + 'me')
                .then(function (res) {
                    if (typeof res.data.username !== 'undefined') {
                        commit('setAccount', res.data)
                        return true
                    }
                }).catch(function (error) {
                    dispatch('handleError', 'accountInfo', error)
                })
        },
        walletInfo ({commit, dispatch, state}) {
            axios.defaults.headers.common['Authorization'] = 'Bearer ' + localStorage.getItem("token");
            return axios.get(state.apiUrl + 'me/balance')
                .then(function (res) {
                    if (typeof res.data[0].balance !== 'undefined') {
                        commit('setWallet', res.data)
                    }
                }).catch(function (error) {
                    dispatch('handleError', {'name': 'walletInfo', 'error': error})
                })
        },
        ticketInfo ({commit, dispatch, state}, args) {
            axios.defaults.headers.common['Authorization'] = 'Bearer ' + localStorage.getItem("token");
            return axios.get(state.apiUrl + 'help/ticket/' + args.id)
                .then(function (res) {
                    if (typeof res.data.id !== 'undefined') {
                        //http://stackoverflow.com/a/784547/1351857
                        res.data.message = res.data.message.replace(/(?:\r\n|\r|\n)/g, '
');
                        commit('setTicket', res.data)
                    }
                }).catch(function (error) {
                    dispatch('handleError', {'name': 'ticketInfo', 'error': error})
                })
        },
        ticketMessages ({commit, dispatch, state}, args) {
            axios.defaults.headers.common['Authorization'] = 'Bearer ' + localStorage.getItem("token");
            return axios.get(state.apiUrl + 'help/ticket/' + args.id + '/message')
                .then(function (res) {
                    for (let i = 0; i < res.data.messages.length; i++) {
                        res.data.messages[i].message = res.data.messages[i].message.replace(/(?:\r\n|\r|\n)/g, '
')
                    }
                    commit('setTicketMessages', res.data)
                }).catch(function (error) {
                    dispatch('handleError', {'name': 'ticketMessages', 'error': error})
                })
        },
        ticketAddMessage ({commit, dispatch, state}, args) {
            axios.defaults.headers.common['Authorization'] = 'Bearer ' + localStorage.getItem("token");
            return axios.post(state.apiUrl + 'help/ticket/' + args.id + '/message', args)
                .then(function (res) {
                    if (typeof res.data.error !== false) {
                        //commit('setTicketMessages', res.data)
                        return true
                    }
                }).catch(function (error) {
                    dispatch('handleError', {'name': 'ticketAddMessage', 'error': error})
                })
        },
        servicesList ({commit, dispatch, state}, args) {
            axios.defaults.headers.common['Authorization'] = 'Bearer ' + localStorage.getItem("token");
            return axios.get(state.apiUrl + 'vps?page=' + args.page + '&limit=' + args.limit)
                .then(function (res) {
                    if (typeof res.data !== 'undefined') {
                        commit('setServices', res.data)
                    }
                }).catch(function (error) {
                    dispatch('handleError', {'name': 'servicesList', 'error': error})
                })
        },
        paginationList ({commit, dispatch, state}, args) {
            axios.defaults.headers.common['Authorization'] = 'Bearer ' + localStorage.getItem("token");
            return axios.get(state.apiUrl + args.url + '?page=' + args.page + '&limit=' + args.limit)
                .then(function (res) {
                    if (typeof res.data !== 'undefined') {
                        commit('setPagination', res.data)
                    }
                }).catch(function (error) {
                    dispatch('handleError', {'name': 'paginationList', 'error': error})
                })
        },
        vpsInfo ({commit, dispatch, state}, args) {
            axios.defaults.headers.common['Authorization'] = 'Bearer ' + localStorage.getItem("token");
            return axios.get(state.apiUrl + 'vps/' + args.id)
                .then(function (res) {
                    if (typeof res.data.status !== 'undefined') {
                        if (typeof state.vps.status !== 'undefined' && state.vps.status === 'running') {
                            let s = res.data.uptime_s - state.vps.uptime_s;
                            let netInChange = res.data.net_in_b - state.vps.net_in_b;
                            let netOutChange = res.data.net_out_b - state.vps.net_out_b;
                            res.data.net_in_bps = netInChange / s;
                            res.data.net_out_bps = netOutChange / s;
                            if (state.vps.virt == 'kvm') {
                                let diskReadChange = res.data.disk_read_b - state.vps.disk_read_b;
                                let diskWriteChange = res.data.disk_write_b - state.vps.disk_write_b;
                                res.data.disk_read_bps = diskReadChange / s;
                                res.data.disk_write_bps = diskWriteChange / s;
                            }
                        } else {
                            res.data.net_in_bps = -1;
                            res.data.net_out_bps = -1;
                            res.data.disk_read_bps = -1;
                            res.data.disk_write_bps = -1;
                        }
                        commit('setVps', res.data)
                    }
                }).catch(function (error) {
                    dispatch('handleError', {'name': 'vpsInfo', 'error': error})
                })
        },
        profileInfo ({commit, dispatch, state}, args) {
            axios.defaults.headers.common['Authorization'] = 'Bearer ' + localStorage.getItem("token");
            return axios.get(state.apiUrl + 'me')
                .then(function (res) {
                    if (typeof res.data.id !== 'undefined') {
                        commit('setProfile', res.data)
                    }
                }).catch(function (error) {
                    dispatch('handleError', {'name': 'profileInfo', 'error': error})
                })
        }
    },
    mutations: {
        setVersion (state, newVersion) {
            state.version = newVersion;
        },
        setToken (state, newToken) {
            state.token = newToken;
        },
        setNetworkError (state, newErrorState) {
            state.networkError = newErrorState;
        },
        setReCaptchaSiteKey (state, newKey) {
            state.reCaptchaSiteKey = newKey;
        },
        setUsername (state, newUsername) {
            state.account.username = newUsername;
        },
        setAccount (state, newAccount) {
            state.account = newAccount;
        },
        setWallet (state, newWallet) {
            state.wallet = newWallet;
        },
        setTicket (state, newTicket) {
            state.ticket = newTicket;
        },
        setVps (state, newVps) {
            state.vps = newVps;
        },
        setProfile (state, newProfile) {
            state.profile = newProfile
        },
        setTicketMessages (state, newTicketMessages) {
            state.ticketMessages = newTicketMessages;
        },
        setServices (state, newServices) {
            state.services = newServices;
        },
        setPagination (state, newPagination) {
            state.pagination = newPagination;
        },
        setToolbarTitle (state, newTitle) {
            state.toolbarTitle = newTitle;
        },
        setToolbarTitleArgs (state, newTitleArgs) {
            state.toolbarTitleArgs = newTitleArgs;
        },
        setLanguage (state, newLanguage) {
            state.language = newLanguage;
            Vue.config.lang = newLanguage;
        },
        setMeta(state, newMeta) {
            state.meta = newMeta;
        },
        setLoading (state) {
            state.loading = true;
        },
        setLoaded (state) {
            state.loading = false;
        },
        setLogout(state, newLogout) {
            state.logout = newLogout;
        },
        setTitle (state, newTitle) {
            document.title = newTitle;
        },
        setDescription (state, newDescription) {
            document.head.querySelector('meta[name=description]').content = newDescription;
        },
        setKeywords (state, newKeywords) {
            document.head.querySelector('meta[name=keywords]').content = newKeywords;
        }
    }
})
<|endoftext|>


<commit_before>import { createStore, applyMiddleware, compose } from 'redux'
import promiseMiddleware from 'redux-promise'
import rootReducer from '../reducers'
import createLogger from 'redux-logger'
import { compact } from 'lodash'
import { serverLogger, apiMiddleware, cacheMiddleware, pendingPromiseMiddleware } from '../middleware'

export function configureStore (initialState, req) {
  const isServer = typeof window === 'undefined'

  var middleware = compact([
    isServer && serverLogger,
    cacheMiddleware,
    apiMiddleware(req),
    pendingPromiseMiddleware,
    promiseMiddleware,
    !isServer && createLogger({collapsed: true})
  ])

  const store = compose(
    applyMiddleware(...middleware)
  )(createStore)(rootReducer, initialState)

  if (module.onReload) {
    module.onReload(() => {
      const nextRootReducer = require('../reducers/index')
      store.replaceReducer(nextRootReducer)
      return true
    })
  }

  return store
}
<commit_msg>support redux dev tools chrome extension<commit_after>import { createStore, applyMiddleware, compose } from 'redux'
import promiseMiddleware from 'redux-promise'
import rootReducer from '../reducers'
import createLogger from 'redux-logger'
import { compact } from 'lodash'
import { serverLogger, apiMiddleware, cacheMiddleware, pendingPromiseMiddleware } from '../middleware'

export function configureStore (initialState, req) {
  const isServer = typeof window === 'undefined'

  var middleware = compact([
    isServer && serverLogger,
    cacheMiddleware,
    apiMiddleware(req),
    pendingPromiseMiddleware,
    promiseMiddleware,
    !isServer && createLogger({collapsed: true})
  ])

  const store = compose(
    applyMiddleware(...middleware),
    !isServer && typeof window.devToolsExtension !== 'undefined' ? window.devToolsExtension() : f => f
  )(createStore)(rootReducer, initialState)

  if (module.onReload) {
    module.onReload(() => {
      const nextRootReducer = require('../reducers/index')
      store.replaceReducer(nextRootReducer)
      return true
    })
  }

  return store
}
<|endoftext|>


<commit_before>import Vue from 'vue'
import Vuex from 'vuex'
import init from './init'

Vue.use(Vuex)

const state = {
  background: null,
  // bgInfo: BackgroundInfo(),
  backgrounds: [],
  backgroundsUpdateTime: 0,
  nextRandomBackgrounds: [],
  previewScale: 75,
  user: {
    id: 0,
  },
  bgSize: {
    w: 0,
    h: 0,
  },
  format: 'jpg',
  inventory: [],
  inventoryUpdateTime: 0,
  v: 1,
  bgJsonUrl: null, // url of file with backgrounds
}

const mutations = {
  setBackground (state, { background, info }) {
    state.background = background
    state.bgInfo = info
    state.format = background.split('.').slice(-1)[0]
  },
  setBackgroundURL (state, value) {
    state.background = value
    state.format = value.split('.').slice(-1)[0]
  },
  setBackgrounds (state, backgrounds) {
    state.backgrounds = backgrounds
    state.nextRandomBackgrounds.push(state.backgrounds[Math.floor(state.backgrounds.length * Math.random())])
    state.backgroundsUpdateTime = Date.now()
  },
  setCurrentWindow (state, window) {
    state.currWindow = window
  },
  setIsMaximized (state, max) {
    state.isMaximized = max
  },
  setWindowWidth (state, value) {
    state.windowWidth = value
  },
  setWindowHeight (state, value) {
    state.windowHeight = value
  },
  setWindowSize (state, size) {
    state.windowWidth = size.width
    state.windowHeight = size.height
  },
  randomBackground (state) {
    const newBg = state.nextRandomBackgrounds.shift()
    if (newBg) {
      if (newBg.steamUrl.indexOf('http://cdn.akamai.steamstatic.com') !== -1) {
        state.background = newBg.steamUrl.replace('http://cdn.akamai.steamstatic.com', 'https://steamcdn-a.akamaihd.net')
      } else {
        state.background = newBg.steamUrl
      }
      state.bgInfo = newBg
      state.format = newBg.steamUrl.split('.').slice(-1)[0]
      window.location.hash = '#' + state.background
    }

    if (state.nextRandomBackgrounds.length < 3) {
      for (let i = 0; i < 5; i++) {
        const randomBg = state.backgrounds[Math.floor(state.backgrounds.length * Math.random())]
        let found = false
        for (const bg of state.nextRandomBackgrounds) {
          if (bg.steamUrl === randomBg.steamUrl) {
            found = true
            break
          }
        }

        if (!found) {
          state.nextRandomBackgrounds.push(randomBg)
        }
      }
    }
  },
  setPreviewScale (state, value) {
    state.previewScale = parseInt(value)
  },
  setGreenworks (state, value) {
    state.greenworks = value
  },
  setUser (state, value) {
    state.user = value
  },
  setBgSize (state, value) {
    state.bgSize = value
  },
  setState (state, newState) {
    state = newState
  },
  setAvatar (state, avatar) {
    state.user = {
      ...state.user,
      avatar: avatar,
    }
  },
  setInventoryBackgrounds (state, items) {
    state.inventory = items
    state.inventoryUpdateTime = Date.now()
  },

  logout () {
    state.user = {
      id: 0,
    }
    state.inventory = []
  },

  setBgJsonUrl (state, v) {
    state.bgJsonUrl = v
  },
}

const actions = {
  downloadZip ({ state }) {
    // sizes based on 1920x1108
    // https://steamcdn-a.akamaihd.net/steamcommunity/public/images/items/570/982491acceb6c9dde0d5e49dab1e7540c5faa1de.webm
    const bgSaveInfo = {
      url: state.background,
      images: [
        { name: 'Artwork_Middle.png', x: 492, y: 256, w: 506, h: 2000 },
        { name: 'Artwork_Right_Top.png', x: 1006, y: 256, w: 100, h: 2000 },
        { name: 'Avatar.png', x: 499, y: 34, w: 164, h: 164 },
      ],
    }

    const backUrl = 'https://steam.design/raw/'
    // const backUrl = 'http://localhost:8899/raw/'
    const url = backUrl + btoa(JSON.stringify(bgSaveInfo))
    window.open(url)
  },

  randomBackground ({ commit }) {
    commit('randomBackground')
  },

  getCurrentBg ({ state }) {
    var _goUrl = state.bgInfo && state.bgInfo.url
      ? 'https://steamcommunity.com/market/listings/' + state.bgInfo.url
      : 'https://images.google.com/searchbyimage?image_url=' + state.background
    // shell.openExternal(_goUrl)
    window.open(_goUrl)
  },

  async loadBackpack ({ commit }) {
    const data = await fetch(`https://steam.design/backpack/${state.user.id}/items.json`).then(r => r.json())
    commit('setInventoryBackgrounds', data.backgrounds)
  },

  trackClick (ctx, [where, subject]) {
    if (window && window.gtag) {
      window.gtag('event', where, {
        event_category: 'userClick',
        event_label: subject || 'click',
      })
    }
  },
}

export default new Vuex.Store({
  state,
  mutations,
  actions,

  modules: {
  },

  plugins: [init],
})
<commit_msg>Don't crop webms<commit_after>import Vue from 'vue'
import Vuex from 'vuex'
import init from './init'

Vue.use(Vuex)

const state = {
  background: null,
  // bgInfo: BackgroundInfo(),
  backgrounds: [],
  backgroundsUpdateTime: 0,
  nextRandomBackgrounds: [],
  previewScale: 75,
  user: {
    id: 0,
  },
  bgSize: {
    w: 0,
    h: 0,
  },
  format: 'jpg',
  inventory: [],
  inventoryUpdateTime: 0,
  v: 1,
  bgJsonUrl: null, // url of file with backgrounds
}

const mutations = {
  setBackground (state, { background, info }) {
    state.background = background
    state.bgInfo = info
    state.format = background.split('.').slice(-1)[0]
  },
  setBackgroundURL (state, value) {
    state.background = value
    state.format = value.split('.').slice(-1)[0]
  },
  setBackgrounds (state, backgrounds) {
    state.backgrounds = backgrounds
    state.nextRandomBackgrounds.push(state.backgrounds[Math.floor(state.backgrounds.length * Math.random())])
    state.backgroundsUpdateTime = Date.now()
  },
  setCurrentWindow (state, window) {
    state.currWindow = window
  },
  setIsMaximized (state, max) {
    state.isMaximized = max
  },
  setWindowWidth (state, value) {
    state.windowWidth = value
  },
  setWindowHeight (state, value) {
    state.windowHeight = value
  },
  setWindowSize (state, size) {
    state.windowWidth = size.width
    state.windowHeight = size.height
  },
  randomBackground (state) {
    const newBg = state.nextRandomBackgrounds.shift()
    if (newBg) {
      if (newBg.steamUrl.indexOf('http://cdn.akamai.steamstatic.com') !== -1) {
        state.background = newBg.steamUrl.replace('http://cdn.akamai.steamstatic.com', 'https://steamcdn-a.akamaihd.net')
      } else {
        state.background = newBg.steamUrl
      }
      state.bgInfo = newBg
      state.format = newBg.steamUrl.split('.').slice(-1)[0]
      window.location.hash = '#' + state.background
    }

    if (state.nextRandomBackgrounds.length < 3) {
      for (let i = 0; i < 5; i++) {
        const randomBg = state.backgrounds[Math.floor(state.backgrounds.length * Math.random())]
        let found = false
        for (const bg of state.nextRandomBackgrounds) {
          if (bg.steamUrl === randomBg.steamUrl) {
            found = true
            break
          }
        }

        if (!found) {
          state.nextRandomBackgrounds.push(randomBg)
        }
      }
    }
  },
  setPreviewScale (state, value) {
    state.previewScale = parseInt(value)
  },
  setGreenworks (state, value) {
    state.greenworks = value
  },
  setUser (state, value) {
    state.user = value
  },
  setBgSize (state, value) {
    state.bgSize = value
  },
  setState (state, newState) {
    state = newState
  },
  setAvatar (state, avatar) {
    state.user = {
      ...state.user,
      avatar: avatar,
    }
  },
  setInventoryBackgrounds (state, items) {
    state.inventory = items
    state.inventoryUpdateTime = Date.now()
  },

  logout () {
    state.user = {
      id: 0,
    }
    state.inventory = []
  },

  setBgJsonUrl (state, v) {
    state.bgJsonUrl = v
  },
}

const actions = {
  downloadZip ({ state }) {
    // sizes based on 1920x1108
    // https://steamcdn-a.akamaihd.net/steamcommunity/public/images/items/570/982491acceb6c9dde0d5e49dab1e7540c5faa1de.webm

    if (state.background.indexOf('.webm') !== -1) {
      return
    }

    const bgSaveInfo = {
      url: state.background,
      images: [
        { name: 'Artwork_Middle.png', x: 492, y: 256, w: 506, h: 2000 },
        { name: 'Artwork_Right_Top.png', x: 1006, y: 256, w: 100, h: 2000 },
        { name: 'Avatar.png', x: 499, y: 34, w: 164, h: 164 },
      ],
    }

    const backUrl = 'https://steam.design/raw/'
    // const backUrl = 'http://localhost:8899/raw/'
    const url = backUrl + btoa(JSON.stringify(bgSaveInfo))
    window.open(url)
  },

  randomBackground ({ commit }) {
    commit('randomBackground')
  },

  getCurrentBg ({ state }) {
    var _goUrl = state.bgInfo && state.bgInfo.url
      ? 'https://steamcommunity.com/market/listings/' + state.bgInfo.url
      : 'https://images.google.com/searchbyimage?image_url=' + state.background
    // shell.openExternal(_goUrl)
    window.open(_goUrl)
  },

  async loadBackpack ({ commit }) {
    const data = await fetch(`https://steam.design/backpack/${state.user.id}/items.json`).then(r => r.json())
    commit('setInventoryBackgrounds', data.backgrounds)
  },

  trackClick (ctx, [where, subject]) {
    if (window && window.gtag) {
      window.gtag('event', where, {
        event_category: 'userClick',
        event_label: subject || 'click',
      })
    }
  },
}

export default new Vuex.Store({
  state,
  mutations,
  actions,

  modules: {
  },

  plugins: [init],
})
<|endoftext|>


<commit_before>/**
 * @file NodeJS specific stream implementation
 */
/*#ifndef(UMD)*/
"use strict";
/*global _GPF_EVENT_DATA*/ // gpf.events.EVENT_DATA
/*global _GPF_EVENT_END_OF_DATA*/ // gpf.events.EVENT_END_OF_DATA
/*global _GPF_EVENT_ERROR*/ // gpf.events.EVENT_ERROR
/*global _GPF_EVENT_READY*/ // gpf.events.EVENT_READY
/*global _gpfAssert*/ // Assertion method
/*global _gpfDefine*/ // Shortcut for gpf.define
/*global _gpfEventsFire*/ // gpf.events.fire (internal, parameters must match)
/*global _gpfI*/ // gpf.interfaces
/*global _gpfIgnore*/ // Helper to remove unused parameter warning
/*global _GPF_HOST*/
/*global _gpfHost*/
/*exported _GpfNodeStream*/ // Encapsulation of nodeJS streams
/*#endif*/

if (_GPF_HOST.NODEJS === _gpfHost) {

    var // Base class of GPF NodeJS streams
        _GpfNodeStream = _gpfDefine("gpf.node.Stream", {
            "+": {

                // @param {Object} stream
                constructor: function (stream) {
                    this._stream = stream;
                },

                /**
                 * Close the current stream
                 *
                 * @param {gpf.events.Handler} eventsHandler
                 *
                 * @event gpf.events.EVENT_READY
                 */
                close: function (eventsHandler) {
                    _gpfIgnore(eventsHandler);
                    throw gpf.Error.abstractMethod();
                }

            },
            "#": {

                // property {Object} Node stream
                _stream: null,

                // @property {gpf.events.Handler} Last read eventsHandler also used as a critical section
                _eventsHandler: null,

                /**
                 * Sets the _eventsHandler variable, fails if already existing (this is significant of a read/write
                 * operation that is in progress).
                 *
                 * @param {gpf.events.Handler} eventsHandler Events handler
                 * @param {Function} errorFactory Builds the error to throw
                 */
                _setEventsHandler: function (eventsHandler, errorFactory) {
                    if (null !== this._eventsHandler) {
                        throw errorFactory();
                    }
                    this._eventsHandler = eventsHandler;
                },

                /**
                 * Provides an atomic access to the _eventsHandler variable (that is immediately cleared)
                 *
                 * @return {gpf.events.Handler}
                 */
                _getEventsHandler: function () {
                    var result = this._eventsHandler;
                    _gpfAssert(null !== result, "Event handler expected");
                    this._eventsHandler = null;
                    return result;
                }

            }
        });

    /**
     * Wraps a readable stream from NodeJS into a IReadableStream
     *
     * @class gpf.stream.NodeReadable
     * @extends {gpf.node.Stream}
     * @implements gpf.interfaces.IReadableStream
     */
    _gpfDefine("gpf.node.ReadableStream", _GpfNodeStream, {
        "[Class]": [gpf.$InterfaceImplement(_gpfI.IReadableStream)],
        "+": {

            // @param {stream.Readable} stream
            constructor: function (stream) {
                this._super(stream);
                stream.on("end", this._onEnd.bind(this));
                stream.on("error", this._onError.bind(this));
                this._boundOnReadable =  this._onReadable.bind(this);
            },

            // @inheritdoc gpf.node.Stream#close
            close: function (eventsHandler) {
                _gpfEventsFire.call(this, _GPF_EVENT_READY, {}, eventsHandler);
            },

            //region gpf.interfaces.IReadableStream

            // @inheritdoc gpf.interfaces.IReadableStream#read
            read: function (size, eventsHandler) {
                var chunk;
                this._setEventsHandler(eventsHandler, gpf.Error.readInProgress);
                // If we received the "readable" event
                if (this._readable) {
                    // We try to read a chunk
                    chunk = this._stream.read(size);
                    if (chunk) {
                        this._onData(chunk);
                        return;
                    }
                    // No chunk means we must wait for next "readable"
                    this._readable = false;
                }
                this._size = size;
                this._stream.once("readable", this._boundOnReadable);
            }

            //endregion

        },
        "#": {

            // @property {Boolean} Before doing any read on the stream, we wait for the readable event to be thrown
            _readable: false,

            // @property {Number} Last read size (if pending)
            _size: 0,

            /**
             * Handles "readable" stream event
             * NOTE that it was registered with once
             */
            _onReadable: function () {
                this._readable = true;
                this._onData(this._stream.read(this._size));
            },

            // @property {Function} onReadable bound to this
            _boundOnReadable: null,

            /**
             * Handles "data" stream event
             *
             * @param {Buffer} chunk
             */
            _onData: function (chunk) {
                _gpfEventsFire.call(this, _GPF_EVENT_DATA, {buffer: gpf.node.buffer2JsArray(chunk)},
                    this._getEventsHandler());
            },

            _onEnd: function () {
                _gpfEventsFire.call(this, _GPF_EVENT_END_OF_DATA, {}, this._getEventsHandler());
            },

            _onError: function (error) {
                _gpfEventsFire.call(this, _GPF_EVENT_ERROR, {error: error}, this._getEventsHandler());
            }

        }
    });

    /**
     * Wraps a writable stream from NodeJS into a IReadableStream
     *
     * @class gpf.stream.NodeWritable
     * @extends {gpf.node.Stream}
     * @implements gpf.interfaces.IReadableStream
     */
    _gpfDefine("gpf.node.WritableStream", _GpfNodeStream, {
        "[Class]": [gpf.$InterfaceImplement(_gpfI.IWritableStream)],
        "+": {

            // @param {stream.Writable} stream
            constructor: function (stream) {
                this._super(stream);
                this._boundOnWritten =  this._onWritten.bind(this);
            },

            // @inheritdoc gpf.node.Stream#close
            close: function (eventsHandler) {
                var me = this;
                this._stream.end(function () {
                    _gpfEventsFire.call(me, _GPF_EVENT_READY, {}, eventsHandler);
                });
            },

            //region gpf.interfaces.IWritableStream

            // @inheritdoc gpf.interfaces.IWritableStream#write
            write: function (buffer, eventsHandler) {
                this._setEventsHandler(eventsHandler, gpf.Error.writeInProgress);
                this._stream.write(buffer, this._boundOnWritten);
            }

            //endregion

        },
        "#": {

            // @property {gpf.events.Handler} Last read eventsHandler also used as a critical section
            _eventsHandler: null,

            /**
             * Handles the write callback
             * NOTE used through the _boundOnWritten member
             */
            _onWritten: function (error) {
                if (error) {
                    _gpfEventsFire.call(this, _GPF_EVENT_ERROR, {error: error}, this._getEventsHandler());
                } else {
                    _gpfEventsFire.call(this, _GPF_EVENT_READY, {}, this._getEventsHandler());
                }
            },

            // @property {Function} onWritten bound to this
            _boundOnWritten: null

        }
    });

}
<commit_msg>Removes throw<commit_after>/**
 * @file NodeJS specific stream implementation
 */
/*#ifndef(UMD)*/
"use strict";
/*global _GPF_EVENT_DATA*/ // gpf.events.EVENT_DATA
/*global _GPF_EVENT_END_OF_DATA*/ // gpf.events.EVENT_END_OF_DATA
/*global _GPF_EVENT_ERROR*/ // gpf.events.EVENT_ERROR
/*global _GPF_EVENT_READY*/ // gpf.events.EVENT_READY
/*global _gpfAssert*/ // Assertion method
/*global _gpfDefine*/ // Shortcut for gpf.define
/*global _gpfEventsFire*/ // gpf.events.fire (internal, parameters must match)
/*global _gpfI*/ // gpf.interfaces
/*global _gpfIgnore*/ // Helper to remove unused parameter warning
/*global _GPF_HOST*/
/*global _gpfHost*/
/*exported _GpfNodeStream*/ // Encapsulation of nodeJS streams
/*#endif*/

if (_GPF_HOST.NODEJS === _gpfHost) {

    var // Base class of GPF NodeJS streams
        _GpfNodeStream = _gpfDefine("gpf.node.Stream", {
            "+": {

                // @param {Object} stream
                constructor: function (stream) {
                    this._stream = stream;
                },

                /**
                 * Close the current stream
                 *
                 * @param {gpf.events.Handler} eventsHandler
                 *
                 * @event gpf.events.EVENT_READY
                 */
                close: function (eventsHandler) {
                    _gpfIgnore(eventsHandler);
                    gpf.Error.abstractMethod();
                }

            },
            "#": {

                // property {Object} Node stream
                _stream: null,

                // @property {gpf.events.Handler} Last read eventsHandler also used as a critical section
                _eventsHandler: null,

                /**
                 * Sets the _eventsHandler variable, fails if already existing (this is significant of a read/write
                 * operation that is in progress).
                 *
                 * @param {gpf.events.Handler} eventsHandler Events handler
                 * @param {Function} errorFactory Builds the error to throw
                 */
                _setEventsHandler: function (eventsHandler, errorFactory) {
                    if (null !== this._eventsHandler) {
                        throw errorFactory();
                    }
                    this._eventsHandler = eventsHandler;
                },

                /**
                 * Provides an atomic access to the _eventsHandler variable (that is immediately cleared)
                 *
                 * @return {gpf.events.Handler}
                 */
                _getEventsHandler: function () {
                    var result = this._eventsHandler;
                    _gpfAssert(null !== result, "Event handler expected");
                    this._eventsHandler = null;
                    return result;
                }

            }
        });

    /**
     * Wraps a readable stream from NodeJS into a IReadableStream
     *
     * @class gpf.stream.NodeReadable
     * @extends {gpf.node.Stream}
     * @implements gpf.interfaces.IReadableStream
     */
    _gpfDefine("gpf.node.ReadableStream", _GpfNodeStream, {
        "[Class]": [gpf.$InterfaceImplement(_gpfI.IReadableStream)],
        "+": {

            // @param {stream.Readable} stream
            constructor: function (stream) {
                this._super(stream);
                stream.on("end", this._onEnd.bind(this));
                stream.on("error", this._onError.bind(this));
                this._boundOnReadable =  this._onReadable.bind(this);
            },

            // @inheritdoc gpf.node.Stream#close
            close: function (eventsHandler) {
                _gpfEventsFire.call(this, _GPF_EVENT_READY, {}, eventsHandler);
            },

            //region gpf.interfaces.IReadableStream

            // @inheritdoc gpf.interfaces.IReadableStream#read
            read: function (size, eventsHandler) {
                var chunk;
                this._setEventsHandler(eventsHandler, gpf.Error.readInProgress);
                // If we received the "readable" event
                if (this._readable) {
                    // We try to read a chunk
                    chunk = this._stream.read(size);
                    if (chunk) {
                        this._onData(chunk);
                        return;
                    }
                    // No chunk means we must wait for next "readable"
                    this._readable = false;
                }
                this._size = size;
                this._stream.once("readable", this._boundOnReadable);
            }

            //endregion

        },
        "#": {

            // @property {Boolean} Before doing any read on the stream, we wait for the readable event to be thrown
            _readable: false,

            // @property {Number} Last read size (if pending)
            _size: 0,

            /**
             * Handles "readable" stream event
             * NOTE that it was registered with once
             */
            _onReadable: function () {
                this._readable = true;
                this._onData(this._stream.read(this._size));
            },

            // @property {Function} onReadable bound to this
            _boundOnReadable: null,

            /**
             * Handles "data" stream event
             *
             * @param {Buffer} chunk
             */
            _onData: function (chunk) {
                _gpfEventsFire.call(this, _GPF_EVENT_DATA, {buffer: gpf.node.buffer2JsArray(chunk)},
                    this._getEventsHandler());
            },

            _onEnd: function () {
                _gpfEventsFire.call(this, _GPF_EVENT_END_OF_DATA, {}, this._getEventsHandler());
            },

            _onError: function (error) {
                _gpfEventsFire.call(this, _GPF_EVENT_ERROR, {error: error}, this._getEventsHandler());
            }

        }
    });

    /**
     * Wraps a writable stream from NodeJS into a IReadableStream
     *
     * @class gpf.stream.NodeWritable
     * @extends {gpf.node.Stream}
     * @implements gpf.interfaces.IReadableStream
     */
    _gpfDefine("gpf.node.WritableStream", _GpfNodeStream, {
        "[Class]": [gpf.$InterfaceImplement(_gpfI.IWritableStream)],
        "+": {

            // @param {stream.Writable} stream
            constructor: function (stream) {
                this._super(stream);
                this._boundOnWritten =  this._onWritten.bind(this);
            },

            // @inheritdoc gpf.node.Stream#close
            close: function (eventsHandler) {
                var me = this;
                this._stream.end(function () {
                    _gpfEventsFire.call(me, _GPF_EVENT_READY, {}, eventsHandler);
                });
            },

            //region gpf.interfaces.IWritableStream

            // @inheritdoc gpf.interfaces.IWritableStream#write
            write: function (buffer, eventsHandler) {
                this._setEventsHandler(eventsHandler, gpf.Error.writeInProgress);
                this._stream.write(buffer, this._boundOnWritten);
            }

            //endregion

        },
        "#": {

            // @property {gpf.events.Handler} Last read eventsHandler also used as a critical section
            _eventsHandler: null,

            /**
             * Handles the write callback
             * NOTE used through the _boundOnWritten member
             */
            _onWritten: function (error) {
                if (error) {
                    _gpfEventsFire.call(this, _GPF_EVENT_ERROR, {error: error}, this._getEventsHandler());
                } else {
                    _gpfEventsFire.call(this, _GPF_EVENT_READY, {}, this._getEventsHandler());
                }
            },

            // @property {Function} onWritten bound to this
            _boundOnWritten: null

        }
    });

}
<|endoftext|>


<commit_before>define([
    './stream',
    '../functional/once',
    '../functional/noop'
], function(Stream, once, noopf) {
    'use strict';

    return once(function noop() {
        return new Stream(noopf);
    })
});
<commit_msg>Simplify noop stream<commit_after>define([
    './stream',
    '../functional/once',
    '../functional/noop'
], function(Stream, once, noop) {
    'use strict';

    return once(function() {
        return new Stream(noop);
    })
});
<|endoftext|>


<commit_before>export default {
  list: {
    selected: {
      bg: 'blue',
      fg: 'white'
    }
  },
  scrollbar: {
    ch: ' ',
    style: {
      fg: 'yellow',
      inverse: true
    }
  }
};
<commit_msg>Change scrollbar color<commit_after>export default {
  list: {
    selected: {
      bg: 'blue',
      fg: 'white'
    }
  },
  scrollbar: {
    ch: ' ',
    style: {
      fg: 'white',
      inverse: true
    }
  }
};
<|endoftext|>