<commit_before>import {extent, min, max} from "d3-array";
import {nest} from "d3-collection";
import * as scales from "d3-scale";
import {stack} from "d3-shape";
import {mouse} from "d3-selection";

import {AxisBottom, AxisLeft, date} from "d3plus-axis";
import {assign} from "d3plus-color";
import {accessor, closest, constant, elem} from "d3plus-common";
import * as shapes from "d3plus-shape";
import {Viz} from "d3plus-viz";

import {default as CircleBuffer} from "./buffers/Circle.js";
import {default as RectBuffer} from "./buffers/Rect.js";
import {default as LineBuffer} from "./buffers/Line.js";

/**
    @class Plot
    @extends Viz
    @desc Creates an x/y plot based on an array of data.
*/
export default class Plot extends Viz {

  /**
      @memberof Plot
      @desc Invoked when creating a new class instance, and sets any default parameters.
      @private
  */
  constructor() {

    super();
    this._buffer = {
      Circle: CircleBuffer,
      Line: LineBuffer,
      Rect: RectBuffer
    };
    this._shape = constant("Circle");
    this._shapeConfig = Object.assign(this._shapeConfig, {
      Circle: {
        r: constant(5)
      },
      Line: {
        fill: constant("none"),
        label: false,
        stroke: (d, i) => assign(this._id(d, i)),
        strokeWidth: constant(1)
      },
      Rect: {
        height: constant(10),
        width: constant(10)
      }
    });
    this._x = accessor("x");
    this._xAxis = new AxisBottom().align("end");
    this._xTest = new AxisBottom().align("end").gridSize(0);
    this._xConfig = {
      title: "X Axis"
    };
    this._y = accessor("y");
    this._yAxis = new AxisLeft().align("start");
    this._yTest = new AxisLeft().align("start").gridSize(0);
    this._yConfig = {
      title: "Y Axis"
    };

  }

  /**
      Extends the render behavior of the abstract Viz class.
      @private
  */
  render(callback) {

    super.render(callback);

    const data = this._filteredData.map((d, i) => ({
            data: d,
            i,
            id: this._id(d, i),
            shape: this._shape(d, i),
            x: this._x(d, i),
            y: this._y(d, i)
          })),
          height = this._height - this._margin.top - this._margin.bottom,
          opp = this._discrete ? this._discrete === "x" ? "y" : "x" : undefined,
          parent = this._select,
          that = this,
          transform = `translate(${this._margin.left}, ${this._margin.top})`,
          transition = this._transition,
          width = this._width - this._margin.left - this._margin.right;

    let domains, stackData, stackKeys;
    if (this._stacked) {

      stackKeys = Array.from(new Set(data.map(d => d.id)));
      stackData = stack()
        .keys(stackKeys)
        .value((group, key) => group.filter(d => d.id === key)[0][opp])
        (nest().key(d => d[this._discrete]).entries(data).map(d => d.values));

      domains = {
        [this._discrete]: extent(data, d => d[this._discrete]),
        [opp]: [min(stackData.map(g => min(g.map(p => p[1])))), max(stackData.map(g => max(g.map(p => p[1]))))]
      };

    }
    else domains = {x: extent(data, d => d.x), y: extent(data, d => d.y)};

    const xTime = this._time && this._x(data[0], 0) === this._time(data[0], 0),
          yTime = this._time && this._y(data[0], 0) === this._time(data[0], 0);

    if (xTime || yTime) {
      data.forEach(d => {
        if (xTime) d.x = date(d.x);
        if (yTime) d.y = date(d.y);
      });
    }

    let xDomain = this._xDomain ? this._xDomain.slice() : domains.x;
    if (xDomain[0] === void 0) xDomain[0] = domains.x[0];
    if (xDomain[1] === void 0) xDomain[1] = domains.x[1];
    if (xTime) xDomain = xDomain.map(date);

    let yDomain = this._yDomain ? this._yDomain.slice() : domains.y;
    if (yDomain[0] === void 0) yDomain[0] = domains.y[0];
    if (yDomain[1] === void 0) yDomain[1] = domains.y[1];
    if (yTime) yDomain = yDomain.map(date);

    domains = {x: xDomain, y: yDomain};

    if (opp && this._baseline !== void 0) {
      const b = this._baseline;
      if (domains[opp][0] > b) domains[opp][0] = b;
      else if (domains[opp][1] < b) domains[opp][1] = b;
    }

    let x = scales[`scale${xTime ? "Time" : "Linear"}`]().domain(domains.x).range([0, width]),
        y = scales[`scale${yTime ? "Time" : "Linear"}`]().domain(domains.y.reverse()).range([0, height]);

    const shapeData = nest().key(d => d.shape).entries(data);
    shapeData.forEach(d => {
      if (this._buffer[d.key]) {
        const res = this._buffer[d.key].bind(this)(d.values, x, y, this._shapeConfig[d.key]);
        x = res[0];
        y = res[1];
      }
    });
    xDomain = x.domain();
    yDomain = y.domain();

    const xTicks = this._discrete === "x" && !xTime ? Array.from(new Set(data.map(d => d.x))) : undefined,
          yTicks = this._discrete === "y" && !yTime ? Array.from(new Set(data.map(d => d.y))) : undefined;

    this._yTest
      .domain(yDomain)
      .height(height)
      .scale(yTime ? "time" : "linear")
      .select(elem("g.d3plus-plot-test", {enter: {opacity: 0}, parent: this._select}).node())
      .ticks(yTicks)
      .width(width)
      .config(this._yConfig)
      .render();

    const xOffset = this._yTest.outerBounds().width + this._yTest.padding();

    this._xTest
      .domain(xDomain)
      .height(height)
      .range([xOffset, undefined])
      .scale(xTime ? "time" : "linear")
      .select(elem("g.d3plus-plot-test", {enter: {opacity: 0}, parent: this._select}).node())
      .ticks(xTicks)
      .width(width)
      .config(this._xConfig)
      .render();

    this._xAxis
      .domain(xDomain)
      .height(height)
      .range([xOffset, undefined])
      .scale(xTime ? "time" : "linear")
      .select(elem("g.d3plus-plot-x-axis", {parent, transition, enter: {transform}, update: {transform}}).node())
      .ticks(xTicks)
      .width(width)
      .config(this._xConfig)
      .render();

    x = this._xAxis._d3Scale;

    this._yAxis
      .domain(yDomain)
      .height(height)
      .range([this._xAxis.outerBounds().y, this._xTest.outerBounds().y])
      .scale(yTime ? "time" : "linear")
      .select(elem("g.d3plus-plot-y-axis", {parent, transition, enter: {transform}, update: {transform}}).node())
      .ticks(yTicks)
      .width(x.range()[1] + this._xAxis.padding())
      .config(this._yConfig)
      .render();

    y = this._yAxis._d3Scale;

    let shapeConfig = {
      duration: this._duration,
      label: d => this._drawLabel(d.data, d.i),
      select: elem("g.d3plus-plot-shapes", {parent, transition}).node(),
      x: d => x(d.x),
      y: d => y(d.y)
    };

    function wrapConfig(config) {
      const obj = {};
      for (const k in config) {
        if ({}.hasOwnProperty.call(config, k) && !shapes[k]) {
          obj[k] = typeof config[k] === "function" ? d => config[k](d.data, d.i) : config[k];
        }
      }
      return obj;
    }

    shapeConfig = Object.assign(shapeConfig, wrapConfig(this._shapeConfig));

    const positions = {
      x0: this._discrete === "x" ? shapeConfig.x : x(0),
      x1: this._discrete === "x" ? null : shapeConfig.x,
      y0: this._discrete === "y" ? shapeConfig.y : y(0),
      y1: this._discrete === "y" ? null : shapeConfig.y
    };

    if (this._stacked) {
      positions[`${opp}0`] = (d, i) => (opp === "x" ? x : y)(stackData[stackKeys.indexOf(d.id)][i][0]);
      positions[`${opp}1`] = (d, i) => (opp === "x" ? x : y)(stackData[stackKeys.indexOf(d.id)][i][1]);
    }

    shapeConfig = Object.assign(shapeConfig, positions);

    /**
        @desc Handles mouse events for nested shapes, finding the closest discrete data point to send to the defined event function.
        @private
    */
    function mouseEvent(d) {
      if (d.nested && d.values) {
        const axis = that._discrete,
              cursor = mouse(that._select.node())[axis === "x" ? 0 : 1],
              values = d.values.map(d => shapeConfig[axis](d));
        d = d.values[values.indexOf(closest(cursor, values))];
      }
      return this(d.data, d.i);
    }

    const events = Object.keys(this._on);
    shapeData.forEach(d => {

      const s = new shapes[d.key]().config(shapeConfig).data(d.values);
      const classEvents = events.filter(e => e.includes(`.${d.key}`)),
            globalEvents = events.filter(e => !e.includes(".")),
            shapeEvents = events.filter(e => e.includes(".shape"));
      for (let e = 0; e < globalEvents.length; e++) s.on(globalEvents[e], mouseEvent.bind(this._on[globalEvents[e]]));
      for (let e = 0; e < shapeEvents.length; e++) s.on(shapeEvents[e], mouseEvent.bind(this._on[shapeEvents[e]]));
      for (let e = 0; e < classEvents.length; e++) s.on(classEvents[e], mouseEvent.bind(this._on[classEvents[e]]));
      s.config(this._shapeConfig[d.key] ? wrapConfig(this._shapeConfig[d.key]) : {}).render();
      this._shapes.push(s);

    });

    return this;

  }

  /**
      @memberof Plot
      @desc If *value* is specified, sets the baseline for the x/y plot and returns the current class instance. If *value* is not specified, returns the current baseline.
      @param {Number} [*value*]
  */
  baseline(_) {
    return arguments.length ? (this._baseline = _, this) : this._baseline;
  }

  /**
      @memberof Plot
      @desc If *value* is specified, toggles shape stacking and returns the current class instance. If *value* is not specified, returns the current stack value.
      @param {Boolean} [*value* = false]
  */
  stacked(_) {
    return arguments.length ? (this._stacked = _, this) : this._stacked;
  }

  /**
      @memberof Plot
      @desc If *value* is specified, sets the x accessor to the specified function or number and returns the current class instance. If *value* is not specified, returns the current x accessor.
      @param {Function|Number} [*value*]
  */
  x(_) {
    return arguments.length ? (this._x = typeof _ === "function" ? _ : constant(_), this) : this._x;
  }

  /**
      @memberof Plot
      @desc If *value* is specified, sets the config method for the x-axis and returns the current class instance. If *value* is not specified, returns the current x-axis configuration.
      @param {Object} [*value*]
  */
  xConfig(_) {
    return arguments.length ? (this._xConfig = Object.assign(this._xConfig, _), this) : this._xConfig;
  }

  /**
      @memberof Plot
      @desc If *value* is specified, sets the x domain to the specified array and returns the current class instance. If *value* is not specified, returns the current x domain. Additionally, if either value of the array is undefined, it will be calculated from the data.
      @param {Array} [*value*]
  */
  xDomain(_) {
    return arguments.length ? (this._xDomain = _, this) : this._xDomain;
  }

  /**
      @memberof Plot
      @desc If *value* is specified, sets the y accessor to the specified function or number and returns the current class instance. If *value* is not specified, returns the current y accessor.
      @param {Function|Number} [*value*]
  */
  y(_) {
    return arguments.length ? (this._y = typeof _ === "function" ? _ : constant(_), this) : this._y;
  }

  /**
      @memberof Plot
      @desc If *value* is specified, sets the config method for the y-axis and returns the current class instance. If *value* is not specified, returns the current y-axis configuration.
      @param {Object} [*value*]
  */
  yConfig(_) {
    return arguments.length ? (this._yConfig = Object.assign(this._yConfig, _), this) : this._yConfig;
  }

