<commit_before>"use strict";
var UnitsConstructionUIPlugin;

/**
 * The UnitsConstructionUIPlugin creates and handles UI buttons for starting
 * construction of units.
 */
UnitsConstructionUIPlugin = function () {
    var currentPlayerRace, template, buttonsContainer, createButton,
            createButtons, instance, playerId, buttons, getConstructionButton;

    instance = this;
    buttons = {};

    /**
     * Creates a unit construction button and adds it to the UI.
     *
     * @param {Object} unit Definition of the unit for which the button is to
     *        be created.
     */
    createButton = function (unit) {
        var node, img, progressInfo;
        node = document.createElement(template.tag);
        node.className = template.className;
        node.innerHTML = template.innerHTML;

        img = node.getElementsByTagName('img')[0];
        img.src = unit.imageData[5].src;
        img.alt = unit.name;
        img.style.width = Settings.tileWidth + 'px';
        img.style.height = Settings.tileHeight + 'px';

        progressInfo = node.getElementsByTagName('div')[0];
        node.getElementsByTagName('div')[2].innerHTML = unit.name;
        node.title = '¢ ' + (unit.construction.step[0] *
                (1000 / unit.construction.stepProgress));

        node.addEventListener('click', function () {
            if (!progressInfo.innerHTML) {
                progressInfo.innerHTML = '0 %';
            }
            instance.sendEvent('enqueueUnitConstruction', {
                player: playerId,
                unit: unit.type
            });
        }, false);
        addEventListener('contextmenu', function (event) {
            var clickedNode;
            event.preventDefault();
            clickedNode = getConstructionButton(event.target);
            if (clickedNode === node) {
                instance.sendEvent('cancelUnitConstruction', {
                    player: playerId,
                    unit: unit.type
                });
            }
        });

        node.progressInfo = progressInfo;
        buttons[unit.type] = node;
        buttonsContainer.appendChild(node);
    };

    /**
     * Creates buttons for constructing new units in the UI.
     */
    createButtons = function () {
        var unit, type;
        for (
            type = 0;
            unit = UnitsDefinition.getType(type);
            type++
        ) {
            if ((unit.race !== null) &&
                    (unit.race === currentPlayerRace)) {
                createButton(unit);
            }
        }
    };

    /**
     * Handler for the unitConstructionProgress event. The handler
     * updates the UI with percentual info about the current unit construction
     * progress.
     *
     * @param {Object} data Event's data.
     */
    this.onUnitConstructionProgress = function (data) {
        var unitButton, label;
        if (data.player === playerId) {
            unitButton = buttons[data.unit];
            label = Math.floor(data.progress / 10) + ' %';
            if (data.enqueued) {
                label += ' (' + data.enqueued + ')';
            }
            unitButton.progressInfo.innerHTML = label;
        }
    };

    /**
     * Event handler for the playerInitialization. The event is
     * sent by the GameLoader utility.
     *
     * @param {Player} player The current human player controlling the UI
     *        repesented as a Player instance.
     */
    this.onPlayerInitialization = function (player) {
        playerId = player.id;
        currentPlayerRace = player.race;
        if (template) {
            createButtons();
        }
    };

    /**
     * Event handler for the start event. The event is sent by the
     * GamePlay daemon when it is started and carries a null as
     * its data.
     *
     * @param {Object} tmp A null representing the event's data.
     */
    this.onStart = function (tmp) {
        var templateNode;
        templateNode = document.querySelector(
                '#units-buttons .construction-buttons-content *');
        buttonsContainer = templateNode.parentNode;

        template = {
            tag: templateNode.nodeName,
            className: templateNode.className,
            innerHTML: templateNode.innerHTML,
            width: 70,
            height: 38
        };
        buttonsContainer.innerHTML = '';
        if (currentPlayerRace !== undefined) {
            createButtons();
        }
    };

    /**
     * Event handler for the stop event. The event is sent by the
     * GamePlay daemon when it is stopped and carries a null as
     * its data.
     *
     * @param {Object} tmp A null representing the event's data.
     */
    this.onStop = function (tmp) {
        buttonsContainer.innerHTML = '';
    };

    /**
     * Returns the construction button encapsuling the provided clicked HTML
     * element. The method returns null if the clicked element is
     * not encapsulated by a construction button element.
     *
     * @param {HTMLElement} clickedNode The HTML clicked by the user.
     * @returns {HTMLElement} The construction button element or
     *          null.
     */
    getConstructionButton = function (clickedNode) {
        var limit;
        limit = 3;
        while (clickedNode.className !== 'construction-button') {
            if (!limit--) {
                return null;
            }
            clickedNode = clickedNode.parentNode;
            if (!clickedNode) {
                return null;
            }
        }
        return clickedNode;
    };
};
UnitsConstructionUIPlugin.prototype = new AdvancedEventDrivenPlugin();
<commit_msg>the unit construction buttons clear the progress label once the construction is done now<commit_after>"use strict";
var UnitsConstructionUIPlugin;

/**
 * The UnitsConstructionUIPlugin creates and handles UI buttons for starting
 * construction of units.
 */
UnitsConstructionUIPlugin = function () {
    var currentPlayerRace, template, buttonsContainer, createButton,
            createButtons, instance, playerId, buttons, getConstructionButton;

    instance = this;
    buttons = {};

    /**
     * Creates a unit construction button and adds it to the UI.
     *
     * @param {Object} unit Definition of the unit for which the button is to
     *        be created.
     */
    createButton = function (unit) {
        var node, img, progressInfo;
        node = document.createElement(template.tag);
        node.className = template.className;
        node.innerHTML = template.innerHTML;

        img = node.getElementsByTagName('img')[0];
        img.src = unit.imageData[5].src;
        img.alt = unit.name;
        img.style.width = Settings.tileWidth + 'px';
        img.style.height = Settings.tileHeight + 'px';

        progressInfo = node.getElementsByTagName('div')[0];
        node.getElementsByTagName('div')[2].innerHTML = unit.name;
        node.title = '¢ ' + (unit.construction.step[0] *
                (1000 / unit.construction.stepProgress));

        node.addEventListener('click', function () {
            if (!progressInfo.innerHTML) {
                progressInfo.innerHTML = '0 %';
            }
            instance.sendEvent('enqueueUnitConstruction', {
                player: playerId,
                unit: unit.type
            });
        }, false);
        addEventListener('contextmenu', function (event) {
            var clickedNode;
            event.preventDefault();
            clickedNode = getConstructionButton(event.target);
            if (clickedNode === node) {
                instance.sendEvent('cancelUnitConstruction', {
                    player: playerId,
                    unit: unit.type
                });
            }
        });

        node.progressInfo = progressInfo;
        buttons[unit.type] = node;
        buttonsContainer.appendChild(node);
    };

    /**
     * Creates buttons for constructing new units in the UI.
     */
    createButtons = function () {
        var unit, type;
        for (
            type = 0;
            unit = UnitsDefinition.getType(type);
            type++
        ) {
            if ((unit.race !== null) &&
                    (unit.race === currentPlayerRace)) {
                createButton(unit);
            }
        }
    };

    /**
     * Handler for the unitConstructionProgress event. The handler
     * updates the UI with percentual info about the current unit construction
     * progress.
     *
     * @param {Object} data Event's data.
     */
    this.onUnitConstructionProgress = function (data) {
        var unitButton, label;
        if (data.player === playerId) {
            unitButton = buttons[data.unit];
            label = Math.floor(data.progress / 10) + ' %';
            if (data.enqueued) {
                label += ' (' + data.enqueued + ')';
            } else if (data.progress >= 1000) {
                label = "";
            }
            unitButton.progressInfo.innerHTML = label;
        }
    };

    /**
     * Event handler for the playerInitialization. The event is
     * sent by the GameLoader utility.
     *
     * @param {Player} player The current human player controlling the UI
     *        repesented as a Player instance.
     */
    this.onPlayerInitialization = function (player) {
        playerId = player.id;
        currentPlayerRace = player.race;
        if (template) {
            createButtons();
        }
    };

    /**
     * Event handler for the start event. The event is sent by the
     * GamePlay daemon when it is started and carries a null as
     * its data.
     *
     * @param {Object} tmp A null representing the event's data.
     */
    this.onStart = function (tmp) {
        var templateNode;
        templateNode = document.querySelector(
                '#units-buttons .construction-buttons-content *');
        buttonsContainer = templateNode.parentNode;

        template = {
            tag: templateNode.nodeName,
            className: templateNode.className,
            innerHTML: templateNode.innerHTML,
            width: 70,
            height: 38
        };
        buttonsContainer.innerHTML = '';
        if (currentPlayerRace !== undefined) {
            createButtons();
        }
    };

    /**
     * Event handler for the stop event. The event is sent by the
     * GamePlay daemon when it is stopped and carries a null as
     * its data.
     *
     * @param {Object} tmp A null representing the event's data.
     */
    this.onStop = function (tmp) {
        buttonsContainer.innerHTML = '';
    };

    /**
     * Returns the construction button encapsuling the provided clicked HTML
     * element. The method returns null if the clicked element is
     * not encapsulated by a construction button element.
     *
     * @param {HTMLElement} clickedNode The HTML clicked by the user.
     * @returns {HTMLElement} The construction button element or
     *          null.
     */
    getConstructionButton = function (clickedNode) {
        var limit;
        limit = 3;
        while (clickedNode.className !== 'construction-button') {
            if (!limit--) {
                return null;
            }
            clickedNode = clickedNode.parentNode;
            if (!clickedNode) {
                return null;
            }
        }
        return clickedNode;
    };
};
UnitsConstructionUIPlugin.prototype = new AdvancedEventDrivenPlugin();
<|endoftext|>


