<commit_before>import React, {PureComponent} from 'react';
import Slider from 'material-ui/Slider';

export default class Meter extends PureComponent {
  constructor(props) {
    super(props);
    this.handleSliderChange = this.handleSliderChange.bind(this);
    this.startDragging = this.startDragging.bind(this);
    this.onDrag = this.onDrag.bind(this);
    this.finishDragging = this.finishDragging.bind(this);
  }

  scaleValue(val) {
    const {minValue, maxValue} = this.props;
    let range = maxValue - minValue;
    let scaledValue = minValue != 0 ? val - minValue : val;
    return scaledValue / range;
  }

  absoluteValue(val) {
    const {minValue, maxValue} = this.props;
    let range = maxValue - minValue;
    let absValue = Math.round(val * range);
    absValue = minValue != 0 ? absValue + minValue : absValue;
    return absValue;
  }

  handleSliderChange(event, value) {
    this.setMeterValue(value);
  }

  setMeterValue(val) {
    // sanity check, clamp the value between 0 and 1
    val = val < 0 ? 0 : val > 1 ? 1 : val;
    if (this.props.onMeterChange) {
      this.props.onMeterChange(this.absoluteValue(val));
    }
  }

  describeArc(cx, cy, radius, angleStart, angleEnd = 0){
    // arc draws backwards, so start is the offset point, end is at 0%
    var start = this.getPoint(cx, cy, radius, angleStart);
    var end = this.getPoint(cx, cy, radius, angleEnd);
    var d = [
          "M", start.x, start.y,
          "A", radius, radius, 0, 0, 0, end.x, end.y
      ].join(" ");
      return d;
  }

  getPoint(cx, cy, r, angle){
    var rad = (angle-180) * Math.PI / 180;
    var dx = cx + (r * Math.cos(rad));
    var dy = cy + (r * Math.sin(rad));
    var point = {x: dx, y: dy};
    return point;
  }

  drawMeterLine(cx, cy, length, angle, pointerWidth){
    var end = this.getPoint(cx, cy, length, angle);
    var d = [
      "M", cx, cy,
      "L", end.x, end.y,
      "Z"
    ].join(" ");
    return d;
  }

  generateSegments() {
    const {cx, cy, r, segments, arcWidth } = this.props;
    let arcSegments = [];
    let width = r / 6;

    for (var i = 0; i < segments.length; i++) {
      let segmentId = "arc-s" + i;
      let angleStart = segments[i].end;
      let angleEnd = segments[i].start;
      arcSegments.push(<path id={segmentId} key={segmentId} fill="none" stroke={segments[i].color} strokeWidth={width} d={this.describeArc(cx, cy, (r - (width/2) - arcWidth), angleStart, angleEnd)} />);
    }
    return arcSegments;
  }

  startDragging(event) {
    if (this.props.draggable) {
      let targetRect = event.currentTarget.getBoundingClientRect();
      let centerX = (targetRect.width / 2) + targetRect.left;
      this.setState({ centerX: centerX, minX: centerX - this.props.r, maxX: centerX + this.props.r });
      let clampedX = this.checkPosition(event.clientX);
      if (clampedX) {
        this.updateMeterPosition(clampedX);
      }
    }
    event.preventDefault();
  }
  checkPosition(pos) {
    if (!this.state) return false;
    let {centerX, minX, maxX} = this.state;
    let r = this.props.r;
    let padding = 10;

    if (centerX && pos > (minX - padding) && pos < (maxX + padding)) {
      // clamp position for meter controls while allowing a slight padded tolerance on dragging
      let clampedPos = pos < minX ? minX : pos > maxX ? maxX : pos;
      return clampedPos;
    } else if (centerX) {
      // was dragging, but now out of bounds
      this.endDrag();
    }
    // not dragging
    return false;
  }

  updateMeterPosition(pos) {
    let minX = this.state.minX;
    let r = this.props.r;
    let val = (pos - minX) / (r * 2);
    this.setMeterValue(val);
  }

  onDrag(event) {
    if (this.props.draggable && this.state && this.state.centerX) {
      let clampedX = this.checkPosition(event.clientX);
      if (clampedX) {
        this.updateMeterPosition(clampedX);
      }
    }
  }
  endDrag() {
    this.setState({centerX: undefined, minX: undefined, maxX: undefined});
  }
  finishDragging(event) {
    this.endDrag();
    event.preventDefault();
  }

  render() {
    const {cx, cy, r, showSlider, segments, background, needleColor, arcWidth, needleWidth, currentValue} = this.props;

    let meterValue = this.scaleValue(currentValue);
    let angle = 180 * meterValue;
    let meterLineLength = r - 10;
    let sliderWidth = (r * 2) + "px";
    let sliderStyle = { width: sliderWidth, margin: 'auto'};
    let arcSegments = segments ?
      <g className="segments">
        {this.generateSegments()}
      </g>
      : undefined;
    let backgroundArc = background ?
      <path id="arc-bg" fill="none" stroke={background} strokeWidth={(2*r) - arcWidth} d={this.describeArc(cx, cy, 1, 180)} />
      : undefined;

    return (

        <svg
          onTouchStart={this.startDragging}
          onMouseDown={this.startDragging}
          onTouchMove={this.onDrag}
          onMouseMove={this.onDrag}
          onMouseLeave={this.finishDragging}
          onTouchEnd={this.finishDragging}
          onMouseUp={this.finishDragging}>
          {backgroundArc}
          {arcSegments}
          <path id="arc-incomplete" fill="none" stroke="#cccccc" strokeWidth={arcWidth} d={this.describeArc(cx, cy, r, 180)} />
          <path id="arc" fill="none" stroke="#446688" strokeWidth={arcWidth} d={this.describeArc(cx, cy, r, angle)}/>
          <path id="meterLine" fill="none" stroke="#000" strokeWidth={needleWidth} d={this.drawMeterLine(cx, cy, meterLineLength, angle, 1)} />
          <path id="meterLine" fill="none" stroke={needleColor} strokeWidth={needleWidth-1} d={this.drawMeterLine(cx, cy, meterLineLength, angle, 1)} />
          <circle id="meterLineBase" cx={cx} cy={cy} r="12" stroke="black" strokeWidth="1" fill={needleColor}  />
        </svg>

          {showSlider && <Slider min={0} max={1} value={meterValue}
            style={sliderStyle}
            name="temperature"
            onChange={this.handleSliderChange} />}

    )
  }
}

Meter.PropTypes = {
  cx: React.PropTypes.object.number,
  cy: React.PropTypes.number,
  r: React.PropTypes.number,
  arcWidth: React.PropTypes.number,
  needleWidth: React.PropTypes.number,
  minValue: React.PropTypes.number,
  maxValue: React.PropTypes.number,
  currentValue: React.PropTypes.number,
  showSlider: React.PropTypes.bool,
  segments: React.PropTypes.array,
  background: React.PropTypes.string,
  needleColor: React.PropTypes.string,
  draggable: React.PropTypes.bool,
  onMeterChange: React.PropTypes.func
};

Meter.defaultProps = {
  cx: 150,
  cy: 150,
  r: 100,
  arcWidth: 4,
  needleWidth: 3,
  minValue: 0,
  maxValue: 100,
  currentValue: 30,
  showSlider: false,
  segments: undefined,
  background: undefined,
  needleColor: "#ccc",
  draggable: true
};
<commit_msg>Remove state, add drag handler to document<commit_after>import React, {PureComponent} from 'react';
import Slider from 'material-ui/Slider';

export default class Meter extends PureComponent {
  constructor(props) {
    super(props);
    this.handleSliderChange = this.handleSliderChange.bind(this);
    this.startDragging = this.startDragging.bind(this);
    this.onDrag = this.onDrag.bind(this);
    this.finishDragging = this.finishDragging.bind(this);
  }

  scaleValue(val) {
    const {minValue, maxValue} = this.props;
    let range = maxValue - minValue;
    let scaledValue = minValue != 0 ? val - minValue : val;
    return scaledValue / range;
  }

  absoluteValue(val) {
    const {minValue, maxValue} = this.props;
    let range = maxValue - minValue;
    let absValue = Math.round(val * range);
    absValue = minValue != 0 ? absValue + minValue : absValue;
    return absValue;
  }

  handleSliderChange(event, value) {
    this.setMeterValue(value);
  }

  setMeterValue(val) {
    // sanity check, clamp the value between 0 and 1
    val = val < 0 ? 0 : val > 1 ? 1 : val;
    if (this.props.onMeterChange) {
      this.props.onMeterChange(this.absoluteValue(val));
    }
  }

  describeArc(cx, cy, radius, angleStart, angleEnd = 0){
    // arc draws backwards, so start is the offset point, end is at 0%
    var start = this.getPoint(cx, cy, radius, angleStart);
    var end = this.getPoint(cx, cy, radius, angleEnd);
    var d = [
          "M", start.x, start.y,
          "A", radius, radius, 0, 0, 0, end.x, end.y
      ].join(" ");
      return d;
  }

  getPoint(cx, cy, r, angle){
    var rad = (angle-180) * Math.PI / 180;
    var dx = cx + (r * Math.cos(rad));
    var dy = cy + (r * Math.sin(rad));
    var point = {x: dx, y: dy};
    return point;
  }

  drawMeterLine(cx, cy, length, angle, pointerWidth){
    var end = this.getPoint(cx, cy, length, angle);
    var d = [
      "M", cx, cy,
      "L", end.x, end.y,
      "Z"
    ].join(" ");
    return d;
  }

  generateSegments() {
    const {cx, cy, r, segments, arcWidth } = this.props;
    let arcSegments = [];
    let width = r / 6;

    for (var i = 0; i < segments.length; i++) {
      let segmentId = "arc-s" + i;
      let angleStart = segments[i].end;
      let angleEnd = segments[i].start;
      arcSegments.push(<path id={segmentId} key={segmentId} fill="none" stroke={segments[i].color} strokeWidth={width} d={this.describeArc(cx, cy, (r - (width/2) - arcWidth), angleStart, angleEnd)} />);
    }
    return arcSegments;
  }

  startDragging(event) {
    if (this.props.draggable) {
      let targetRect = this.meter.getBoundingClientRect(),
          centerX = (targetRect.width / 2) + targetRect.left,
          min = centerX - this.props.r,
          max = centerX + this.props.r,
          clampedX = this.clampPosition(event.clientX, min, max);
      this.updateMeterPosition(clampedX, min);
    }

    document.addEventListener('mousemove', this.onDrag);
    document.addEventListener('mouseup', this.finishDragging);

    event.preventDefault();
  }

  clampPosition(pos, min, max) {
    return pos < min ? min : pos > max ? max : pos;
  }

  updateMeterPosition(pos, min) {
    let r = this.props.r,
        val = (pos - min) / (r * 2);
    this.setMeterValue(val);
  }

  onDrag(event) {
    if (this.props.draggable) {
      let targetRect = this.meter.getBoundingClientRect(),
          centerX = (targetRect.width / 2) + targetRect.left,
          min = centerX - this.props.r,
          max = centerX + this.props.r,
          clampedX = this.clampPosition(event.clientX, min, max);
      this.updateMeterPosition(clampedX, min);
    }
  }

  finishDragging(event) {
    document.removeEventListener('mousemove', this.onDrag)
    document.removeEventListener('mouseup', this.finishDragging)
    event.preventDefault();
  }