  /**
      @memberof Plot
      @desc If *value* is specified, sets the y domain to the specified array and returns the current class instance. If *value* is not specified, returns the current y domain. Additionally, if either value of the array is undefined, it will be calculated from the data.
      @param {Array} [*value*]
  */
  yDomain(_) {
    return arguments.length ? (this._yDomain = _, this) : this._yDomain;
  }

}
<commit_msg>fixes stacked shape nestings<commit_after>import {extent, min, max} from "d3-array";
import {nest} from "d3-collection";
import * as scales from "d3-scale";
import {stack} from "d3-shape";
import {mouse} from "d3-selection";

import {AxisBottom, AxisLeft, date} from "d3plus-axis";
import {assign} from "d3plus-color";
import {accessor, closest, constant, elem} from "d3plus-common";
import * as shapes from "d3plus-shape";
import {Viz} from "d3plus-viz";

import {default as CircleBuffer} from "./buffers/Circle.js";
import {default as RectBuffer} from "./buffers/Rect.js";
import {default as LineBuffer} from "./buffers/Line.js";

/**
    @class Plot
    @extends Viz
    @desc Creates an x/y plot based on an array of data.
*/
export default class Plot extends Viz {

  /**
      @memberof Plot
      @desc Invoked when creating a new class instance, and sets any default parameters.
      @private
  */
  constructor() {

    super();
    this._buffer = {
      Circle: CircleBuffer,
      Line: LineBuffer,
      Rect: RectBuffer
    };
    this._shape = constant("Circle");
    this._shapeConfig = Object.assign(this._shapeConfig, {
      Circle: {
        r: constant(5)
      },
      Line: {
        fill: constant("none"),
        label: false,
        stroke: (d, i) => assign(this._id(d, i)),
        strokeWidth: constant(1)
      },
      Rect: {
        height: constant(10),
        width: constant(10)
      }
    });
    this._x = accessor("x");
    this._xAxis = new AxisBottom().align("end");
    this._xTest = new AxisBottom().align("end").gridSize(0);
    this._xConfig = {
      title: "X Axis"
    };
    this._y = accessor("y");
    this._yAxis = new AxisLeft().align("start");
    this._yTest = new AxisLeft().align("start").gridSize(0);
    this._yConfig = {
      title: "Y Axis"
    };

  }

  /**
      Extends the render behavior of the abstract Viz class.
      @private
  */
  render(callback) {

    super.render(callback);

    const data = this._filteredData.map((d, i) => ({
            data: d,
            i,
            id: this._id(d, i),
            shape: this._shape(d, i),
            x: this._x(d, i),
            y: this._y(d, i)
          })),
          height = this._height - this._margin.top - this._margin.bottom,
          opp = this._discrete ? this._discrete === "x" ? "y" : "x" : undefined,
          parent = this._select,
          that = this,
          transform = `translate(${this._margin.left}, ${this._margin.top})`,
          transition = this._transition,
          width = this._width - this._margin.left - this._margin.right;

    let domains, stackData, stackKeys;
    if (this._stacked) {

      stackKeys = Array.from(new Set(data.map(d => d.id)));
      stackData = stack()
        .keys(stackKeys)
        .value((group, key) => group.filter(d => d.id === key)[0][opp])
        (nest().key(d => d[this._discrete]).entries(data).map(d => d.values));

      domains = {
        [this._discrete]: extent(data, d => d[this._discrete]),
        [opp]: [min(stackData.map(g => min(g.map(p => p[1])))), max(stackData.map(g => max(g.map(p => p[1]))))]
      };

    }
    else domains = {x: extent(data, d => d.x), y: extent(data, d => d.y)};

    const xTime = this._time && this._x(data[0], 0) === this._time(data[0], 0),
          yTime = this._time && this._y(data[0], 0) === this._time(data[0], 0);

    if (xTime || yTime) {
      data.forEach(d => {
        if (xTime) d.x = date(d.x);
        if (yTime) d.y = date(d.y);
      });
    }

    let xDomain = this._xDomain ? this._xDomain.slice() : domains.x;
    if (xDomain[0] === void 0) xDomain[0] = domains.x[0];
    if (xDomain[1] === void 0) xDomain[1] = domains.x[1];
    if (xTime) xDomain = xDomain.map(date);

    let yDomain = this._yDomain ? this._yDomain.slice() : domains.y;
    if (yDomain[0] === void 0) yDomain[0] = domains.y[0];
    if (yDomain[1] === void 0) yDomain[1] = domains.y[1];
    if (yTime) yDomain = yDomain.map(date);

    domains = {x: xDomain, y: yDomain};

    if (opp && this._baseline !== void 0) {
      const b = this._baseline;
      if (domains[opp][0] > b) domains[opp][0] = b;
      else if (domains[opp][1] < b) domains[opp][1] = b;
    }

    let x = scales[`scale${xTime ? "Time" : "Linear"}`]().domain(domains.x).range([0, width]),
        y = scales[`scale${yTime ? "Time" : "Linear"}`]().domain(domains.y.reverse()).range([0, height]);

    const shapeData = nest().key(d => d.shape).entries(data);
    shapeData.forEach(d => {
      if (this._buffer[d.key]) {
        const res = this._buffer[d.key].bind(this)(d.values, x, y, this._shapeConfig[d.key]);
        x = res[0];
        y = res[1];
      }
    });
    xDomain = x.domain();
    yDomain = y.domain();

    const xTicks = this._discrete === "x" && !xTime ? Array.from(new Set(data.map(d => d.x))) : undefined,
          yTicks = this._discrete === "y" && !yTime ? Array.from(new Set(data.map(d => d.y))) : undefined;

    this._yTest
      .domain(yDomain)
      .height(height)
      .scale(yTime ? "time" : "linear")
      .select(elem("g.d3plus-plot-test", {enter: {opacity: 0}, parent: this._select}).node())
      .ticks(yTicks)
      .width(width)
      .config(this._yConfig)
      .render();

    const xOffset = this._yTest.outerBounds().width + this._yTest.padding();

    this._xTest
      .domain(xDomain)
      .height(height)
      .range([xOffset, undefined])
      .scale(xTime ? "time" : "linear")
      .select(elem("g.d3plus-plot-test", {enter: {opacity: 0}, parent: this._select}).node())
      .ticks(xTicks)
      .width(width)
      .config(this._xConfig)
      .render();

    this._xAxis
      .domain(xDomain)
      .height(height)
      .range([xOffset, undefined])
      .scale(xTime ? "time" : "linear")
      .select(elem("g.d3plus-plot-x-axis", {parent, transition, enter: {transform}, update: {transform}}).node())
      .ticks(xTicks)
      .width(width)
      .config(this._xConfig)
      .render();

    x = this._xAxis._d3Scale;

    this._yAxis
      .domain(yDomain)
      .height(height)
      .range([this._xAxis.outerBounds().y, this._xTest.outerBounds().y])
      .scale(yTime ? "time" : "linear")
      .select(elem("g.d3plus-plot-y-axis", {parent, transition, enter: {transform}, update: {transform}}).node())
      .ticks(yTicks)
      .width(x.range()[1] + this._xAxis.padding())
      .config(this._yConfig)
      .render();

    y = this._yAxis._d3Scale;

    let shapeConfig = {
      duration: this._duration,
      label: d => this._drawLabel(d.data, d.i),
      select: elem("g.d3plus-plot-shapes", {parent, transition, enter: {transform}, update: {transform}}).node(),
      x: d => x(d.x),
      y: d => y(d.y)
    };

    function wrapConfig(config) {
      const obj = {};
      for (const k in config) {
        if ({}.hasOwnProperty.call(config, k) && !shapes[k]) {
          obj[k] = typeof config[k] === "function" ? d => config[k](d.data, d.i) : config[k];
        }
      }
      return obj;
    }

    shapeConfig = Object.assign(shapeConfig, wrapConfig(this._shapeConfig));

    const positions = {
      x0: this._discrete === "x" ? shapeConfig.x : x(0),
      x1: this._discrete === "x" ? null : shapeConfig.x,
      y0: this._discrete === "y" ? shapeConfig.y : y(0),
      y1: this._discrete === "y" ? null : shapeConfig.y
    };

    if (this._stacked) {
      const scale = opp === "x" ? x : y;
      positions[`${opp}0`] = (d, i) => {
        const index = stackKeys.indexOf(d.id);
        return index >= 0 ? scale(stackData[index][i][0]) : scale(0);
      };
      positions[`${opp}1`] = (d, i) => {
        const index = stackKeys.indexOf(d.id);
        return index >= 0 ? scale(stackData[index][i][1]) : scale(0);
      };
    }

    shapeConfig = Object.assign(shapeConfig, positions);

    /**
        @desc Handles mouse events for nested shapes, finding the closest discrete data point to send to the defined event function.
        @private
    */
    function mouseEvent(d) {
      if (d.nested && d.values) {
        const axis = that._discrete,
              cursor = mouse(that._select.node())[axis === "x" ? 0 : 1],
              values = d.values.map(d => shapeConfig[axis](d));
        d = d.values[values.indexOf(closest(cursor, values))];
      }
      return this(d.data, d.i);
    }

    const events = Object.keys(this._on);
    shapeData.forEach(d => {

      const s = new shapes[d.key]().config(shapeConfig).data(d.values);
      const classEvents = events.filter(e => e.includes(`.${d.key}`)),
            globalEvents = events.filter(e => !e.includes(".")),
            shapeEvents = events.filter(e => e.includes(".shape"));
      for (let e = 0; e < globalEvents.length; e++) s.on(globalEvents[e], mouseEvent.bind(this._on[globalEvents[e]]));
      for (let e = 0; e < shapeEvents.length; e++) s.on(shapeEvents[e], mouseEvent.bind(this._on[shapeEvents[e]]));
      for (let e = 0; e < classEvents.length; e++) s.on(classEvents[e], mouseEvent.bind(this._on[classEvents[e]]));
      s.config(this._shapeConfig[d.key] ? wrapConfig(this._shapeConfig[d.key]) : {}).render();
      this._shapes.push(s);

    });

    return this;

  }

  /**
      @memberof Plot
      @desc If *value* is specified, sets the baseline for the x/y plot and returns the current class instance. If *value* is not specified, returns the current baseline.
      @param {Number} [*value*]
  */
  baseline(_) {
    return arguments.length ? (this._baseline = _, this) : this._baseline;
  }

  /**
      @memberof Plot
      @desc If *value* is specified, toggles shape stacking and returns the current class instance. If *value* is not specified, returns the current stack value.
      @param {Boolean} [*value* = false]
  */
  stacked(_) {
    return arguments.length ? (this._stacked = _, this) : this._stacked;
  }

  /**
      @memberof Plot
      @desc If *value* is specified, sets the x accessor to the specified function or number and returns the current class instance. If *value* is not specified, returns the current x accessor.
      @param {Function|Number} [*value*]
  */
  x(_) {
    return arguments.length ? (this._x = typeof _ === "function" ? _ : constant(_), this) : this._x;
  }

  /**
      @memberof Plot
      @desc If *value* is specified, sets the config method for the x-axis and returns the current class instance. If *value* is not specified, returns the current x-axis configuration.
      @param {Object} [*value*]
  */
  xConfig(_) {
    return arguments.length ? (this._xConfig = Object.assign(this._xConfig, _), this) : this._xConfig;
  }

  /**
      @memberof Plot
      @desc If *value* is specified, sets the x domain to the specified array and returns the current class instance. If *value* is not specified, returns the current x domain. Additionally, if either value of the array is undefined, it will be calculated from the data.
      @param {Array} [*value*]
  */
  xDomain(_) {
    return arguments.length ? (this._xDomain = _, this) : this._xDomain;
  }

  /**
      @memberof Plot
      @desc If *value* is specified, sets the y accessor to the specified function or number and returns the current class instance. If *value* is not specified, returns the current y accessor.
      @param {Function|Number} [*value*]
  */
  y(_) {
    return arguments.length ? (this._y = typeof _ === "function" ? _ : constant(_), this) : this._y;
  }