<commit_before>/*
The MIT License (MIT)

Copyright (c) 2014 Jed Watson

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

var React = require('react');

function getTouchProps(touch) {
  if (!touch) return {};
  return {
    pageX: touch.pageX,
    pageY: touch.pageY,
    clientX: touch.clientX,
    clientY: touch.clientY
  };
}

function extend(target, source) {
  if (!source || Object.prototype.toString.call(source) !== '[object Object]') return target;
  for (var key in source) {
    if (source.hasOwnProperty(key)) {
      target[key] = source[key];
    }
  }
  return target;
}

/**
 * Tappable Component
 * ==================
 */

module.exports = {

  propTypes: {

    disabled: React.PropTypes.bool,              // only applies to buttons

    moveThreshold: React.PropTypes.number,       // pixels to move before cancelling tap
    pressDelay: React.PropTypes.number,          // ms to wait before detecting a press
    pressMoveThreshold: React.PropTypes.number,  // pixels to move before cancelling press
    preventDefault: React.PropTypes.bool,        // whether to preventDefault on all events
    stopPropagation: React.PropTypes.bool,       // whether to stopPropagation on all events

    onTap: React.PropTypes.func,                 // fires when a tap is detected
    onPress: React.PropTypes.func,               // fires when a press is detected
    onTouchStart: React.PropTypes.func,          // pass-through touch event
    onTouchMove: React.PropTypes.func,           // pass-through touch event
    onTouchEnd: React.PropTypes.func,            // pass-through touch event
    onMouseDown: React.PropTypes.func,           // pass-through mouse event
    onMouseUp: React.PropTypes.func,             // pass-through mouse event
    onMouseMove: React.PropTypes.func,           // pass-through mouse event
    onMouseOut: React.PropTypes.func,            // pass-through mouse event,

    maxTapTime: React.PropTypes.number           // disable tap after certain amount of time

  },

  getDefaultProps: function() {
    return {
      moveThreshold: 10,
      pressDelay: 1000,
      pressMoveThreshold: 20,
      delayUntilActive: 20
    };
  },

  getInitialState: function() {
    return {
      tapActive: false,
      touchActive: false
    };
  },

  componentWillUnmount: function() {
    this.cleanupScrollDetection();
    this.cancelPressDetection();
  },

  processEvent: function(event) {
    if (this.props.preventDefault) event.preventDefault();
    if (this.props.stopPropagation) {
      event.stopPropagation();
      event.nativeEvent.stopImmediatePropagation();
    }
  },

  componentDidMount() {
    this.findScrollableParents();
  },

  onTouchStart: function(event) {
    if (this.props.onTouchStart && this.props.onTouchStart(event) === false) return;
    if (this.areParentsScrolling()) return;
    this.processEvent(event);
    window._blockMouseEvents = true;
    this._initialTouch = this._lastTouch = getTouchProps(event.touches[0]);
    this.initPressDetection(this.endTouch);
    this.touchStartTime = Date.now();

    // this.setActiveTimeout = setTimeout(() => {
      this.setState({
        tapActive: true
      });
    // }, this.props.delayUntilActive);
  },

  findScrollableParents: function() {
    this._scrollParents = [];
    this._scrollPos = { top: 0, left: 0 };
    var node = this.getDOMNode();
    while (node) {
      if (node.style && (node.style.overflowY === 'scroll' || node.style.overflowX === 'scroll')) {
        this._scrollParents.push(node);
      }
      node = node.parentNode;
    }
  },

  calculateMovement: function(touch) {
    return {
      x: Math.abs(touch.clientX - this._initialTouch.clientX),
      y: Math.abs(touch.clientY - this._initialTouch.clientY)
    };
  },

  areParentsScrolling: function() {
    for (var i = 0; i < this._scrollParents.length; i++)
      if (this._scrollParents[i].className.indexOf('isScrolling') !== -1)
        return true;

    return false;
  },

  cleanupScrollDetection: function() {
    this._scrollParents = undefined;
    this._scrollPos = undefined;
  },

  initPressDetection: function(callback) {
    if (!this.props.onPress) return;
    this._pressTimeout = setTimeout(function() {
      this.props.onPress();
      callback();
    }.bind(this), this.props.pressDelay);
  },

  cancelPressDetection: function() {
    clearTimeout(this._pressTimeout);
  },

  onTouchMove: function(event) {
    if (!this._initialTouch) return;
    this.processEvent(event);

    if (this.areParentsScrolling())
      return this.endTouch(event);

    this.props.onTouchMove && this.props.onTouchMove(event);
    this._lastTouch = getTouchProps(event.touches[0]);
    var movement = this.calculateMovement(this._lastTouch);
    if (movement.x > this.props.pressMoveThreshold || movement.y > this.props.pressMoveThreshold)
      this.cancelPressDetection();

    if (movement.x > this.props.moveThreshold || movement.y > this.props.moveThreshold)
      this.setInactive();
    else
      if (!this.state.tapActive)
        this.setState({ tapActive: true });
  },

  setInactive() {
    if (this.setActiveTimeout)
      clearTimeout(this.setActiveTimeout);

    this.setState({
      tapActive: false
    });
  },

  onTouchEnd: function(event) {
    if (!this._initialTouch) return;
    this.processEvent(event);
    var movement = this.calculateMovement(this._lastTouch);
    if (movement.x <= this.props.moveThreshold && movement.y <= this.props.moveThreshold &&
        this.tapWithinTime()) {
      this.props.onTap && this.props.onTap(event);
    }
    this.endTouch(event);
  },

  tapWithinTime() {
    return !this.props.maxTapTime || Date.now() - this.touchStartTime <= this.props.maxTapTime;
  },

  endTouch: function() {
    if (this.setActiveTimeout) clearTimeout(this.setActiveTimeout);
    this.cancelPressDetection();
    this.props.onTouchEnd && this.props.onTouchEnd(event);
    this._initialTouch = null;
    this._lastTouch = null;
    this.setInactive();
  },

  onMouseDown: function(event) {
    if (window._blockMouseEvents) {
      window._blockMouseEvents = false;
      return;
    }
    if (this.props.onMouseDown && this.props.onMouseDown(event) === false) return;
    this.processEvent(event);
    this.initPressDetection(this.endMouseEvent);
    this._mouseDown = true;
    this.setInactive();
  },

  onMouseMove: function(event) {
    if (window._blockMouseEvents || !this._mouseDown) return;
    this.processEvent(event);
    this.props.onMouseMove && this.props.onMouseMove(event);
  },

  onMouseUp: function(event) {
    if (window._blockMouseEvents || !this._mouseDown) return;
    this.processEvent(event);
    this.props.onMouseUp && this.props.onMouseUp(event);
    this.props.onTap && this.props.onTap(event);
    this.endMouseEvent();
  },

  onMouseOut: function(event) {
    if (window._blockMouseEvents || !this._mouseDown) return;
    this.processEvent(event);
    this.props.onMouseOut && this.props.onMouseOut(event);
    this.endMouseEvent();
  },

  endMouseEvent: function() {
    this.cancelPressDetection();
    this._mouseDown = false;
    this.setInactive();
  },

  tappableProps(props) {
    var className = this.state.tapActive ? 'tapActive' : 'tapInactive';

    return {
      className: className,
      disabled: this.props.disabled,
      onTouchStart: this.onTouchStart,
      onTouchMove: this.onTouchMove,
      onTouchEnd: this.onTouchEnd,
      onMouseDown: this.onMouseDown,
      onMouseMove: this.onMouseMove,
      onMouseUp: this.onMouseUp,
      onMouseOut: this.onMouseOut
    };
  }

};<commit_msg>implement maxtaptime and delayuntilactive<commit_after>/*
The MIT License (MIT)

Copyright (c) 2014 Jed Watson

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

var React = require('react');

function getTouchProps(touch) {
  if (!touch) return {};
  return {
    pageX: touch.pageX,
    pageY: touch.pageY,
    clientX: touch.clientX,
    clientY: touch.clientY
  };
}

function extend(target, source) {
  if (!source || Object.prototype.toString.call(source) !== '[object Object]') return target;
  for (var key in source) {
    if (source.hasOwnProperty(key)) {
      target[key] = source[key];
    }
  }
  return target;
}

/**
 * Tappable Component
 * ==================
 */