  render() {
    const {cx, cy, r, showSlider, segments, background, needleColor, arcWidth, needleWidth, currentValue} = this.props;

    let meterValue = this.scaleValue(currentValue);
    let angle = 180 * meterValue;
    let meterLineLength = r - 10;
    let sliderWidth = (r * 2) + "px";
    let sliderStyle = { width: sliderWidth, margin: 'auto'};
    let arcSegments = segments ?
      <g className="segments">
        {this.generateSegments()}
      </g>
      : undefined;
    let backgroundArc = background ?
      <path id="arc-bg" fill="none" stroke={background} strokeWidth={(2*r) - arcWidth} d={this.describeArc(cx, cy, 1, 180)} />
      : undefined;

    return (

        <svg
          onTouchStart={this.startDragging}
          onMouseDown={this.startDragging}
          ref={(m) => { this.meter = m }}>
          {backgroundArc}
          {arcSegments}
          <path id="arc-incomplete" fill="none" stroke="#cccccc" strokeWidth={arcWidth} d={this.describeArc(cx, cy, r, 180)} />
          <path id="arc" fill="none" stroke="#446688" strokeWidth={arcWidth} d={this.describeArc(cx, cy, r, angle)}/>
          <path id="meterLine" fill="none" stroke="#000" strokeWidth={needleWidth} d={this.drawMeterLine(cx, cy, meterLineLength, angle, 1)} />
          <path id="meterLine" fill="none" stroke={needleColor} strokeWidth={needleWidth-1} d={this.drawMeterLine(cx, cy, meterLineLength, angle, 1)} />
          <circle id="meterLineBase" cx={cx} cy={cy} r="12" stroke="black" strokeWidth="1" fill={needleColor}  />
        </svg>

          {showSlider && <Slider min={0} max={1} value={meterValue}
            style={sliderStyle}
            name="temperature"
            onChange={this.handleSliderChange} />}

    )
  }
}

Meter.PropTypes = {
  cx: React.PropTypes.object.number,
  cy: React.PropTypes.number,
  r: React.PropTypes.number,
  arcWidth: React.PropTypes.number,
  needleWidth: React.PropTypes.number,
  minValue: React.PropTypes.number,
  maxValue: React.PropTypes.number,
  currentValue: React.PropTypes.number,
  showSlider: React.PropTypes.bool,
  segments: React.PropTypes.array,
  background: React.PropTypes.string,
  needleColor: React.PropTypes.string,
  draggable: React.PropTypes.bool,
  onMeterChange: React.PropTypes.func
};

Meter.defaultProps = {
  cx: 150,
  cy: 150,
  r: 100,
  arcWidth: 4,
  needleWidth: 3,
  minValue: 0,
  maxValue: 100,
  currentValue: 30,
  showSlider: false,
  segments: undefined,
  background: undefined,
  needleColor: "#ccc",
  draggable: true
};
<|endoftext|>


<commit_before>import {
  getScrollableElement,
  getScrollbarWidth,
  getScrollLeft,
  getScrollTop,
} from './../../../helpers/dom/element';
import {isKey} from './../../../helpers/unicode';
import {EventManager} from './../../../eventManager';

/**
 * @class WalkontableOverlays
 */
class WalkontableOverlays {
  /**
   * @param {Walkontable} wotInstance
   */
  constructor(wotInstance) {
    this.wot = wotInstance;

    // legacy support
    this.instance = this.wot;
    this.eventManager = new EventManager(this.wot);

    this.wot.update('scrollbarWidth', getScrollbarWidth());
    this.wot.update('scrollbarHeight', getScrollbarWidth());

    this.mainTableScrollableElement = getScrollableElement(this.wot.wtTable.TABLE);

    this.topOverlay = WalkontableOverlay.createOverlay(WalkontableOverlay.CLONE_TOP, this.wot);

    if (typeof WalkontableBottomOverlay === 'undefined') {
      this.bottomOverlay = {
        needFullRender: false
      };

    } else {
      this.bottomOverlay = WalkontableOverlay.createOverlay(WalkontableOverlay.CLONE_BOTTOM, this.wot);
    }

    this.leftOverlay = WalkontableOverlay.createOverlay(WalkontableOverlay.CLONE_LEFT, this.wot);

    if (this.topOverlay.needFullRender && this.leftOverlay.needFullRender) {
      this.topLeftCornerOverlay = WalkontableOverlay.createOverlay(WalkontableOverlay.CLONE_TOP_LEFT_CORNER, this.wot);
    }

    if (this.bottomOverlay.needFullRender && this.leftOverlay.needFullRender && typeof WalkontableBottomLeftCornerOverlay !== 'undefined') {
      this.bottomLeftCornerOverlay = WalkontableOverlay.createOverlay(WalkontableOverlay.CLONE_BOTTOM_LEFT_CORNER, this.wot);
    } else {
      this.bottomLeftCornerOverlay = {
        needFullRender: false
      };
    }

    if (this.wot.getSetting('debug')) {
      this.debug = WalkontableOverlay.createOverlay(WalkontableOverlay.CLONE_DEBUG, this.wot);
    }

    this.destroyed = false;
    this.keyPressed = false;
    this.spreaderLastSize = {
      width: null,
      height: null,
    };
    this.overlayScrollPositions = {
      master: {
        top: 0,
        left: 0,
      },
      top: {
        top: null,
        left: 0,
      },
      bottom: {
        top: null,
        left: 0

      },
      left: {
        top: 0,
        left: null
      }
    };

    this.verticalScrolling = false;
    this.horizontalScrolling = false;
    this.delegatedScrollCallback = false;

    this.registerListeners();
  }

  /**
   * Refresh and redraw table
   */
  refreshAll() {
    if (!this.wot.drawn) {
      return;
    }
    if (!this.wot.wtTable.holder.parentNode) {
      // Walkontable was detached from DOM, but this handler was not removed
      this.destroy();

      return;
    }
    this.wot.draw(true);

    if (this.verticalScrolling) {
      this.leftOverlay.onScroll();
    }

    if (this.horizontalScrolling) {
      this.topOverlay.onScroll();
    }

    this.verticalScrolling = false;
    this.horizontalScrolling = false;
  }

  /**
   * Register all necessary event listeners
   */
  registerListeners() {
    this.eventManager.addEventListener(document.documentElement, 'keydown', (event) => this.onKeyDown(event));
    this.eventManager.addEventListener(document.documentElement, 'keyup', () => this.onKeyUp());
    this.eventManager.addEventListener(document, 'visibilitychange', () => this.onKeyUp());

    this.eventManager.addEventListener(this.mainTableScrollableElement, 'scroll', (event) => this.onTableScroll(event));

    if (this.topOverlay.needFullRender) {
      this.eventManager.addEventListener(this.topOverlay.clone.wtTable.holder, 'scroll', (event) => this.onTableScroll(event));
      this.eventManager.addEventListener(this.topOverlay.clone.wtTable.holder, 'wheel', (event) => this.onTableScroll(event));
    }

    if (this.bottomOverlay.needFullRender) {
      this.eventManager.addEventListener(this.bottomOverlay.clone.wtTable.holder, 'scroll', (event) => this.onTableScroll(event));
      this.eventManager.addEventListener(this.bottomOverlay.clone.wtTable.holder, 'wheel', (event) => this.onTableScroll(event));
    }

    if (this.leftOverlay.needFullRender) {
      this.eventManager.addEventListener(this.leftOverlay.clone.wtTable.holder, 'scroll', (event) => this.onTableScroll(event));
      this.eventManager.addEventListener(this.leftOverlay.clone.wtTable.holder, 'wheel', (event) => this.onTableScroll(event));
    }

    if (this.topOverlay.trimmingContainer !== window && this.leftOverlay.trimmingContainer !== window) {
      // This is necessary?
      //eventManager.addEventListener(window, 'scroll', (event) => this.refreshAll(event));
      this.eventManager.addEventListener(window, 'wheel', (event) => {
        let overlay;
        let deltaY = event.wheelDeltaY || event.deltaY;
        let deltaX = event.wheelDeltaX || event.deltaX;

        if (this.topOverlay.clone.wtTable.holder.contains(event.realTarget)) {
          overlay = 'top';

        } else if (this.bottomOverlay.clone && this.bottomOverlay.clone.wtTable.holder.contains(event.realTarget)) {
          overlay = 'bottom';

        } else if (this.leftOverlay.clone.wtTable.holder.contains(event.realTarget)) {
          overlay = 'left';
        }

        if (overlay == 'top' && deltaY !== 0) {
          event.preventDefault();
        } else if (overlay == 'left' && deltaX !== 0) {
          event.preventDefault();
        } else if (overlay == 'bottom' && deltaY !== 0) {
          event.preventDefault();
        }
      });
    }
  }

  /**
   * Scroll listener
   *
   * @param {Event} event
   */
  onTableScroll(event) {
    // if mobile browser, do not update scroll positions, as the overlays are hidden during the scroll
    if (Handsontable.mobileBrowser) {
      return;
    }

    // For key press, sync only master -> overlay position because while pressing Walkontable.render is triggered
    // by hot.refreshBorder
    if (this.keyPressed && this.mainTableScrollableElement !== window && !event.target.contains(this.mainTableScrollableElement)) {
      return;
    }
    if (event.type === 'scroll') {
      this.syncScrollPositions(event);

    } else {
      this.translateMouseWheelToScroll(event);
    }
  }

  /**
   * Key down listener
   */
  onKeyDown(event) {
    this.keyPressed = isKey(event.keyCode, 'ARROW_UP|ARROW_RIGHT|ARROW_DOWN|ARROW_LEFT');
  }

  /**
   * Key up listener
   */
  onKeyUp() {
    this.keyPressed = false;
  }

  /**
   * Translate wheel event into scroll event and sync scroll overlays position
   *
   * @param {Event} event
   * @returns {Boolean}
   */
  translateMouseWheelToScroll(event) {
    let topOverlay = this.topOverlay.clone.wtTable.holder;
    let bottomOverlay = this.bottomOverlay.clone ? this.bottomOverlay.clone.wtTable.holder : null;
    let leftOverlay = this.leftOverlay.clone.wtTable.holder;
    let eventMockup = {type: 'wheel'};
    let tempElem = event.target;
    let deltaY = event.wheelDeltaY || (-1) * event.deltaY;
    let deltaX = event.wheelDeltaX || (-1) * event.deltaX;
    let parentHolder;

    while (tempElem != document && tempElem != null) {
      if (tempElem.className.indexOf('wtHolder') > -1) {
        parentHolder = tempElem;
        break;
      }
      tempElem = tempElem.parentNode;
    }
    eventMockup.target = parentHolder;

    if (parentHolder == topOverlay) {
      this.syncScrollPositions(eventMockup, (-0.2) * deltaY);

    } else if (parentHolder == bottomOverlay) {
      this.syncScrollPositions(eventMockup, (-0.2) * deltaY);

    } else if (parentHolder == leftOverlay) {
      this.syncScrollPositions(eventMockup, (-0.2) * deltaX);
    }

    return false;
  }