  /**
      @memberof Plot
      @desc If *value* is specified, sets the config method for the y-axis and returns the current class instance. If *value* is not specified, returns the current y-axis configuration.
      @param {Object} [*value*]
  */
  yConfig(_) {
    return arguments.length ? (this._yConfig = Object.assign(this._yConfig, _), this) : this._yConfig;
  }

  /**
      @memberof Plot
      @desc If *value* is specified, sets the y domain to the specified array and returns the current class instance. If *value* is not specified, returns the current y domain. Additionally, if either value of the array is undefined, it will be calculated from the data.
      @param {Array} [*value*]
  */
  yDomain(_) {
    return arguments.length ? (this._yDomain = _, this) : this._yDomain;
  }

}
<|endoftext|>


<commit_before>function Seed(value, delimiter) {
    this.value = value || '';
    this.delimiter = delimiter || ' ';
}

Seed.prototype = {

    getValue: function () {
        return this.value;
    },

    setValue: function (value) {
        return new Seed(value, this.delimiter);
    },

    getDelimiter: function () {
        return this.delimiter;
    },

    setDelimiter: function (delimiter) {
        return new Seed(this.value, delimiter);
    },

    append: function (value) {
        var value = this.value + this.delimiter + value;

        return new Seed(value, this.delimiter);
    }

};

module.exports = Seed;
<commit_msg>Updates seed to class syntax<commit_after>class Seed {
    constructor(value, delimiter) {
        this.value = value || '';
        this.delimiter = delimiter || ' ';
    }

    getValue() {
        return this.value;
    }

    setValue(value) {
        return new Seed(value, this.delimiter);
    }

    getDelimiter() {
        return this.delimiter;
    }

    setDelimiter(delimiter) {
        return new Seed(this.value, delimiter);
    }

    append(value) {
        var value = this.value + this.delimiter + value;

        return new Seed(value, this.delimiter);
    }
}

module.exports = Seed;
<|endoftext|>


<commit_before>
module.exports = {
    new: function () {
        return true;
    }
};
<commit_msg>Factory create new Task instance.<commit_after>function Task (name) {
    var name;
}

module.exports = {
    new: function (name) {
        return new Task(name);
    }
};
<|endoftext|>


<commit_before>/* global Y, Element */
'use strict'

var diff = require('fast-diff')

function extend (Y) {
  Y.requestModules(['Array']).then(function () {
    class YText extends Y.Array.typeDefinition['class'] {
      constructor (os, _model, _content) {
        super(os, _model, _content)
        this.textfields = []
        this.aceInstances = []
        this.codeMirrorInstances = []
      }
      toString () {
        return this._content.map(function (c) {
          return c.val
        }).join('')
      }
      insert (pos, content) {
        super.insert(pos, content.split(''))
      }
      unbindAll () {
        this.unbindTextareaAll()
        this.unbindAceAll()
        this.unbindCodeMirrorAll()
      }
      unbindCodeMirror (codeMirrorInstance) {
        var i = this.codeMirrorInstances.findIndex(function (binding) {
          return binding.editor === codeMirrorInstance
        })
        if (i >= 0) {
          var binding = this.codeMirrorInstances[i]
          this.unobserve(binding.yCallback)
          binding.editor.off('change', binding.codeMirrorCallback)
          this.codeMirrorInstances.splice(i, 1)
        }
      }
      unbindCodeMirrorAll () {
        for (let i = this.codeMirrorInstances.length - 1; i >= 0; i--) {
          this.unbindCodeMirror(this.codeMirrorInstances[i].editor)
        }
      }
      bindCodeMirror (codeMirrorInstance, options) {
        var self = this
        options = options || {}

        // this function makes sure that either the
        // codemirror event is executed, or the yjs observer is executed
        var token = true
        function mutualExcluse (f) {
          if (token) {
            token = false
            try {
              f()
            } catch (e) {
              token = true
              throw new Error(e)
            }
            token = true
          }
        }
        codeMirrorInstance.setValue(this.toString())

        function codeMirrorCallback (cm, delta) {
          mutualExcluse(function () {
            var start = codeMirrorInstance.indexFromPos(delta.from)
            // apply the delete operation first
            if (delta.removed.length > 0) {
              var delLength = 0
              for (var i = 0; i < delta.removed.length; i++) {
                delLength += delta.removed[i].length
              }
              // "enter" is also a character in our case
              delLength += delta.removed.length - 1
              self.delete(start, delLength)
            }
            // apply insert operation
            self.insert(start, delta.text.join('\n'))
          })
        }
        codeMirrorInstance.on('change', codeMirrorCallback)

        function yCallback (event) {
          mutualExcluse(function () {
            let from = codeMirrorInstance.posFromIndex(event.index)
            if (event.type === 'insert') {
              let to = from
              codeMirrorInstance.replaceRange(event.values.join(''), from, to)
            } else if (event.type === 'delete') {
              let to = codeMirrorInstance.posFromIndex(event.index + event.length)
              codeMirrorInstance.replaceRange('', from, to)
            }
          })
        }
        this.observe(yCallback)
        this.codeMirrorInstances.push({
          editor: codeMirrorInstance,
          yCallback: yCallback,
          codeMirrorCallback: codeMirrorCallback
        })
      }
      unbindAce (aceInstance) {
        var i = this.aceInstances.findIndex(function (binding) {
          return binding.editor === aceInstance
        })
        if (i >= 0) {
          var binding = this.aceInstances[i]
          this.unobserve(binding.yCallback)
          binding.editor.off('change', binding.aceCallback)
          this.aceInstances.splice(i, 1)
        }
      }
      unbindAceAll () {
        for (let i = this.aceInstances.length - 1; i >= 0; i--) {
          this.unbindAce(this.aceInstances[i].editor)
        }
      }
      bindAce (aceInstance, options) {
        var self = this
        options = options || {}

        // this function makes sure that either the
        // ace event is executed, or the yjs observer is executed
        var token = true
        function mutualExcluse (f) {
          if (token) {
            token = false
            try {
              f()
            } catch (e) {
              token = true
              throw new Error(e)
            }
            token = true
          }
        }
        aceInstance.setValue(this.toString())

        function aceCallback (delta) {
          mutualExcluse(function () {
            var start
            var length

            var aceDocument = aceInstance.getSession().getDocument()
            if (delta.action === 'insert') {
              start = aceDocument.positionToIndex(delta.start, 0)
              self.insert(start, delta.lines.join('\n'))
            } else if (delta.action === 'remove') {
              start = aceDocument.positionToIndex(delta.start, 0)
              length = delta.lines.join('\n').length
              self.delete(start, length)
            }
          })
        }
        aceInstance.on('change', aceCallback)

        aceInstance.selection.clearSelection()

        // We don't that ace is a global variable
        // see #2
        var aceClass
        if (typeof ace !== 'undefined' && options.aceClass == null) {
          aceClass = ace // eslint-disable-line
        } else {
          aceClass = options.aceClass
        }
        var aceRequire = options.aceRequire || aceClass.require
        var Range = aceRequire('ace/range').Range

        function yCallback (event) {
          var aceDocument = aceInstance.getSession().getDocument()
          mutualExcluse(function () {
            if (event.type === 'insert') {
              let start = aceDocument.indexToPosition(event.index, 0)
              aceDocument.insert(start, event.values.join(''))
            } else if (event.type === 'delete') {
              let start = aceDocument.indexToPosition(event.index, 0)
              let end = aceDocument.indexToPosition(event.index + event.length, 0)
              var range = new Range(start.row, start.column, end.row, end.column)
              aceDocument.remove(range)
            }
          })
        }
        this.observe(yCallback)
        this.aceInstances.push({
          editor: aceInstance,
          yCallback: yCallback,
          aceCallback: aceCallback
        })
      }
      bind () {
        var e = arguments[0]
        if (e instanceof Element) {
          this.bindTextarea.apply(this, arguments)
        } else if (e != null && e.session != null && e.getSession != null && e.setValue != null) {
          this.bindAce.apply(this, arguments)
        } else if (e != null && e.posFromIndex != null && e.replaceRange != null) {
          this.bindCodeMirror.apply(this, arguments)
        } else {
          console.error('Cannot bind, unsupported editor!')
        }
      }
      unbindTextarea (textarea) {
        var i = this.textfields.findIndex(function (binding) {
          return binding.editor === textarea
        })
        if (i >= 0) {
          var binding = this.textfields[i]
          this.unobserve(binding.yCallback)
          var e = binding.editor
          e.removeEventListener('input', binding.eventListener)
          this.textfields.splice(i, 1)
        }
      }
      unbindTextareaAll () {
        for (let i = this.textfields.length - 1; i >= 0; i--) {
          this.unbindTextarea(this.textfields[i].editor)
        }
      }
      bindTextarea (textfield, domRoot) {
        domRoot = domRoot || window; // eslint-disable-line
        if (domRoot.getSelection == null) {
          domRoot = window; // eslint-disable-line
        }

        // don't duplicate!
        for (var t = 0; t < this.textfields.length; t++) {
          if (this.textfields[t].editor === textfield) {
            return
          }
        }
        // this function makes sure that either the
        // textfieldt event is executed, or the yjs observer is executed
        var token = true
        function mutualExcluse (f) {
          if (token) {
            token = false
            try {
              f()
            } catch (e) {
              token = true
              throw new Error(e)
            }
            token = true
          }
        }

        var self = this
        textfield.value = this.toString()

        var createRange, writeRange, writeContent, getContent
        if (textfield.selectionStart != null && textfield.setSelectionRange != null) {
          createRange = function (fix) {
            var left = textfield.selectionStart
            var right = textfield.selectionEnd
            if (fix != null) {
              left = fix(left)
              right = fix(right)
            }
            return {
              left: left,
              right: right
            }
          }
          writeRange = function (range) {
            writeContent(self.toString())
            textfield.setSelectionRange(range.left, range.right)
          }
          writeContent = function (content) {
            textfield.value = content
          }
          getContent = function () {
            return textfield.value
          }
        } else {
          createRange = function (fix) {
            var range = {}
            var s = domRoot.getSelection()
            var clength = textfield.textContent.length
            range.left = Math.min(s.anchorOffset, clength)
            range.right = Math.min(s.focusOffset, clength)
            if (fix != null) {
              range.left = fix(range.left)
              range.right = fix(range.right)
            }
            var editedElement = s.focusNode
            if (editedElement === textfield || editedElement === textfield.childNodes[0]) {
              range.isReal = true
            } else {
              range.isReal = false
            }
            return range
          }

          writeRange = function (range) {
            writeContent(self.toString())
            var textnode = textfield.childNodes[0]
            if (range.isReal && textnode != null) {
              if (range.left < 0) {
                range.left = 0
              }
              range.right = Math.max(range.left, range.right)
              if (range.right > textnode.length) {
                range.right = textnode.length
              }
              range.left = Math.min(range.left, range.right)
              var r = document.createRange(); // eslint-disable-line
              r.setStart(textnode, range.left)
              r.setEnd(textnode, range.right)
              var s = domRoot.getSelection(); // eslint-disable-line
              s.removeAllRanges()
              s.addRange(r)
            }
          }
          writeContent = function (content) {
            textfield.innerText = content
            /*
            var contentArray = content.replace(new RegExp('\n', 'g'), ' ').split(' '); // eslint-disable-line
            textfield.innerText = ''
            for (var i = 0; i < contentArray.length; i++) {
              var c = contentArray[i]
              textfield.innerText += c
              if (i !== contentArray.length - 1) {
                textfield.innerHTML += ' '
              }
            }
            */
          }
          getContent = function () {
            return textfield.innerText
          }
        }
        writeContent(this.toString())

        function yCallback (event) {
          mutualExcluse(() => {
            var oPos, fix
            if (event.type === 'insert') {
              oPos = event.index
              fix = function (cursor) { // eslint-disable-line
                if (cursor <= oPos) {
                  return cursor
                } else {
                  cursor += 1
                  return cursor
                }
              }
              var r = createRange(fix)
              writeRange(r)
            } else if (event.type === 'delete') {
              oPos = event.index
              fix = function (cursor) { // eslint-disable-line
                if (cursor < oPos) {
                  return cursor
                } else {
                  cursor -= 1
                  return cursor
                }
              }
              r = createRange(fix)
              writeRange(r)
            }
          })
        }
        this.observe(yCallback)

        var textfieldObserver = function textfieldObserver () {
          mutualExcluse(function () {
            var r = createRange(function (x) { return x })
            var oldContent = self.toString()
            var content = getContent()
            var diffs = diff(oldContent, content, r.left)
            var pos = 0
            for (var i = 0; i < diffs.length; i++) {
              var d = diffs[i]
              if (d[0] === 0) { // EQUAL
                pos += d[1].length
              } else if (d[0] === -1) { // DELETE
                self.delete(pos, d[1].length)
              } else { // INSERT
                self.insert(pos, d[1])
                pos += d[1].length
              }
            }
          })
        }
        textfield.addEventListener('input', textfieldObserver)
        this.textfields.push({
          editor: textfield,
          yCallback: yCallback,
          eventListener: textfieldObserver
        })
      }
      _destroy () {
        this.unbindAll()
        this.textfields = null
        this.aceInstances = null
        super._destroy()
      }
    }
    Y.extend('Text', new Y.utils.CustomTypeDefinition({
      name: 'Text',
      class: YText,
      struct: 'List',
      initType: function * YTextInitializer (os, model) {
        var _content = []
        yield* Y.Struct.List.map.call(this, model, function (op) {
          if (op.hasOwnProperty('opContent')) {
            throw new Error('Text must not contain types!')
          } else {
            op.content.forEach(function (c, i) {
              _content.push({
                id: [op.id[0], op.id[1] + i],
                val: op.content[i]
              })
            })
          }
        })
        return new YText(os, model.id, _content)
      },
      createType: function YTextCreator (os, model) {
        return new YText(os, model.id, [])
      }
    }))
  })
}