module.exports = {

  propTypes: {

    disabled: React.PropTypes.bool,              // only applies to buttons

    moveThreshold: React.PropTypes.number,       // pixels to move before cancelling tap
    pressDelay: React.PropTypes.number,          // ms to wait before detecting a press
    pressMoveThreshold: React.PropTypes.number,  // pixels to move before cancelling press
    preventDefault: React.PropTypes.bool,        // whether to preventDefault on all events
    stopPropagation: React.PropTypes.bool,       // whether to stopPropagation on all events

    onTap: React.PropTypes.func,                 // fires when a tap is detected
    onPress: React.PropTypes.func,               // fires when a press is detected
    onTouchStart: React.PropTypes.func,          // pass-through touch event
    onTouchMove: React.PropTypes.func,           // pass-through touch event
    onTouchEnd: React.PropTypes.func,            // pass-through touch event
    onMouseDown: React.PropTypes.func,           // pass-through mouse event
    onMouseUp: React.PropTypes.func,             // pass-through mouse event
    onMouseMove: React.PropTypes.func,           // pass-through mouse event
    onMouseOut: React.PropTypes.func,            // pass-through mouse event,

    maxTapTime: React.PropTypes.number,          // disable tap after certain amount of time
    delayUntilActive: React.PropTypes.number     // add a delay until a tap is considered a tap

  },

  getDefaultProps: function() {
    return {
      moveThreshold: 10,
      pressDelay: 1000,
      pressMoveThreshold: 20,
      delayUntilActive: 20
    };
  },

  getInitialState: function() {
    return {
      tapActive: false,
      touchActive: false
    };
  },

  componentWillUnmount: function() {
    this.cleanupScrollDetection();
    this.cancelPressDetection();
    clearTimeout(this.delayUntilActiveTimeout)
  },

  processEvent: function(event) {
    if (this.props.preventDefault) event.preventDefault();
    if (this.props.stopPropagation) {
      event.stopPropagation();
      event.nativeEvent.stopImmediatePropagation();
    }
  },

  componentDidMount() {
    this.findScrollableParents();
  },

  onTouchStart: function(event) {
    if (this.props.onTouchStart && this.props.onTouchStart(event) === false) return;
    if (this.areParentsScrolling()) return;
    this.processEvent(event);
    window._blockMouseEvents = true;
    this._initialTouch = this._lastTouch = getTouchProps(event.touches[0]);
    this.initPressDetection(this.endTouch);
    this.touchStartTime = Date.now();

    if (this.props.delayUntilActive)
      this.delayUntilActiveTimeout = setTimeout(() => {
        this.setState({
          tapActive: true
        });
      }, this.props.delayUntilActive);
    else
      this.setState({
        tapActive: true
      });

    // if we have max tap time and no press, lets disable it
    if (this.props.maxTapTime && !this.props.onPress)
      this.delayUntilTapInactive = setTimeout(() => {
        this.setState({
          tapActive: false
        })
      }, this.props.maxTapTime);

  },

  findScrollableParents: function() {
    this._scrollParents = [];
    this._scrollPos = { top: 0, left: 0 };
    var node = this.getDOMNode();
    while (node) {
      if (node.style && (node.style.overflowY === 'scroll' || node.style.overflowX === 'scroll')) {
        this._scrollParents.push(node);
      }
      node = node.parentNode;
    }
  },

  calculateMovement: function(touch) {
    return {
      x: Math.abs(touch.clientX - this._initialTouch.clientX),
      y: Math.abs(touch.clientY - this._initialTouch.clientY)
    };
  },

  areParentsScrolling: function() {
    for (var i = 0; i < this._scrollParents.length; i++)
      if (this._scrollParents[i].className.indexOf('isScrolling') !== -1)
        return true;

    return false;
  },

  cleanupScrollDetection: function() {
    this._scrollParents = undefined;
    this._scrollPos = undefined;
  },

  initPressDetection: function(callback) {
    if (!this.props.onPress) return;
    this._pressTimeout = setTimeout(function() {
      this.props.onPress();
      callback();
    }.bind(this), this.props.pressDelay);
  },

  cancelPressDetection: function() {
    clearTimeout(this._pressTimeout);
  },

  onTouchMove: function(event) {
    if (!this._initialTouch) return;
    this.processEvent(event);

    if (this.areParentsScrolling())
      return this.endTouch(event);

    this.props.onTouchMove && this.props.onTouchMove(event);
    this._lastTouch = getTouchProps(event.touches[0]);
    var movement = this.calculateMovement(this._lastTouch);
    if (movement.x > this.props.pressMoveThreshold || movement.y > this.props.pressMoveThreshold)
      this.cancelPressDetection();

    if (movement.x > this.props.moveThreshold || movement.y > this.props.moveThreshold)
      this.setInactive();
    else
      if (!this.state.tapActive)
        this.setState({ tapActive: true });
  },

  setInactive() {
    if (this.delayUntilActiveTimeout)
      clearTimeout(this.delayUntilActiveTimeout);

    this.setState({
      tapActive: false
    });
  },

  onTouchEnd: function(event) {
    if (!this._initialTouch) return;
    this.processEvent(event);
    var movement = this.calculateMovement(this._lastTouch);
    if (movement.x <= this.props.moveThreshold && movement.y <= this.props.moveThreshold &&
        this.tapWithinTime()) {
      this.props.onTap && this.props.onTap(event);
    }
    this.endTouch(event);
  },

  tapWithinTime() {
    return !this.props.maxTapTime || Date.now() - this.touchStartTime <= this.props.maxTapTime;
  },

  endTouch: function() {
    if (this.delayUntilActiveTimeout) clearTimeout(this.delayUntilActiveTimeout);
    this.cancelPressDetection();
    this.props.onTouchEnd && this.props.onTouchEnd(event);
    this._initialTouch = null;
    this._lastTouch = null;
    this.setInactive();
  },

  onMouseDown: function(event) {
    if (window._blockMouseEvents) {
      window._blockMouseEvents = false;
      return;
    }
    if (this.props.onMouseDown && this.props.onMouseDown(event) === false) return;
    this.processEvent(event);
    this.initPressDetection(this.endMouseEvent);
    this._mouseDown = true;
    this.setInactive();
  },

  onMouseMove: function(event) {
    if (window._blockMouseEvents || !this._mouseDown) return;
    this.processEvent(event);
    this.props.onMouseMove && this.props.onMouseMove(event);
  },

  onMouseUp: function(event) {
    if (window._blockMouseEvents || !this._mouseDown) return;
    this.processEvent(event);
    this.props.onMouseUp && this.props.onMouseUp(event);
    this.props.onTap && this.props.onTap(event);
    this.endMouseEvent();
  },

  onMouseOut: function(event) {
    if (window._blockMouseEvents || !this._mouseDown) return;
    this.processEvent(event);
    this.props.onMouseOut && this.props.onMouseOut(event);
    this.endMouseEvent();
  },

  endMouseEvent: function() {
    this.cancelPressDetection();
    this._mouseDown = false;
    this.setInactive();
  },

  tappableProps(props) {
    var className = this.state.tapActive ? 'tapActive' : 'tapInactive';

    return {
      className: className,
      disabled: this.props.disabled,
      onTouchStart: this.onTouchStart,
      onTouchMove: this.onTouchMove,
      onTouchEnd: this.onTouchEnd,
      onMouseDown: this.onMouseDown,
      onMouseMove: this.onMouseMove,
      onMouseUp: this.onMouseUp,
      onMouseOut: this.onMouseOut
    };
  }

};<|endoftext|>


<commit_before>import { CONNECT, GET_CURRENT_USER_SUCCESS } from './actionTypes'
import { combineReducers } from 'redux'

let isNewLoginValue = false
export const nuxeoReducer = combineReducers({
  connect: (state = { isConnected: false }, action = {}) => {
    switch (action.type) {
      case CONNECT:
        return {
          ...state,
          isConnected: true,
          client: action.client,
        }
      default: // NOTE: do nothing
    }
    return { state } // NOTE: should this be `return state`?
  },

  computeLogin: (
    state = {
      isFetching: false,
      response: {
        get: () => {
          return ''
        },
      },
      success: false,
    },
    action = {}
  ) => {
    switch (action.type) {
      case GET_CURRENT_USER_SUCCESS: {
        const newLoginValue = isNewLoginValue
        isNewLoginValue = false
        return {
          ...state,
          response: action.user,
          isFetching: false,
          success: true,
          isConnected: !action.isAnonymous,
          isNewLogin: newLoginValue,
        }
      }

      default:
        return { ...state, isFetching: false }
    }
  },
})
<commit_msg>Fix bad return `return { state }` > `return state`<commit_after>import { CONNECT, GET_CURRENT_USER_SUCCESS } from './actionTypes'
import { combineReducers } from 'redux'

let isNewLoginValue = false
export const nuxeoReducer = combineReducers({
  connect: (state = { isConnected: false }, action = {}) => {
    switch (action.type) {
      case CONNECT:
        return {
          ...state,
          isConnected: true,
          client: action.client,
        }
      default:
        return state
    }
  },

  computeLogin: (
    state = {
      isFetching: false,
      response: {
        get: () => {
          return ''
        },
      },
      success: false,
    },
    action = {}
  ) => {
    switch (action.type) {
      case GET_CURRENT_USER_SUCCESS: {
        const newLoginValue = isNewLoginValue
        isNewLoginValue = false
        return {
          ...state,
          response: action.user,
          isFetching: false,
          success: true,
          isConnected: !action.isAnonymous,
          isNewLogin: newLoginValue,
        }
      }

      default:
        return { ...state, isFetching: false }
    }
  },
})
<|endoftext|>


<commit_before>import React, {PropTypes} from 'react'
import ReactDOM from 'react-dom'
import {Editor, State, Data} from 'slate'
import {uniqueId} from 'lodash'
import FormField from 'part:@sanity/components/formfields/default'
import InsertBlockOnEnter from 'slate-insert-block-on-enter'

import prepareSlateSchema from './util/prepareSlateSchema'
import styles from './styles/BlockEditor.css'

import FormBuilderNodeOnDrop from './plugins/FormBuilderNodeOnDrop'
import FormBuilderNodeOnPaste from './plugins/FormBuilderNodeOnPaste'
import TextFormattingOnKeyDown from './plugins/TextFormattingOnKeyDown'
import ListItemOnEnterKey from './plugins/ListItemOnEnterKey'
import TextBlockOnEnterKey from './plugins/TextBlockOnEnterKey'
import OnPasteHtml from './plugins/OnPasteHtml'

import Portal from 'react-portal'

import {
  SLATE_DEFAULT_STYLE,
  SLATE_LINK_TYPE,
} from './constants'

import Toolbar from './toolbar/Toolbar'