  /**
   * Synchronize scroll position between master table and overlay table
   *
   * @param {Event|Object} event
   * @param {Number} [fakeScrollValue=null]
   */
  syncScrollPositions(event, fakeScrollValue = null) {
    if (this.destroyed) {
      return;
    }
    if (arguments.length === 0) {
      this.syncScrollWithMaster();

      return;
    }
    let master = this.mainTableScrollableElement;
    let target = event.target;
    let tempScrollValue = 0;
    let scrollValueChanged = false;
    let topOverlay;
    let leftOverlay;
    let bottomOverlay;
    let delegatedScroll = false;

    if (this.topOverlay.needFullRender) {
      topOverlay = this.topOverlay.clone.wtTable.holder;
    }

    if (this.bottomOverlay.needFullRender) {
      bottomOverlay = this.bottomOverlay.clone.wtTable.holder;
    }

    if (this.leftOverlay.needFullRender) {
      leftOverlay = this.leftOverlay.clone.wtTable.holder;
    }

    if (target === document) {
      target = window;
    }

    if (target === master) {
      tempScrollValue = getScrollLeft(target);

      // if scrolling the master table - populate the scroll values to both top and left overlays
      if (this.overlayScrollPositions.master.left !== tempScrollValue) {
        this.horizontalScrolling = true;
        this.overlayScrollPositions.master.left = tempScrollValue;
        scrollValueChanged = true;

        if (topOverlay) {
          topOverlay.scrollLeft = tempScrollValue;
          delegatedScroll = true;
        }

        if (bottomOverlay) {
          bottomOverlay.scrollLeft = tempScrollValue;
          delegatedScroll = true;
        }
      }
      tempScrollValue = getScrollTop(target);

      if (this.overlayScrollPositions.master.top !== tempScrollValue) {
        this.verticalScrolling = true;
        this.overlayScrollPositions.master.top = tempScrollValue;
        scrollValueChanged = true;

        if (leftOverlay) {
          leftOverlay.scrollTop = tempScrollValue;
          delegatedScroll = true;
        }
      }

    } else if (target === bottomOverlay) {
      tempScrollValue = getScrollLeft(target);

      // if scrolling the bottom overlay - populate the horizontal scroll to the master table
      if (this.overlayScrollPositions.bottom.left !== tempScrollValue) {
        this.horizontalScrolling = true;
        this.overlayScrollPositions.bottom.left = tempScrollValue;
        scrollValueChanged = true;

        master.scrollLeft = tempScrollValue;
      }

      // "fake" scroll value calculated from the mousewheel event
      if (fakeScrollValue !== null) {
        scrollValueChanged = true;
        master.scrollTop += fakeScrollValue;
      }

    } else if (target === topOverlay) {
      tempScrollValue = getScrollLeft(target);

      // if scrolling the top overlay - populate the horizontal scroll to the master table
      if (this.overlayScrollPositions.top.left !== tempScrollValue) {
        this.horizontalScrolling = true;
        this.overlayScrollPositions.top.left = tempScrollValue;
        scrollValueChanged = true;

        master.scrollLeft = tempScrollValue;
      }

      // "fake" scroll value calculated from the mousewheel event
      if (fakeScrollValue !== null) {
        scrollValueChanged = true;
        master.scrollTop += fakeScrollValue;
      }

    } else if (target === leftOverlay) {
      tempScrollValue = getScrollTop(target);

      // if scrolling the left overlay - populate the vertical scroll to the master table
      if (this.overlayScrollPositions.left.top !== tempScrollValue) {
        this.verticalScrolling = true;
        this.overlayScrollPositions.left.top = tempScrollValue;
        scrollValueChanged = true;

        master.scrollTop = tempScrollValue;
      }

      // "fake" scroll value calculated from the mousewheel event
      if (fakeScrollValue !== null) {
        scrollValueChanged = true;
        master.scrollLeft += fakeScrollValue;
      }
    }

    if (!this.keyPressed && scrollValueChanged && event.type === 'scroll') {
      if (this.delegatedScrollCallback) {
        this.delegatedScrollCallback = false;
      } else {
        this.refreshAll();
      }

      if (delegatedScroll) {
        this.delegatedScrollCallback = true;
      }
    }
  }

  /**
   * Synchronize overlay scrollbars with the master scrollbar
   */
  syncScrollWithMaster() {
    var master = this.topOverlay.mainTableScrollableElement;

    if (this.topOverlay.needFullRender) {
      this.topOverlay.clone.wtTable.holder.scrollLeft = master.scrollLeft;
    }
    if (this.leftOverlay.needFullRender) {
      this.leftOverlay.clone.wtTable.holder.scrollTop = master.scrollTop;
    }
  }

  /**
   *
   */
  destroy() {
    this.eventManager.destroy();
    this.topOverlay.destroy();

    if (this.bottomOverlay.clone) {
      this.bottomOverlay.destroy();
    }
    this.leftOverlay.destroy();

    if (this.topLeftCornerOverlay) {
      this.topLeftCornerOverlay.destroy();
    }

    if (this.bottomLeftCornerOverlay && this.bottomLeftCornerOverlay.clone) {
      this.bottomLeftCornerOverlay.destroy();
    }

    if (this.debug) {
      this.debug.destroy();
    }
    this.destroyed = true;
  }

  /**
   * @param {Boolean} [fastDraw=false]
   */
  refresh(fastDraw = false) {
    if (this.topOverlay.areElementSizesAdjusted && this.leftOverlay.areElementSizesAdjusted) {
      let container = this.wot.wtTable.wtRootElement.parentNode || this.wot.wtTable.wtRootElement;
      let width = container.clientWidth;
      let height = container.clientHeight;

      if (width !== this.spreaderLastSize.width || height !== this.spreaderLastSize.height) {
        this.spreaderLastSize.width = width;
        this.spreaderLastSize.height = height;
        this.adjustElementsSize();
      }
    }

    if (this.bottomOverlay.clone) {
      this.bottomOverlay.refresh(fastDraw);
    }

    this.leftOverlay.refresh(fastDraw);
    this.topOverlay.refresh(fastDraw);

    if (this.topLeftCornerOverlay) {
      this.topLeftCornerOverlay.refresh(fastDraw);
    }

    if (this.bottomLeftCornerOverlay && this.bottomLeftCornerOverlay.clone) {
      this.bottomLeftCornerOverlay.refresh(fastDraw);
    }

    if (this.debug) {
      this.debug.refresh(fastDraw);
    }
  }

  /**
   * Adjust overlays elements size and master table size
   *
   * @param {Boolean} [force=false]
   */
  adjustElementsSize(force = false) {
    let totalColumns = this.wot.getSetting('totalColumns');
    let totalRows = this.wot.getSetting('totalRows');
    let headerRowSize = this.wot.wtViewport.getRowHeaderWidth();
    let headerColumnSize = this.wot.wtViewport.getColumnHeaderHeight();
    let hiderStyle = this.wot.wtTable.hider.style;

    hiderStyle.width = (headerRowSize + this.leftOverlay.sumCellSizes(0, totalColumns)) + 'px';
    hiderStyle.height = (headerColumnSize + this.topOverlay.sumCellSizes(0, totalRows) + 1) + 'px';

    this.topOverlay.adjustElementsSize(force);
    this.leftOverlay.adjustElementsSize(force);

    if (this.bottomOverlay.clone) {
      this.bottomOverlay.adjustElementsSize(force);
    }
  }

  /**
   *
   */
  applyToDOM() {
    if (!this.topOverlay.areElementSizesAdjusted || !this.leftOverlay.areElementSizesAdjusted) {
      this.adjustElementsSize();
    }
    this.topOverlay.applyToDOM();

    if (this.bottomOverlay.clone) {
      this.bottomOverlay.applyToDOM();
    }

    this.leftOverlay.applyToDOM();
  }
}

export {WalkontableOverlays};

window.WalkontableOverlays = WalkontableOverlays;
<commit_msg>fix full-window scrolling<commit_after>import {
  getScrollableElement,
  getScrollbarWidth,
  getScrollLeft,
  getScrollTop,
} from './../../../helpers/dom/element';
import {isKey} from './../../../helpers/unicode';
import {EventManager} from './../../../eventManager';

/**
 * @class WalkontableOverlays
 */
class WalkontableOverlays {
  /**
   * @param {Walkontable} wotInstance
   */
  constructor(wotInstance) {
    this.wot = wotInstance;

    // legacy support
    this.instance = this.wot;
    this.eventManager = new EventManager(this.wot);

    this.wot.update('scrollbarWidth', getScrollbarWidth());
    this.wot.update('scrollbarHeight', getScrollbarWidth());

    this.mainTableScrollableElement = getScrollableElement(this.wot.wtTable.TABLE);

    this.topOverlay = WalkontableOverlay.createOverlay(WalkontableOverlay.CLONE_TOP, this.wot);

    if (typeof WalkontableBottomOverlay === 'undefined') {
      this.bottomOverlay = {
        needFullRender: false
      };

    } else {
      this.bottomOverlay = WalkontableOverlay.createOverlay(WalkontableOverlay.CLONE_BOTTOM, this.wot);
    }

    this.leftOverlay = WalkontableOverlay.createOverlay(WalkontableOverlay.CLONE_LEFT, this.wot);

    if (this.topOverlay.needFullRender && this.leftOverlay.needFullRender) {
      this.topLeftCornerOverlay = WalkontableOverlay.createOverlay(WalkontableOverlay.CLONE_TOP_LEFT_CORNER, this.wot);
    }

    if (this.bottomOverlay.needFullRender && this.leftOverlay.needFullRender && typeof WalkontableBottomLeftCornerOverlay !== 'undefined') {
      this.bottomLeftCornerOverlay = WalkontableOverlay.createOverlay(WalkontableOverlay.CLONE_BOTTOM_LEFT_CORNER, this.wot);
    } else {
      this.bottomLeftCornerOverlay = {
        needFullRender: false
      };
    }

    if (this.wot.getSetting('debug')) {
      this.debug = WalkontableOverlay.createOverlay(WalkontableOverlay.CLONE_DEBUG, this.wot);
    }

    this.destroyed = false;
    this.keyPressed = false;
    this.spreaderLastSize = {
      width: null,
      height: null,
    };
    this.overlayScrollPositions = {
      master: {
        top: 0,
        left: 0,
      },
      top: {
        top: null,
        left: 0,
      },
      bottom: {
        top: null,
        left: 0

      },
      left: {
        top: 0,
        left: null
      }
    };

    this.verticalScrolling = false;
    this.horizontalScrolling = false;
    this.delegatedScrollCallback = false;

    this.registerListeners();
  }

  /**
   * Refresh and redraw table
   */
  refreshAll() {
    if (!this.wot.drawn) {
      return;
    }
    if (!this.wot.wtTable.holder.parentNode) {
      // Walkontable was detached from DOM, but this handler was not removed
      this.destroy();

      return;
    }
    this.wot.draw(true);

    if (this.verticalScrolling) {
      this.leftOverlay.onScroll();
    }

    if (this.horizontalScrolling) {
      this.topOverlay.onScroll();
    }

    this.verticalScrolling = false;
    this.horizontalScrolling = false;
  }

  /**
   * Register all necessary event listeners
   */
  registerListeners() {
    this.eventManager.addEventListener(document.documentElement, 'keydown', (event) => this.onKeyDown(event));
    this.eventManager.addEventListener(document.documentElement, 'keyup', () => this.onKeyUp());
    this.eventManager.addEventListener(document, 'visibilitychange', () => this.onKeyUp());

    this.eventManager.addEventListener(this.mainTableScrollableElement, 'scroll', (event) => this.onTableScroll(event));

    if (this.topOverlay.needFullRender) {
      this.eventManager.addEventListener(this.topOverlay.clone.wtTable.holder, 'scroll', (event) => this.onTableScroll(event));
      this.eventManager.addEventListener(this.topOverlay.clone.wtTable.holder, 'wheel', (event) => this.onTableScroll(event));
    }

    if (this.bottomOverlay.needFullRender) {
      this.eventManager.addEventListener(this.bottomOverlay.clone.wtTable.holder, 'scroll', (event) => this.onTableScroll(event));
      this.eventManager.addEventListener(this.bottomOverlay.clone.wtTable.holder, 'wheel', (event) => this.onTableScroll(event));
    }

    if (this.leftOverlay.needFullRender) {
      this.eventManager.addEventListener(this.leftOverlay.clone.wtTable.holder, 'scroll', (event) => this.onTableScroll(event));
      this.eventManager.addEventListener(this.leftOverlay.clone.wtTable.holder, 'wheel', (event) => this.onTableScroll(event));
    }

    if (this.topOverlay.trimmingContainer !== window && this.leftOverlay.trimmingContainer !== window) {
      // This is necessary?
      //eventManager.addEventListener(window, 'scroll', (event) => this.refreshAll(event));
      this.eventManager.addEventListener(window, 'wheel', (event) => {
        let overlay;
        let deltaY = event.wheelDeltaY || event.deltaY;
        let deltaX = event.wheelDeltaX || event.deltaX;

        if (this.topOverlay.clone.wtTable.holder.contains(event.realTarget)) {
          overlay = 'top';

        } else if (this.bottomOverlay.clone && this.bottomOverlay.clone.wtTable.holder.contains(event.realTarget)) {
          overlay = 'bottom';

        } else if (this.leftOverlay.clone.wtTable.holder.contains(event.realTarget)) {
          overlay = 'left';
        }

        if (overlay == 'top' && deltaY !== 0) {
          event.preventDefault();
        } else if (overlay == 'left' && deltaX !== 0) {
          event.preventDefault();
        } else if (overlay == 'bottom' && deltaY !== 0) {
          event.preventDefault();
        }
      });
    }
  }