module.exports = extend
if (typeof Y !== 'undefined') {
  extend(Y)
}
<commit_msg>Use the synchronous 'changes' event instead of the asynchronous 'change' event. Related to #8<commit_after>/* global Y, Element */
'use strict'

var diff = require('fast-diff')

function extend (Y) {
  Y.requestModules(['Array']).then(function () {
    class YText extends Y.Array.typeDefinition['class'] {
      constructor (os, _model, _content) {
        super(os, _model, _content)
        this.textfields = []
        this.aceInstances = []
        this.codeMirrorInstances = []
      }
      toString () {
        return this._content.map(function (c) {
          return c.val
        }).join('')
      }
      insert (pos, content) {
        super.insert(pos, content.split(''))
      }
      unbindAll () {
        this.unbindTextareaAll()
        this.unbindAceAll()
        this.unbindCodeMirrorAll()
      }
      unbindCodeMirror (codeMirrorInstance) {
        var i = this.codeMirrorInstances.findIndex(function (binding) {
          return binding.editor === codeMirrorInstance
        })
        if (i >= 0) {
          var binding = this.codeMirrorInstances[i]
          this.unobserve(binding.yCallback)
          binding.editor.off('changes', binding.codeMirrorCallback)
          this.codeMirrorInstances.splice(i, 1)
        }
      }
      unbindCodeMirrorAll () {
        for (let i = this.codeMirrorInstances.length - 1; i >= 0; i--) {
          this.unbindCodeMirror(this.codeMirrorInstances[i].editor)
        }
      }
      bindCodeMirror (codeMirrorInstance, options) {
        var self = this
        options = options || {}

        // this function makes sure that either the
        // codemirror event is executed, or the yjs observer is executed
        var token = true
        function mutualExcluse (f) {
          if (token) {
            token = false
            try {
              f()
            } catch (e) {
              token = true
              throw new Error(e)
            }
            token = true
          }
        }
        codeMirrorInstance.setValue(this.toString())

        function codeMirrorCallback (cm, deltas) {
          mutualExcluse(function () {
            for (var i = 0; i < deltas.length; i++) {
              var delta = deltas[i]
              var start = codeMirrorInstance.indexFromPos(delta.from)
              // apply the delete operation first
              if (delta.removed.length > 0 ||
                (delta.removed.length === 1 && delta.removed[0].length === 0)
              ) {
                var delLength = 0
                for (var j = 0; j < delta.removed.length; j++) {
                  delLength += delta.removed[j].length
                }
                // "enter" is also a character in our case
                delLength += delta.removed.length - 1
                self.delete(start, delLength)
              }
              // apply insert operation
              self.insert(start, delta.text.join('\n'))
            }
          })
        }
        codeMirrorInstance.on('changes', codeMirrorCallback)

        function yCallback (event) {
          mutualExcluse(function () {
            let from = codeMirrorInstance.posFromIndex(event.index)
            if (event.type === 'insert') {
              let to = from
              codeMirrorInstance.replaceRange(event.values.join(''), from, to)
            } else if (event.type === 'delete') {
              let to = codeMirrorInstance.posFromIndex(event.index + event.length)
              codeMirrorInstance.replaceRange('', from, to)
            }
          })
        }
        this.observe(yCallback)
        this.codeMirrorInstances.push({
          editor: codeMirrorInstance,
          yCallback: yCallback,
          codeMirrorCallback: codeMirrorCallback
        })
      }
      unbindAce (aceInstance) {
        var i = this.aceInstances.findIndex(function (binding) {
          return binding.editor === aceInstance
        })
        if (i >= 0) {
          var binding = this.aceInstances[i]
          this.unobserve(binding.yCallback)
          binding.editor.off('change', binding.aceCallback)
          this.aceInstances.splice(i, 1)
        }
      }
      unbindAceAll () {
        for (let i = this.aceInstances.length - 1; i >= 0; i--) {
          this.unbindAce(this.aceInstances[i].editor)
        }
      }
      bindAce (aceInstance, options) {
        var self = this
        options = options || {}

        // this function makes sure that either the
        // ace event is executed, or the yjs observer is executed
        var token = true
        function mutualExcluse (f) {
          if (token) {
            token = false
            try {
              f()
            } catch (e) {
              token = true
              throw new Error(e)
            }
            token = true
          }
        }
        aceInstance.setValue(this.toString())

        function aceCallback (delta) {
          mutualExcluse(function () {
            var start
            var length

            var aceDocument = aceInstance.getSession().getDocument()
            if (delta.action === 'insert') {
              start = aceDocument.positionToIndex(delta.start, 0)
              self.insert(start, delta.lines.join('\n'))
            } else if (delta.action === 'remove') {
              start = aceDocument.positionToIndex(delta.start, 0)
              length = delta.lines.join('\n').length
              self.delete(start, length)
            }
          })
        }
        aceInstance.on('change', aceCallback)

        aceInstance.selection.clearSelection()

        // We don't that ace is a global variable
        // see #2
        var aceClass
        if (typeof ace !== 'undefined' && options.aceClass == null) {
          aceClass = ace // eslint-disable-line
        } else {
          aceClass = options.aceClass
        }
        var aceRequire = options.aceRequire || aceClass.require
        var Range = aceRequire('ace/range').Range

        function yCallback (event) {
          var aceDocument = aceInstance.getSession().getDocument()
          mutualExcluse(function () {
            if (event.type === 'insert') {
              let start = aceDocument.indexToPosition(event.index, 0)
              aceDocument.insert(start, event.values.join(''))
            } else if (event.type === 'delete') {
              let start = aceDocument.indexToPosition(event.index, 0)
              let end = aceDocument.indexToPosition(event.index + event.length, 0)
              var range = new Range(start.row, start.column, end.row, end.column)
              aceDocument.remove(range)
            }
          })
        }
        this.observe(yCallback)
        this.aceInstances.push({
          editor: aceInstance,
          yCallback: yCallback,
          aceCallback: aceCallback
        })
      }
      bind () {
        var e = arguments[0]
        if (e instanceof Element) {
          this.bindTextarea.apply(this, arguments)
        } else if (e != null && e.session != null && e.getSession != null && e.setValue != null) {
          this.bindAce.apply(this, arguments)
        } else if (e != null && e.posFromIndex != null && e.replaceRange != null) {
          this.bindCodeMirror.apply(this, arguments)
        } else {
          console.error('Cannot bind, unsupported editor!')
        }
      }
      unbindTextarea (textarea) {
        var i = this.textfields.findIndex(function (binding) {
          return binding.editor === textarea
        })
        if (i >= 0) {
          var binding = this.textfields[i]
          this.unobserve(binding.yCallback)
          var e = binding.editor
          e.removeEventListener('input', binding.eventListener)
          this.textfields.splice(i, 1)
        }
      }
      unbindTextareaAll () {
        for (let i = this.textfields.length - 1; i >= 0; i--) {
          this.unbindTextarea(this.textfields[i].editor)
        }
      }
      bindTextarea (textfield, domRoot) {
        domRoot = domRoot || window; // eslint-disable-line
        if (domRoot.getSelection == null) {
          domRoot = window; // eslint-disable-line
        }

        // don't duplicate!
        for (var t = 0; t < this.textfields.length; t++) {
          if (this.textfields[t].editor === textfield) {
            return
          }
        }
        // this function makes sure that either the
        // textfieldt event is executed, or the yjs observer is executed
        var token = true
        function mutualExcluse (f) {
          if (token) {
            token = false
            try {
              f()
            } catch (e) {
              token = true
              throw new Error(e)
            }
            token = true
          }
        }

        var self = this
        textfield.value = this.toString()

        var createRange, writeRange, writeContent, getContent
        if (textfield.selectionStart != null && textfield.setSelectionRange != null) {
          createRange = function (fix) {
            var left = textfield.selectionStart
            var right = textfield.selectionEnd
            if (fix != null) {
              left = fix(left)
              right = fix(right)
            }
            return {
              left: left,
              right: right
            }
          }
          writeRange = function (range) {
            writeContent(self.toString())
            textfield.setSelectionRange(range.left, range.right)
          }
          writeContent = function (content) {
            textfield.value = content
          }
          getContent = function () {
            return textfield.value
          }
        } else {
          createRange = function (fix) {
            var range = {}
            var s = domRoot.getSelection()
            var clength = textfield.textContent.length
            range.left = Math.min(s.anchorOffset, clength)
            range.right = Math.min(s.focusOffset, clength)
            if (fix != null) {
              range.left = fix(range.left)
              range.right = fix(range.right)
            }
            var editedElement = s.focusNode
            if (editedElement === textfield || editedElement === textfield.childNodes[0]) {
              range.isReal = true
            } else {
              range.isReal = false
            }
            return range
          }

          writeRange = function (range) {
            writeContent(self.toString())
            var textnode = textfield.childNodes[0]
            if (range.isReal && textnode != null) {
              if (range.left < 0) {
                range.left = 0
              }
              range.right = Math.max(range.left, range.right)
              if (range.right > textnode.length) {
                range.right = textnode.length
              }
              range.left = Math.min(range.left, range.right)
              var r = document.createRange(); // eslint-disable-line
              r.setStart(textnode, range.left)
              r.setEnd(textnode, range.right)
              var s = domRoot.getSelection(); // eslint-disable-line
              s.removeAllRanges()
              s.addRange(r)
            }
          }
          writeContent = function (content) {
            textfield.innerText = content
            /*
            var contentArray = content.replace(new RegExp('\n', 'g'), ' ').split(' '); // eslint-disable-line
            textfield.innerText = ''
            for (var i = 0; i < contentArray.length; i++) {
              var c = contentArray[i]
              textfield.innerText += c
              if (i !== contentArray.length - 1) {
                textfield.innerHTML += ' '
              }
            }
            */
          }
          getContent = function () {
            return textfield.innerText
          }
        }
        writeContent(this.toString())

        function yCallback (event) {
          mutualExcluse(() => {
            var oPos, fix
            if (event.type === 'insert') {
              oPos = event.index
              fix = function (cursor) { // eslint-disable-line
                if (cursor <= oPos) {
                  return cursor
                } else {
                  cursor += 1
                  return cursor
                }
              }
              var r = createRange(fix)
              writeRange(r)
            } else if (event.type === 'delete') {
              oPos = event.index
              fix = function (cursor) { // eslint-disable-line
                if (cursor < oPos) {
                  return cursor
                } else {
                  cursor -= 1
                  return cursor
                }
              }
              r = createRange(fix)
              writeRange(r)
            }
          })
        }
        this.observe(yCallback)

        var textfieldObserver = function textfieldObserver () {
          mutualExcluse(function () {
            var r = createRange(function (x) { return x })
            var oldContent = self.toString()
            var content = getContent()
            var diffs = diff(oldContent, content, r.left)
            var pos = 0
            for (var i = 0; i < diffs.length; i++) {
              var d = diffs[i]
              if (d[0] === 0) { // EQUAL
                pos += d[1].length
              } else if (d[0] === -1) { // DELETE
                self.delete(pos, d[1].length)
              } else { // INSERT
                self.insert(pos, d[1])
                pos += d[1].length
              }
            }
          })
        }
        textfield.addEventListener('input', textfieldObserver)
        this.textfields.push({
          editor: textfield,
          yCallback: yCallback,
          eventListener: textfieldObserver
        })
      }
      _destroy () {
        this.unbindAll()
        this.textfields = null
        this.aceInstances = null
        super._destroy()
      }
    }
    Y.extend('Text', new Y.utils.CustomTypeDefinition({
      name: 'Text',
      class: YText,
      struct: 'List',
      initType: function * YTextInitializer (os, model) {
        var _content = []
        yield* Y.Struct.List.map.call(this, model, function (op) {
          if (op.hasOwnProperty('opContent')) {
            throw new Error('Text must not contain types!')
          } else {
            op.content.forEach(function (c, i) {
              _content.push({
                id: [op.id[0], op.id[1] + i],
                val: op.content[i]
              })
            })
          }
        })
        return new YText(os, model.id, _content)
      },
      createType: function YTextCreator (os, model) {
        return new YText(os, model.id, [])
      }
    }))
  })
}