export default class BlockEditor extends React.Component {
  static propTypes = {
    type: PropTypes.any,
    level: PropTypes.number,
    value: PropTypes.instanceOf(State),
    validation: PropTypes.shape({
      errors: PropTypes.array
    }),
    onChange: PropTypes.func
  }

  static defaultProps = {
    onChange() {}
  }

  static contextTypes = {
    formBuilder: PropTypes.object
  }

  _inputId = uniqueId('SlateBlockEditor')

  constructor(props, context) {
    super(props, context)

    const slateSchema = prepareSlateSchema(this.props.type)
    this.textStyles = slateSchema.textStyles
    this.listItems = slateSchema.listItems
    this.slateSchema = slateSchema.schema
    this.groupedTypes = slateSchema.types
    this.slatePlugins = [
      InsertBlockOnEnter({
        type: 'contentBlock',
        kind: 'block',
        data: {
          style: SLATE_DEFAULT_STYLE
        },
        nodes: [{kind: 'text', text: '', ranges: []}]
      }),
      OnPasteHtml(),
      FormBuilderNodeOnDrop(),
      FormBuilderNodeOnPaste(this.context.formBuilder, this.props.type.of),
      TextFormattingOnKeyDown(),
      ListItemOnEnterKey(
        SLATE_DEFAULT_STYLE
      ),
      TextBlockOnEnterKey(SLATE_DEFAULT_STYLE)
    ]

    this.state = {
      fullscreen: false
    }
  }

  handleInsert = item => {
    const {value, onChange} = this.props
    const addItemValue = this.context.formBuilder.createFieldValue(undefined, item)
    const props = {
      type: item.type.name,
      isVoid: true,
      data: {
        value: addItemValue
      }
    }
    let transform = value.transform()
    if (item.options && item.options.inline) {
      transform = transform.insertInline(props)
    } else {
      transform = transform.insertBlock(props)
    }
    const nextState = transform.apply()

    onChange(nextState)
  }

  handleOnClickMarkButton = (event, type) => {
    event.preventDefault()
    const {value, onChange} = this.props
    const nextState = value
      .transform()
      .toggleMark(type)
      .apply()
    onChange(nextState)
  }

  handleOnClickListFormattingButton = (event, listStyle, active) => {
    const {value, onChange} = this.props
    const normalBlock = {
      type: 'contentBlock',
      data: {style: SLATE_DEFAULT_STYLE}
    }
    const listItemBlock = {
      type: 'contentBlock',
      data: {listItem: listStyle}
    }
    let transform = value.transform()

    if (active) {
      transform = transform
        .setBlock(listItemBlock)
    } else {
      transform = transform
        .setBlock(normalBlock)
    }
    const nextState = transform.apply()
    onChange(nextState)
    this.refreshCSS()
  }

  handleSelectBlockFormatting = selectedValue => {
    const {value, onChange} = this.props
    const {selection, startBlock, endBlock} = value
    const block = {
      type: 'contentBlock',
      data: {style: selectedValue.style.value}
    }
    let transform = value.transform()

    // If a single block is selected partially, split block conditionally (selection in start, middle or end of text)
    if (startBlock === endBlock
      && selection.isExpanded
      && !(
        selection.hasStartAtStartOf(startBlock)
        && selection.hasEndAtEndOf(startBlock
      )
    )) {
      const hasTextBefore = !selection.hasStartAtStartOf(startBlock)
      const hasTextAfter = !selection.hasEndAtEndOf(startBlock)
      if (hasTextAfter) {
        const extendForward = selection.isForward
          ? (selection.focusOffset - selection.anchorOffset)
          : (selection.anchorOffset - selection.focusOffset)
        transform
          .collapseToStart()
          .splitBlock()
          .moveForward()
          .extendForward(extendForward)
          .collapseToEnd()
          .splitBlock()
          .collapseToStartOfPreviousText()
      } else {
        transform = hasTextBefore ? (
          transform
            .collapseToStart()
            .splitBlock()
            .moveForward()
        ) : (
          transform
            .collapseToEnd()
            .splitBlock()
            .moveTo(selection)
        )
      }
    }
    transform
      .setBlock(block)
    const nextState = transform.apply()
    onChange(nextState)
    this.refreshCSS()
  }

  handleOnClickLinkButton = (href, target, text) => {
    const {value, onChange} = this.props
    let transform = value.transform()

    if (!href) {
      transform = transform
       .unwrapInline(SLATE_LINK_TYPE)
       .focus()
    } else if (href) {
      if (value.isExpanded) {
        transform = transform
          .unwrapInline(SLATE_LINK_TYPE)
          .wrapInline({
            type: SLATE_LINK_TYPE,
            data: {href: href, target: target}
          })
          .focus()
      } else {
        const linkNode = value.inlines
          .find(inline => inline.type === SLATE_LINK_TYPE)
        transform = transform
          .focus()
          .moveToRangeOf(linkNode)
          .unwrapInline(SLATE_LINK_TYPE)
          .wrapInline({
            type: SLATE_LINK_TYPE,
            data: {href: href, target: target}
          })
      }
    }
    const nextState = transform.apply()
    onChange(nextState)
  }

  hasMark(markName) {
    const {value} = this.props
    return value.marks.some(mark => mark.type == markName)
  }

  hasBlock(type) {
    const {value} = this.props
    return value.blocks.some(block => block.type === type)
  }

  hasStyle(styleName) {
    const {value} = this.props
    return value.blocks.some(block => block.data.get('style') === styleName)
  }

  hasLinks() {
    const {value} = this.props
    return value.inlines.some(inline => inline.type == SLATE_LINK_TYPE)
  }

  isWithinList() {
    const {value} = this.props
    return value.blocks.some(block => block.data.get('listItem'))
  }

  hasListItem(listItem) {
    const {value} = this.props
    return value.blocks.some(block => {
      return block.data.get('listItem') === listItem
    })
  }

  getActiveMarks() {
    return Object.keys(this.slateSchema.marks).map(mark => {
      return {
        type: mark,
        active: this.hasMark(mark)
      }
    })
  }

  getListTypes() {
    return (this.listItems)
      .map((item, index) => {
        return {
          type: item.value,
          title: item.title,
          active: this.hasListItem(item.value)
        }
      })
  }

  getStyles() {
    function Preview(props) {
      return {props.children}
    }
    const items = this.textStyles
      .map((style, index) => {
        return {
          key: `blockFormat-${index}`,
          style: style,
          preview: this.slateSchema.nodes.contentBlock({
            children: [
              <Preview
                key={style.value}
                parent={{data: Data.create({style: style.value})}}
              >
                {style.title}
              </Preview>
            ]
          }),
          title: ` ${style.title}`,
          active: this.hasStyle(style.value)
        }
      })
    let value = items.filter(item => item.active)
    if (value.length === 0) {
      value = [{
        key: 'blockFormat-none',
        preview: null,
        type: null,
        title: 'No style',
        active: true
      }]
    }
    return {
      items: items,
      value: value
    }
  }

  getActiveLink() {
    const {value} = this.props
    if (!value.inlines) {
      return null
    }
    if (this.hasLinks()) {
      const linkNode = value.inlines
        .find(inline => inline.type === SLATE_LINK_TYPE)
      if (linkNode) {
        return {
          href: linkNode.data.get('href'),
          target: linkNode.data.get('target')
        }
      }
      return null
    }
    return null
  }

  handleToggleFullscreen = () => {
    this.setState({
      fullscreen: !this.state.fullscreen
    })
  }

  handleEditorChange = nextState => {
    this.props.onChange(nextState)
  }

  refEditor = editor => {
    this.editor = editor
  }

  refBlockDragMarker = marker => {
    this.blockDragMarker = marker
  }

  // Hack to force the browser to reapply CSS rules
  // This is needed to make ::before and ::after CSS rules work properly
  // under certain conditions (like the list counters for number lists)
  // http://stackoverflow.com/questions/3485365/how-can-i-force-webkit-to-redraw-repaint-to-propagate-style-changes/3485654#3485654
  refreshCSS() {
    const editorDOMNode = ReactDOM.findDOMNode(this.editor)
    editorDOMNode.style.display = 'none'
    editorDOMNode.offsetHeight
    editorDOMNode.style.display = ''
  }

  showBlockDragMarker(pos, node) {
    this.blockDragMarker.style.display = 'block'
    const editorDOMNode = ReactDOM.findDOMNode(this.editor)
    const editorRect = editorDOMNode.getBoundingClientRect()
    const elemRect = node.getBoundingClientRect()
    const topPos = elemRect.top - editorRect.top
    const bottomPos = topPos + (elemRect.bottom - elemRect.top)
    if (pos == 'after') {
      this.blockDragMarker.style.top = `${parseInt(bottomPos, 0)}px`
    } else {
      this.blockDragMarker.style.top = `${parseInt(topPos, 0)}px`
    }
  }

  hideBlockDragMarker() {
    this.blockDragMarker.style.display = 'none'
  }

  renderBlockEditor() {
    const {validation, value, type, level} = this.props
    const hasError = validation && validation.messages && validation.messages.length > 0
    const activeLink = this.getActiveLink()
    const showLinkButton = (value.selection && value.selection.isExpanded)
      || !!activeLink

    return (
      <FormField
        label={type.title}
        labelHtmlFor={this._inputId}
        level={level}
        className={this.state.fullscreen ? styles.formFieldFullscreen : styles.formField}
      >

          <Toolbar
            className={styles.toolbar}
            onInsertBlock={this.handleInsert}
            insertBlocks={this.groupedTypes.formBuilder || []}
            onFullscreenEnable={this.handleToggleFullscreen}
            fullscreen={this.state.fullscreen}
            onMarkButtonClick={this.handleOnClickMarkButton}
            onListButtonClick={this.handleOnClickListFormattingButton}
            onFormatSelectChange={this.handleSelectBlockFormatting}
            listFormats={this.getListTypes()}
            textFormats={this.getStyles()}
            onLinkButtonClick={this.handleOnClickLinkButton}
            activeLink={activeLink}
            showLinkButton={showLinkButton}
            marks={this.getActiveMarks()}
          />

            <Editor
              ref={this.refEditor}
              className={styles.input}
              onChange={this.handleEditorChange}
              placeholder=""
              state={value}
              blockEditor={this}
              plugins={this.slatePlugins}
              schema={this.slateSchema}
            />

      </FormField>
    )
  }