  /**
   * Scroll listener
   *
   * @param {Event} event
   */
  onTableScroll(event) {
    // if mobile browser, do not update scroll positions, as the overlays are hidden during the scroll
    if (Handsontable.mobileBrowser) {
      return;
    }

    // For key press, sync only master -> overlay position because while pressing Walkontable.render is triggered
    // by hot.refreshBorder
    if (this.keyPressed && this.mainTableScrollableElement !== window && !event.target.contains(this.mainTableScrollableElement)) {
      return;
    }
    if (event.type === 'scroll') {
      this.syncScrollPositions(event);

    } else {
      this.translateMouseWheelToScroll(event);
    }
  }

  /**
   * Key down listener
   */
  onKeyDown(event) {
    this.keyPressed = isKey(event.keyCode, 'ARROW_UP|ARROW_RIGHT|ARROW_DOWN|ARROW_LEFT');
  }

  /**
   * Key up listener
   */
  onKeyUp() {
    this.keyPressed = false;
  }

  /**
   * Translate wheel event into scroll event and sync scroll overlays position
   *
   * @param {Event} event
   * @returns {Boolean}
   */
  translateMouseWheelToScroll(event) {
    let topOverlay = this.topOverlay.clone.wtTable.holder;
    let bottomOverlay = this.bottomOverlay.clone ? this.bottomOverlay.clone.wtTable.holder : null;
    let leftOverlay = this.leftOverlay.clone.wtTable.holder;
    let eventMockup = {type: 'wheel'};
    let tempElem = event.target;
    let deltaY = event.wheelDeltaY || (-1) * event.deltaY;
    let deltaX = event.wheelDeltaX || (-1) * event.deltaX;
    let parentHolder;

    while (tempElem != document && tempElem != null) {
      if (tempElem.className.indexOf('wtHolder') > -1) {
        parentHolder = tempElem;
        break;
      }
      tempElem = tempElem.parentNode;
    }
    eventMockup.target = parentHolder;

    if (parentHolder == topOverlay) {
      this.syncScrollPositions(eventMockup, (-0.2) * deltaY);

    } else if (parentHolder == bottomOverlay) {
      this.syncScrollPositions(eventMockup, (-0.2) * deltaY);

    } else if (parentHolder == leftOverlay) {
      this.syncScrollPositions(eventMockup, (-0.2) * deltaX);
    }

    return false;
  }

  /**
   * Synchronize scroll position between master table and overlay table
   *
   * @param {Event|Object} event
   * @param {Number} [fakeScrollValue=null]
   */
  syncScrollPositions(event, fakeScrollValue = null) {
    if (this.destroyed) {
      return;
    }
    if (arguments.length === 0) {
      this.syncScrollWithMaster();

      return;
    }
    let master = this.mainTableScrollableElement;
    let target = event.target;
    let tempScrollValue = 0;
    let scrollValueChanged = false;
    let topOverlay;
    let leftOverlay;
    let bottomOverlay;
    let delegatedScroll = false;

    if (this.topOverlay.needFullRender) {
      topOverlay = this.topOverlay.clone.wtTable.holder;
    }

    if (this.bottomOverlay.needFullRender) {
      bottomOverlay = this.bottomOverlay.clone.wtTable.holder;
    }

    if (this.leftOverlay.needFullRender) {
      leftOverlay = this.leftOverlay.clone.wtTable.holder;
    }

    if (target === document) {
      target = window;
    }

    if (target === master) {
      tempScrollValue = getScrollLeft(target);

      // if scrolling the master table - populate the scroll values to both top and left overlays
      if (this.overlayScrollPositions.master.left !== tempScrollValue) {
        this.horizontalScrolling = true;
        this.overlayScrollPositions.master.left = tempScrollValue;
        scrollValueChanged = true;

        if (topOverlay) {
          topOverlay.scrollLeft = tempScrollValue;
          delegatedScroll = (this.mainTableScrollableElement !== window);
        }

        if (bottomOverlay) {
          bottomOverlay.scrollLeft = tempScrollValue;
          delegatedScroll = (this.mainTableScrollableElement !== window);
        }
      }
      tempScrollValue = getScrollTop(target);

      if (this.overlayScrollPositions.master.top !== tempScrollValue) {
        this.verticalScrolling = true;
        this.overlayScrollPositions.master.top = tempScrollValue;
        scrollValueChanged = true;

        if (leftOverlay) {
          leftOverlay.scrollTop = tempScrollValue;
          delegatedScroll = (this.mainTableScrollableElement !== window);
        }
      }

    } else if (target === bottomOverlay) {
      tempScrollValue = getScrollLeft(target);

      // if scrolling the bottom overlay - populate the horizontal scroll to the master table
      if (this.overlayScrollPositions.bottom.left !== tempScrollValue) {
        this.horizontalScrolling = true;
        this.overlayScrollPositions.bottom.left = tempScrollValue;
        scrollValueChanged = true;

        master.scrollLeft = tempScrollValue;
      }

      // "fake" scroll value calculated from the mousewheel event
      if (fakeScrollValue !== null) {
        scrollValueChanged = true;
        master.scrollTop += fakeScrollValue;
      }

    } else if (target === topOverlay) {
      tempScrollValue = getScrollLeft(target);

      // if scrolling the top overlay - populate the horizontal scroll to the master table
      if (this.overlayScrollPositions.top.left !== tempScrollValue) {
        this.horizontalScrolling = true;
        this.overlayScrollPositions.top.left = tempScrollValue;
        scrollValueChanged = true;

        master.scrollLeft = tempScrollValue;
      }

      // "fake" scroll value calculated from the mousewheel event
      if (fakeScrollValue !== null) {
        scrollValueChanged = true;
        master.scrollTop += fakeScrollValue;
      }

    } else if (target === leftOverlay) {
      tempScrollValue = getScrollTop(target);

      // if scrolling the left overlay - populate the vertical scroll to the master table
      if (this.overlayScrollPositions.left.top !== tempScrollValue) {
        this.verticalScrolling = true;
        this.overlayScrollPositions.left.top = tempScrollValue;
        scrollValueChanged = true;

        master.scrollTop = tempScrollValue;
      }

      // "fake" scroll value calculated from the mousewheel event
      if (fakeScrollValue !== null) {
        scrollValueChanged = true;
        master.scrollLeft += fakeScrollValue;
      }
    }

    if (!this.keyPressed && scrollValueChanged && event.type === 'scroll') {
      if (this.delegatedScrollCallback) {
        this.delegatedScrollCallback = false;
      } else {
        this.refreshAll();
      }

      if (delegatedScroll) {
        this.delegatedScrollCallback = true;
      }
    }
  }

  /**
   * Synchronize overlay scrollbars with the master scrollbar
   */
  syncScrollWithMaster() {
    var master = this.topOverlay.mainTableScrollableElement;

    if (this.topOverlay.needFullRender) {
      this.topOverlay.clone.wtTable.holder.scrollLeft = master.scrollLeft;
    }
    if (this.leftOverlay.needFullRender) {
      this.leftOverlay.clone.wtTable.holder.scrollTop = master.scrollTop;
    }
  }

  /**
   *
   */
  destroy() {
    this.eventManager.destroy();
    this.topOverlay.destroy();

    if (this.bottomOverlay.clone) {
      this.bottomOverlay.destroy();
    }
    this.leftOverlay.destroy();

    if (this.topLeftCornerOverlay) {
      this.topLeftCornerOverlay.destroy();
    }

    if (this.bottomLeftCornerOverlay && this.bottomLeftCornerOverlay.clone) {
      this.bottomLeftCornerOverlay.destroy();
    }

    if (this.debug) {
      this.debug.destroy();
    }
    this.destroyed = true;
  }

  /**
   * @param {Boolean} [fastDraw=false]
   */
  refresh(fastDraw = false) {
    if (this.topOverlay.areElementSizesAdjusted && this.leftOverlay.areElementSizesAdjusted) {
      let container = this.wot.wtTable.wtRootElement.parentNode || this.wot.wtTable.wtRootElement;
      let width = container.clientWidth;
      let height = container.clientHeight;

      if (width !== this.spreaderLastSize.width || height !== this.spreaderLastSize.height) {
        this.spreaderLastSize.width = width;
        this.spreaderLastSize.height = height;
        this.adjustElementsSize();
      }
    }

    if (this.bottomOverlay.clone) {
      this.bottomOverlay.refresh(fastDraw);
    }

    this.leftOverlay.refresh(fastDraw);
    this.topOverlay.refresh(fastDraw);

    if (this.topLeftCornerOverlay) {
      this.topLeftCornerOverlay.refresh(fastDraw);
    }

    if (this.bottomLeftCornerOverlay && this.bottomLeftCornerOverlay.clone) {
      this.bottomLeftCornerOverlay.refresh(fastDraw);
    }

    if (this.debug) {
      this.debug.refresh(fastDraw);
    }
  }

  /**
   * Adjust overlays elements size and master table size
   *
   * @param {Boolean} [force=false]
   */
  adjustElementsSize(force = false) {
    let totalColumns = this.wot.getSetting('totalColumns');
    let totalRows = this.wot.getSetting('totalRows');
    let headerRowSize = this.wot.wtViewport.getRowHeaderWidth();
    let headerColumnSize = this.wot.wtViewport.getColumnHeaderHeight();
    let hiderStyle = this.wot.wtTable.hider.style;

    hiderStyle.width = (headerRowSize + this.leftOverlay.sumCellSizes(0, totalColumns)) + 'px';
    hiderStyle.height = (headerColumnSize + this.topOverlay.sumCellSizes(0, totalRows) + 1) + 'px';

    this.topOverlay.adjustElementsSize(force);
    this.leftOverlay.adjustElementsSize(force);

    if (this.bottomOverlay.clone) {
      this.bottomOverlay.adjustElementsSize(force);
    }
  }

  /**
   *
   */
  applyToDOM() {
    if (!this.topOverlay.areElementSizesAdjusted || !this.leftOverlay.areElementSizesAdjusted) {
      this.adjustElementsSize();
    }
    this.topOverlay.applyToDOM();

    if (this.bottomOverlay.clone) {
      this.bottomOverlay.applyToDOM();
    }

    this.leftOverlay.applyToDOM();
  }
}

export {WalkontableOverlays};

window.WalkontableOverlays = WalkontableOverlays;
<|endoftext|>