module.exports = extend
if (typeof Y !== 'undefined') {
  extend(Y)
}
<|endoftext|>


<commit_before>import Shape from 'kittik-shape-basic';

/**
 * Implements Text shape which is rendering the text at specified point.
 * Supports different styles kinda bold, dim, underlined, etc...
 *
 * @since 1.0.0
 */
export default class Text extends Shape {
  constructor(options = {}) {
    super(options);

    this.setBold(options.bold);
    this.setDim(options.dim);
    this.setUnderlined(options.underlined);
    this.setBlink(options.blink);
    this.setReverse(options.reverse);
    this.setHidden(options.hidden);
  }

  /**
   * Check if text should be rendered as bold.
   *
   * @returns {Boolean}
   */
  isBold() {
    return !!this.get('bold');
  }

  /**
   * Toggle bold mode.
   *
   * @param {Boolean} [bold=false] If true, print bold text
   * @returns {Shape}
   */
  setBold(bold = false) {
    return this.set('bold', bold);
  }

  /**
   * Check if text should be rendered as dim.
   *
   * @returns {Boolean}
   */
  isDim() {
    return !!this.get('dim');
  }

  /**
   * Toggle dim mode.
   *
   * @param {Boolean} [dim=false] If true, print dim text
   * @returns {Shape}
   */
  setDim(dim = false) {
    return this.set('dim', dim);
  }

  /**
   * Check if text should be rendered as underlined.
   *
   * @returns {Boolean}
   */
  isUnderlined() {
    return !!this.get('underlined');
  }

  /**
   * Toggle underlined mode.
   *
   * @param {Boolean} [underlined=false] If true, print underlined text
   * @returns {Shape}
   */
  setUnderlined(underlined = false) {
    return this.set('underlined', underlined);
  }

  /**
   * Check if text should be rendered as blink.
   *
   * @returns {Boolean}
   */
  isBlink() {
    return !!this.get('blink');
  }

  /**
   * Toggle blink mode.
   *
   * @param {Boolean} [blink=false] If true, print blink text
   * @returns {Shape}
   */
  setBlink(blink = false) {
    return this.set('blink', blink);
  }

  /**
   * Check if text should be rendered with reversed colors.
   *
   * @returns {Boolean}
   */
  isReverse() {
    return !!this.get('reverse');
  }

  /**
   * Toggle reverse mode.
   *
   * @param {Boolean} [reverse=false] If true, print text with reversed colors
   * @returns {Shape}
   */
  setReverse(reverse = false) {
    return this.set('reverse', reverse);
  }

  /**
   * Check if text should be rendered as hidden text.
   *
   * @returns {Boolean}
   */
  isHidden() {
    return !!this.get('hidden');
  }

  /**
   * Toggle hidden mode.
   *
   * @param {Boolean} [hidden=false] If true, print hidden text
   * @returns {Shape}
   */
  setHidden(hidden = false) {
    return this.set('hidden', hidden);
  }

  /**
   * Render the shape based on options.
   *
   * @override
   * @param {Cursor} cursor
   * @returns {Text}
   */
  render(cursor) {
    let text = this.getText();
    let x = this.getX();
    let y = this.getY();
    let foreground = this.getForeground();
    let background = this.getBackground();
    let isBold = this.isBold();
    let isDim = this.isDim();
    let isUnderlined = this.isUnderlined();
    let isBlink = this.isBlink();
    let isReverse = this.isReverse();
    let isHidden = this.isHidden();

    if (foreground !== undefined) cursor.foreground(foreground);
    if (background !== undefined) cursor.background(background);

    cursor
      .bold(isBold)
      .dim(isDim)
      .underlined(isUnderlined)
      .blink(isBlink)
      .reverse(isReverse)
      .hidden(isHidden)
      .moveTo(x, y)
      .write(text);

    return this;
  }

  /**
   * Overrides default toObject() method because we have new fields here.
   *
   * @override
   * @returns {{name, options}|*}
   */
  toObject() {
    let obj = super.toObject();

    Object.assign(obj, {
      options: {
        bold: this.isBold(),
        dim: this.isDim(),
        underlined: this.isUnderlined(),
        blink: this.isBlink(),
        reverse: this.isReverse(),
        hidden: this.isHidden()
      }
    });

    return obj;
  }
}
<commit_msg>fix(shape): Fix issue with assigning new options on toObject()<commit_after>import Shape from 'kittik-shape-basic';

/**
 * Implements Text shape which is rendering the text at specified point.
 * Supports different styles kinda bold, dim, underlined, etc...
 *
 * @since 1.0.0
 */
export default class Text extends Shape {
  constructor(options = {}) {
    super(options);

    this.setBold(options.bold);
    this.setDim(options.dim);
    this.setUnderlined(options.underlined);
    this.setBlink(options.blink);
    this.setReverse(options.reverse);
    this.setHidden(options.hidden);
  }

  /**
   * Check if text should be rendered as bold.
   *
   * @returns {Boolean}
   */
  isBold() {
    return !!this.get('bold');
  }

  /**
   * Toggle bold mode.
   *
   * @param {Boolean} [bold=false] If true, print bold text
   * @returns {Shape}
   */
  setBold(bold = false) {
    return this.set('bold', bold);
  }

  /**
   * Check if text should be rendered as dim.
   *
   * @returns {Boolean}
   */
  isDim() {
    return !!this.get('dim');
  }

  /**
   * Toggle dim mode.
   *
   * @param {Boolean} [dim=false] If true, print dim text
   * @returns {Shape}
   */
  setDim(dim = false) {
    return this.set('dim', dim);
  }

  /**
   * Check if text should be rendered as underlined.
   *
   * @returns {Boolean}
   */
  isUnderlined() {
    return !!this.get('underlined');
  }

  /**
   * Toggle underlined mode.
   *
   * @param {Boolean} [underlined=false] If true, print underlined text
   * @returns {Shape}
   */
  setUnderlined(underlined = false) {
    return this.set('underlined', underlined);
  }

  /**
   * Check if text should be rendered as blink.
   *
   * @returns {Boolean}
   */
  isBlink() {
    return !!this.get('blink');
  }

  /**
   * Toggle blink mode.
   *
   * @param {Boolean} [blink=false] If true, print blink text
   * @returns {Shape}
   */
  setBlink(blink = false) {
    return this.set('blink', blink);
  }

  /**
   * Check if text should be rendered with reversed colors.
   *
   * @returns {Boolean}
   */
  isReverse() {
    return !!this.get('reverse');
  }

  /**
   * Toggle reverse mode.
   *
   * @param {Boolean} [reverse=false] If true, print text with reversed colors
   * @returns {Shape}
   */
  setReverse(reverse = false) {
    return this.set('reverse', reverse);
  }

  /**
   * Check if text should be rendered as hidden text.
   *
   * @returns {Boolean}
   */
  isHidden() {
    return !!this.get('hidden');
  }

  /**
   * Toggle hidden mode.
   *
   * @param {Boolean} [hidden=false] If true, print hidden text
   * @returns {Shape}
   */
  setHidden(hidden = false) {
    return this.set('hidden', hidden);
  }

  /**
   * Render the shape based on options.
   *
   * @override
   * @param {Cursor} cursor
   * @returns {Text}
   */
  render(cursor) {
    let text = this.getText();
    let x = this.getX();
    let y = this.getY();
    let foreground = this.getForeground();
    let background = this.getBackground();
    let isBold = this.isBold();
    let isDim = this.isDim();
    let isUnderlined = this.isUnderlined();
    let isBlink = this.isBlink();
    let isReverse = this.isReverse();
    let isHidden = this.isHidden();

    if (foreground !== undefined) cursor.foreground(foreground);
    if (background !== undefined) cursor.background(background);

    cursor
      .bold(isBold)
      .dim(isDim)
      .underlined(isUnderlined)
      .blink(isBlink)
      .reverse(isReverse)
      .hidden(isHidden)
      .moveTo(x, y)
      .write(text);

    return this;
  }

  /**
   * Overrides default toObject() method because we have new fields here.
   *
   * @override
   * @returns {{name, options}|*}
   */
  toObject() {
    let obj = super.toObject();

    Object.assign(obj.options, {
      bold: this.isBold(),
      dim: this.isDim(),
      underlined: this.isUnderlined(),
      blink: this.isBlink(),
      reverse: this.isReverse(),
      hidden: this.isHidden()
    });

    return obj;
  }
}
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import { Button, Panel } from 'react-bootstrap';
import Modal from 'react-modal';
import update from 'immutability-helper';
import ConfigurationValue from './ConfigurationValue';

class Type extends Component {
    constructor(props) {
        super(props);

        this.state = {
            newPropertyName: "",
            newPropertyContentType: "ContentString",
            type: {
                "elementType": "TYPE",
                "developerName": "",
                "developerSummary": "",
                "id": null,
                "serviceElementId": null,
                "properties": [],
                "bindings": null,
                "updateByName": false
            }
        };
    }

    closeModal = () => {
        this.props.addType(this.state.type);
        this.setState({
            newPropertyName: "",
            newPropertyContentType: "ContentString",
            type: {
                "elementType": "TYPE",
                "developerName": "",
                "developerSummary": "",
                "id": null,
                "serviceElementId": null,
                "properties": [],
                "bindings": null,
                "updateByName": false
            }
        });
        this.props.onClose();
    };

    handleTypeName = (event) => {
        const stateCopy = update(this.state.type, {developerName: { $set: event.target.value}});
        this.setState({type: stateCopy});
    };

    handleTypeSummary = (event) => {
        const stateCopy = update(this.state.type, {developerSummary: { $set: event.target.value}});
        this.setState({type: stateCopy});
    };

    handlePropertyName = (event) => {
        this.setState({newPropertyName: event.target.value});
    };

    handlePropertyContentType = (event) => {
        this.setState({newPropertyContentType: event.target.value});
    };