  render() {
    const {fullscreen} = this.state
    if (fullscreen) {
      return (
        <Portal isOpened>
          {this.renderBlockEditor()}
        </Portal>
      )
    }
    return this.renderBlockEditor()
  }
}
<commit_msg>[form-builder] Just a line break<commit_after>import React, {PropTypes} from 'react'
import ReactDOM from 'react-dom'
import {Editor, State, Data} from 'slate'
import {uniqueId} from 'lodash'
import FormField from 'part:@sanity/components/formfields/default'
import InsertBlockOnEnter from 'slate-insert-block-on-enter'

import prepareSlateSchema from './util/prepareSlateSchema'
import styles from './styles/BlockEditor.css'

import FormBuilderNodeOnDrop from './plugins/FormBuilderNodeOnDrop'
import FormBuilderNodeOnPaste from './plugins/FormBuilderNodeOnPaste'
import TextFormattingOnKeyDown from './plugins/TextFormattingOnKeyDown'
import ListItemOnEnterKey from './plugins/ListItemOnEnterKey'
import TextBlockOnEnterKey from './plugins/TextBlockOnEnterKey'
import OnPasteHtml from './plugins/OnPasteHtml'

import Portal from 'react-portal'

import {
  SLATE_DEFAULT_STYLE,
  SLATE_LINK_TYPE,
} from './constants'

import Toolbar from './toolbar/Toolbar'

export default class BlockEditor extends React.Component {
  static propTypes = {
    type: PropTypes.any,
    level: PropTypes.number,
    value: PropTypes.instanceOf(State),
    validation: PropTypes.shape({
      errors: PropTypes.array
    }),
    onChange: PropTypes.func
  }

  static defaultProps = {
    onChange() {}
  }

  static contextTypes = {
    formBuilder: PropTypes.object
  }

  _inputId = uniqueId('SlateBlockEditor')

  constructor(props, context) {
    super(props, context)

    const slateSchema = prepareSlateSchema(this.props.type)
    this.textStyles = slateSchema.textStyles
    this.listItems = slateSchema.listItems
    this.slateSchema = slateSchema.schema
    this.groupedTypes = slateSchema.types
    this.slatePlugins = [
      InsertBlockOnEnter({
        type: 'contentBlock',
        kind: 'block',
        data: {
          style: SLATE_DEFAULT_STYLE
        },
        nodes: [{kind: 'text', text: '', ranges: []}]
      }),
      OnPasteHtml(),
      FormBuilderNodeOnDrop(),
      FormBuilderNodeOnPaste(this.context.formBuilder, this.props.type.of),
      TextFormattingOnKeyDown(),
      ListItemOnEnterKey(
        SLATE_DEFAULT_STYLE
      ),
      TextBlockOnEnterKey(SLATE_DEFAULT_STYLE)
    ]

    this.state = {
      fullscreen: false
    }
  }

  handleInsert = item => {
    const {value, onChange} = this.props
    const addItemValue = this.context.formBuilder.createFieldValue(undefined, item)
    const props = {
      type: item.type.name,
      isVoid: true,
      data: {
        value: addItemValue
      }
    }
    let transform = value.transform()
    if (item.options && item.options.inline) {
      transform = transform.insertInline(props)
    } else {
      transform = transform.insertBlock(props)
    }
    const nextState = transform.apply()

    onChange(nextState)
  }

  handleOnClickMarkButton = (event, type) => {
    event.preventDefault()
    const {value, onChange} = this.props
    const nextState = value
      .transform()
      .toggleMark(type)
      .apply()
    onChange(nextState)
  }

  handleOnClickListFormattingButton = (event, listStyle, active) => {
    const {value, onChange} = this.props
    const normalBlock = {
      type: 'contentBlock',
      data: {style: SLATE_DEFAULT_STYLE}
    }
    const listItemBlock = {
      type: 'contentBlock',
      data: {listItem: listStyle}
    }
    let transform = value.transform()

    if (active) {
      transform = transform
        .setBlock(listItemBlock)
    } else {
      transform = transform
        .setBlock(normalBlock)
    }
    const nextState = transform.apply()
    onChange(nextState)
    this.refreshCSS()
  }

  handleSelectBlockFormatting = selectedValue => {
    const {value, onChange} = this.props
    const {selection, startBlock, endBlock} = value
    const block = {
      type: 'contentBlock',
      data: {style: selectedValue.style.value}
    }
    let transform = value.transform()

    // If a single block is selected partially, split block conditionally
    // (selection in start, middle or end of text)
    if (startBlock === endBlock
      && selection.isExpanded
      && !(
        selection.hasStartAtStartOf(startBlock)
        && selection.hasEndAtEndOf(startBlock
      )
    )) {
      const hasTextBefore = !selection.hasStartAtStartOf(startBlock)
      const hasTextAfter = !selection.hasEndAtEndOf(startBlock)
      if (hasTextAfter) {
        const extendForward = selection.isForward
          ? (selection.focusOffset - selection.anchorOffset)
          : (selection.anchorOffset - selection.focusOffset)
        transform
          .collapseToStart()
          .splitBlock()
          .moveForward()
          .extendForward(extendForward)
          .collapseToEnd()
          .splitBlock()
          .collapseToStartOfPreviousText()
      } else {
        transform = hasTextBefore ? (
          transform
            .collapseToStart()
            .splitBlock()
            .moveForward()
        ) : (
          transform
            .collapseToEnd()
            .splitBlock()
            .moveTo(selection)
        )
      }
    }
    transform
      .setBlock(block)
    const nextState = transform.apply()
    onChange(nextState)
    this.refreshCSS()
  }

  handleOnClickLinkButton = (href, target, text) => {
    const {value, onChange} = this.props
    let transform = value.transform()

    if (!href) {
      transform = transform
       .unwrapInline(SLATE_LINK_TYPE)
       .focus()
    } else if (href) {
      if (value.isExpanded) {
        transform = transform
          .unwrapInline(SLATE_LINK_TYPE)
          .wrapInline({
            type: SLATE_LINK_TYPE,
            data: {href: href, target: target}
          })
          .focus()
      } else {
        const linkNode = value.inlines
          .find(inline => inline.type === SLATE_LINK_TYPE)
        transform = transform
          .focus()
          .moveToRangeOf(linkNode)
          .unwrapInline(SLATE_LINK_TYPE)
          .wrapInline({
            type: SLATE_LINK_TYPE,
            data: {href: href, target: target}
          })
      }
    }
    const nextState = transform.apply()
    onChange(nextState)
  }

  hasMark(markName) {
    const {value} = this.props
    return value.marks.some(mark => mark.type == markName)
  }

  hasBlock(type) {
    const {value} = this.props
    return value.blocks.some(block => block.type === type)
  }

  hasStyle(styleName) {
    const {value} = this.props
    return value.blocks.some(block => block.data.get('style') === styleName)
  }

  hasLinks() {
    const {value} = this.props
    return value.inlines.some(inline => inline.type == SLATE_LINK_TYPE)
  }

  isWithinList() {
    const {value} = this.props
    return value.blocks.some(block => block.data.get('listItem'))
  }

  hasListItem(listItem) {
    const {value} = this.props
    return value.blocks.some(block => {
      return block.data.get('listItem') === listItem
    })
  }

  getActiveMarks() {
    return Object.keys(this.slateSchema.marks).map(mark => {
      return {
        type: mark,
        active: this.hasMark(mark)
      }
    })
  }

  getListTypes() {
    return (this.listItems)
      .map((item, index) => {
        return {
          type: item.value,
          title: item.title,
          active: this.hasListItem(item.value)
        }
      })
  }

  getStyles() {
    function Preview(props) {
      return {props.children}
    }
    const items = this.textStyles
      .map((style, index) => {
        return {
          key: `blockFormat-${index}`,
          style: style,
          preview: this.slateSchema.nodes.contentBlock({
            children: [
              <Preview
                key={style.value}
                parent={{data: Data.create({style: style.value})}}
              >
                {style.title}
              </Preview>
            ]
          }),
          title: ` ${style.title}`,
          active: this.hasStyle(style.value)
        }
      })
    let value = items.filter(item => item.active)
    if (value.length === 0) {
      value = [{
        key: 'blockFormat-none',
        preview: null,
        type: null,
        title: 'No style',
        active: true
      }]
    }
    return {
      items: items,
      value: value
    }
  }

  getActiveLink() {
    const {value} = this.props
    if (!value.inlines) {
      return null
    }
    if (this.hasLinks()) {
      const linkNode = value.inlines
        .find(inline => inline.type === SLATE_LINK_TYPE)
      if (linkNode) {
        return {
          href: linkNode.data.get('href'),
          target: linkNode.data.get('target')
        }
      }
      return null
    }
    return null
  }

  handleToggleFullscreen = () => {
    this.setState({
      fullscreen: !this.state.fullscreen
    })
  }

  handleEditorChange = nextState => {
    this.props.onChange(nextState)
  }