<commit_before>
/** = Description
  * The extended object model.
  *
  * HClass is the foundation class of all other classes. It implements
  * a proper object oriented environment in Javascript. It supports
  * inheritance (HClass.extend), superclass invocation ( this.base ),
  * interfaces (HClass.implement) and such.
  *
  * It's derived from and backwards compatible with  Dean Edward's Base.js,
  * so you can mix and match code written against Base.js.
  *
  * It is intended for the following main purposes:
  *   * To easily create classes without the MyClass.prototype cruft
  *   * Method overriding with intuitive access to the overridden method (like Ruby's super)
  *   * To avoid calling a class' constructor function during the prototyping phase
  *   * To easily add static (class) properties and methods
  *   * To achieve the above without resorting to global functions to build prototype chains
  *   * To achieve the above without affecting Object.prototype
  *
  * The HClass class extends the 'Object' object by adding one instance method (base)
  * and two class methods (extend, implement). Instance method extend can be also called directly.
  *
  * == Example:
  *    MyClass = HClass.extend({
  *      constructor: function( foo ){
  *        this.setFoo( foo );
  *      },
  *      setFoo: function( foo ){
  *        this.foo = foo;
  *      }
  *    });
  *
  *    myClassInstance1 = MyClass.nu( 'rabbids' );
  *    myClassInstance2 = new MyClass( 'ribbit' );
  *
  *    MyEqualsClass = MyClass.extend({
  *      testFoo: function( that ){
  *        return this.foo === that.foo;
  *      }
  *    });
  *    myEqualsClassInstance1 = MyEqualsClass.nu( 'woof' );
  *    equals1 = myEqualsClassInstance1.testFoo( myClassIntance1 );
  *    myClassInstance2.setFoo( myEqualsClassInstance1.foo );
  *    equals2 = myEqualsClassInstance1.testFoo( myClassIntance2 );
  *
  **/
var//RSence.Core
HClass = function() {
  if ( arguments.length ) {
    if (this === window) {
      HClass.prototype.extend.call( arguments[0], arguments.callee.prototype );
    }
    else {
      this.extend( arguments[0] );
    }
  }
};

HClass.prototype = {

  // detection for production build (compressed source; '_compressed' would be something like '_x4', whereas '_'+'compressed' is still '_compressed')
  isProduction: '_'+'compressed' !== '_compressed',

  _exceptionProperties: function(e){
    var err = {}, prop;
    for (prop in e) {
      err[prop] = e[prop];
    }
    err['string'] = e.toString();
    return err;
  },

 /* The property copying method. */
  extend: function(_source, _value) {
    var _extend = HClass.prototype.extend,
        _ancestor, _method, _returnValue, i, _name, _prototype, _protected;
    if (arguments.length === 2) {
      _ancestor = this[_source];
      // only methods are inherited
      if ((_ancestor instanceof Function) && (_value instanceof Function) &&
          _ancestor.valueOf() !== _value.valueOf() && (/\bbase\b/).test(_value)) {
        _method = _value;
        _value = function() {
          // saves the this.base that is the this.base method of this child
          var _previous = this.base;
          // copies previous this.base from the direction from HClass
          this.base = _ancestor;

          // The current class's method is called with an exception handler to
          // ignore the common mistake of calling super, while being in the class
          // declaring the method; has no super.
          try{
            // now inside the function when called this.base points to parent method
            _returnValue = _method.apply(this, arguments);
          }
          catch(e){
            !this.isProduction && console.warn("An exception occurred while calling base: ",HClass.prototype._exceptionProperties(e)," object: ",_ancestor);
            _returnValue = null;
          }
          // then because event this function can be called from child method
          // resets the base to as is was before calling this function
          this.base = _previous;
          return _returnValue;
        };
        _value.valueOf = function() {
          return _method;
        };
        _value.toString = function() {
          return String(_method);
        };
      }
      return this[_source] = _value;
    // this is called when called by HClass.extend
    }
    else if (_source) {
      _prototype = {toSource: null};
      _protected = ["toString", "valueOf"];
      // we want default constructor function
      if (HClass._prototyping) {
        // 3. index
        _protected.push("constructor");
      }
      for (i = 0; (_name = _protected[i]); i++) {
        if (_source[_name] !== _prototype[_name]) {
          _extend.call(this, _name, _source[_name]);
        }
      }
      for (_name in _source) {
        if (!_prototype[_name]) {
          _extend.call(this, _name, _source[_name]);
        }
      }
    }

    // alternative constructor (use instead of the new keywoard)
    this.nu = function() {
      return new (
        this.extend( {
          constructor: function( args ){
            this.base.apply( this, args );
          }
        } )
      )( arguments );
    };
    this.hasAncestor = function( _obj ){
      return !!~this.ancestors.indexOf( _obj );
    };
    this['new'] = this.nu;
    return this;
  },
  /** = Description
    * If a method has been overridden then the base method provides access to the overridden method.
    * Call this method from any other method to invoke that method's ancestor.
    * It is also possible to call the base method from within a constructor function.
    *
    **/
  base: function() {
    // this method can be called from any other method to invoke that method's parent
  }
};

/** = Description
  * The class method method to create a new extended class.
  *
  * If the method name constructor is defined null as the +_instance+ parameter,
  * it will return a single instance.
  *
  * = Parameters:
  * +_instance+:: An object that has properties and methods of inherited class.
  * +_static+::   An object that has properties and methods of inherited class's
  *               class methods if the method named constructor is defined null
  *               in _instance parameter.
  *
  * = Returns:
  *   Returns the extended class object.
  *
  * = Usage:
  *   Point = HClass.extend({
  *   constructor: function(x, y) {
  *     this.x = x;
  *     this.y = y;
  *   }
  *   });
  *   Rectangle = Point.extend({
  *   constructor: function(x, y, width, height) {
  *     this.base(x, y);
  *     this.width = width;
  *     this.height = height;
  *   },
  *   getWidth: function() {
  *     return this.width;
  *   },
  *   getHeight: function() {
  *     return this.height;
  *   }
  *   },
  *   {
  *   // class methods
  *   description: "this is a Rectangle",
  *   getClass: function() {
  *     return this;
  *   }
  *   });
  *
  **/
HClass.extend = function(_instance, _static) {
  // reference to HClass's prototype extend method (HClass's class structure extend method)
  var _extend = HClass.prototype.extend,
      _prototype, _constructor, _klass, _object,
      _ancestors, i=0;
  // if _instance is undefined,null,"" etc. creates object so that code below works
  if (!_instance) {
    _instance = {};
  }
  HClass._prototyping = true;
  // this is base for single instance or prototype (class structure) for object that are created
  // from this class
  _prototype = new this();
  // copies properties and methods from _instance to _prototype (class structure)
  _extend.call(_prototype, _instance);
  // this constructor came from _instance
  _constructor = _prototype.constructor;
  _prototype.constructor = this;
  delete HClass._prototyping;

  _ancestors = [];
  _klass = function() {
    if (!HClass._prototyping) {
      _constructor.apply(this, arguments);
    }
    this.constructor = _klass;
    this.ancestors = _ancestors;
  };
  // this is the new class's prototype (class structure)
  _klass.prototype = _prototype;
  // copies static method (class method)
  // acts like HClass.extend
  _klass.extend = this.extend;
  // copies static method (class method)
  // acts like HClass.implement
  _klass.implement = this.implement;
  _klass.toString = function() {
    return String(_constructor);
  };
  // copies properties and methods from _static directly to statc methods (class methods)
  // of new class
  _extend.call(_klass, _static);
  // if _constructor is marked as null returns the created instance (that is also class structure for
  // instances if class is returned
  _object = (_constructor !== null) ? _klass : _prototype;
  if (_object.init instanceof Function) {
    _object.init();
  }
  _ancestors.push( _object );
  for( ; i < this.ancestors.length; i++ ){
    _ancestors.push( this.ancestors[i] );
  }
  _object.ancestors = _ancestors;
  return _object;
};
HClass.ancestors = [ HClass ];

/** = Description
  * Copies the prototype properties and methods from _interface or if it is an object it's properties and functions
  * to HClass or class inherited from HClass. Mimics the interface behaviour of ordinary programming languages.
  *
  * = Usage:
  * Defines an interface:
  *
  *  AreaInterface = HClass.extend({
  *  constructor: null,
  *    // implement
  *    // don't define here
  *    //getWidth: function() {},
  *    //getHeight: function() {},
  *  area: function() {
  *    return this.getWidth() * this.getHeight();
  *  }
  *  });
  *
  *  Rectangle = HClass.extend({
  *  constructor: function(x, y, width, height) {
  *    this.x = x;
  *    this.y = y;
  *    this.width = width;
  *    this.height = height;
  *  },
  *  getWidth: function() {
  *    return this.width;
  *  },
  *  getHeight: function() {
  *    return this.height;
  *  }
  *  });
  *
  *  Rectangle.implement(AreaInterface);
  *
  **/
HClass.implement = function(_interface) {
  // copies prototype fields and methods (class structures properties and methods)
  if (_interface instanceof Function) {
    _interface = _interface.prototype;
  }
  this.prototype.extend(_interface);
};

//var Base = HClass;

// Array fix
if ([]['indexOf']===undefined) {
  // Object.extend = function(_destination, _source) {
  //   for (var _property in _source) {
  //     _destination[_property] = _source[_property];
  //   }
  //   return _destination;
  // };
  // Object.extend(Array.prototype, {
  Array.prototype.indexOf = function(_anObject){
    var i = 0, l = this.length;
    for (; i < l; i++) {
      if (this[i] === _anObject) {
        return i;
      }
    }
    return -1;
  };
}

// String fix
if(typeof String.prototype.trim !== 'function') {
  String.prototype.trim = function() {
    return this.replace(/^\s+|\s+$/g, '');
  };
}

// ff version 3.0.3 fails on this, when firebug installed: try/catch block
try {

// console.log surrogate for browsers without a console
if(window['console']===undefined){
  window.console = {
    log: function(){},
    warn: function(){}
  };
}

} catch(e) {}
<commit_msg>Added workaround for browsers that don't support bind<commit_after>
/** = Description
  * The extended object model.
  *
  * HClass is the foundation class of all other classes. It implements
  * a proper object oriented environment in Javascript. It supports
  * inheritance (HClass.extend), superclass invocation ( this.base ),
  * interfaces (HClass.implement) and such.
  *
  * It's derived from and backwards compatible with  Dean Edward's Base.js,
  * so you can mix and match code written against Base.js.
  *
  * It is intended for the following main purposes:
  *   * To easily create classes without the MyClass.prototype cruft
  *   * Method overriding with intuitive access to the overridden method (like Ruby's super)
  *   * To avoid calling a class' constructor function during the prototyping phase
  *   * To easily add static (class) properties and methods
  *   * To achieve the above without resorting to global functions to build prototype chains
  *   * To achieve the above without affecting Object.prototype
  *
  * The HClass class extends the 'Object' object by adding one instance method (base)
  * and two class methods (extend, implement). Instance method extend can be also called directly.
  *
  * == Example:
  *    MyClass = HClass.extend({
  *      constructor: function( foo ){
  *        this.setFoo( foo );
  *      },
  *      setFoo: function( foo ){
  *        this.foo = foo;
  *      }
  *    });
  *
  *    myClassInstance1 = MyClass.nu( 'rabbids' );
  *    myClassInstance2 = new MyClass( 'ribbit' );
  *
  *    MyEqualsClass = MyClass.extend({
  *      testFoo: function( that ){
  *        return this.foo === that.foo;
  *      }
  *    });
  *    myEqualsClassInstance1 = MyEqualsClass.nu( 'woof' );
  *    equals1 = myEqualsClassInstance1.testFoo( myClassIntance1 );
  *    myClassInstance2.setFoo( myEqualsClassInstance1.foo );
  *    equals2 = myEqualsClassInstance1.testFoo( myClassIntance2 );
  *
  **/
var//RSence.Core
HClass = function() {
  if ( arguments.length ) {
    if (this === window) {
      HClass.prototype.extend.call( arguments[0], arguments.callee.prototype );
    }
    else {
      this.extend( arguments[0] );
    }
  }
};