    removeProperty = (propertyName) => {
        const index = this.state.type.properties.findIndex((value) => value.developerName = propertyName);
        const stateCopy = update(this.state.type, {properties: { $splice: [[index,1]]}});
        this.setState({type: stateCopy});
    };

    addProperty = () => {
        if(this.state.newPropertyName) {
            const property = {
                "id": null,
                "developerName": this.state.newPropertyName,
                "contentFormat": null,
                "contentType": this.state.newPropertyContentType,
                "typeElementId": null,
                "typeElementDeveloperName": null
            };
            const stateCopy = update(this.state.type, {properties: {$push: [property]}});
            this.setState({newPropertyName: ""});
            this.setState({newPropertyContentType: "ContentString"});
            this.setState({type: stateCopy});
        }
    };

    render  () {
        const customStyles = {
            content : {
                top                   : '50%',
                left                  : '50%',
                right                 : 'auto',
                bottom                : 'auto',
                marginRight           : '-50%',
                transform             : 'translate(-50%, -50%)'
            }
        };

        const listProperties = this.state.type.properties.map( (prop) =>
{prop.developerName} ({prop.contentType})
)

        return (
            <Modal isOpen={this.props.isVisible} onRequestClose={this.closeModal} style={customStyles} contentLabel="Example Modal">
                <form>
                    Type Name

                    <input value={this.state.type.developerName} onChange={this.handleTypeName}/>

                    Type Summary

                    <input value={this.state.type.developerSummary} onChange={this.handleTypeSummary}/>

                    Property Name

                    <input value={this.state.newPropertyName} onChange={this.handlePropertyName}/>

                    Property ContentType

                    <select value={this.state.newPropertyContentType} onChange={this.handlePropertyContentType}>
                        <option value="ContentString">ContentString</option>
                        <option value="ContentNumber">ContentNumber</option>
                        <option value="ContentPassword">ContentPassword</option>
                        <option value="ContentDateTime">ContentDateTime</option>
                    </select>
                </form>

                {listProperties}
                <button onClick={this.addProperty}> Create Property </button>
                <Button bsStyle="success" onClick={this.closeModal}> Create Type </Button>
            </Modal>
        );
    }
}
export default Type;<commit_msg>improved type style<commit_after>import React, { Component } from 'react';
import { Button, Panel } from 'react-bootstrap';
import Modal from 'react-modal';
import update from 'immutability-helper';
import ConfigurationValue from './ConfigurationValue';

class Type extends Component {
    constructor(props) {
        super(props);

        this.state = {
            newPropertyName: "",
            newPropertyContentType: "ContentString",
            type: {
                "elementType": "TYPE",
                "developerName": "",
                "developerSummary": "",
                "id": null,
                "serviceElementId": null,
                "properties": [],
                "bindings": null,
                "updateByName": false
            }
        };
    }

    closeModal = () => {
        this.props.addType(this.state.type);
        this.setState({
            newPropertyName: "",
            newPropertyContentType: "ContentString",
            type: {
                "elementType": "TYPE",
                "developerName": "",
                "developerSummary": "",
                "id": null,
                "serviceElementId": null,
                "properties": [],
                "bindings": null,
                "updateByName": false
            }
        });
        this.props.onClose();
    };

    handleTypeName = (event) => {
        const stateCopy = update(this.state.type, {developerName: { $set: event.target.value}});
        this.setState({type: stateCopy});
    };

    handleTypeSummary = (event) => {
        const stateCopy = update(this.state.type, {developerSummary: { $set: event.target.value}});
        this.setState({type: stateCopy});
    };

    handlePropertyName = (event) => {
        this.setState({newPropertyName: event.target.value});
    };

    handlePropertyContentType = (event) => {
        this.setState({newPropertyContentType: event.target.value});
    };

    removeProperty = (propertyName) => {
        const index = this.state.type.properties.findIndex((value) => value.developerName = propertyName);
        const stateCopy = update(this.state.type, {properties: { $splice: [[index,1]]}});
        this.setState({type: stateCopy});
    };

    addProperty = () => {
        if(this.state.newPropertyName) {
            const property = {
                "id": null,
                "developerName": this.state.newPropertyName,
                "contentFormat": null,
                "contentType": this.state.newPropertyContentType,
                "typeElementId": null,
                "typeElementDeveloperName": null
            };
            const stateCopy = update(this.state.type, {properties: {$push: [property]}});
            this.setState({newPropertyName: ""});
            this.setState({newPropertyContentType: "ContentString"});
            this.setState({type: stateCopy});
        }
    };

    render  () {
        const customStyles = {
            content : {
                top                   : '50%',
                left                  : '50%',
                right                 : 'auto',
                bottom                : 'auto',
                marginRight           : '-50%',
                transform             : 'translate(-50%, -50%)'
            }
        };

        const listProperties = this.state.type.properties.map( (prop) =>
{prop.developerName} ({prop.contentType})
)

        return (
            <Modal isOpen={this.props.isVisible} onRequestClose={this.closeModal} style={customStyles} contentLabel="Example Modal">
                <form>
                    Type Name

                    <input value={this.state.type.developerName} onChange={this.handleTypeName}/>

                    Type Summary

                    <input value={this.state.type.developerSummary} onChange={this.handleTypeSummary}/>

                    Properties of the Type
                    {listProperties}

                    Property Name

                    <input value={this.state.newPropertyName} onChange={this.handlePropertyName}/>

                    Property ContentType

                    <select value={this.state.newPropertyContentType} onChange={this.handlePropertyContentType}>
                        <option value="ContentString">ContentString</option>
                        <option value="ContentNumber">ContentNumber</option>
                        <option value="ContentPassword">ContentPassword</option>
                        <option value="ContentDateTime">ContentDateTime</option>
                    </select>
                </form>

                <button onClick={this.addProperty}> Create Property </button>

                <Button bsStyle="success" onClick={this.closeModal}> Create Type </Button>
            </Modal>
        );
    }
}
export default Type;<|endoftext|>


<commit_before>Ext.define('Ext.ux.ColumnTree.View', {
	extend: 'Ext.panel.Panel',
	alias: 'widget.columntreeview',
	requires:[
		'Ext.data.TreeStore',
		'Ext.ux.ColumnTree.Column',
		'Ext.layout.container.Border'
	],

	layout:"border",

	defaultType:'columntreecolumn',

	store:null, // must be a treestore

	config: {
		columnViews:[
			[{ flex:2, dataIndex:"text", hideable: false}]
		],
		numColumnConfig:{
			1:{ colWidths:[1], colMap:[0]	},
			2:{ colWidths:[2,1], colMap:[0,0]	},
			3:{ colWidths:[1,2,1], colMap:[0,0,0]	}
		},
		levelOffset:2,
		panelConfig:{
			collapseMode:'mini'
		}
	},

	viewModel:{
		data:{
			treeStore:null
		}
	},

	initComponent: function() {
		if (Ext.isString(this.store)) {
			this.store = Ext.StoreMgr.lookup(this.store);
		} else if (!this.store || !this.store.isStore) {
			this.store = Ext.StoreMgr.lookup(Ext.apply({
				type: 'tree',
				root: this.root || {Title:this.getTitle()},
				fields: this.fields,
				model: this.model,
				folderSort: this.folderSort
			}, this.store));
		}

		this.callParent(arguments);

		this.store.load({
			callback:function() {
				this.store.getRootNode().expand();
				this.getViewModel().set("treeStore",this.store);
				this.add(Ext.apply({},{
					rootNode:this.store.getRootNode()
				},this.getPanelConfig()));
			},
			scope:this
		});
	},

	getColumnsForPanel: function(width) {
		return this.columnViews[0];
	},

	listeners:{
		'add':function(container,comp, index) {
			this.redoLayout();
			comp.on("expand",this.onBeforePanelExpaneded,this);
		},
		'remove': function(container, comp) {this.redoLayout(); 	}
	},
	redoLayout:function() {
			var panels = this.query('>columntreecolumn');

			var config = this.numColumnConfig[panels.length];

			var colWidths = [];
			var colMap = [];
			var i, len;
			var panel;

			for(i = 0, len = panels.length; i < len; i++) {
				panel = panels[i];
				if(!Ext.isEmpty(this.numColumnConfig[panels.length - i])) {
					Ext.Array.push(colWidths, this.numColumnConfig[panels.length - i].colWidths);
					Ext.Array.push(colMap, this.numColumnConfig[panels.length - i].colMap);
					break;
				}
				else {
					colWidths.push(0);
					colMap.push(null);
				}

			}

			Ext.suspendLayouts();

			for(i = 0, len = panels.length; i < len; i++) {
				panel = panels[i];

				//1. set width (has to happen before regions change)
				if(colWidths[i] > 0 ) {
					panel.setFlex(colWidths[i]);

					if(!panel.rendered) {
						var topMargin = i * this.getLevelOffset();
						panel.margin = topMargin + " 0 0 0";
					}
					else {
						panel.margin = 0;
					}

				}

				//2. set region
				if(i < panels.length - 1) {
					panel.setRegion("west");
				}
				else {
					this.getLayout().centerRegion = null;
					panel.setRegion("center");
				}

				//4. set columns
				if(!Ext.isEmpty(colMap[i])) {
					panel.reconfigure(this.columnViews[colMap[i]]);
				}

				//3. check visibility
				if(colWidths[i] <= 0 && panel.getCollapsed() === false) {
					panel.collapse();
				}
				else if(colWidths[i] > 0 && panel.getCollapsed() !== false) {
					panel.suspendEvent("expand");
					panel.expand(false);
					panel.resumeEvent("expand");
				}

			}
			Ext.resumeLayouts(true);
	},

	onBeforePanelExpaneded:function(panel) {
		var panels = this.query(">columntreecolumn");

		var indexOfExpaneded = panels.indexOf(panel);
		var needsLayout = false;

		var i, len;
		this.suspendEvent("remove");
		for(i = indexOfExpaneded, len = panels.length; i < len; i++) {
			if(!Ext.isEmpty(this.numColumnConfig[panels.length - indexOfExpaneded])) {
				break;
			}
			else {
				this.remove(panels.pop());
				panels[panels.length-1].setSelection();
				needsLayout = true;
			}
		}
		this.resumeEvent("remove");
		if(needsLayout) {
			this.redoLayout();
		}
	}

});
<commit_msg>add breadcrumb trail view<commit_after>Ext.define('Ext.ux.ColumnTree.View', {
	extend: 'Ext.panel.Panel',
	alias: 'widget.columntreeview',
	requires:[
		'Ext.data.TreeStore',
		'Ext.ux.ColumnTree.Column',
		'Ext.layout.container.Border',
		'Ext.toolbar.Toolbar'
	],

	layout:"border",

	defaultType:'columntreecolumn',

	store:null, // must be a treestore

	config: {
		columnViews:[
			[{ flex:2, dataIndex:"text", hideable: false}]
		],
		numColumnConfig:{
			1:{ colWidths:[1], colMap:[0]	},
			2:{ colWidths:[2,1], colMap:[0,0]	},
			3:{ colWidths:[1,2,1], colMap:[0,0,0]	}
		},
		levelOffset:1,
		panelConfig:{
			collapseMode:'mini'
		},
		breadCrumbEnabled:true
	},

	updateBreadCrumbEnabled: function(value) {
		this.getViewModel().set("breadCrumbEnabled",value);
	},

	dockedItems:[{
		xtype: 'toolbar',
		cls:"BreadCrumbTrail",
		bind:{
			hidden:"{!breadCrumbEnabled}"
		},
		dock: 'top'
	}],

	viewModel:{
		data:{
			treeStore:null
		}
	},

	initComponent: function() {
		if (Ext.isString(this.store)) {
			this.store = Ext.StoreMgr.lookup(this.store);
		} else if (!this.store || !this.store.isStore) {
			this.store = Ext.StoreMgr.lookup(Ext.apply({
				type: 'tree',
				root: this.root || {Title:this.getTitle()},
				fields: this.fields,
				model: this.model,
				folderSort: this.folderSort
			}, this.store));
		}

		this.callParent(arguments);

		this.store.load({
			callback:function() {
				this.store.getRootNode().expand();
				this.getViewModel().set("treeStore",this.store);
				this.add(Ext.apply({},{
					rootNode:this.store.getRootNode()
				},this.getPanelConfig()));
			},
			scope:this
		});
	},

	getColumnsForPanel: function(width) {
		return this.columnViews[0];
	},

	listeners:{
		'add':function(container,comp, index) {
			this.redoLayout();
			if(this.getBreadCrumbEnabled() === true) {
				this.pushBreadCrumbTrail(comp.getRootNode());
			}
			comp.on("expand",this.onBeforePanelExpaneded,this);
		},
		'remove': function(container, comp) {
			this.redoLayout();
			if(this.getBreadCrumbEnabled() === true) {
				this.popBreadCrumbTrail();
			}
		}
	},
	redoLayout:function() {
			var panels = this.query('>columntreecolumn');

			var config = this.numColumnConfig[panels.length];

			var colWidths = [];
			var colMap = [];
			var i, len;
			var panel;

			for(i = 0, len = panels.length; i < len; i++) {
				panel = panels[i];
				if(!Ext.isEmpty(this.numColumnConfig[panels.length - i])) {
					Ext.Array.push(colWidths, this.numColumnConfig[panels.length - i].colWidths);
					Ext.Array.push(colMap, this.numColumnConfig[panels.length - i].colMap);
					break;
				}
				else {
					colWidths.push(0);
					colMap.push(null);
				}

			}

			Ext.suspendLayouts();

			for(i = 0, len = panels.length; i < len; i++) {
				panel = panels[i];

				//1. set width (has to happen before regions change)
				if(colWidths[i] > 0 ) {
					panel.setFlex(colWidths[i]);

					if(!panel.rendered) {
						var topMargin = i * this.getLevelOffset();
						panel.margin = topMargin + " 0 0 0";
					}
					else {
						panel.margin = 0;
					}

				}

				//2. set region
				if(i < panels.length - 1) {
					panel.setRegion("west");
				}
				else {
					this.getLayout().centerRegion = null;
					panel.setRegion("center");
				}

				//4. set columns
				if(!Ext.isEmpty(colMap[i])) {
					panel.reconfigure(this.columnViews[colMap[i]]);
				}

				//3. check visibility
				if(colWidths[i] <= 0 && panel.getCollapsed() === false) {
					panel.collapse();
				}
				else if(colWidths[i] > 0 && panel.getCollapsed() !== false) {
					panel.suspendEvent("expand");
					panel.expand(false);
					panel.resumeEvent("expand");
				}

			}
			Ext.resumeLayouts(true);
	},

	onBeforePanelExpaneded:function(panel) {
		var panels = this.query(">columntreecolumn");

		var indexOfExpaneded = panels.indexOf(panel);
		var needsLayout = false;

		var i, len;
		this.suspendEvent("remove");
		for(i = indexOfExpaneded, len = panels.length; i < len; i++) {
			if(!Ext.isEmpty(this.numColumnConfig[panels.length - indexOfExpaneded])) {
				break;
			}
			else {
				this.remove(panels.pop());
				panels[panels.length-1].setSelection();
				needsLayout = true;
			}
		}
		this.resumeEvent("remove");
		if(needsLayout) {
			this.redoLayout();
		}
	},

	popBreadCrumbTrail: function() {
		var trail = this.down("toolbar[cls=BreadCrumbTrail]");
		trail.remove(trail.items.last());
		trail.remove(trail.items.last());
	},

	pushBreadCrumbTrail: function(node) {
		this.down("toolbar[cls=BreadCrumbTrail]").add([{
				text: node.get("Title"),
				node:node,
				handler:function(button) {
					this.breadCrumbTrailClick(button.node);
				},
				scope:this
			},{	xtype: 'tbtext', text: '>' }]);
	},

	breadCrumbTrailClick:function (node) {
		var panels = this.query(">columntreecolumn");
		var index = panels.length - 1;
		var panel;
		while(index > 0) {
			panel = panels[index];
			if(panel.getRootNode() === node) {
				break;
			}
			else {
				this.remove(panel);
				index--;
			}
		}

	}

});
<|endoftext|>