  refEditor = editor => {
    this.editor = editor
  }

  refBlockDragMarker = marker => {
    this.blockDragMarker = marker
  }

  // Hack to force the browser to reapply CSS rules
  // This is needed to make ::before and ::after CSS rules work properly
  // under certain conditions (like the list counters for number lists)
  // http://stackoverflow.com/questions/3485365/how-can-i-force-webkit-to-redraw-repaint-to-propagate-style-changes/3485654#3485654
  refreshCSS() {
    const editorDOMNode = ReactDOM.findDOMNode(this.editor)
    editorDOMNode.style.display = 'none'
    editorDOMNode.offsetHeight
    editorDOMNode.style.display = ''
  }

  showBlockDragMarker(pos, node) {
    this.blockDragMarker.style.display = 'block'
    const editorDOMNode = ReactDOM.findDOMNode(this.editor)
    const editorRect = editorDOMNode.getBoundingClientRect()
    const elemRect = node.getBoundingClientRect()
    const topPos = elemRect.top - editorRect.top
    const bottomPos = topPos + (elemRect.bottom - elemRect.top)
    if (pos == 'after') {
      this.blockDragMarker.style.top = `${parseInt(bottomPos, 0)}px`
    } else {
      this.blockDragMarker.style.top = `${parseInt(topPos, 0)}px`
    }
  }

  hideBlockDragMarker() {
    this.blockDragMarker.style.display = 'none'
  }

  renderBlockEditor() {
    const {validation, value, type, level} = this.props
    const hasError = validation && validation.messages && validation.messages.length > 0
    const activeLink = this.getActiveLink()
    const showLinkButton = (value.selection && value.selection.isExpanded)
      || !!activeLink

    return (
      <FormField
        label={type.title}
        labelHtmlFor={this._inputId}
        level={level}
        className={this.state.fullscreen ? styles.formFieldFullscreen : styles.formField}
      >

          <Toolbar
            className={styles.toolbar}
            onInsertBlock={this.handleInsert}
            insertBlocks={this.groupedTypes.formBuilder || []}
            onFullscreenEnable={this.handleToggleFullscreen}
            fullscreen={this.state.fullscreen}
            onMarkButtonClick={this.handleOnClickMarkButton}
            onListButtonClick={this.handleOnClickListFormattingButton}
            onFormatSelectChange={this.handleSelectBlockFormatting}
            listFormats={this.getListTypes()}
            textFormats={this.getStyles()}
            onLinkButtonClick={this.handleOnClickLinkButton}
            activeLink={activeLink}
            showLinkButton={showLinkButton}
            marks={this.getActiveMarks()}
          />

            <Editor
              ref={this.refEditor}
              className={styles.input}
              onChange={this.handleEditorChange}
              placeholder=""
              state={value}
              blockEditor={this}
              plugins={this.slatePlugins}
              schema={this.slateSchema}
            />

      </FormField>
    )
  }

  render() {
    const {fullscreen} = this.state
    if (fullscreen) {
      return (
        <Portal isOpened>
          {this.renderBlockEditor()}
        </Portal>
      )
    }
    return this.renderBlockEditor()
  }
}
<|endoftext|>


<commit_before>$(function () {
    $('#confirm').on('show.bs.modal', function(e) {
        $(this).find('.btn-ok').attr('href', $(e.relatedTarget).data('href'));
    });
});