HClass.prototype = {

  // detection for production build (compressed source; '_compressed' would be something like '_x4', whereas '_'+'compressed' is still '_compressed')
  isProduction: '_'+'compressed' !== '_compressed',

  _exceptionProperties: function(e){
    var err = {}, prop;
    for (prop in e) {
      err[prop] = e[prop];
    }
    err['string'] = e.toString();
    return err;
  },

 /* The property copying method. */
  extend: function(_source, _value) {
    var _extend = HClass.prototype.extend,
        _ancestor, _method, _returnValue, i, _name, _prototype, _protected;
    if (arguments.length === 2) {
      _ancestor = this[_source];
      // only methods are inherited
      if ((_ancestor instanceof Function) && (_value instanceof Function) &&
          _ancestor.valueOf() !== _value.valueOf() && (/\bbase\b/).test(_value)) {
        _method = _value;
        _value = function() {
          // saves the this.base that is the this.base method of this child
          var _previous = this.base;
          // copies previous this.base from the direction from HClass
          this.base = _ancestor;

          // The current class's method is called with an exception handler to
          // ignore the common mistake of calling super, while being in the class
          // declaring the method; has no super.
          try{
            // now inside the function when called this.base points to parent method
            _returnValue = _method.apply(this, arguments);
          }
          catch(e){
            !this.isProduction && console.warn("An exception occurred while calling base: ",HClass.prototype._exceptionProperties(e)," object: ",_ancestor);
            _returnValue = null;
          }
          // then because event this function can be called from child method
          // resets the base to as is was before calling this function
          this.base = _previous;
          return _returnValue;
        };
        _value.valueOf = function() {
          return _method;
        };
        _value.toString = function() {
          return String(_method);
        };
      }
      return this[_source] = _value;
    // this is called when called by HClass.extend
    }
    else if (_source) {
      _prototype = {toSource: null};
      _protected = ["toString", "valueOf"];
      // we want default constructor function
      if (HClass._prototyping) {
        // 3. index
        _protected.push("constructor");
      }
      for (i = 0; (_name = _protected[i]); i++) {
        if (_source[_name] !== _prototype[_name]) {
          _extend.call(this, _name, _source[_name]);
        }
      }
      for (_name in _source) {
        if (!_prototype[_name]) {
          _extend.call(this, _name, _source[_name]);
        }
      }
    }

    // alternative constructor (use instead of the new keywoard)
    this.nu = function() {
      return new (
        this.extend( {
          constructor: function( args ){
            this.base.apply( this, args );
          }
        } )
      )( arguments );
    };
    this.hasAncestor = function( _obj ){
      return !!~this.ancestors.indexOf( _obj );
    };
    this['new'] = this.nu;
    return this;
  },
  /** = Description
    * If a method has been overridden then the base method provides access to the overridden method.
    * Call this method from any other method to invoke that method's ancestor.
    * It is also possible to call the base method from within a constructor function.
    *
    **/
  base: function() {
    // this method can be called from any other method to invoke that method's parent
  }
};

/** = Description
  * The class method method to create a new extended class.
  *
  * If the method name constructor is defined null as the +_instance+ parameter,
  * it will return a single instance.
  *
  * = Parameters:
  * +_instance+:: An object that has properties and methods of inherited class.
  * +_static+::   An object that has properties and methods of inherited class's
  *               class methods if the method named constructor is defined null
  *               in _instance parameter.
  *
  * = Returns:
  *   Returns the extended class object.
  *
  * = Usage:
  *   Point = HClass.extend({
  *   constructor: function(x, y) {
  *     this.x = x;
  *     this.y = y;
  *   }
  *   });
  *   Rectangle = Point.extend({
  *   constructor: function(x, y, width, height) {
  *     this.base(x, y);
  *     this.width = width;
  *     this.height = height;
  *   },
  *   getWidth: function() {
  *     return this.width;
  *   },
  *   getHeight: function() {
  *     return this.height;
  *   }
  *   },
  *   {
  *   // class methods
  *   description: "this is a Rectangle",
  *   getClass: function() {
  *     return this;
  *   }
  *   });
  *
  **/
HClass.extend = function(_instance, _static) {
  // reference to HClass's prototype extend method (HClass's class structure extend method)
  var _extend = HClass.prototype.extend,
      _prototype, _constructor, _klass, _object,
      _ancestors, i=0;
  // if _instance is undefined,null,"" etc. creates object so that code below works
  if (!_instance) {
    _instance = {};
  }
  HClass._prototyping = true;
  // this is base for single instance or prototype (class structure) for object that are created
  // from this class
  _prototype = new this();
  // copies properties and methods from _instance to _prototype (class structure)
  _extend.call(_prototype, _instance);
  // this constructor came from _instance
  _constructor = _prototype.constructor;
  _prototype.constructor = this;
  delete HClass._prototyping;

  _ancestors = [];
  _klass = function() {
    if (!HClass._prototyping) {
      _constructor.apply(this, arguments);
    }
    this.constructor = _klass;
    this.ancestors = _ancestors;
  };
  // this is the new class's prototype (class structure)
  _klass.prototype = _prototype;
  // copies static method (class method)
  // acts like HClass.extend
  _klass.extend = this.extend;
  // copies static method (class method)
  // acts like HClass.implement
  _klass.implement = this.implement;
  _klass.toString = function() {
    return String(_constructor);
  };
  // copies properties and methods from _static directly to statc methods (class methods)
  // of new class
  _extend.call(_klass, _static);
  // if _constructor is marked as null returns the created instance (that is also class structure for
  // instances if class is returned
  _object = (_constructor !== null) ? _klass : _prototype;
  if (_object.init instanceof Function) {
    _object.init();
  }
  _ancestors.push( _object );
  for( ; i < this.ancestors.length; i++ ){
    _ancestors.push( this.ancestors[i] );
  }
  _object.ancestors = _ancestors;
  return _object;
};
HClass.ancestors = [ HClass ];

/** = Description
  * Copies the prototype properties and methods from _interface or if it is an object it's properties and functions
  * to HClass or class inherited from HClass. Mimics the interface behaviour of ordinary programming languages.
  *
  * = Usage:
  * Defines an interface:
  *
  *  AreaInterface = HClass.extend({
  *  constructor: null,
  *    // implement
  *    // don't define here
  *    //getWidth: function() {},
  *    //getHeight: function() {},
  *  area: function() {
  *    return this.getWidth() * this.getHeight();
  *  }
  *  });
  *
  *  Rectangle = HClass.extend({
  *  constructor: function(x, y, width, height) {
  *    this.x = x;
  *    this.y = y;
  *    this.width = width;
  *    this.height = height;
  *  },
  *  getWidth: function() {
  *    return this.width;
  *  },
  *  getHeight: function() {
  *    return this.height;
  *  }
  *  });
  *
  *  Rectangle.implement(AreaInterface);
  *
  **/
HClass.implement = function(_interface) {
  // copies prototype fields and methods (class structures properties and methods)
  if (_interface instanceof Function) {
    _interface = _interface.prototype;
  }
  this.prototype.extend(_interface);
};

//var Base = HClass;

// Array fix
if (![].indexOf) {
  // Object.extend = function(_destination, _source) {
  //   for (var _property in _source) {
  //     _destination[_property] = _source[_property];
  //   }
  //   return _destination;
  // };
  // Object.extend(Array.prototype, {
  Array.prototype.indexOf = function(_anObject){
    var i = 0, l = this.length;
    for (; i < l; i++) {
      if (this[i] === _anObject) {
        return i;
      }
    }
    return -1;
  };
}

// String fix
if(typeof String.prototype.trim !== 'function') {
  String.prototype.trim = function() {
    return this.replace(/^\s+|\s+$/g, '');
  };
}

// ff version 3.0.3 fails on this, when firebug installed: try/catch block
try {
  // console.log surrogate for browsers without a console
  if(!window.console){
    window.console = {
      log: function(){},
      warn: function(){}
    };
  }
} catch(e) {}

// Mozilla reference bind workaround (bind was introduced in JS 1.8.5)
if (!Function.prototype.bind) {
  Function.prototype.bind = function (oThis) {
    if (typeof this !== "function") {
      // closest thing possible to the ECMAScript 5 internal IsCallable function
      throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
    }
    var aArgs = Array.prototype.slice.call(arguments, 1),
        fToBind = this,
        fNOP = function () {},
        fBound = function () {
          return fToBind.apply(
            this instanceof fNOP && oThis ? this : oThis,
            aArgs.concat(Array.prototype.slice.call(arguments))
          );
        };

    fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();
    return fBound;
  };
}
<|endoftext|>


<commit_before>const Eknc = imports.gi.EosKnowledgeContent;
const Gtk = imports.gi.Gtk;
const InstanceOfMatcher = imports.tests.InstanceOfMatcher;

const CssClassMatcher = imports.tests.CssClassMatcher;
const Utils = imports.tests.utils;
Utils.register_gresource();

const Media = imports.app.modules.card.media;

Gtk.init(null);

describe ('Card.Media', function () {
    let imageObject;

    beforeEach(function () {
        jasmine.addMatchers(CssClassMatcher.customMatchers);
        jasmine.addMatchers(InstanceOfMatcher.customMatchers);

        imageObject = Eknc.ImageObjectModel.new_from_props({
            caption: 'foo',
            license: 'bar',
            copyright_holder: 'baz',
            content_type: 'image/jpeg',
        });
        imageObject.get_content_stream = () => null;
    });

    it ('hides separator when caption or attribution not visible', function () {
        let noCaption = Eknc.ImageObjectModel.new_from_props({
            license: 'bar',
            copyright_holder: 'baz',
            content_type: 'image/jpeg',
        });
        noCaption.get_content_stream = () => null;

        let noAttribution = Eknc.ImageObjectModel.new_from_props({
            caption: 'foo',
            content_type: 'image/jpeg',
        });
        noAttribution.get_content_stream = () => null;

        let media_card = new Media.Media({
            model: imageObject,
        });
        media_card.get_content_stream = () => null;
        expect(media_card._separator.visible).toBe(true);
        media_card = new Media.Media({
            model: noCaption,
        });
        media_card.get_content_stream = () => null;
        expect(media_card._separator.visible).toBe(false);
        media_card = new Media.Media({
            model: noAttribution,
        });
        media_card.get_content_stream = () => null;
        expect(media_card._separator.visible).toBe(false);
    });

    it('has labels that understand Pango markup', function () {
        let model = Eknc.ImageObjectModel.new_from_props({
            copyright_holder: '!!!',
            caption: '@@@',
        });
        model.get_content_stream = () => null;
        let card = new Media.Media({
            model: model,
        });
        expect(Gtk.test_find_label(card, '*!!!*').use_markup).toBeTruthy();
        expect(Gtk.test_find_label(card, '*@@@*').use_markup).toBeTruthy();
    });

    describe ('Attribution button', function () {
        let card;
        let copyright_holder = '!!!';
        let attribution_button;

        it ('displays license file when license is known', function () {
            let model = Eknc.ImageObjectModel.new_from_props({
                license: 'CC BY 4.0',
                copyright_holder: copyright_holder,
            });
            model.get_content_stream = () => null;

            card = new Media.Media({
                model: model,
            });

            attribution_button = Gtk.test_find_label(card, '*!!!*').get_parent();
            expect(attribution_button.sensitive).toBe(true);
        });

        it ('does not display license file when license is unknown', function () {
            let model = Eknc.ImageObjectModel.new_from_props({
                license: 'foobar',
                copyright_holder: copyright_holder,
            });
            model.get_content_stream = () => null;

            card = new Media.Media({
                model: model,
            });

            attribution_button = Gtk.test_find_label(card, '*!!!*').get_parent();
            expect(attribution_button.sensitive).toBe(false);
        });
    });
});
<commit_msg>tests: Remove obsolete test of Card.Media separator<commit_after>const Eknc = imports.gi.EosKnowledgeContent;
const Gtk = imports.gi.Gtk;
const InstanceOfMatcher = imports.tests.InstanceOfMatcher;

const CssClassMatcher = imports.tests.CssClassMatcher;
const Utils = imports.tests.utils;
Utils.register_gresource();