<commit_before>/**
 * @fileoverview
 * @author FE개발팀
 */

(function(ne) {
    'use strict';
    if (!ne) {
        ne = window.ne = {};
    }

    ne.type = ne.type || {
        isPlaneObject: function(obj) {
            return Object.prototype.toString.call(obj) === '[object Object]';
        },
        isFunction: function(obj) {
            return Object.prototype.toString.call(obj) === '[object Function]';
        },
        isTruthy: function(obj){
            return ne.type.isDefined(obj) && obj !== false;
        },
        isDefined: function(obj) {
            return obj !== null && obj !== undefined;
        }
    };

    var ajax = {};

    /**
     * Ajax 요청 정보 저장 객체
     * @property _ajaxRequestData
     * @private
     * @type {DataObject}
     */
    ajax._ajaxRequestData = {};
    /**
     * 공통 Ajax 처리 함수
     * @method ajax
     * @param {String} api urlCode 페이지 코드 (pug.variables._url에 저장된 값) 혹은 API URL 직접 입력
     * @param {DataObject} options Ajax 요청 옵션 객체
     * 		@param {DataObject} [options.data] Ajax 요청 시 함께 전달할 파라미터 객체
     * 		@param {String} [options.type="post"] ajax 요청 형식
     * 		@param {String} [options.dataType="json"] ajax 응답받을 데이터 형식
     * 		@param {Funtion} [options.complete] 전송이 완료되었을 시 실행될 콜백함수
     * 		@param {Funtion} [options.success] 전송이 성공하였을 시 실행될 콜백함수
     * 		@param {Funtion} [options.error] 전송이 실패하였을 시 실행될 콜백함수
     * - $Ajax() 옵션 그대로 사용가능

     * - 추가적인 옵션

     *  bNotUseErrorAlert : Ajax 통신 중에, 에러가 발생하더라도, 얼럿을 띄우지 않도록 할 경우 true로 설정
     *  sResultType : 응답 포맷
     * @return {Object} $Ajax() 객체
     * @example
     * ne.ajax.request(url, {
     *	 "action" : "insertList",
     *	 "page" : 3
     *	 "success" : function(responseData, status, jqXHR){
     *		console.log("응답 데이터 : ", responseData.myData);
     *	 }
     * });
     */
    ajax.request = function(api, options) {
        // Ajax 요청을 할 API 설정
        var url = api;

        if (url) {
            // 랜덤 아이디 생성
            var randomId = ajax.util._getRandomId();

            // Ajax 요청용 파라미터 객체 설정
            options = options || {};
            options._complete = options.complete;
            options._success = options.success;
            options._error = options.error;
            options = $.extend(options, {
                url: url,
                data: options.data || {},
                type: options.type || 'post',
                dataType: options.dataType || 'json',
                complete: $.proxy(this._onAjaxComplete, this, randomId),
                success: $.proxy(this._onAjaxSuccess, this, randomId),
                error: $.proxy(this._onAjaxError, this, randomId)
            });

            // 중복된 요청일 경우 요청 중단
            var isMatchedURL, isExistJSON;
            for (var x in this._ajaxRequestData) {
                isMatchedURL = options.url === this._ajaxRequestData[x].url;
                isExistJSON = $.compareJSON(this._ajaxRequestData[x].data, options.data);
                if (isMatchedURL && isExistJSON) {
                    return false;
                }
            }

            // 요청 정보를 저장
            this._ajaxRequestData[randomId] = ajax.util.cloningObject(options);
            $.ajax(options);
        } else {
            alert('요청을 보낼 URL을 입력해 주시기 바랍니다.');
            return false;
        }
    };

    /**
     * ajax 전송이 완료되었을 때 실행되는 콜백함수
     * - Ajax 요청이 완료되었을 때 수행되며, HTTP 상태 코드와 서버에서의 응답 결과에 따라 공통된 처리를 수행한다.
     * - Ajax 요청 시, 사용자가 등록한 콜백이 있으면 실행한다.
     *
     * @method _onAjaxComplete
     * @param {String} requestKey Ajax 요청에 대한 고유 아이디
     * @param {jqXHR} jqXHR Ajax 응답 객체
     * @param {String} status Ajax 응답 status
     * @private
     */
    ajax._onAjaxComplete = function(requestKey, jqXHR, status) {
        var requestData = this._ajaxRequestData[requestKey];
        if (requestData) {
            if (requestData['_complete'] && $.isFunction(requestData['_complete'])) {
                requestData['_complete'](status, jqXHR);
            }
            delete this._ajaxRequestData[requestKey];
        } else {
            throw this.error('Ajax 요청 정보가 없습니다.');
        }
    };

    /**
     * ajax 전송이 성공하였을 때 실행되는 콜백함수
     * - 응답값의 result가 true일 때 실행된다
     * - Ajax 요청이 완료되었을 때 수행되며, HTTP 상태 코드와 서버에서의 응답 결과에 따라 공통된 처리를 수행한다.
     * - Ajax 요청 시, 사용자가 등록한 콜백이 있으면 실행한다.
     *
     * @method _onAjaxSuccess
     * @param {String} requestKey Ajax 요청에 대한 고유 아이디
     * @param {DataObject} responseData Ajax 응답받은 데이터
     * 		@param {Boolean} [responseData.result] 정상적인 응답결과인지 여부
     * 		@param {String} [responseData.data.message] 정의되어 있을 경우, 이 문자열로 시스템얼럿 발생
     * 		@param {String} [responseData.data.redirectUrl] 이동시킬 url, 있을 경우 리다이렉트 처리
     * 		@param {Boolean} [responseData.data.closeWindow] 창 닫을지 여부, true일 경우 현재창 닫음
     * @param {String} status Ajax 응답 status
     * @param {jqXHR} jqXHR Ajax 응답 객체
     * @private
     */
    ajax._onAjaxSuccess = function(requestKey, responseData, status, jqXHR) {
        var requestData = this._ajaxRequestData[requestKey];

        if (requestData) {
            var result = true;
            if (requestData['_success'] && ne.type.isFunction(requestData['_success'])) {
                result = requestData['_success'](responseData, status, jqXHR);
            }

            if (result !== false && responseData && responseData.data) {
                if (responseData.data.message) {
                    alert(responseData.data.message);
                }

                if (responseData.data.redirectUrl) {
                    location.href = responseData.data.redirectUrl;
                }

                if (responseData.data.closeWindow) {
                    ajax.util.close(true);
                }
            }
        } else {
            throw this.error('Ajax 요청 정보가 없습니다.');
        }
    };

    /**
     * ajax 전송이 성공하였을 때 실행되는 콜백함수
     * - 응답값의 result가 false일 때 실행된다
     * - Ajax 요청이 완료되었을 때 수행되며, HTTP 상태 코드와 서버에서의 응답 결과에 따라 공통된 처리를 수행한다.
     * - Ajax 요청 시, 사용자가 등록한 콜백이 있으면 실행한다.
     *
     * @method _onAjaxError
     * @param {String} requestKey Ajax 요청에 대한 고유 아이디
     * @param {jqXHR} jqXHR Ajax 응답 객체
     * @param {String} status Ajax 응답 status
     * @param {String} errorMessage 서버로부터 전달받은 에러메세지
     * @private
     */
    ajax._onAjaxError = function(requestKey, jqXHR, status, errorMessage){
        var requestData = this._ajaxRequestData[requestKey];
        if (requestData) {
            var result = true;
            if (requestData['_error'] && $.isFunction(requestData['_error'])) {
                result = requestData['_error'](jqXHR, status, errorMessage);
            }
        } else {
            throw new Error('Ajax 요청 정보가 없습니다.');
        }
    };

    /**
     * @modules ne.ajax.util
     */

    ajax.util = {};
    /**
     * 객체를 복제하여 돌려준다.
     *
     * @param {*} sourceTarget 복제할 대상
     * @returns {*}
     */
    ajax.util.cloningObject = function(sourceTarget) {
        var constructor,
            destinationTarget,
            x, y;

        if (ne.type.isPlaneObject(sourceTarget) && (constructor = this.getConstructorName(sourceTarget))) {
            destinationTarget = eval('new ' + constructor + '()');

            if (sourceTarget.prototype) {
                for (x in sourceTarget.prototype) {
                    destinationTarget.prototype[x] = this.cloningObject(sourceTarget.prototype[x]);
                }
            }

            for (y in sourceTarget) {
                if (sourceTarget[y] instanceof RegExp) {
                    destinationTarget[y] = eval(sourceTarget[y].toString());
                } else {
                    destinationTarget[y] = this.cloningObject(sourceTarget[y]);
                }
            }
            return destinationTarget;
        }

        destinationTarget = sourceTarget;
        return destinationTarget;
    };

    /**
     * 클래스의 이름을 문자열로 가져온다.
     *
     * @param {*} target constructor를 체크 할 오브젝트
     * @returns {String|null}
     */
    ajax.util.getConstructorName = function(target) {
        if (target && target.constructor) {
            var code = target.constructor.toString();
            var match = code.match(/function ([^\(]*)/);
            return (match && match[1]) || null;
        }
        return null;
    };

    /**
     * 랜덤한 문자열을 생성하는 함수
     *
     * @private
     * @return {String} 랜덤값
     */
    ajax.util._getRandomId = function() {
        return String(parseInt(Math.random() * 10000000000, 10));
    };

    /**
     * 닫기를 실행, 대상이 엘리먼트이면 이벤트 제거만 수행
     *
     * @param {*} skipBeforeUnload
     * @param popup
     */
    ajax.util.close = function(skipBeforeUnload, popup) {
        var target = popup || window;

        if (ne.type.isTruthy(skipBeforeUnload)) {
            $(target).off('beforeunload');
        }

        if (!target.closed) {
            target.opener = window.location.href;
            target.close();
        }

    };

    ne.ajax = ajax;

})(window.ne);<commit_msg>ajax this.error 제거<commit_after>/**
 * @fileoverview
 * @author FE개발팀
 */

(function(ne) {
    'use strict';
    if (!ne) {
        ne = window.ne = {};
    }

    ne.type = ne.type || {
        isPlaneObject: function(obj) {
            return Object.prototype.toString.call(obj) === '[object Object]';
        },
        isFunction: function(obj) {
            return Object.prototype.toString.call(obj) === '[object Function]';
        },
        isTruthy: function(obj){
            return ne.type.isDefined(obj) && obj !== false;
        },
        isDefined: function(obj) {
            return obj !== null && obj !== undefined;
        }
    };

    var ajax = {};

    /**
     * Ajax 요청 정보 저장 객체
     * @property _ajaxRequestData
     * @private
     * @type {DataObject}
     */
    ajax._ajaxRequestData = {};
    /**
     * 공통 Ajax 처리 함수
     * @method ajax
     * @param {String} api urlCode 페이지 코드 (pug.variables._url에 저장된 값) 혹은 API URL 직접 입력
     * @param {DataObject} options Ajax 요청 옵션 객체
     * 		@param {DataObject} [options.data] Ajax 요청 시 함께 전달할 파라미터 객체
     * 		@param {String} [options.type="post"] ajax 요청 형식
     * 		@param {String} [options.dataType="json"] ajax 응답받을 데이터 형식
     * 		@param {Funtion} [options.complete] 전송이 완료되었을 시 실행될 콜백함수
     * 		@param {Funtion} [options.success] 전송이 성공하였을 시 실행될 콜백함수
     * 		@param {Funtion} [options.error] 전송이 실패하였을 시 실행될 콜백함수
     * - $Ajax() 옵션 그대로 사용가능

     * - 추가적인 옵션

     *  bNotUseErrorAlert : Ajax 통신 중에, 에러가 발생하더라도, 얼럿을 띄우지 않도록 할 경우 true로 설정
     *  sResultType : 응답 포맷
     * @return {Object} $Ajax() 객체
     * @example
     * ne.ajax.request(url, {
     *	 "action" : "insertList",
     *	 "page" : 3
     *	 "success" : function(responseData, status, jqXHR){
     *		console.log("응답 데이터 : ", responseData.myData);
     *	 }
     * });
     */
    ajax.request = function(api, options) {
        // Ajax 요청을 할 API 설정
        var url = api;

        if (url) {
            // 랜덤 아이디 생성
            var randomId = ajax.util._getRandomId();

            // Ajax 요청용 파라미터 객체 설정
            options = options || {};
            options._complete = options.complete;
            options._success = options.success;
            options._error = options.error;
            options = $.extend(options, {
                url: url,
                data: options.data || {},
                type: options.type || 'post',
                dataType: options.dataType || 'json',
                complete: $.proxy(this._onAjaxComplete, this, randomId),
                success: $.proxy(this._onAjaxSuccess, this, randomId),
                error: $.proxy(this._onAjaxError, this, randomId)
            });

            // 중복된 요청일 경우 요청 중단
            var isMatchedURL, isExistJSON;
            for (var x in this._ajaxRequestData) {
                isMatchedURL = options.url === this._ajaxRequestData[x].url;
                isExistJSON = $.compareJSON(this._ajaxRequestData[x].data, options.data);
                if (isMatchedURL && isExistJSON) {
                    return false;
                }
            }

            // 요청 정보를 저장
            this._ajaxRequestData[randomId] = ajax.util.cloningObject(options);
            $.ajax(options);
        } else {
            alert('요청을 보낼 URL을 입력해 주시기 바랍니다.');
            return false;
        }
    };

    /**
     * ajax 전송이 완료되었을 때 실행되는 콜백함수
     * - Ajax 요청이 완료되었을 때 수행되며, HTTP 상태 코드와 서버에서의 응답 결과에 따라 공통된 처리를 수행한다.
     * - Ajax 요청 시, 사용자가 등록한 콜백이 있으면 실행한다.
     *
     * @method _onAjaxComplete
     * @param {String} requestKey Ajax 요청에 대한 고유 아이디
     * @param {jqXHR} jqXHR Ajax 응답 객체
     * @param {String} status Ajax 응답 status
     * @private
     */
    ajax._onAjaxComplete = function(requestKey, jqXHR, status) {
        var requestData = this._ajaxRequestData[requestKey];
        if (requestData) {
            if (requestData['_complete'] && $.isFunction(requestData['_complete'])) {
                requestData['_complete'](status, jqXHR);
            }
            delete this._ajaxRequestData[requestKey];
        } else {
            throw new Error('Ajax 요청 정보가 없습니다.');
        }
    };

    /**
     * ajax 전송이 성공하였을 때 실행되는 콜백함수
     * - 응답값의 result가 true일 때 실행된다
     * - Ajax 요청이 완료되었을 때 수행되며, HTTP 상태 코드와 서버에서의 응답 결과에 따라 공통된 처리를 수행한다.
     * - Ajax 요청 시, 사용자가 등록한 콜백이 있으면 실행한다.
     *
     * @method _onAjaxSuccess
     * @param {String} requestKey Ajax 요청에 대한 고유 아이디
     * @param {DataObject} responseData Ajax 응답받은 데이터
     * 		@param {Boolean} [responseData.result] 정상적인 응답결과인지 여부
     * 		@param {String} [responseData.data.message] 정의되어 있을 경우, 이 문자열로 시스템얼럿 발생
     * 		@param {String} [responseData.data.redirectUrl] 이동시킬 url, 있을 경우 리다이렉트 처리
     * 		@param {Boolean} [responseData.data.closeWindow] 창 닫을지 여부, true일 경우 현재창 닫음
     * @param {String} status Ajax 응답 status
     * @param {jqXHR} jqXHR Ajax 응답 객체
     * @private
     */
    ajax._onAjaxSuccess = function(requestKey, responseData, status, jqXHR) {
        var requestData = this._ajaxRequestData[requestKey];

        if (requestData) {
            var result = true;
            if (requestData['_success'] && ne.type.isFunction(requestData['_success'])) {
                result = requestData['_success'](responseData, status, jqXHR);
            }

            if (result !== false && responseData && responseData.data) {
                if (responseData.data.message) {
                    alert(responseData.data.message);
                }

                if (responseData.data.redirectUrl) {
                    location.href = responseData.data.redirectUrl;
                }

                if (responseData.data.closeWindow) {
                    ajax.util.close(true);
                }
            }
        } else {
            throw new Error('Ajax 요청 정보가 없습니다.');
        }
    };

    /**
     * ajax 전송이 성공하였을 때 실행되는 콜백함수
     * - 응답값의 result가 false일 때 실행된다
     * - Ajax 요청이 완료되었을 때 수행되며, HTTP 상태 코드와 서버에서의 응답 결과에 따라 공통된 처리를 수행한다.
     * - Ajax 요청 시, 사용자가 등록한 콜백이 있으면 실행한다.
     *
     * @method _onAjaxError
     * @param {String} requestKey Ajax 요청에 대한 고유 아이디
     * @param {jqXHR} jqXHR Ajax 응답 객체
     * @param {String} status Ajax 응답 status
     * @param {String} errorMessage 서버로부터 전달받은 에러메세지
     * @private
     */
    ajax._onAjaxError = function(requestKey, jqXHR, status, errorMessage){
        var requestData = this._ajaxRequestData[requestKey];
        if (requestData) {
            var result = true;
            if (requestData['_error'] && $.isFunction(requestData['_error'])) {
                result = requestData['_error'](jqXHR, status, errorMessage);
            }
        } else {
            throw new Error('Ajax 요청 정보가 없습니다.');
        }
    };

    /**
     * @modules ne.ajax.util
     */

    ajax.util = {};
    /**
     * 객체를 복제하여 돌려준다.
     *
     * @param {*} sourceTarget 복제할 대상
     * @returns {*}
     */
    ajax.util.cloningObject = function(sourceTarget) {
        var constructor,
            destinationTarget,
            x, y;

        if (ne.type.isPlaneObject(sourceTarget) && (constructor = this.getConstructorName(sourceTarget))) {
            destinationTarget = eval('new ' + constructor + '()');

            if (sourceTarget.prototype) {
                for (x in sourceTarget.prototype) {
                    destinationTarget.prototype[x] = this.cloningObject(sourceTarget.prototype[x]);
                }
            }

            for (y in sourceTarget) {
                if (sourceTarget[y] instanceof RegExp) {
                    destinationTarget[y] = eval(sourceTarget[y].toString());
                } else {
                    destinationTarget[y] = this.cloningObject(sourceTarget[y]);
                }
            }
            return destinationTarget;
        }

        destinationTarget = sourceTarget;
        return destinationTarget;
    };

    /**
     * 클래스의 이름을 문자열로 가져온다.
     *
     * @param {*} target constructor를 체크 할 오브젝트
     * @returns {String|null}
     */
    ajax.util.getConstructorName = function(target) {
        if (target && target.constructor) {
            var code = target.constructor.toString();
            var match = code.match(/function ([^\(]*)/);
            return (match && match[1]) || null;
        }
        return null;
    };

    /**
     * 랜덤한 문자열을 생성하는 함수
     *
     * @private
     * @return {String} 랜덤값
     */
    ajax.util._getRandomId = function() {
        return String(parseInt(Math.random() * 10000000000, 10));
    };

    /**
     * 닫기를 실행, 대상이 엘리먼트이면 이벤트 제거만 수행
     *
     * @param {*} skipBeforeUnload
     * @param popup
     */
    ajax.util.close = function(skipBeforeUnload, popup) {
        var target = popup || window;

        if (ne.type.isTruthy(skipBeforeUnload)) {
            $(target).off('beforeunload');
        }

        if (!target.closed) {
            target.opener = window.location.href;
            target.close();
        }

    };

    ne.ajax = ajax;

})(window.ne);<|endoftext|>