function getCookie(name) {
    var cookieValue = null;
    if (document.cookie && document.cookie != '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = jQuery.trim(cookies[i]);
            if (cookie.substring(0, name.length + 1) == (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}

$.ajaxSetup({
    beforeSend: function(xhr, settings) {
        if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {
            xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
        }
    }
});

function start_countup(time) {
    var since = new Date(time.getFullYear(), time.getMonth(), time.getDate(), 0, 0, 0);
    $('#countup').countdown({
        since: since, compact: true,
        layout: 'Время сервера: {hnn}{sep}{mnn}{sep}{snn}.'
    });
}
<commit_msg>Fix view server time<commit_after>$(function () {
    $('#confirm').on('show.bs.modal', function(e) {
        $(this).find('.btn-ok').attr('href', $(e.relatedTarget).data('href'));
    });
});

function getCookie(name) {
    var cookieValue = null;
    if (document.cookie && document.cookie != '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = jQuery.trim(cookies[i]);
            if (cookie.substring(0, name.length + 1) == (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}

$.ajaxSetup({
    beforeSend: function(xhr, settings) {
        if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {
            xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
        }
    }
});

function start_countup(time) {
    var tz = time.getTimezoneOffset() * 2;
    $('#countup').countdown({
        since: -time, timezone: -tz, compact: true,
        layout: 'Время сервера: {hnn}{sep}{mnn}{sep}{snn}.'
    });
}
<|endoftext|>


<commit_before>var WAIT_TIMEOUT = 5000;

module.exports = function(casper, scenario) {
	var waitFor = require('./waitForHelperHelper')(casper, WAIT_TIMEOUT);
	var hoverSelector = scenario.hoverSelector;
	var clickSelector = scenario.clickSelector;
	var postInteractionWait = scenario.postInteractionWait;

	if (hoverSelector) {
		waitFor(hoverSelector);
		casper.then(function() {
			casper.mouse.move(hoverSelector);
		});
	}

	if (clickSelector) {
		waitFor(clickSelector);
		casper.then(function () {
			casper.click(clickSelector);
		});
	}

	// TODO: if postInteractionWait === integer then do ==> wait(postInteractionWait) || elsevvv
	waitFor(postInteractionWait);
};
<commit_msg>style(generator): small style change<commit_after>var WAIT_TIMEOUT = 5000;

module.exports = function(casper, scenario) {
	var waitFor = require('./waitForHelperHelper')(casper, WAIT_TIMEOUT);
	var hoverSelector = scenario.hoverSelector;
	var clickSelector = scenario.clickSelector;
	var postInteractionWait = scenario.postInteractionWait;

	if (hoverSelector) {
		waitFor(hoverSelector);
		casper.then(function() {
			casper.mouse.move(hoverSelector);
		});
	}

	if (clickSelector) {
		waitFor(clickSelector);
		casper.then(function() {
			casper.click(clickSelector);
		});
	}

	// TODO: if postInteractionWait === integer then do ==> wait(postInteractionWait) || elsevvv
	waitFor(postInteractionWait);
};
<|endoftext|>


<commit_before><commit_msg>test(connectHierarchicalMenu): name rendering options variables<commit_after><|endoftext|>


<commit_before>import { createSelector } from 'reselect';
import isEmpty from 'lodash/isEmpty';
import groupBy from 'lodash/groupBy';
import qs from 'query-string';
import { lowerUpperFirst } from 'utils';

const getIso = state => state.iso || null;
const getAdaptations = state => state.adaptations || null;
const getSearch = state => state.search || null;
const getCountries = state => state.countriesData || null;

const getSection = (sections, slug) => sections.find(s => s.slug === slug);

const getDataFromIso = (sections, iso, slug) => {
  const section = getSection(sections, slug);
  if (section && section.values) {
    const isoValue = section.values.find(country => country.location === iso);
    return isoValue ? [{ ...isoValue, id: iso }] : [{ id: iso }];
  }
  return null;
};

const getDataFromQuery = (sections, isos, countries, slug) => {
  const section = getSection(sections, slug);
  return (
    section &&
    section.values &&
    isos.map(iso => {
      if (iso) {
        const isoValues = section.values.find(
          country => country.location === iso
        );
        const countryLabel = countries.find(
          country => country.iso_code3 === iso
        );
        return isoValues
          ? { ...isoValues, id: iso, label: countryLabel.wri_standard_name }
          : {
            location: iso,
            value: null,
            id: iso,
            label: countryLabel.wri_standard_name
          };
      }
      return { location: null, value: null, id: Math.random() };
    })
  );
};

const getData = (sections, iso, isos, countries, slug) => {
  if (!isos) {
    return getDataFromIso(sections, iso, slug);
  }
  return getDataFromQuery(sections, isos, countries, slug);
};

const getListDataFromIso = (sections, iso, slug) => {
  const items = sections.filter(section => section.slug.startsWith(slug));
  const itemList = [];
  if (
    !items.some(item => item.values.some(country => country.location === iso))
  ) {
    itemList.push('No data');
  } else {
    items.forEach(item => {
      const countryHasItem = item.values.find(
        country => country.location === iso && country.value === true
      );
      if (countryHasItem) itemList.push({ name: item.name });
    });
  }
  return itemList;
};

const getListDataFromQuery = (sections, isos, slug) => {
  const items = sections.filter(section => section.slug.startsWith(slug));
  const itemList = [];
  isos.forEach((iso, index) => {
    if (
      isos.length === 1 &&
      !items.some(item => item.values.some(country => country.location === iso))
    ) {
      itemList.push('No data');
    } else {
      items.forEach(item => {
        const countryHasItem = item.values.find(
          country => country.location === iso && country.value === true
        );
        if (countryHasItem) {
          itemList.push({ name: item.name, countryIndex: [index] });
        }
      });
    }
  });
  if (itemList[0] !== 'No data') {
    const groupedList = groupBy(itemList, 'name');
    const reducedList = Object.keys(groupedList).reduce(
      (array, key) =>
        array.concat({
          name: key,
          countryIndex: groupedList[key].map(x => x.countryIndex)
        }),
      []
    );
    return reducedList;
  }
  return itemList;
};

const getListData = (sections, iso, isos, slug) => {
  if (!isos) {
    return getListDataFromIso(sections, iso, slug);
  }
  return getListDataFromQuery(sections, isos, slug);
};

const getListMetadata = (sections, iso, slug) => {
  const section = getSection(sections, slug);
  const locationUrl = section.values.find(country => country.location === iso);
  return locationUrl && locationUrl.value;
};

export const getQueryIsos = search => {
  if (!search) return null;
  const query = qs.parse(search);
  if (!query.locations) return null;
  return query.locations.split(',').map(val => {
    if (val) return val;
    return null;
  });
};

export const getSectionData = createSelector(
  [getIso, getAdaptations, getSearch, getCountries],
  (iso, adaptations, search, countries) => {
    const queryIsos = getQueryIsos(search);
    const sections = adaptations.data;
    if ((!iso || isEmpty(sections)) && (!search || isEmpty(sections))) {
      return null;
    } // eslint-disable-line no-mixed-operators

    return [
      {
        sectionType: 'CIRCULAR',
        title: getSection(sections, 'poverty_14').name,
        slug: 'poverty_14',
        data: getData(sections, iso, queryIsos, countries, 'poverty_14')
      },
      {
        sectionType: 'LIST',
        title: 'Key hazards',
        slug: 'key_hazard',
        data: getListData(sections, iso, queryIsos, 'hazard'),
        metadataUrl: getListMetadata(sections, iso, 'wb_urls')
      },
      {
        sectionType: 'LINE',
        title: lowerUpperFirst(getSection(sections, 'climate_risks').name),
        subtitle: 'Lower score indicates high levels of climate risk',
        slug: 'climate_risks',
        data: getData(sections, iso, queryIsos, countries, 'climate_risks'),
        maximum: getSection(sections, 'climate_risks').maximum
      },
      {
        sectionType: 'LINE',
        title: getSection(sections, 'vulnerability').name,
        subtitle: 'Lower score indicates less vulnerability',
        slug: 'vulnerability',
        data: getData(sections, iso, queryIsos, countries, 'vulnerability'),
        maximum: getSection(sections, 'vulnerability').maximum
      },
      {
        sectionType: 'LINE',
        title: getSection(sections, 'readiness').name,
        subtitle: 'Higher score indicates high levels of readiness',
        slug: 'readiness',
        data: getData(sections, iso, queryIsos, countries, 'readiness'),
        maximum: getSection(sections, 'readiness').maximum
      },
      {
        sectionType: 'LINK',
        slug: 'link'
      }
    ];
  }
);

export default {
  getSectionData
};
<commit_msg>Fix country compare page crash on wri country labels<commit_after>import { createSelector } from 'reselect';
import isEmpty from 'lodash/isEmpty';
import groupBy from 'lodash/groupBy';
import qs from 'query-string';
import { lowerUpperFirst } from 'utils';

const getIso = state => state.iso || null;
const getAdaptations = state => state.adaptations || null;
const getSearch = state => state.search || null;
const getCountries = state => state.countriesData || null;

const getSection = (sections, slug) => sections.find(s => s.slug === slug);

const getDataFromIso = (sections, iso, slug) => {
  const section = getSection(sections, slug);
  if (section && section.values) {
    const isoValue = section.values.find(country => country.location === iso);
    return isoValue ? [{ ...isoValue, id: iso }] : [{ id: iso }];
  }
  return null;
};

const getDataFromQuery = (sections, isos, countries, slug) => {
  const section = getSection(sections, slug);
  return (
    section &&
    section.values &&
    isos.map(iso => {
      if (iso) {
        const isoValues = section.values.find(
          country => country.location === iso
        );
        const countryLabel = countries.find(
          country => country.iso_code3 === iso
        );
        return isoValues
          ? { ...isoValues, id: iso, label: countryLabel.wri_standard_name }
          : {
            location: iso,
            value: null,
            id: iso,
            label: countryLabel.wri_standard_name
          };
      }
      return { location: null, value: null, id: Math.random() };
    })
  );
};

const getData = (sections, iso, isos, countries, slug) => {
  if (!isos) {
    return getDataFromIso(sections, iso, slug);
  }
  return getDataFromQuery(sections, isos, countries, slug);
};

const getListDataFromIso = (sections, iso, slug) => {
  const items = sections.filter(section => section.slug.startsWith(slug));
  const itemList = [];
  if (
    !items.some(item => item.values.some(country => country.location === iso))
  ) {
    itemList.push('No data');
  } else {
    items.forEach(item => {
      const countryHasItem = item.values.find(
        country => country.location === iso && country.value === true
      );
      if (countryHasItem) itemList.push({ name: item.name });
    });
  }
  return itemList;
};

const getListDataFromQuery = (sections, isos, slug) => {
  const items = sections.filter(section => section.slug.startsWith(slug));
  const itemList = [];
  isos.forEach((iso, index) => {
    if (
      isos.length === 1 &&
      !items.some(item => item.values.some(country => country.location === iso))
    ) {
      itemList.push('No data');
    } else {
      items.forEach(item => {
        const countryHasItem = item.values.find(
          country => country.location === iso && country.value === true
        );
        if (countryHasItem) {
          itemList.push({ name: item.name, countryIndex: [index] });
        }
      });
    }
  });
  if (itemList[0] !== 'No data') {
    const groupedList = groupBy(itemList, 'name');
    const reducedList = Object.keys(groupedList).reduce(
      (array, key) =>
        array.concat({
          name: key,
          countryIndex: groupedList[key].map(x => x.countryIndex)
        }),
      []
    );
    return reducedList;
  }
  return itemList;
};

const getListData = (sections, iso, isos, slug) => {
  if (!isos) {
    return getListDataFromIso(sections, iso, slug);
  }
  return getListDataFromQuery(sections, isos, slug);
};

const getListMetadata = (sections, iso, slug) => {
  const section = getSection(sections, slug);
  const locationUrl = section.values.find(country => country.location === iso);
  return locationUrl && locationUrl.value;
};

export const getQueryIsos = search => {
  if (!search) return null;
  const query = qs.parse(search);
  if (!query.locations) return null;
  return query.locations.split(',').map(val => {
    if (val) return val;
    return null;
  });
};

export const getSectionData = createSelector(
  [getIso, getAdaptations, getSearch, getCountries],
  (iso, adaptations, search, countries) => {
    const queryIsos = getQueryIsos(search);
    const sections = adaptations.data;
    const noCountriesSelected = !iso && !search;

    if (noCountriesSelected || isEmpty(countries) || isEmpty(sections)) {
      return null;
    } // eslint-disable-line no-mixed-operators

    return [
      {
        sectionType: 'CIRCULAR',
        title: getSection(sections, 'poverty_14').name,
        slug: 'poverty_14',
        data: getData(sections, iso, queryIsos, countries, 'poverty_14')
      },
      {
        sectionType: 'LIST',
        title: 'Key hazards',
        slug: 'key_hazard',
        data: getListData(sections, iso, queryIsos, 'hazard'),
        metadataUrl: getListMetadata(sections, iso, 'wb_urls')
      },
      {
        sectionType: 'LINE',
        title: lowerUpperFirst(getSection(sections, 'climate_risks').name),
        subtitle: 'Lower score indicates high levels of climate risk',
        slug: 'climate_risks',
        data: getData(sections, iso, queryIsos, countries, 'climate_risks'),
        maximum: getSection(sections, 'climate_risks').maximum
      },
      {
        sectionType: 'LINE',
        title: getSection(sections, 'vulnerability').name,
        subtitle: 'Lower score indicates less vulnerability',
        slug: 'vulnerability',
        data: getData(sections, iso, queryIsos, countries, 'vulnerability'),
        maximum: getSection(sections, 'vulnerability').maximum
      },
      {
        sectionType: 'LINE',
        title: getSection(sections, 'readiness').name,
        subtitle: 'Higher score indicates high levels of readiness',
        slug: 'readiness',
        data: getData(sections, iso, queryIsos, countries, 'readiness'),
        maximum: getSection(sections, 'readiness').maximum
      },
      {
        sectionType: 'LINK',
        slug: 'link'
      }
    ];
  }
);

export default {
  getSectionData
};
<|endoftext|>


<commit_before>import {PolymerElement, html} from '@polymer/polymer/polymer-element.js';
import {Debouncer} from '@polymer/polymer/lib/utils/debounce.js';
import '@polymer/neon-animation/neon-animations.js';
import '@polymer/paper-dialog/paper-dialog.js';
import '@polymer/paper-dialog-scrollable/paper-dialog-scrollable.js';
import '@polymer/paper-button/paper-button.js';
import '@polymer/paper-icon-button/paper-icon-button.js';
import '@polymer/iron-icons/iron-icons.js';
import '@unicef-polymer/etools-loading/etools-loading';
import {DialogSpinnerMixin} from './dialog-spinner-mixin.js';
import {timeOut} from '@polymer/polymer/lib/utils/async.js';

/**
 * @customElement
 * @appliesMixin DialogSpinnerMixin
 * @demo demo/index.html
 */
export class EtoolsDialog extends DialogSpinnerMixin(PolymerElement) { // eslint-disable-line new-cap
  static get template() {
    // language=HTML
    return html`

      <focus-trap>
        <paper-dialog id="dialog" class\$="[[getDialogClass(size, theme)]]" opened="{{opened}}"
                      with-backdrop="[[backdrop]]" modal="[[modal]]" entry-animation="scale-up-animation"
                      exit-animation="fade-out-animation" on-iron-overlay-closed="_dialogCloseHandling"
                      on-iron-overlay-opened="_dialogOpenedHandling" no-auto-focus="[[noAutoFocus]]"
                      on-dom-change="_onDomChange">
          <paper-icon-button icon="close"
                            dialog-dismiss
                            class="close-btn"
                            disabled="[[disableDismissBtn]]">
          </paper-icon-button>

[[dialogTitle]]

          <paper-dialog-scrollable class\$="relative no-padding [[getScrollableDialogClass(noPadding)]]"
                                  part="ed-scrollable">

<slot></slot>

            <etools-loading id="etoolsLoading" loading-text="[[spinnerText]]" active="[[showSpinner]]"></etools-loading>
          </paper-dialog-scrollable>

          <template is="dom-if" if="[[showButtons]]">

                <paper-button dialog-dismiss
                              class="cancel-btn"
                              disabled="[[disableDismissBtn]]">
                  [[cancelBtnText]]
                </paper-button>
                <paper-button dialog-confirm\$="[[!keepDialogOpen]]" on-tap="_confirmBtClicked"
                              disabled="{{disableConfirmBtn}}" hidden="[[hideConfirmBtn]]" class="confirm-btn">
                  [[okBtnText]]
                </paper-button>

          </template>

          <slot id="buttons" name="buttons"></slot>
        </paper-dialog>
      </focus-trap>
    `;
  }

  static get is() {
    return 'etools-dialog';
  }

  static get properties() {
    return {
      dialogTitle: {
        type: String,
        value: ''
      },
      okBtnText: {
        type: String,
        value: 'Ok'
      },
      cancelBtnText: {
        type: String,
        value: 'Cancel'
      },
      size: {
        type: String,
        value: 'sm'
      },
      opened: {
        type: Boolean,
        value: false,
        notify: true
      },
      backdrop: {
        type: Boolean,
        value: true
      },
      modal: {
        type: Boolean,
        value: true
      },
      noPadding: {
        type: Boolean,
        value: false,
        observer: '_noContentPaddingChanged'
      },
      disableConfirmBtn: {
        type: Boolean,
        value: false
      },
      disableDismissBtn: {
        type: Boolean,
        value: false
      },
      hideConfirmBtn: {
        type: Boolean,
        value: false,
        reflectToAttribute: true
      },
      theme: {
        type: String,
        value: 'default',
        reflectToAttribute: true
      },
      noAutoFocus: {
        type: Boolean,
        value: false,
        reflectToAttribute: true
      },
      showButtons: {
        type: Boolean,
        value: true,
        reflectToAttribute: true
      }
    };
  }

  _dialogCloseHandling(event) {
    if (event.detail.confirmed === undefined) {
      //* prevent handling, here, events fired by containing elements,
      // that also use the iron-overlay-behavior
      return;
    }
    this.dispatchEvent(new CustomEvent('close', {
      detail: {confirmed: event.detail.confirmed},
      bubbles: true,
      composed: true
    }));
  }

  connectedCallback() {
    super.connectedCallback();

    this.set('showButtons', this.$.buttons.assignedNodes().length === 0);
  }

  _dialogOpenedHandling() {
    this.notifyResize();
  }

  _onDomChange() {
    this._domChangeDebouncer = Debouncer.debounce(this._domChangeDebouncer,
      timeOut.after(20),
      () => {
        this.notifyResize();
      });
  }

  getDialogClass(size, theme) {
    return size + ' ' + theme;
  }

  getScrollableDialogClass(noPadding) {
    return noPadding ? '' : 'padded-content';
  }

  _noContentPaddingChanged(noPaddingValue) {
    if (noPaddingValue === undefined) {
      return;
    }
    this.updateStyles();
  }

  getPaperDialog() {
    return this.shadowRoot.querySelector('#dialog');
  }

  notifyResize() {
    this.getPaperDialog().notifyResize();
  }

  scrollDown() {
    setTimeout(() => {
      const d = this.getPaperDialog();
      if (d) {
        const dialogScrollable = d.querySelector('paper-dialog-scrollable');
        if (dialogScrollable) {
          const scrollTarget = dialogScrollable.scrollTarget;
          scrollTarget.scrollTop = scrollTarget.scrollHeight;
        }
      }
    }, 100);
  }
}

<commit_msg>[ch25327] Remove the unnecessary scroll showing up at save in any dialog<commit_after>import {PolymerElement, html} from '@polymer/polymer/polymer-element.js';
import {Debouncer} from '@polymer/polymer/lib/utils/debounce.js';
import '@polymer/neon-animation/neon-animations.js';
import '@polymer/paper-dialog/paper-dialog.js';
import '@polymer/paper-dialog-scrollable/paper-dialog-scrollable.js';
import '@polymer/paper-button/paper-button.js';
import '@polymer/paper-icon-button/paper-icon-button.js';
import '@polymer/iron-icons/iron-icons.js';
import '@unicef-polymer/etools-loading/etools-loading';
import {DialogSpinnerMixin} from './dialog-spinner-mixin.js';
import {timeOut} from '@polymer/polymer/lib/utils/async.js';

/**
 * @customElement
 * @appliesMixin DialogSpinnerMixin
 * @demo demo/index.html
 */
export class EtoolsDialog extends DialogSpinnerMixin(PolymerElement) { // eslint-disable-line new-cap
  static get template() {
    // language=HTML
    return html`

      <focus-trap>
        <paper-dialog id="dialog" class\$="[[getDialogClass(size, theme)]]" opened="{{opened}}"
                      with-backdrop="[[backdrop]]" modal="[[modal]]" entry-animation="scale-up-animation"
                      exit-animation="fade-out-animation" on-iron-overlay-closed="_dialogCloseHandling"
                      on-iron-overlay-opened="_dialogOpenedHandling" no-auto-focus="[[noAutoFocus]]"
                      on-dom-change="_onDomChange">
          <paper-icon-button icon="close"
                            dialog-dismiss
                            class="close-btn"
                            disabled="[[disableDismissBtn]]">
          </paper-icon-button>

[[dialogTitle]]

          <etools-loading id="etoolsLoading" loading-text="[[spinnerText]]" active="[[showSpinner]]"></etools-loading>
          <paper-dialog-scrollable class\$="relative no-padding [[getScrollableDialogClass(noPadding)]]"
                                  part="ed-scrollable">

<slot></slot>

          </paper-dialog-scrollable>

          <template is="dom-if" if="[[showButtons]]">

                <paper-button dialog-dismiss
                              class="cancel-btn"
                              disabled="[[disableDismissBtn]]">
                  [[cancelBtnText]]
                </paper-button>
                <paper-button dialog-confirm\$="[[!keepDialogOpen]]" on-tap="_confirmBtClicked"
                              disabled="{{disableConfirmBtn}}" hidden="[[hideConfirmBtn]]" class="confirm-btn">
                  [[okBtnText]]
                </paper-button>

          </template>

          <slot id="buttons" name="buttons"></slot>
        </paper-dialog>
      </focus-trap>
    `;
  }

  static get is() {
    return 'etools-dialog';
  }

  static get properties() {
    return {
      dialogTitle: {
        type: String,
        value: ''
      },
      okBtnText: {
        type: String,
        value: 'Ok'
      },
      cancelBtnText: {
        type: String,
        value: 'Cancel'
      },
      size: {
        type: String,
        value: 'sm'
      },
      opened: {
        type: Boolean,
        value: false,
        notify: true
      },
      backdrop: {
        type: Boolean,
        value: true
      },
      modal: {
        type: Boolean,
        value: true
      },
      noPadding: {
        type: Boolean,
        value: false,
        observer: '_noContentPaddingChanged'
      },
      disableConfirmBtn: {
        type: Boolean,
        value: false
      },
      disableDismissBtn: {
        type: Boolean,
        value: false
      },
      hideConfirmBtn: {
        type: Boolean,
        value: false,
        reflectToAttribute: true
      },
      theme: {
        type: String,
        value: 'default',
        reflectToAttribute: true
      },
      noAutoFocus: {
        type: Boolean,
        value: false,
        reflectToAttribute: true
      },
      showButtons: {
        type: Boolean,
        value: true,
        reflectToAttribute: true
      }
    };
  }

  _dialogCloseHandling(event) {
    if (event.detail.confirmed === undefined) {
      //* prevent handling, here, events fired by containing elements,
      // that also use the iron-overlay-behavior
      return;
    }
    this.dispatchEvent(new CustomEvent('close', {
      detail: {confirmed: event.detail.confirmed},
      bubbles: true,
      composed: true
    }));
  }

  connectedCallback() {
    super.connectedCallback();

    this.set('showButtons', this.$.buttons.assignedNodes().length === 0);
  }

  _dialogOpenedHandling() {
    this.notifyResize();
  }

  _onDomChange() {
    this._domChangeDebouncer = Debouncer.debounce(this._domChangeDebouncer,
      timeOut.after(20),
      () => {
        this.notifyResize();
      });
  }

  getDialogClass(size, theme) {
    return size + ' ' + theme;
  }

  getScrollableDialogClass(noPadding) {
    return noPadding ? '' : 'padded-content';
  }

  _noContentPaddingChanged(noPaddingValue) {
    if (noPaddingValue === undefined) {
      return;
    }
    this.updateStyles();
  }

  getPaperDialog() {
    return this.shadowRoot.querySelector('#dialog');
  }

  notifyResize() {
    this.getPaperDialog().notifyResize();
  }

  scrollDown() {
    setTimeout(() => {
      const d = this.getPaperDialog();
      if (d) {
        const dialogScrollable = d.querySelector('paper-dialog-scrollable');
        if (dialogScrollable) {
          const scrollTarget = dialogScrollable.scrollTarget;
          scrollTarget.scrollTop = scrollTarget.scrollHeight;
        }
      }
    }, 100);
  }
}

<|endoftext|>