const Media = imports.app.modules.card.media;

Gtk.init(null);

describe ('Card.Media', function () {
    let imageObject;

    beforeEach(function () {
        jasmine.addMatchers(CssClassMatcher.customMatchers);
        jasmine.addMatchers(InstanceOfMatcher.customMatchers);

        imageObject = Eknc.ImageObjectModel.new_from_props({
            caption: 'foo',
            license: 'bar',
            copyright_holder: 'baz',
            content_type: 'image/jpeg',
        });
        imageObject.get_content_stream = () => null;
    });

    it('has labels that understand Pango markup', function () {
        let model = Eknc.ImageObjectModel.new_from_props({
            copyright_holder: '!!!',
            caption: '@@@',
        });
        model.get_content_stream = () => null;
        let card = new Media.Media({
            model: model,
        });
        expect(Gtk.test_find_label(card, '*!!!*').use_markup).toBeTruthy();
        expect(Gtk.test_find_label(card, '*@@@*').use_markup).toBeTruthy();
    });

    describe ('Attribution button', function () {
        let card;
        let copyright_holder = '!!!';
        let attribution_button;

        it ('displays license file when license is known', function () {
            let model = Eknc.ImageObjectModel.new_from_props({
                license: 'CC BY 4.0',
                copyright_holder: copyright_holder,
            });
            model.get_content_stream = () => null;

            card = new Media.Media({
                model: model,
            });

            attribution_button = Gtk.test_find_label(card, '*!!!*').get_parent();
            expect(attribution_button.sensitive).toBe(true);
        });

        it ('does not display license file when license is unknown', function () {
            let model = Eknc.ImageObjectModel.new_from_props({
                license: 'foobar',
                copyright_holder: copyright_holder,
            });
            model.get_content_stream = () => null;

            card = new Media.Media({
                model: model,
            });

            attribution_button = Gtk.test_find_label(card, '*!!!*').get_parent();
            expect(attribution_button.sensitive).toBe(false);
        });
    });
});
<|endoftext|>


<commit_before>
module.exports = function(gulp, config, watch) {

	return function(done) {
		var c = require('better-console')
		c.info('Watching sources for change and recompilation...')

		var minimatch = require('minimatch')
		var path = require('path')
		var runcmd = require('../helpers/runcmd')
		var glob = '**/*.{css,styl,scss,less,js,jsx,es6,es,coffee,jade,json,html,htm}'
		var globDist = config.dist_folder + '/**/*.*'

		var mapping = {
			styles: ['css', 'less', 'scss', 'styl'],
			scripts: ['js', 'jsx', 'es6', 'es', 'coffee'],
			templates: ['jade', 'json', 'html', 'htm']
		};

		if(config.mapping) {
			Object.keys(config.mapping).forEach(function(key) {
				mapping[key] = config.mapping[key]
			})
		}

		Object.keys(mapping).forEach(function(key) {
			mapping[key].map(function(i) {
				return '.' + i.replace(/^\./, '') // remove . from the beginning of the string
			})
		})

		var completeHandler = function(e) {
			if(config.hooks && config.hooks.watch) runcmd(config.hooks.watch)
		}

		var changeHandler = function(filepath) {
			// Filter dist sources
			if (minimatch(filepath, globDist)) {
				return
			}

			config._lastChanged = filepath

			var handler = false

			Object.keys(mapping).forEach(function (key) {

				mapping[key].forEach(function(ext) {
					if(filepath.match(new RegExp(ext + '$'))) {
						handler = key
					}
				})
			})

			if (!handler) {
				c.warn('! unknown extension', path.extname(filepath), filepath)
				return
			}

			gulp.start(handler, completeHandler)
		}

		watch(glob, changeHandler)

		done()
	}
}
<commit_msg>optimization of custom file extension mapping<commit_after>var mapping = {
	styles: ['css', 'less', 'scss', 'styl'],
	scripts: ['js', 'jsx', 'es6', 'es', 'coffee'],
	templates: ['jade', 'json', 'html', 'htm']
}

module.exports = function(gulp, config, watch) {

	if(config.mapping) {
		Object.keys(config.mapping).forEach(function(key) {
			mapping[key] = config.mapping[key]
		})
	}

	Object.keys(mapping).forEach(function(key) {
		mapping[key].map(function(i) {
			var ext = '.' + i.replace(/^\./, '') // remove . from the beginning of the string
			return new RegExp(ext + '$')
		})
	})

	return function(done) {
		var c = require('better-console')
		c.info('Watching sources for change and recompilation...')

		var minimatch = require('minimatch')
		var path = require('path')
		var runcmd = require('../helpers/runcmd')
		var glob = '**/*.{css,styl,scss,less,js,jsx,es6,es,coffee,jade,json,html,htm}'
		var globDist = config.dist_folder + '/**/*.*'

		var completeHandler = function(e) {
			if(config.hooks && config.hooks.watch) runcmd(config.hooks.watch)
		}

		var changeHandler = function(filepath) {
			// Filter dist sources
			if (minimatch(filepath, globDist)) {
				return
			}

			config._lastChanged = filepath

			var handler = false

			Object.keys(mapping).forEach(function (key) {

				mapping[key].forEach(function(regexp) {
					if(filepath.match(regexp)) {
						handler = key
					}
				})
			})

			if (!handler) {
				c.warn('! unknown extension', path.extname(filepath), filepath)
				return
			}

			gulp.start(handler, completeHandler)
		}

		watch(glob, changeHandler)

		done()
	}
}
<|endoftext|>


<commit_before>// View model for the courses page
function pageViewModel(gvm) {
    // Page specific i18n bindings
    gvm.title = ko.computed(function(){i18n.setLocale(gvm.lang()); return gvm.app() + ' - ' + i18n.__("CoursesTitle");}, gvm);
    gvm.pageHeader = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("CoursesHeader");}, gvm);
    gvm.projectname = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("ProjectName");}, gvm);

    gvm.availableLocations = ko.observableArray([]);
    gvm.availableTrainings = ko.observableArray([]);
    gvm.availableCourses = ko.observableArray([]);

    gvm.addAvailableLocations = function(id, name) {
        var selectObject = {id: id, locationName: name};
        gvm.availableLocations.push(selectObject);
    }
    gvm.addAvailableTrainings = function(id, name) {
        var selectObject = {id: id, trainingName: name};
        gvm.availableTrainings.push(selectObject);
    }

    gvm.addAvailableCourses = function(id, name) {
        var selectObject = {id: id, courseName: name};
        gvm.availableCourses.push(selectObject);
    }

    gvm.clearAll = function() {
        gvm.availableLocations.removeAll();
        gvm.availableTrainings.removeAll();
        gvm.availableCourses.removeAll();
    }
}

function loadAllSelects($locationid, $trainingid)
{
    $.getJSON('/api/courses/' + $locationid + '/' +  $trainingid, function(data) {
        $.each(data[1],function(i, item) {
            viewModel.addAvailableLocations(item.id, item.name);
        });
        $.each(data[2], function(i, item) {
            viewModel.addAvailableTrainings(item.id, item.name);
        });
        $.each(data[3], function(i, item) {
            viewModel.addAvailableCourses(item.id, item.name);
        });
    });
    bindEvents();
}

function bindEvents() {
    $("#location").on("change", function() {
        loadAllSelects($("#location").val(), $("#training").val());
    });
}

function initPage() {
    loadAllSelects(1, 1);
}<commit_msg>courses<commit_after>// View model for the courses page
function pageViewModel(gvm) {
    // Page specific i18n bindings
    gvm.title = ko.computed(function(){i18n.setLocale(gvm.lang()); return gvm.app() + ' - ' + i18n.__("CoursesTitle");}, gvm);
    gvm.pageHeader = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("CoursesHeader");}, gvm);
    gvm.projectname = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("ProjectName");}, gvm);

    gvm.availableLocations = ko.observableArray([]);
    gvm.availableTrainings = ko.observableArray([]);
    gvm.availableCourses = ko.observableArray([]);

    gvm.addAvailableLocations = function(id, name) {
        var selectObject = {id: id, locationName: name};
        gvm.availableLocations.push(selectObject);
    }
    gvm.addAvailableTrainings = function(id, name) {
        var selectObject = {id: id, trainingName: name};
        gvm.availableTrainings.push(selectObject);
    }

    gvm.addAvailableCourses = function(id, name) {
        var selectObject = {id: id, courseName: name};
        gvm.availableCourses.push(selectObject);
    }

    gvm.clearAll = function() {
        gvm.availableLocations.removeAll();
        gvm.availableTrainings.removeAll();
        gvm.availableCourses.removeAll();
    }
}

function loadAllSelects($locationid, $trainingid)
{

    $.getJSON('/api/courses/' + $locationid + '/' +  $trainingid, function(data) {
        $.each(data[1],function(i, item) {
            viewModel.addAvailableLocations(item.id, item.name);
        });
        $.each(data[2], function(i, item) {
            viewModel.addAvailableTrainings(item.id, item.name);
        });
        $.each(data[3], function(i, item) {
            viewModel.addAvailableCourses(item.id, item.name);
        });
    });
    bindEvents();
}

function bindEvents() {
    viewModel.clearAll();
    $("#location").on("change", function() {
        loadAllSelects($("#location").val(), $("#training").val());
    });
}

function initPage() {
    loadAllSelects(1, 1);
}<|endoftext|>


<commit_before>dojo.provide("dijit.util.wai");

dijit.util.waiNames  = ["waiRole", "waiState"];

dijit.util.wai = {
	// summary: Contains functions to set accessibility roles and states
	//		onto widget elements
	waiRole: {
				// name: String:
				//		information for mapping accessibility role
				name: "waiRole",
				// namespace: String:
				//		URI of the namespace for the set of roles
				"namespace": "http://www.w3.org/TR/xhtml2",
				// alias: String:
				//		The alias to assign the namespace
				alias: "x2",
				// prefix: String:
				//		The prefix to assign to the role value
				prefix: "wairole:"
	},
	waiState: {
				// name: String:
				//		information for mapping accessibility state
				name: "waiState",
				// namespace: String:
				//		URI of the namespace for the set of states
				"namespace": "http://www.w3.org/2005/07/aaa",
				// alias: String:
				//		The alias to assign the namespace
				alias: "aaa",
				// prefix: String:
				//		empty string - state value does not require prefix
				prefix: ""
	},
	setAttr: function(/*DomNode*/node, /*String*/ ns, /*String*/ attr, /*String|Boolean*/value){
		// summary: Use appropriate API to set the role or state attribute onto the element.
		// description: In IE use the generic setAttribute() api.  Append a namespace
		//   alias to the attribute name and appropriate prefix to the value.
		//   Otherwise, use the setAttribueNS api to set the namespaced attribute. Also
		//   add the appropriate prefix to the attribute value.
		if(dojo.isIE){
			node.setAttribute(this[ns].alias+":"+ attr, this[ns].prefix+value);
		}else{
			node.setAttributeNS(this[ns]["namespace"], attr, this[ns].prefix+value);
		}
	},

	getAttr: function(/*DomNode*/ node, /*String*/ ns, /*String|Boolena*/ attr){
		// Summary:  Use the appropriate API to retrieve the role or state value
		// Description: In IE use the generic getAttribute() api.  An alias value
		// 	was added to the attribute name to simulate a namespace when the attribute
		//  was set.  Otherwise use the getAttributeNS() api to retrieve the state value
		if(dojo.isIE){
			return node.getAttribute(this[ns].alias+":"+attr);
		}else{
			return node.getAttributeNS(this[ns]["namespace"], attr);
		}
	},
	removeAttr: function(/*DomNode*/ node, /*String*/ ns, /*String|Boolena*/ attr){
		// summary:  Use the appropriate API to remove the role or state value
		// description: In IE use the generic removeAttribute() api.  An alias value
		// 	was added to the attribute name to simulate a namespace when the attribute
		//  was set.  Otherwise use the removeAttributeNS() api to remove the state value
		var success = true; //only IE returns a value
		if(dojo.isIE){
			 success = node.removeAttribute(this[ns].alias+":"+attr);
		}else{
			node.removeAttributeNS(this[ns]["namespace"], attr);
		}
		return success;
	},

	imageBgToSrc : function(/* Node | Node[] */ images) {
		// summary:
		//		Given a single image or array of images
		//		figure out the background-image style property
		//		and apply that to the image.src property.
		// description:
		//		For accessibility reasons, all images that are necessary to the
		//		functioning of a widget should use <image> tags.  Using this method
		//		allows the image URLs to come from themes (via CSS),
		//		while still using the image tags.
		// todo:
		//		* have this examine background-position and, if set,
		//			wrap the image in an inline div that allows us to crop
		//			the image according to width and height specified in CSS.
		if (!dojo.isArrayLike(images)) { images = [images]; }
		dojo.forEach(images,
			function(image) {
				var style = image && dojo.getComputedStyle(image);
				if (!style) return;
				var href = style.backgroundImage.match(/url\(['"]?(.*?)['"]?\)/);
				if (!href) return;
				image.src = href[1];
				image.style.backgroundImage = "none";
			}
		);
	}
};

// On page load and at intervals, detect if we are in high-contrast mode or not
dojo._loaders.unshift(function(){
	// create div for testing
	var div = document.createElement("div");
	div.id = "a11yTestNode";
	dojo.body().appendChild(div);

	// test it
	function check(){
		var cs = dojo.getComputedStyle(div);
		var bkImg = cs.backgroundImage;
		var needsA11y = (cs.borderTopColor==cs.borderRightColor) || (bkImg != null && (bkImg == "none" || bkImg == "url(invalid-url:)" ));
		dojo[needsA11y ? "addClass" : "removeClass"](dojo.body(), "dijit_a11y");
	}
	// TODO: only perform check if on windows!
	if(dojo.isIE){
		check();	// NOTE: checking in Safari messes things up
		setInterval(check, 4000);
	}
});

<commit_msg>Not sure what the problem is on Safari, but at the least we should be testing for high contrast mode on FF in addition to IE (at least on Windows)<commit_after>dojo.provide("dijit.util.wai");

dijit.util.waiNames  = ["waiRole", "waiState"];

dijit.util.wai = {
	// summary: Contains functions to set accessibility roles and states
	//		onto widget elements
	waiRole: {
				// name: String:
				//		information for mapping accessibility role
				name: "waiRole",
				// namespace: String:
				//		URI of the namespace for the set of roles
				"namespace": "http://www.w3.org/TR/xhtml2",
				// alias: String:
				//		The alias to assign the namespace
				alias: "x2",
				// prefix: String:
				//		The prefix to assign to the role value
				prefix: "wairole:"
	},
	waiState: {
				// name: String:
				//		information for mapping accessibility state
				name: "waiState",
				// namespace: String:
				//		URI of the namespace for the set of states
				"namespace": "http://www.w3.org/2005/07/aaa",
				// alias: String:
				//		The alias to assign the namespace
				alias: "aaa",
				// prefix: String:
				//		empty string - state value does not require prefix
				prefix: ""
	},
	setAttr: function(/*DomNode*/node, /*String*/ ns, /*String*/ attr, /*String|Boolean*/value){
		// summary: Use appropriate API to set the role or state attribute onto the element.
		// description: In IE use the generic setAttribute() api.  Append a namespace
		//   alias to the attribute name and appropriate prefix to the value.
		//   Otherwise, use the setAttribueNS api to set the namespaced attribute. Also
		//   add the appropriate prefix to the attribute value.
		if(dojo.isIE){
			node.setAttribute(this[ns].alias+":"+ attr, this[ns].prefix+value);
		}else{
			node.setAttributeNS(this[ns]["namespace"], attr, this[ns].prefix+value);
		}
	},

	getAttr: function(/*DomNode*/ node, /*String*/ ns, /*String|Boolena*/ attr){
		// Summary:  Use the appropriate API to retrieve the role or state value
		// Description: In IE use the generic getAttribute() api.  An alias value
		// 	was added to the attribute name to simulate a namespace when the attribute
		//  was set.  Otherwise use the getAttributeNS() api to retrieve the state value
		if(dojo.isIE){
			return node.getAttribute(this[ns].alias+":"+attr);
		}else{
			return node.getAttributeNS(this[ns]["namespace"], attr);
		}
	},
	removeAttr: function(/*DomNode*/ node, /*String*/ ns, /*String|Boolena*/ attr){
		// summary:  Use the appropriate API to remove the role or state value
		// description: In IE use the generic removeAttribute() api.  An alias value
		// 	was added to the attribute name to simulate a namespace when the attribute
		//  was set.  Otherwise use the removeAttributeNS() api to remove the state value
		var success = true; //only IE returns a value
		if(dojo.isIE){
			 success = node.removeAttribute(this[ns].alias+":"+attr);
		}else{
			node.removeAttributeNS(this[ns]["namespace"], attr);
		}
		return success;
	},

	imageBgToSrc : function(/* Node | Node[] */ images) {
		// summary:
		//		Given a single image or array of images
		//		figure out the background-image style property
		//		and apply that to the image.src property.
		// description:
		//		For accessibility reasons, all images that are necessary to the
		//		functioning of a widget should use <image> tags.  Using this method
		//		allows the image URLs to come from themes (via CSS),
		//		while still using the image tags.
		// todo:
		//		* have this examine background-position and, if set,
		//			wrap the image in an inline div that allows us to crop
		//			the image according to width and height specified in CSS.
		if (!dojo.isArrayLike(images)) { images = [images]; }
		dojo.forEach(images,
			function(image) {
				var style = image && dojo.getComputedStyle(image);
				if (!style) return;
				var href = style.backgroundImage.match(/url\(['"]?(.*?)['"]?\)/);
				if (!href) return;
				image.src = href[1];
				image.style.backgroundImage = "none";
			}
		);
	}
};

// On page load and at intervals, detect if we are in high-contrast mode or not
dojo._loaders.unshift(function(){
	// create div for testing
	var div = document.createElement("div");
	div.id = "a11yTestNode";
	dojo.body().appendChild(div);

	// test it
	function check(){
		var cs = dojo.getComputedStyle(div);
		var bkImg = cs.backgroundImage;
		var needsA11y = (cs.borderTopColor==cs.borderRightColor) || (bkImg != null && (bkImg == "none" || bkImg == "url(invalid-url:)" ));
		dojo[needsA11y ? "addClass" : "removeClass"](dojo.body(), "dijit_a11y");
	}
	if(dojo.isIE || dojo.isMoz){	// NOTE: checking in Safari messes things up
		check();
		if(dojo.isIE){
			setInterval(check, 4000);
		}
	}
});

<|endoftext|>


<commit_before>// View model for the courses page
function pageViewModel(gvm) {
    // Page specific i18n bindings
    gvm.title = ko.computed(function(){i18n.setLocale(gvm.lang()); return gvm.app() + ' - ' + i18n.__("CoursesTitle");}, gvm);
    gvm.pageHeader = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("CoursesHeader");}, gvm);
    gvm.projectname = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("ProjectName");}, gvm);

    gvm.isUpdating = false;

    gvm.availableLocations = ko.observableArray([]);
    gvm.availableTrainings = ko.observableArray([]);
    gvm.availableCourses = ko.observableArray([]);

    gvm.addAvailableLocations = function(id, name) {
        var selectObject = {id: id, locationName: name};
        gvm.availableLocations.push(selectObject);
    }
    gvm.addAvailableTrainings = function(id, name) {
        var selectObject = {id: id, trainingName: name};
        gvm.availableTrainings.push(selectObject);
    }

    gvm.addAvailableCourses = function(id, name) {
        var selectObject = {id: id, courseName: name};
        gvm.availableCourses.push(selectObject);
    }

    gvm.clearAll = function() {
        gvm.availableLocations.removeAll();
        gvm.availableTrainings.removeAll();
        gvm.availableCourses.removeAll();
    }
}

function loadAllSelects($locationid, $trainingid)
{
    viewModel.isUpdating = true;
    viewModel.clearAll();
    $.getJSON('/api/courses/' + $locationid + '/' +  $trainingid, function(data) {
        $.each(data[1],function(i, item) {
            viewModel.addAvailableLocations(item.id, item.name);
        });
        $.each(data[2], function(i, item) {
            viewModel.addAvailableTrainings(item.id, item.name);
        });
        $.each(data[3], function(i, item) {
            viewModel.addAvailableCourses(item.id, item.name);
        });
    });
    viewModel.isUpdating = false;
    bindEvents();
}

function bindEvents() {
    if(!viewModel.isUpdating) {
        $("#location").bind("change", function() {
            loadAllSelects($("#location").val(), $("#training").val());
        });
        $("#training").bind("change", function() {
            loadAllSelects($("#location").val(), $("#training").val());
        });
        viewModel.isUpdating = false;
    } else {
        $("#location").unbind("change");
        $("#training").unbind("change");
    }
}

function initPage() {
    loadAllSelects(1,1);
}<commit_msg>courses<commit_after>// View model for the courses page
function pageViewModel(gvm) {
    // Page specific i18n bindings
    gvm.title = ko.computed(function(){i18n.setLocale(gvm.lang()); return gvm.app() + ' - ' + i18n.__("CoursesTitle");}, gvm);
    gvm.pageHeader = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("CoursesHeader");}, gvm);
    gvm.projectname = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("ProjectName");}, gvm);

    gvm.isUpdating = false;

    gvm.availableLocations = ko.observableArray([]);
    gvm.availableTrainings = ko.observableArray([]);
    gvm.availableCourses = ko.observableArray([]);

    gvm.addAvailableLocations = function(id, name) {
        var selectObject = {id: id, locationName: name};
        gvm.availableLocations.push(selectObject);
    }
    gvm.addAvailableTrainings = function(id, name) {
        var selectObject = {id: id, trainingName: name};
        gvm.availableTrainings.push(selectObject);
    }

    gvm.addAvailableCourses = function(id, name) {
        var selectObject = {id: id, courseName: name};
        gvm.availableCourses.push(selectObject);
    }

    gvm.clearAll = function() {
        gvm.availableLocations.removeAll();
        gvm.availableTrainings.removeAll();
        gvm.availableCourses.removeAll();
    }
}

function loadAllSelects($locationid, $trainingid)
{
    viewModel.isUpdating = true;
    viewModel.clearAll();
    $.getJSON('/api/courses/' + $locationid + '/' +  $trainingid, function(data) {
        $.each(data[1],function(i, item) {
            viewModel.addAvailableLocations(item.id, item.name);
        });
        $.each(data[2], function(i, item) {
            viewModel.addAvailableTrainings(item.id, item.name);
        });
        $.each(data[3], function(i, item) {
            viewModel.addAvailableCourses(item.id, item.name);
        });
    });
    viewModel.isUpdating = false;
    if(viewModel.isUpdating = false) {
        $("#location").one("change", function() {
            if(viewModel.isUpdating = false)
                loadAllSelects($("#location").val(), $("#training").val());

        });
        $("#training").one("change", function() {
            if(viewModel.isUpdating = false)
                loadAllSelects($("#location").val(), $("#training").val());
        });
    }
}

function bindEvents() {
    if(!viewModel.isUpdating) {
        $("#location").bind("change", function() {
            loadAllSelects($("#location").val(), $("#training").val());
        });
        $("#training").bind("change", function() {
            loadAllSelects($("#location").val(), $("#training").val());
        });
        viewModel.isUpdating = false;
    }
}

function initPage() {
    loadAllSelects(1,1);
}<|endoftext|>