<commit_before>import platform from '../../util/platform'
import Component from '../../ui/Component'
import Scrollbar from '../scrollbar/Scrollbar'
import getRelativeBoundingRect from '../../util/getRelativeBoundingRect'
import getRelativeMouseBounds from '../../util/getRelativeMouseBounds'

/**
  Wraps content in a scroll pane.

  @class ScrollPane
  @component

  @prop {String} scrollbarType 'native' or 'substance' for a more advanced visual scrollbar. Defaults to 'native'
  @prop {String} [scrollbarPosition] 'left' or 'right' only relevant when scrollBarType: 'substance'. Defaults to 'right'
  @prop {ui/Highlights} [highlights] object that maintains highlights and can be manipulated from different sources
  @prop {ui/TOCProvider} [tocProvider] object that maintains table of content entries

  @example

  ```js
  $$(ScrollPane, {
    scrollbarType: 'substance', // defaults to native
    scrollbarPosition: 'left', // defaults to right
    onScroll: this.onScroll.bind(this),
    highlights: this.contentHighlights,
    tocProvider: this.tocProvider
  })
  ```
*/
class ScrollPane extends Component {

  /*
    Expose scrollPane as a child context
  */
  getChildContext() {
    return {
      scrollPane: this
    }
  }

  didMount() {
    if (this.refs.scrollbar && this.props.highlights) {
      this.props.highlights.on('highlights:updated', this.onHighlightsUpdated, this)
    }
    if (this.refs.scrollbar) {
      this.domObserver = new window.MutationObserver(this._onContentChanged.bind(this))
      this.domObserver.observe(this.el.getNativeElement(), {
        subtree: true,
        attributes: true,
        characterData: true,
        childList: true,
      })
      this.context.editorSession.onPosition(this._onPosition, this)
    }

    this.handleActions({
      'domSelectionRendered': this._onDomSelectionRendered
    })
    this.context.dragManager.on('drop-teaser:position-requested', this._onDropTeaserPositionRequested, this)
  }

  dispose() {
    if (this.props.highlights) {
      this.props.highlights.off(this)
    }
    this.context.editorSession.off(this)
    this.context.dragManager.off(this)
  }

  render($$) {
    let el = $$('div')
      .addClass('sc-scroll-pane')

    if (platform.isFF) {
      el.addClass('sm-firefox')
    }

    // When noStyle is provided we just use ScrollPane as a container, but without
    // any absolute positioned containers, leaving the body scrollable.
    if (!this.props.noStyle) {
      el.addClass('sm-default-style')
    }

    // Initialize Substance scrollbar (if enabled)
    if (this.props.scrollbarType === 'substance') {
      el.addClass('sm-substance-scrollbar')
      el.addClass('sm-scrollbar-position-' + this.props.scrollbarPosition)

      el.append(
        // TODO: is there a way to pass scrollbar highlights already
        // via props? Currently the are initialized with a delay
        $$(Scrollbar, {
          scrollPane: this
        }).ref('scrollbar')
          .attr('id', 'content-scrollbar')
      )

      // Scanline is debugging purposes, display: none by default.
      el.append(
        $$('div').ref("scanline").addClass('se-scanline')
      )
    }

    el.append(
      $$('div').ref('scrollable').addClass('se-scrollable').append(
        this.renderContent($$)
      ).on('scroll', this.onScroll)
    )
    return el
  }

  renderContent($$) {
    let contentEl = $$('div').ref('content').addClass('se-content')
    contentEl.append(this.props.children)
    if (this.props.contextMenu === 'custom') {
      contentEl.on('contextmenu', this._onContextMenu)
    }
    return contentEl
  }

  _onContentChanged() {
    this._contentChanged = true
  }

  _onPosition() {
    if (this.refs.scrollbar && this._contentChanged) {
      this._contentChanged = false
      this._updateScrollbar()
    }
  }

  _updateScrollbar() {
    if (this.refs.scrollbar) {
      this.refs.scrollbar.updatePositions()
    }
  }

  onHighlightsUpdated(highlights) {
    this.refs.scrollbar.extendProps({
      highlights: highlights
    })
  }

  onScroll() {
    let scrollPos = this.getScrollPosition()
    let scrollable = this.refs.scrollable
    if (this.props.onScroll) {
      this.props.onScroll(scrollPos, scrollable)
    }
    // Update TOCProvider given
    if (this.props.tocProvider) {
      this.props.tocProvider.markActiveEntry(this)
    }
    this.emit('scroll', scrollPos, scrollable)
  }

  /**
    Returns the height of scrollPane (inner content overflows)
  */
  getHeight() {
    let scrollableEl = this.getScrollableElement()
    return scrollableEl.height
  }

  /**
    Returns the cumulated height of a panel's content
  */
  getContentHeight() {
    let contentEl = this.refs.content.el.getNativeElement()
    // Important to use scrollHeight here (e.g. to consider overflowing
    // content, that stretches the content area, such as an overlay or
    // a context menu)
    return contentEl.scrollHeight
  }

  /**
    Get the `.se-content` element
  */
  getContentElement() {
    return this.refs.content.el
  }

  /**
    Get the `.se-scrollable` element
  */
  getScrollableElement() {
    return this.refs.scrollable.el
  }

  /**
    Get current scroll position (scrollTop) of `.se-scrollable` element
  */
  getScrollPosition() {
    let scrollableEl = this.getScrollableElement()
    return scrollableEl.getProperty('scrollTop')
  }

  /**
    Get offset relative to `.se-content`.

    @param {DOMNode} el DOM node that lives inside the
  */
  getPanelOffsetForElement(el) {
    let nativeEl = el.getNativeElement()
    let contentContainerEl = this.refs.content.getNativeElement()
    let rect = getRelativeBoundingRect(nativeEl, contentContainerEl)
    return rect.top
  }

  /**
    Scroll to a given sub component.

    @param {String} componentId component id, must be present in data-id attribute
  */
  scrollTo(componentId, onlyIfNotVisible) {
    let scrollableEl = this.getScrollableElement()
    let targetNode = scrollableEl.find('*[data-id="'+componentId+'"]')
    if (targetNode) {
      const offset = this.getPanelOffsetForElement(targetNode)
      let shouldScroll = true
      if (onlyIfNotVisible) {
        const height = scrollableEl.height
        const oldOffset = scrollableEl.getProperty('scrollTop')
        shouldScroll = (offset < oldOffset || oldOffset+height<offset)
      }
      if (shouldScroll) {
        scrollableEl.setProperty('scrollTop', offset)
      }
    } else {
      console.warn(componentId, 'not found in scrollable container')
    }
  }

  _onDropTeaserPositionRequested(dragState) {
    let targetEl = dragState.targetEl
    if (!targetEl) return

    let contentContainerEl = this.refs.content.getNativeElement()
    let rect = getRelativeBoundingRect(targetEl.getNativeElement(), contentContainerEl)

    if (dragState.insertMode === 'before') {
      rect.bottom = rect.bottom + rect.height
    } else {
      rect.top = rect.top + rect.height
    }
    this.emit('drop-teaser:position', { rect: rect, visible: dragState.isContainerDrop })
  }

  _onDomSelectionRendered() {
    const wsel = window.getSelection()
    if (wsel.rangeCount === 0) return
    const wrange = wsel.getRangeAt(0)
    const contentRect = this.refs.content.getNativeElement().getBoundingClientRect()
    const innerContentRect = this.refs.content.getChildAt(0).getNativeElement().getBoundingClientRect()
    const selectionRect = wrange.getBoundingClientRect()
    const positionHints = {
      contentWidth: contentRect.width,
      contentHeight: contentRect.height,
      selectionRect: _getRelativeRect(contentRect, selectionRect),
      innerContentRect: _getRelativeRect(contentRect, innerContentRect)
    }

    this.emit('overlay:position', positionHints)
    this._updateScrollbar()
  }

  _onContextMenu(e) {
    e.preventDefault()
    let contentContainerEl = this.refs.content.el.el
    let mouseBounds = getRelativeMouseBounds(e, contentContainerEl)
    let positionHints = {
      mouseBounds: mouseBounds
    }
    this.emit('context-menu:position', positionHints)
    this._updateScrollbar()
  }
}

function _getRelativeRect(parentRect, childRect) {
  var left = childRect.left - parentRect.left
  var top = childRect.top - parentRect.top
  return {
    left: left,
    top: top,
    right: parentRect.width - left - childRect.width,
    bottom: parentRect.height - top - childRect.height,
    width: childRect.width,
    height: childRect.height
  }
}

export default ScrollPane
<commit_msg>Added a workaround for the cursor bounding rect problem.<commit_after>import platform from '../../util/platform'
import Component from '../../ui/Component'
import Scrollbar from '../scrollbar/Scrollbar'
import getRelativeBoundingRect from '../../util/getRelativeBoundingRect'
import getRelativeMouseBounds from '../../util/getRelativeMouseBounds'

/**
  Wraps content in a scroll pane.

  @class ScrollPane
  @component

  @prop {String} scrollbarType 'native' or 'substance' for a more advanced visual scrollbar. Defaults to 'native'
  @prop {String} [scrollbarPosition] 'left' or 'right' only relevant when scrollBarType: 'substance'. Defaults to 'right'
  @prop {ui/Highlights} [highlights] object that maintains highlights and can be manipulated from different sources
  @prop {ui/TOCProvider} [tocProvider] object that maintains table of content entries

  @example

  ```js
  $$(ScrollPane, {
    scrollbarType: 'substance', // defaults to native
    scrollbarPosition: 'left', // defaults to right
    onScroll: this.onScroll.bind(this),
    highlights: this.contentHighlights,
    tocProvider: this.tocProvider
  })
  ```
*/
class ScrollPane extends Component {

  /*
    Expose scrollPane as a child context
  */
  getChildContext() {
    return {
      scrollPane: this
    }
  }

  didMount() {
    if (this.refs.scrollbar && this.props.highlights) {
      this.props.highlights.on('highlights:updated', this.onHighlightsUpdated, this)
    }
    if (this.refs.scrollbar) {
      this.domObserver = new window.MutationObserver(this._onContentChanged.bind(this))
      this.domObserver.observe(this.el.getNativeElement(), {
        subtree: true,
        attributes: true,
        characterData: true,
        childList: true,
      })
      this.context.editorSession.onPosition(this._onPosition, this)
    }

    this.handleActions({
      'domSelectionRendered': this._onDomSelectionRendered
    })
    this.context.dragManager.on('drop-teaser:position-requested', this._onDropTeaserPositionRequested, this)
  }

  dispose() {
    if (this.props.highlights) {
      this.props.highlights.off(this)
    }
    this.context.editorSession.off(this)
    this.context.dragManager.off(this)
  }

  render($$) {
    let el = $$('div')
      .addClass('sc-scroll-pane')

    if (platform.isFF) {
      el.addClass('sm-firefox')
    }

    // When noStyle is provided we just use ScrollPane as a container, but without
    // any absolute positioned containers, leaving the body scrollable.
    if (!this.props.noStyle) {
      el.addClass('sm-default-style')
    }

    // Initialize Substance scrollbar (if enabled)
    if (this.props.scrollbarType === 'substance') {
      el.addClass('sm-substance-scrollbar')
      el.addClass('sm-scrollbar-position-' + this.props.scrollbarPosition)

      el.append(
        // TODO: is there a way to pass scrollbar highlights already
        // via props? Currently the are initialized with a delay
        $$(Scrollbar, {
          scrollPane: this
        }).ref('scrollbar')
          .attr('id', 'content-scrollbar')
      )

      // Scanline is debugging purposes, display: none by default.
      el.append(
        $$('div').ref("scanline").addClass('se-scanline')
      )
    }

    el.append(
      $$('div').ref('scrollable').addClass('se-scrollable').append(
        this.renderContent($$)
      ).on('scroll', this.onScroll)
    )
    return el
  }

  renderContent($$) {
    let contentEl = $$('div').ref('content').addClass('se-content')
    contentEl.append(this.props.children)
    if (this.props.contextMenu === 'custom') {
      contentEl.on('contextmenu', this._onContextMenu)
    }
    return contentEl
  }

  _onContentChanged() {
    this._contentChanged = true
  }

  _onPosition() {
    if (this.refs.scrollbar && this._contentChanged) {
      this._contentChanged = false
      this._updateScrollbar()
    }
  }

  _updateScrollbar() {
    if (this.refs.scrollbar) {
      this.refs.scrollbar.updatePositions()
    }
  }

  onHighlightsUpdated(highlights) {
    this.refs.scrollbar.extendProps({
      highlights: highlights
    })
  }

  onScroll() {
    let scrollPos = this.getScrollPosition()
    let scrollable = this.refs.scrollable
    if (this.props.onScroll) {
      this.props.onScroll(scrollPos, scrollable)
    }
    // Update TOCProvider given
    if (this.props.tocProvider) {
      this.props.tocProvider.markActiveEntry(this)
    }
    this.emit('scroll', scrollPos, scrollable)
  }

  /**
    Returns the height of scrollPane (inner content overflows)
  */
  getHeight() {
    let scrollableEl = this.getScrollableElement()
    return scrollableEl.height
  }

  /**
    Returns the cumulated height of a panel's content
  */
  getContentHeight() {
    let contentEl = this.refs.content.el.getNativeElement()
    // Important to use scrollHeight here (e.g. to consider overflowing
    // content, that stretches the content area, such as an overlay or
    // a context menu)
    return contentEl.scrollHeight
  }

  /**
    Get the `.se-content` element
  */
  getContentElement() {
    return this.refs.content.el
  }

  /**
    Get the `.se-scrollable` element
  */
  getScrollableElement() {
    return this.refs.scrollable.el
  }

  /**
    Get current scroll position (scrollTop) of `.se-scrollable` element
  */
  getScrollPosition() {
    let scrollableEl = this.getScrollableElement()
    return scrollableEl.getProperty('scrollTop')
  }

  /**
    Get offset relative to `.se-content`.

    @param {DOMNode} el DOM node that lives inside the
  */
  getPanelOffsetForElement(el) {
    let nativeEl = el.getNativeElement()
    let contentContainerEl = this.refs.content.getNativeElement()
    let rect = getRelativeBoundingRect(nativeEl, contentContainerEl)
    return rect.top
  }

  /**
    Scroll to a given sub component.

    @param {String} componentId component id, must be present in data-id attribute
  */
  scrollTo(componentId, onlyIfNotVisible) {
    let scrollableEl = this.getScrollableElement()
    let targetNode = scrollableEl.find('*[data-id="'+componentId+'"]')
    if (targetNode) {
      const offset = this.getPanelOffsetForElement(targetNode)
      let shouldScroll = true
      if (onlyIfNotVisible) {
        const height = scrollableEl.height
        const oldOffset = scrollableEl.getProperty('scrollTop')
        shouldScroll = (offset < oldOffset || oldOffset+height<offset)
      }
      if (shouldScroll) {
        scrollableEl.setProperty('scrollTop', offset)
      }
    } else {
      console.warn(componentId, 'not found in scrollable container')
    }
  }

  _onDropTeaserPositionRequested(dragState) {
    let targetEl = dragState.targetEl
    if (!targetEl) return

    let contentContainerEl = this.refs.content.getNativeElement()
    let rect = getRelativeBoundingRect(targetEl.getNativeElement(), contentContainerEl)

    if (dragState.insertMode === 'before') {
      rect.bottom = rect.bottom + rect.height
    } else {
      rect.top = rect.top + rect.height
    }
    this.emit('drop-teaser:position', { rect: rect, visible: dragState.isContainerDrop })
  }

  _onDomSelectionRendered() {
    const wsel = window.getSelection()
    if (wsel.rangeCount === 0) return
    const wrange = wsel.getRangeAt(0)
    const contentRect = this.refs.content.getNativeElement().getBoundingClientRect()
    const innerContentRect = this.refs.content.getChildAt(0).getNativeElement().getBoundingClientRect()
    let selectionRect = wrange.getBoundingClientRect()
    if (selectionRect.top === 0 && selectionRect.bottom === 0) {
      selectionRect = this._fixForCursorRectBug()
    }
    const positionHints = {
      contentWidth: contentRect.width,
      contentHeight: contentRect.height,
      selectionRect: _getRelativeRect(contentRect, selectionRect),
      innerContentRect: _getRelativeRect(contentRect, innerContentRect)
    }

    this.emit('overlay:position', positionHints)
    this._updateScrollbar()
  }

  _fixForCursorRectBug() {
    let wsel = window.getSelection()
    let rects = wsel.anchorNode.parentElement.getClientRects()
    return rects[0]
  }

  _onContextMenu(e) {
    e.preventDefault()
    let contentContainerEl = this.refs.content.el.el
    let mouseBounds = getRelativeMouseBounds(e, contentContainerEl)
    let positionHints = {
      mouseBounds: mouseBounds
    }
    this.emit('context-menu:position', positionHints)
    this._updateScrollbar()
  }
}

function _getRelativeRect(parentRect, childRect) {
  var left = childRect.left - parentRect.left
  var top = childRect.top - parentRect.top
  return {
    left: left,
    top: top,
    right: parentRect.width - left - childRect.width,
    bottom: parentRect.height - top - childRect.height,
    width: childRect.width,
    height: childRect.height
  }
}

export default ScrollPane
<|endoftext|>


<commit_before>const path = require('path');
import { dappPath, fileTreeSort } from 'embark-utils';

class API {

  constructor(embark, _options) {
    this.plugins = embark.config.plugins;
  }

  registerAPIs() {
    let plugin = this.plugins.createPlugin('deployment', {});
    plugin.registerAPICall(
      'get',
      '/embark-api/file',
      (req, res) => {
        try {
          this.apiGuardBadFile(req.query.path);
        } catch (error) {
          return res.send({ error: error.message });
        }

        const name = path.basename(req.query.path);
        const content = this.fs.readFileSync(req.query.path, 'utf8');
        res.send({ name, content, path: req.query.path });
      }
    );

    plugin.registerAPICall(
      'post',
      '/embark-api/folders',
      (req, res) => {
        try {
          this.apiGuardBadFile(req.body.path);
        } catch (error) {
          return res.send({ error: error.message });
        }

        this.fs.mkdirpSync(req.body.path);
        const name = path.basename(req.body.path);
        res.send({ name, path: req.body.path });
      }
    );

    plugin.registerAPICall(
      'post',
      '/embark-api/files',
      (req, res) => {
        try {
          this.apiGuardBadFile(req.body.path);
        } catch (error) {
          return res.send({ error: error.message });
        }

        this.fs.writeFileSync(req.body.path, req.body.content, { encoding: 'utf8' });
        const name = path.basename(req.body.path);
        res.send({ name, path: req.body.path, content: req.body.content });
      }
    );

    plugin.registerAPICall(
      'delete',
      '/embark-api/file',
      (req, res) => {
        try {
          this.apiGuardBadFile(req.query.path, { ensureExists: true });
        } catch (error) {
          return res.send({ error: error.message });
        }
        this.fs.removeSync(req.query.path);
        res.send();
      }
    );

    plugin.registerAPICall(
      'get',
      '/embark-api/files',
      (req, res) => {
        const rootPath = dappPath();

        const walk = (dir, filelist = []) => this.fs.readdirSync(dir).map(name => {
          let isRoot = rootPath === dir;
          if (this.fs.statSync(path.join(dir, name)).isDirectory()) {
            return {
              isRoot,
              name,
              dirname: dir,
              path: path.join(dir, name),
              isHidden: (name.indexOf('.') === 0 || name === "node_modules"),
              children: fileTreeSort(walk(path.join(dir, name), filelist))
            };
          }
          return {
            name,
            isRoot,
            path: path.join(dir, name),
            dirname: dir,
            isHidden: (name.indexOf('.') === 0 || name === "node_modules")
          };
        });
        const files = fileTreeSort(walk(dappPath()));
        res.send(files);
      }
    );
  }

  apiGuardBadFile(pathToCheck, options = {ensureExists: false}) {
    const dir = path.dirname(pathToCheck);
    const error = new Error('Path is invalid');
    if (options.ensureExists && !this.fs.existsSync(pathToCheck)) {
      throw error;
    }
    if (!dir.startsWith(dappPath())) {
      throw error;
    }
  }

}

module.exports = API;
<commit_msg>fix(@embark/stack/pipeline): set missing `this.fs` to `embark.fs`<commit_after>const path = require('path');
import { dappPath, fileTreeSort } from 'embark-utils';

class API {

  constructor(embark, _options) {
    this.plugins = embark.config.plugins;
    this.fs = embark.fs;
  }

  registerAPIs() {
    let plugin = this.plugins.createPlugin('deployment', {});
    plugin.registerAPICall(
      'get',
      '/embark-api/file',
      (req, res) => {
        try {
          this.apiGuardBadFile(req.query.path);
        } catch (error) {
          return res.send({ error: error.message });
        }

        const name = path.basename(req.query.path);
        const content = this.fs.readFileSync(req.query.path, 'utf8');
        res.send({ name, content, path: req.query.path });
      }
    );

    plugin.registerAPICall(
      'post',
      '/embark-api/folders',
      (req, res) => {
        try {
          this.apiGuardBadFile(req.body.path);
        } catch (error) {
          return res.send({ error: error.message });
        }

        this.fs.mkdirpSync(req.body.path);
        const name = path.basename(req.body.path);
        res.send({ name, path: req.body.path });
      }
    );

    plugin.registerAPICall(
      'post',
      '/embark-api/files',
      (req, res) => {
        try {
          this.apiGuardBadFile(req.body.path);
        } catch (error) {
          return res.send({ error: error.message });
        }

        this.fs.writeFileSync(req.body.path, req.body.content, { encoding: 'utf8' });
        const name = path.basename(req.body.path);
        res.send({ name, path: req.body.path, content: req.body.content });
      }
    );

    plugin.registerAPICall(
      'delete',
      '/embark-api/file',
      (req, res) => {
        try {
          this.apiGuardBadFile(req.query.path, { ensureExists: true });
        } catch (error) {
          return res.send({ error: error.message });
        }
        this.fs.removeSync(req.query.path);
        res.send();
      }
    );

    plugin.registerAPICall(
      'get',
      '/embark-api/files',
      (req, res) => {
        const rootPath = dappPath();

        const walk = (dir, filelist = []) => this.fs.readdirSync(dir).map(name => {
          let isRoot = rootPath === dir;
          if (this.fs.statSync(path.join(dir, name)).isDirectory()) {
            return {
              isRoot,
              name,
              dirname: dir,
              path: path.join(dir, name),
              isHidden: (name.indexOf('.') === 0 || name === "node_modules"),
              children: fileTreeSort(walk(path.join(dir, name), filelist))
            };
          }
          return {
            name,
            isRoot,
            path: path.join(dir, name),
            dirname: dir,
            isHidden: (name.indexOf('.') === 0 || name === "node_modules")
          };
        });
        const files = fileTreeSort(walk(dappPath()));
        res.send(files);
      }
    );
  }

  apiGuardBadFile(pathToCheck, options = {ensureExists: false}) {
    const dir = path.dirname(pathToCheck);
    const error = new Error('Path is invalid');
    if (options.ensureExists && !this.fs.existsSync(pathToCheck)) {
      throw error;
    }
    if (!dir.startsWith(dappPath())) {
      throw error;
    }
  }

}

module.exports = API;
<|endoftext|>


<commit_before>var OS = require("os");
var dir = require("node-dir");
var temp = require("temp");
var Config = require("truffle-config");
var Resolver = require("truffle-resolver");
var Artifactor = require("truffle-artifactor");
var Test = require("../test");
var fs = require("fs");
var copy = require("../copy");
var Environment = require("../environment");

var command = {
  command: 'test',
  description: 'Run Mocha and Solidity tests',
  builder: {},
  run: function (options, done) {
    var config = Config.detect(options);
    //config.network = "test";

    if (!config.resolver) {
      config.resolver = new Resolver(config);
    }

    var files = [];

    if (options.file) {
      files = [options.file];
    } else if (options._.length > 0) {
      Array.prototype.push.apply(files, options._);
    }

    function getFiles(callback) {
      if (files.length != 0) {
        return callback(null, files);
      }

      dir.files(config.test_directory, callback);
    };

    getFiles(function(err, files) {
      files = files.filter(function(file) {
        return file.match(config.test_file_extension_regexp) != null;
      });

      temp.mkdir('test-', function(err, temporaryDirectory) {
        if (err) return done(err);

        function cleanup() {
          var args = arguments;
          // Ensure directory cleanup.
          temp.cleanup(function(err) {
            // Ignore cleanup errors.
            done.apply(null, args);
          });
        };

        function run() {
          if (!config.artifactor) {
            config.artifactor = new Artifactor(temporaryDirectory);
          }

          Test.run(config.with({
            test_files: files,
            contracts_build_directory: temporaryDirectory
          }), cleanup);
        };

        // Copy all the built files over to a temporary directory, because we
        // don't want to save any tests artifacts. Only do this if the build directory
        // exists.
        fs.stat(config.contracts_build_directory, function(err, stat) {
          if (err) return run();

          copy(config.contracts_build_directory, temporaryDirectory, function(err) {
            if (err) return done(err);

            Environment.detect(config, function(err) {
              if (err) return done(err);

              config.logger.log("Using network '" + config.network + "'." + OS.EOL);

              run();
            });
          });
        });
      });
    });
  }
}

module.exports = command;
<commit_msg>Fix `truffle test`. Looks like `Environments` saved an artifactor and then our artifactor that pointed to the temp directory didn’t get created.<commit_after>var OS = require("os");
var dir = require("node-dir");
var temp = require("temp");
var Config = require("truffle-config");
var Resolver = require("truffle-resolver");
var Artifactor = require("truffle-artifactor");
var Test = require("../test");
var fs = require("fs");
var copy = require("../copy");
var Environment = require("../environment");

var command = {
  command: 'test',
  description: 'Run Mocha and Solidity tests',
  builder: {},
  run: function (options, done) {
    var config = Config.detect(options);
    //config.network = "test";

    if (!config.resolver) {
      config.resolver = new Resolver(config);
    }

    var files = [];

    if (options.file) {
      files = [options.file];
    } else if (options._.length > 0) {
      Array.prototype.push.apply(files, options._);
    }

    function getFiles(callback) {
      if (files.length != 0) {
        return callback(null, files);
      }

      dir.files(config.test_directory, callback);
    };

    getFiles(function(err, files) {
      files = files.filter(function(file) {
        return file.match(config.test_file_extension_regexp) != null;
      });

      temp.mkdir('test-', function(err, temporaryDirectory) {
        if (err) return done(err);

        function cleanup() {
          var args = arguments;
          // Ensure directory cleanup.
          temp.cleanup(function(err) {
            // Ignore cleanup errors.
            done.apply(null, args);
          });
        };

        function run() {
          // Set a new artifactor; don't rely on the one created by Environments.
          // TODO: Make the test artifactor configurable.
          config.artifactor = new Artifactor(temporaryDirectory);

          Test.run(config.with({
            test_files: files,
            contracts_build_directory: temporaryDirectory
          }), cleanup);
        };

        // Copy all the built files over to a temporary directory, because we
        // don't want to save any tests artifacts. Only do this if the build directory
        // exists.
        fs.stat(config.contracts_build_directory, function(err, stat) {
          if (err) return run();

          copy(config.contracts_build_directory, temporaryDirectory, function(err) {
            if (err) return done(err);

            Environment.detect(config, function(err) {
              if (err) return done(err);

              config.logger.log("Using network '" + config.network + "'." + OS.EOL);

              run();
            });
          });
        });
      });
    });
  }
}

module.exports = command;
<|endoftext|>


<commit_before>import React, {useState, useRef, useEffect} from 'react'
import PropTypes from 'prop-types'
import {usePopper} from 'react-popper'
import ReactDOM from 'react-dom'

import {StyledPopper} from './StyledComponents'

/**
 * Menu realised with popper.
 */
const Menu = ({referenceElement, onClose, open, children}) => {
  const thisEl = useRef(null)
  const [popperElement, setPopperElement] = useState(null)

  const handleClickOutside = e => {
    if (thisEl && thisEl.current && !thisEl.current.contains(e.target)
      && referenceElement && !referenceElement.contains(e.target)) {
      onClose()
    }
  }

  useEffect(() => {
    document.addEventListener('mousedown', handleClickOutside)
    return () => {
      document.removeEventListener('mousedown', handleClickOutside)
    }
  }, [referenceElement])

  const {styles, attributes} = usePopper(referenceElement, popperElement, {
    placement: 'bottom-start',
    modifiers: [
      {
        name: 'offset',
        options: {
          offset: [0, 5]
        }
      }
    ]
  }
  )

  if (!open) {
    return null
  }

  return ReactDOM.createPortal(

      {children && }

    , document.body)
}

Menu.propTypes = {
  /**
   * Element to attach popper menu to
   */
  referenceElement: PropTypes.any,
  /**
   * Will be called if menu is closed
   */
  onClose: PropTypes.func.isRequired,
  /**
   * Tree of <MenuItems>
   */
  children: PropTypes.node,
  /**
   * Whether menu is open or not
   */
  open: PropTypes.bool
}

export default Menu
<commit_msg>refactor(tocco-ui): fix menu<commit_after>import React, {useState, useRef, useEffect} from 'react'
import PropTypes from 'prop-types'
import {usePopper} from 'react-popper'

import {StyledPopper} from './StyledComponents'

/**
 * Menu realised with popper.
 */
const Menu = ({referenceElement, onClose, open, children}) => {
  const thisEl = useRef(null)
  const [popperElement, setPopperElement] = useState(null)

  const handleClickOutside = e => {
    if (thisEl && thisEl.current && !thisEl.current.contains(e.target)
      && referenceElement && !referenceElement.contains(e.target)) {
      onClose()
    }
  }

  useEffect(() => {
    document.addEventListener('mousedown', handleClickOutside)
    return () => {
      document.removeEventListener('mousedown', handleClickOutside)
    }
  }, [referenceElement])

  const {styles, attributes} = usePopper(referenceElement, popperElement, {
    placement: 'bottom-start',
    modifiers: [
      {
        name: 'offset',
        options: {
          offset: [0, 5]
        }
      }
    ]
  }
  )

  if (!open) {
    return null
  }

  return (

      {children && }

  )
}

Menu.propTypes = {
  /**
   * Element to attach popper menu to
   */
  referenceElement: PropTypes.any,
  /**
   * Will be called if menu is closed
   */
  onClose: PropTypes.func.isRequired,
  /**
   * Tree of <MenuItems>
   */
  children: PropTypes.node,
  /**
   * Whether menu is open or not
   */
  open: PropTypes.bool
}

export default Menu
<|endoftext|>


<commit_before>var Backbone = require('backbone');
var WidgetOptionModel = require('../../../../../../../javascripts/cartodb3/components/modals/add-widgets/widget-option-model');
var StatView = require('../../../../../../../javascripts/cartodb3/editor/layers/layer-content-views/data/stat-view');
var TableStats = require('../../../../../../../javascripts/cartodb3/components/modals/add-widgets/tablestats');

describe('editor/layers/layers-content-view/data/stat-view', function () {
  var nullValue = 0.0059;
  var topCatValue = 0.8534;
  var stackLayoutModel;
  var view;
  var graph;

  function createView (withBooleanColumn, withProperBoolean) {
    var layerDefinitionModel = new Backbone.Model();
    var analysisDefinitionNodeModel = new Backbone.Model({
      id: 'a0'
    });

    var tuples = [{
      columnModel: new Backbone.Model(),
      analysisDefinitionNodeModel: analysisDefinitionNodeModel,
      layerDefinitionModel: layerDefinitionModel
    }];

    var statModel = new WidgetOptionModel({
      type: 'category',
      column: withBooleanColumn ? 'boolean' : 'number',
      title: 'location of wadus',
      name: 'location',
      layer_index: 0,
      table: 'marias',
      selected: false,
      tuples: tuples
    });

    stackLayoutModel = jasmine.createSpyObj('stackLayoutModel', ['goToStep']);

    view = new StatView({
      stackLayoutModel: stackLayoutModel,
      statModel: statModel,
      isVisible: true
    });

    spyOn(view, 'clean');

    graph = {
      stats: {
        freqs: true
      },
      getNullsPercentage: function () {
        return nullValue;
      },
      getPercentageInTopCategories: function () {
        return topCatValue;
      },
      getCategory: function () {
        return 'foo';
      },
      getTrues: function () {
        return withProperBoolean ? 0.45635 : undefined;
      }
    };

    spyOn(TableStats.prototype, 'graphFor').and.callFake(function () {
      view.model.set({graph: graph});
    });

    view.render();
    view.$el.appendTo(document.body);
  }

  beforeEach(function () {
  });

  afterEach(function () {
    var parent = view.el.parentNode;
    parent && parent.removeChild(view.el);
    view.clean();
  });

  it('should render properly', function () {
    createView();
    expect(view.$('.js-checkbox').length).toBe(1);
    expect(view.$('.js-stat').length).toBe(1);
    expect(view.$('.js-title').text()).toBe('location of wadus');
    expect(view.$('.StatsList-tag').text()).toBe('number');
  });

  it('should select/unselect the widget properly', function () {
    createView();
    view.$('.js-checkbox').trigger('click');
    expect(view._statModel.get('selected')).toBe(true);
    expect(view.$('.js-style').hasClass('is-hidden')).toBe(false);
    view.$('.js-checkbox').trigger('click');
    expect(view._statModel.get('selected')).toBe(false);
    expect(view.$('.js-style').length).toBe(0);
  });

  it('should be selected when widget is added', function () {
    createView();
    view._statModel.set({selected: true});
    expect(view.$('.js-checkbox').prop('checked')).toBe(true);
  });

  it('should go to style widget when clicked on style link', function () {
    createView();
    view.$('.js-checkbox').trigger('click');
    expect(view.$('.js-style').hasClass('is-hidden')).toBe(false);
    view.$('.js-style').trigger('click');
    expect(stackLayoutModel.goToStep).toHaveBeenCalled();
  });

  it('should not have any leaks', function () {
    createView();
    expect(view).toHaveNoLeaks();
  });

  describe('_showCategory', function () {
    it('non-zero null data should be rounded up to two decimal positions.', function () {
      // Arrange
      createView();

      // Act
      view._showCategory(graph);

      // Assert
      expect(view.$('.js-null')[0].textContent.trim()).toEqual('0.59editor.data.stats.null');
      expect(view.$('.js-percent')[0].textContent.trim()).toEqual('85.3editor.data.stats.top-cat');
    });

    it('boolean column proper data should be rounded up to two decimal positions', function () {
      // Arrange
      createView(true, true);

      // Act
      view._showCategory(graph);

      // Arrange
      expect(view.$('.js-percent')[0].textContent.trim()).toEqual('45.6editor.data.stats.trues');
    });

    it('boolean column with no proper data should not be rendered', function () {
      // Arrange
      createView(true, false);

      // Act
      view._showCategory(graph);

      // Arrange
      expect(view.$('.js-percent')[0].textContent.trim()).toBeFalsy();
    });
  });
});
<commit_msg>tooltips: stat view specs<commit_after>var Backbone = require('backbone');
var _ = require('underscore');
var WidgetOptionModel = require('../../../../../../../javascripts/cartodb3/components/modals/add-widgets/widget-option-model');
var StatView = require('../../../../../../../javascripts/cartodb3/editor/layers/layer-content-views/data/stat-view');
var TableStats = require('../../../../../../../javascripts/cartodb3/components/modals/add-widgets/tablestats');

describe('editor/layers/layers-content-view/data/stat-view', function () {
  var nullValue = 0.0059;
  var topCatValue = 0.8534;
  var stackLayoutModel;
  var view;
  var graph;

  function createView (withBooleanColumn, withProperBoolean) {
    var layerDefinitionModel = new Backbone.Model();
    var analysisDefinitionNodeModel = new Backbone.Model({
      id: 'a0'
    });

    var tuples = [{
      columnModel: new Backbone.Model(),
      analysisDefinitionNodeModel: analysisDefinitionNodeModel,
      layerDefinitionModel: layerDefinitionModel
    }];

    var statModel = new WidgetOptionModel({
      type: 'category',
      column: withBooleanColumn ? 'boolean' : 'number',
      title: 'location of wadus',
      name: 'location',
      layer_index: 0,
      table: 'marias',
      selected: false,
      tuples: tuples
    });

    stackLayoutModel = jasmine.createSpyObj('stackLayoutModel', ['goToStep']);

    view = new StatView({
      stackLayoutModel: stackLayoutModel,
      statModel: statModel,
      isVisible: true
    });

    spyOn(view, 'clean');

    graph = {
      stats: {
        freqs: true
      },
      getNullsPercentage: function () {
        return nullValue;
      },
      getPercentageInTopCategories: function () {
        return topCatValue;
      },
      getCategory: function () {
        return 'foo';
      },
      getTrues: function () {
        return withProperBoolean ? 0.45635 : undefined;
      }
    };

    spyOn(TableStats.prototype, 'graphFor').and.callFake(function () {
      view.model.set({graph: graph});
    });

    view.render();
    view.$el.appendTo(document.body);
  }

  describe('.render', function () {
    it('should render properly', function () {
      createView();
      expect(view.$('.js-checkbox').length).toBe(1);
      expect(view.$('.js-stat').length).toBe(1);
      expect(view.$('.js-title').text()).toBe('location of wadus');
      expect(view.$('.StatsList-tag').text()).toBe('number');
      expect(_.size(view._subviews)).toBe(1); // [tooltip]
    });
  });

  it('should select/unselect the widget properly', function () {
    createView();
    view.$('.js-checkbox').trigger('click');
    expect(view._statModel.get('selected')).toBe(true);
    expect(view.$('.js-style').hasClass('is-hidden')).toBe(false);
    view.$('.js-checkbox').trigger('click');
    expect(view._statModel.get('selected')).toBe(false);
    expect(view.$('.js-style').length).toBe(0);
  });

  it('should be selected when widget is added', function () {
    createView();
    view._statModel.set({selected: true});
    expect(view.$('.js-checkbox').prop('checked')).toBe(true);
  });

  it('should go to style widget when clicked on style link', function () {
    createView();
    view.$('.js-checkbox').trigger('click');
    expect(view.$('.js-style').hasClass('is-hidden')).toBe(false);
    view.$('.js-style').trigger('click');
    expect(stackLayoutModel.goToStep).toHaveBeenCalled();
  });

  it('should not have any leaks', function () {
    createView();
    expect(view).toHaveNoLeaks();
  });

  describe('_showCategory', function () {
    it('non-zero null data should be rounded up to two decimal positions.', function () {
      // Arrange
      createView();

      // Act
      view._showCategory(graph);

      // Assert
      expect(view.$('.js-null')[0].textContent.trim()).toEqual('0.59editor.data.stats.null');
      expect(view.$('.js-percent')[0].textContent.trim()).toEqual('85.3editor.data.stats.top-cat');
    });

    it('boolean column proper data should be rounded up to two decimal positions', function () {
      // Arrange
      createView(true, true);

      // Act
      view._showCategory(graph);

      // Arrange
      expect(view.$('.js-percent')[0].textContent.trim()).toEqual('45.6editor.data.stats.trues');
    });

    it('boolean column with no proper data should not be rendered', function () {
      // Arrange
      createView(true, false);

      // Act
      view._showCategory(graph);

      // Arrange
      expect(view.$('.js-percent')[0].textContent.trim()).toBeFalsy();
    });
  });

  it('should have no leaks', function () {
    expect(view).toHaveNoLeaks();
  });

  afterEach(function () {
    var parent = view.el.parentNode;
    parent && parent.removeChild(view.el);
    view.clean();
  });
});
<|endoftext|>


<commit_before>import {isArray} from 'vega-util';

function valueSchema(type, nullable) {
  type = isArray(type) ? {"enum": type} : {"type": type};

  var modType = type.type === "number" ? "number" : "string",
      valueType = nullable ? {"oneOf": [type, {"type": "null"}]} : type;

  var valueRef = {
    "type": "object",
    "allOf": [
      { "$ref": "#/refs/" + modType + "Modifiers" },
      {
        "anyOf": [
          {
            "oneOf": [
              { "$ref": "#/refs/signal" },
              {
                "properties": {"value": valueType},
                "required": ["value"]
              },
              {
                "properties": {"field": {"$ref": "#/refs/field"}},
                "required": ["field"]
              },
              {
                "properties": {"range": {"type": ["number", "boolean"]}},
                "required": ["range"]
              }
            ]
          },
          {
            "required": ["scale", "value"]
          },
          {
            "required": ["scale", "band"]
          },
          {
            "required": ["offset"]
          }
        ]
      }
    ]
  };

  return {
    "oneOf": [
      {
        "type": "array",
        "items": {
          "allOf": [
            {"$ref": "#/defs/rule"},
            valueRef
          ]
        }
      },
      valueRef
    ]
  };
}

export default {
  "refs": {

    "field": {
      "title": "FieldRef",
      "oneOf": [
        {"type": "string"},
        {
          "oneOf": [
            { "$ref": "#/refs/signal" },
            {
              "type": "object",
              "properties": {
                "datum": {"$ref": "#/refs/field"}
              },
              "required": ["datum"],
              "additionalProperties": false
            },
            {
              "type": "object",
              "properties": {
                "group": {"$ref": "#/refs/field"},
                "level": {"type": "number"}
              },
              "required": ["group"],
              "additionalProperties": false
            },
            {
              "type": "object",
              "properties": {
                "parent": {"$ref": "#/refs/field"},
                "level": {"type": "number"}
              },
              "required": ["parent"],
              "additionalProperties": false
            }
          ]
        }
      ]
    },

    "scale": {
      "$ref": "#/refs/field"
    },

    "stringModifiers": {
      "properties": {
        "scale": {"$ref": "#/refs/scale"}
      }
    },

    "numberModifiers": {
      "properties": {
        "exponent": {
          "oneOf": [
            {"type": "number"},
            {"$ref": "#/refs/numberValue"}
          ]
        },
        "mult": {
          "oneOf": [
            {"type": "number"},
            {"$ref": "#/refs/numberValue"}
          ]
        },
        "offset": {
          "oneOf": [
            {"type": "number"},
            {"$ref": "#/refs/numberValue"}
          ]
        },
        "round": {"type": "boolean", "default": false},
        "scale": {"$ref": "#/refs/scale"},
        "band": {"type": ["number", "boolean"]},
        "extra": {"type": "boolean"}
      }
    },

    "value": valueSchema({}, "value"),
    "numberValue": valueSchema("number"),
    "stringValue": valueSchema("string"),
    "booleanValue": valueSchema("boolean"),
    "arrayValue": valueSchema("array"),
    "nullableStringValue": valueSchema("string", true),

    "colorRGB": {
      "type": "object",
      "properties": {
        "r": {"$ref": "#/refs/numberValue"},
        "g": {"$ref": "#/refs/numberValue"},
        "b": {"$ref": "#/refs/numberValue"}
      },
      "required": ["r", "g", "b"]
    },

    "colorHSL": {
      "type": "object",
      "properties": {
        "h": {"$ref": "#/refs/numberValue"},
        "s": {"$ref": "#/refs/numberValue"},
        "l": {"$ref": "#/refs/numberValue"}
      },
      "required": ["h", "s", "l"]
    },

    "colorLAB": {
      "type": "object",
      "properties": {
        "l": {"$ref": "#/refs/numberValue"},
        "a": {"$ref": "#/refs/numberValue"},
        "b": {"$ref": "#/refs/numberValue"}
      },
      "required": ["l", "a", "b"]
    },

    "colorHCL": {
      "type": "object",
      "properties": {
        "h": {"$ref": "#/refs/numberValue"},
        "c": {"$ref": "#/refs/numberValue"},
        "l": {"$ref": "#/refs/numberValue"}
      },
      "required": ["h", "c", "l"]
    },

    "colorValue": {
      "title": "ColorRef",
      "oneOf": [
        {"$ref": "#/refs/nullableStringValue"},
        {
          "type": "object",
          "properties": {
            "gradient": {"$ref": "#/refs/scale"}
          },
          "additionalProperties": false,
          "required": ["gradient"]
        },
        {
          "type": "object",
          "properties": {
            "color": {
              "oneOf": [
                {"$ref": "#/refs/colorRGB"},
                {"$ref": "#/refs/colorHSL"},
                {"$ref": "#/refs/colorLAB"},
                {"$ref": "#/refs/colorHCL"}
              ]
            }
          },
          "additionalProperties": false,
          "required": ["color"]
        }
      ]
    }
  },

  "defs": {
    "rule": {
      "type": "object",
      "properties": {
        "test": {"type": "string"}
      }
    },
    "encodeEntry": {
      "title": "Mark encode property set",
      "type": "object",
      "properties": {
        // Common Properties
        "x": {"$ref": "#/refs/numberValue"},
        "x2": {"$ref": "#/refs/numberValue"},
        "xc": {"$ref": "#/refs/numberValue"},
        "width": {"$ref": "#/refs/numberValue"},
        "y": {"$ref": "#/refs/numberValue"},
        "y2": {"$ref": "#/refs/numberValue"},
        "yc": {"$ref": "#/refs/numberValue"},
        "height": {"$ref": "#/refs/numberValue"},
        "opacity": {"$ref": "#/refs/numberValue"},
        "fill": {"$ref": "#/refs/colorValue"},
        "fillOpacity": {"$ref": "#/refs/numberValue"},
        "stroke": {"$ref": "#/refs/colorValue"},
        "strokeWidth": {"$ref": "#/refs/numberValue"},
        "strokeOpacity": {"$ref": "#/refs/numberValue"},
        "strokeDash": {"$ref": "#/refs/arrayValue"},
        "strokeDashOffset": {"$ref": "#/refs/numberValue"},
        "cursor": {"$ref": "#/refs/stringValue"},

        // Group-mark properties
        "clip": {"$ref": "#/refs/booleanValue"},

        // Symbol-mark properties
        "size": {"$ref": "#/refs/numberValue"},
        "shape": {
          "anyOf": [
            {"type": "string"},
            {"$ref": "#/refs/stringValue"}
          ]
        },

        // Path-mark properties
        "path": {"$ref": "#/refs/stringValue"},

        // Arc-mark properties
        "innerRadius": {"$ref": "#/refs/numberValue"},
        "outerRadius": {"$ref": "#/refs/numberValue"},
        "startAngle": {"$ref": "#/refs/numberValue"},
        "endAngle": {"$ref": "#/refs/numberValue"},

        // Area- and line-mark properties
        "interpolate": {"$ref": "#/refs/stringValue"},
        "tension": {"$ref": "#/refs/numberValue"},
        "orient": valueSchema(["horizontal", "vertical"]),

        // Image-mark properties
        "url": {"$ref": "#/refs/stringValue"},
        "align": valueSchema(["left", "right", "center"]),
        "baseline": valueSchema(["top", "middle", "bottom", "alphabetic"]),

        // Text-mark properties
        "text": {"$ref": "#/refs/stringValue"},
        "dir": {"$ref": "#/refs/stringValue"},
        "ellipsis": {"$ref": "#/refs/stringValue"},
        "limit": {"$ref": "#/refs/numberValue"},
        "dx": {"$ref": "#/refs/numberValue"},
        "dy": {"$ref": "#/refs/numberValue"},
        "radius":{"$ref": "#/refs/numberValue"},
        "theta": {"$ref": "#/refs/numberValue"},
        "angle": {"$ref": "#/refs/numberValue"},
        "font": {"$ref": "#/refs/stringValue"},
        "fontSize": {"$ref": "#/refs/numberValue"},
        "fontWeight": {"$ref": "#/refs/nullableStringValue"},
        "fontStyle": {"$ref": "#/refs/stringValue"}
      },
      "additionalProperties": true
    },
    "encode": {
      "type": "object",
      "patternProperties": {
        "^.+$": {"$ref": "#/defs/encodeEntry"},
      },
      "additionalProperties": false
    }
  }
};
<commit_msg>Fix fontWeight schema.<commit_after>import {isArray} from 'vega-util';

function valueSchema(type, nullable) {
  type = isArray(type) ? {"enum": type} : {"type": type};

  var modType = type.type === "number" ? "number" : "string",
      valueType = nullable ? {"oneOf": [type, {"type": "null"}]} : type;

  var valueRef = {
    "type": "object",
    "allOf": [
      { "$ref": "#/refs/" + modType + "Modifiers" },
      {
        "anyOf": [
          {
            "oneOf": [
              { "$ref": "#/refs/signal" },
              {
                "properties": {"value": valueType},
                "required": ["value"]
              },
              {
                "properties": {"field": {"$ref": "#/refs/field"}},
                "required": ["field"]
              },
              {
                "properties": {"range": {"type": ["number", "boolean"]}},
                "required": ["range"]
              }
            ]
          },
          {
            "required": ["scale", "value"]
          },
          {
            "required": ["scale", "band"]
          },
          {
            "required": ["offset"]
          }
        ]
      }
    ]
  };

  return {
    "oneOf": [
      {
        "type": "array",
        "items": {
          "allOf": [
            {"$ref": "#/defs/rule"},
            valueRef
          ]
        }
      },
      valueRef
    ]
  };
}

export default {
  "refs": {

    "field": {
      "title": "FieldRef",
      "oneOf": [
        {"type": "string"},
        {
          "oneOf": [
            { "$ref": "#/refs/signal" },
            {
              "type": "object",
              "properties": {
                "datum": {"$ref": "#/refs/field"}
              },
              "required": ["datum"],
              "additionalProperties": false
            },
            {
              "type": "object",
              "properties": {
                "group": {"$ref": "#/refs/field"},
                "level": {"type": "number"}
              },
              "required": ["group"],
              "additionalProperties": false
            },
            {
              "type": "object",
              "properties": {
                "parent": {"$ref": "#/refs/field"},
                "level": {"type": "number"}
              },
              "required": ["parent"],
              "additionalProperties": false
            }
          ]
        }
      ]
    },

    "scale": {
      "$ref": "#/refs/field"
    },

    "stringModifiers": {
      "properties": {
        "scale": {"$ref": "#/refs/scale"}
      }
    },

    "numberModifiers": {
      "properties": {
        "exponent": {
          "oneOf": [
            {"type": "number"},
            {"$ref": "#/refs/numberValue"}
          ]
        },
        "mult": {
          "oneOf": [
            {"type": "number"},
            {"$ref": "#/refs/numberValue"}
          ]
        },
        "offset": {
          "oneOf": [
            {"type": "number"},
            {"$ref": "#/refs/numberValue"}
          ]
        },
        "round": {"type": "boolean", "default": false},
        "scale": {"$ref": "#/refs/scale"},
        "band": {"type": ["number", "boolean"]},
        "extra": {"type": "boolean"}
      }
    },

    "value": valueSchema({}, "value"),
    "numberValue": valueSchema("number"),
    "stringValue": valueSchema("string"),
    "booleanValue": valueSchema("boolean"),
    "arrayValue": valueSchema("array"),
    "nullableStringValue": valueSchema("string", true),
    "fontWeightValue": valueSchema([
      null, "normal", "bold", "lighter", "bolder",
      "100", "200", "300", "400", "500", "600", "700", "800", "900",
      100, 200, 300, 400, 500, 600, 700, 800, 900
    ]),

    "colorRGB": {
      "type": "object",
      "properties": {
        "r": {"$ref": "#/refs/numberValue"},
        "g": {"$ref": "#/refs/numberValue"},
        "b": {"$ref": "#/refs/numberValue"}
      },
      "required": ["r", "g", "b"]
    },

    "colorHSL": {
      "type": "object",
      "properties": {
        "h": {"$ref": "#/refs/numberValue"},
        "s": {"$ref": "#/refs/numberValue"},
        "l": {"$ref": "#/refs/numberValue"}
      },
      "required": ["h", "s", "l"]
    },

    "colorLAB": {
      "type": "object",
      "properties": {
        "l": {"$ref": "#/refs/numberValue"},
        "a": {"$ref": "#/refs/numberValue"},
        "b": {"$ref": "#/refs/numberValue"}
      },
      "required": ["l", "a", "b"]
    },

    "colorHCL": {
      "type": "object",
      "properties": {
        "h": {"$ref": "#/refs/numberValue"},
        "c": {"$ref": "#/refs/numberValue"},
        "l": {"$ref": "#/refs/numberValue"}
      },
      "required": ["h", "c", "l"]
    },

    "colorValue": {
      "title": "ColorRef",
      "oneOf": [
        {"$ref": "#/refs/nullableStringValue"},
        {
          "type": "object",
          "properties": {
            "gradient": {"$ref": "#/refs/scale"}
          },
          "additionalProperties": false,
          "required": ["gradient"]
        },
        {
          "type": "object",
          "properties": {
            "color": {
              "oneOf": [
                {"$ref": "#/refs/colorRGB"},
                {"$ref": "#/refs/colorHSL"},
                {"$ref": "#/refs/colorLAB"},
                {"$ref": "#/refs/colorHCL"}
              ]
            }
          },
          "additionalProperties": false,
          "required": ["color"]
        }
      ]
    }
  },

  "defs": {
    "rule": {
      "type": "object",
      "properties": {
        "test": {"type": "string"}
      }
    },
    "encodeEntry": {
      "title": "Mark encode property set",
      "type": "object",
      "properties": {
        // Common Properties
        "x": {"$ref": "#/refs/numberValue"},
        "x2": {"$ref": "#/refs/numberValue"},
        "xc": {"$ref": "#/refs/numberValue"},
        "width": {"$ref": "#/refs/numberValue"},
        "y": {"$ref": "#/refs/numberValue"},
        "y2": {"$ref": "#/refs/numberValue"},
        "yc": {"$ref": "#/refs/numberValue"},
        "height": {"$ref": "#/refs/numberValue"},
        "opacity": {"$ref": "#/refs/numberValue"},
        "fill": {"$ref": "#/refs/colorValue"},
        "fillOpacity": {"$ref": "#/refs/numberValue"},
        "stroke": {"$ref": "#/refs/colorValue"},
        "strokeWidth": {"$ref": "#/refs/numberValue"},
        "strokeOpacity": {"$ref": "#/refs/numberValue"},
        "strokeDash": {"$ref": "#/refs/arrayValue"},
        "strokeDashOffset": {"$ref": "#/refs/numberValue"},
        "cursor": {"$ref": "#/refs/stringValue"},

        // Group-mark properties
        "clip": {"$ref": "#/refs/booleanValue"},

        // Symbol-mark properties
        "size": {"$ref": "#/refs/numberValue"},
        "shape": {
          "anyOf": [
            {"type": "string"},
            {"$ref": "#/refs/stringValue"}
          ]
        },

        // Path-mark properties
        "path": {"$ref": "#/refs/stringValue"},

        // Arc-mark properties
        "innerRadius": {"$ref": "#/refs/numberValue"},
        "outerRadius": {"$ref": "#/refs/numberValue"},
        "startAngle": {"$ref": "#/refs/numberValue"},
        "endAngle": {"$ref": "#/refs/numberValue"},

        // Area- and line-mark properties
        "interpolate": {"$ref": "#/refs/stringValue"},
        "tension": {"$ref": "#/refs/numberValue"},
        "orient": valueSchema(["horizontal", "vertical"]),

        // Image-mark properties
        "url": {"$ref": "#/refs/stringValue"},
        "align": valueSchema(["left", "right", "center"]),
        "baseline": valueSchema(["top", "middle", "bottom", "alphabetic"]),

        // Text-mark properties
        "text": {"$ref": "#/refs/stringValue"},
        "dir": {"$ref": "#/refs/stringValue"},
        "ellipsis": {"$ref": "#/refs/stringValue"},
        "limit": {"$ref": "#/refs/numberValue"},
        "dx": {"$ref": "#/refs/numberValue"},
        "dy": {"$ref": "#/refs/numberValue"},
        "radius":{"$ref": "#/refs/numberValue"},
        "theta": {"$ref": "#/refs/numberValue"},
        "angle": {"$ref": "#/refs/numberValue"},
        "font": {"$ref": "#/refs/stringValue"},
        "fontSize": {"$ref": "#/refs/numberValue"},
        "fontWeight": {"$ref": "#/refs/fontWeightValue"},
        "fontStyle": {"$ref": "#/refs/stringValue"}
      },
      "additionalProperties": true
    },
    "encode": {
      "type": "object",
      "patternProperties": {
        "^.+$": {"$ref": "#/defs/encodeEntry"},
      },
      "additionalProperties": false
    }
  }
};
<|endoftext|>


<commit_before>import React, { Component } from "react";
import PropTypes from "prop-types";

import TreeViewBehavior from "./behaviors/TreeViewBehavior";

import TreeViewPresenter from "./presenters/TreeViewPresenter";

import { AVAILABLE_INDICATORS, indicators } from "./constants";

export default class TreeView extends Component {
  static propTypes = {
    /**
     * Alternating background stripes
     */
    alternateBg: PropTypes.bool,
    /**
     * Content of the TreeView
     * Accepts TreeItem components
     */
    children: PropTypes.node,
    /**
     * Visual guidelines for each TreeItem
     */
    guidelines: PropTypes.bool,
    /**
     * Indicator icon
     */
    indicator: PropTypes.oneOf(AVAILABLE_INDICATORS),
    /**
     * Triggers when a key is pressed
     * In addition to passing back the event you get the
     * following internal methods in an object:
     *  - getActiveTreeItemId(): => gets the active treeitem's id
     *  - getActiveTreeItemIndex(): =>  gets the active treeitem's index
     *  - getTreeItemArray(): => gets the array of treeitems that are visible
     *  - setActiveTreeItemId(id: string): => sets the active treeitem's id
     *  - setActiveTreeItemIndex(index: number) => sets the active treeitem's index
     *  - setKeyboardOpenId(id: string) => opens the treeitem
     */
    onKeyDown: PropTypes.func,
    /**
     * Adds custom/overriding styles
     */
    stylesheet: PropTypes.func,
    /**
     * If treeNode prop is used it will render instead
     * of children.
     * Additional info can be passed into the meta property
     * Additional HTML attributes can be added to the top
     * level of and spread to the HTML elements
     */
    treeNode: PropTypes.arrayOf(
      PropTypes.shape({
        id: PropTypes.number,
        parentId: PropTypes.number,
        meta: PropTypes.shape({
          label: PropTypes.string,
          collapsed: PropTypes.bool,
          active: PropTypes.bool,
          icon: PropTypes.node
        })
      })
    )
  };

  static defaultProps = {
    indicator: indicators.CARET
  };

  render() {
    const {
      children,
      alternateBg,
      guidelines,
      onKeyDown,
      stylesheet,
      treeNode,
      ...otherProps
    } = this.props;

    return (
      <TreeViewBehavior
        {...otherProps}
        onKeyDown={onKeyDown}
        treeNode={treeNode}
      >
        {({
          getActiveTreeItemId,
          getActiveTreeItemIndex,
          getCurrentItemClicked,
          getKeyboardOpenId,
          getTreeItemArray,
          setActiveTreeItemId,
          setActiveTreeItemIndex,
          setKeyboardOpenId,
          setTreeItemArray,
          handleKeyDown,
          setTreeViewRef,
          treeViewRef
        }) => (
          <TreeViewPresenter
            {...otherProps}
            alternateBg={alternateBg}
            treeNode={treeNode}
            getActiveTreeItemId={getActiveTreeItemId}
            getActiveTreeItemIndex={getActiveTreeItemIndex}
            getCurrentItemClicked={getCurrentItemClicked}
            getKeyboardOpenId={getKeyboardOpenId}
            getTreeItemArray={getTreeItemArray}
            setActiveTreeItemId={setActiveTreeItemId}
            setActiveTreeItemIndex={setActiveTreeItemIndex}
            setKeyboardOpenId={setKeyboardOpenId}
            setTreeItemArray={setTreeItemArray}
            guidelines={guidelines}
            onKeyDown={handleKeyDown}
            setTreeViewRef={setTreeViewRef}
            stylesheet={stylesheet}
            treeViewRef={treeViewRef}
          >
            {children}
          </TreeViewPresenter>
        )}
      </TreeViewBehavior>
    );
  }
}
<commit_msg>fix: tree-view change prop type<commit_after>import React, { Component } from "react";
import PropTypes from "prop-types";

import TreeViewBehavior from "./behaviors/TreeViewBehavior";

import TreeViewPresenter from "./presenters/TreeViewPresenter";

import { AVAILABLE_INDICATORS, indicators } from "./constants";

export default class TreeView extends Component {
  static propTypes = {
    /**
     * Alternating background stripes
     */
    alternateBg: PropTypes.bool,
    /**
     * Content of the TreeView
     * Accepts TreeItem components
     */
    children: PropTypes.node,
    /**
     * Visual guidelines for each TreeItem
     */
    guidelines: PropTypes.bool,
    /**
     * Indicator icon
     */
    indicator: PropTypes.oneOf(AVAILABLE_INDICATORS),
    /**
     * Triggers when a key is pressed
     * In addition to passing back the event you get the
     * following internal methods in an object:
     *  - getActiveTreeItemId(): => gets the active treeitem's id
     *  - getActiveTreeItemIndex(): =>  gets the active treeitem's index
     *  - getTreeItemArray(): => gets the array of treeitems that are visible
     *  - setActiveTreeItemId(id: string): => sets the active treeitem's id
     *  - setActiveTreeItemIndex(index: number) => sets the active treeitem's index
     *  - setKeyboardOpenId(id: string) => opens the treeitem
     */
    onKeyDown: PropTypes.func,
    /**
     * Adds custom/overriding styles
     */
    stylesheet: PropTypes.func,
    /**
     * If treeNode prop is used it will render instead
     * of children.
     * Additional info can be passed into the meta property
     * Additional HTML attributes can be added to the top
     * level of and spread to the HTML elements
     */
    treeNode: PropTypes.arrayOf(
      PropTypes.shape({
        id: PropTypes.number,
        parentId: PropTypes.number,
        meta: PropTypes.shape({
          label: PropTypes.node,
          collapsed: PropTypes.bool,
          active: PropTypes.bool,
          icon: PropTypes.node
        })
      })
    )
  };

  static defaultProps = {
    indicator: indicators.CARET
  };

  render() {
    const {
      children,
      alternateBg,
      guidelines,
      onKeyDown,
      stylesheet,
      treeNode,
      ...otherProps
    } = this.props;

    return (
      <TreeViewBehavior
        {...otherProps}
        onKeyDown={onKeyDown}
        treeNode={treeNode}
      >
        {({
          getActiveTreeItemId,
          getActiveTreeItemIndex,
          getCurrentItemClicked,
          getKeyboardOpenId,
          getTreeItemArray,
          setActiveTreeItemId,
          setActiveTreeItemIndex,
          setKeyboardOpenId,
          setTreeItemArray,
          handleKeyDown,
          setTreeViewRef,
          treeViewRef
        }) => (
          <TreeViewPresenter
            {...otherProps}
            alternateBg={alternateBg}
            treeNode={treeNode}
            getActiveTreeItemId={getActiveTreeItemId}
            getActiveTreeItemIndex={getActiveTreeItemIndex}
            getCurrentItemClicked={getCurrentItemClicked}
            getKeyboardOpenId={getKeyboardOpenId}
            getTreeItemArray={getTreeItemArray}
            setActiveTreeItemId={setActiveTreeItemId}
            setActiveTreeItemIndex={setActiveTreeItemIndex}
            setKeyboardOpenId={setKeyboardOpenId}
            setTreeItemArray={setTreeItemArray}
            guidelines={guidelines}
            onKeyDown={handleKeyDown}
            setTreeViewRef={setTreeViewRef}
            stylesheet={stylesheet}
            treeViewRef={treeViewRef}
          >
            {children}
          </TreeViewPresenter>
        )}
      </TreeViewBehavior>
    );
  }
}
<|endoftext|>


<commit_before>import '/resources/polymer/@polymer/polymer/polymer-legacy.js';
import '/resources/components/tg-tooltip.js';
/**
 * Provides tooltip support for component that uses this behaviour. In order to set tooltip for specific element in the component, one should
 * mark element with tooltip-text attribute.
 */

const toolTipElement = document.createElement("tg-tooltip");
/**
 * Returns the element in hierarchy that has tooltip-id attribute set otherwise returns null.
 */
const extractActiveElement = function (path, thisElement) {
    const elementPath = path.filter(node => node.nodeType === Node.ELEMENT_NODE);
    let elemIdx = 0;
    while (elemIdx < elementPath.length && !elementPath[elemIdx].hasAttribute("tooltip-text") && elementPath[elemIdx] !== thisElement) {
        elemIdx++;
    }
    return elementPath[elemIdx].hasAttribute("tooltip-text") ? elementPath[elemIdx] : null;
};
//Adds tooltip element to document's body so that it only one for all tooltips.
document.body.appendChild(toolTipElement);

export const TgTooltipBehavior = {

    properties: {
        triggerManual: {
            type: String,
            observer: "_triggerManualChanged"
        },

        /**
         * The element that will be observed for mouse move events. This element or it's children should have tooltip-text attribute set in order the tooltip to be shown.
         */
        triggerElement: {
            type: Object,
            observer: "_triggerElementChanged"
        },

        /**
         * Saved mouse positions.
         */
        _mousePosX: Number,
        _mousePosY: Number,
        /**
         * Element under mouse pointer, for which tooltip should be shown. Also this element should be marked with tooltip-id attribute.
         */
        _activeComponent: {
            type: Object
        }
    },

    ready: function () {
        //Bind mouse events
        this._handleMouseMove = this._handleMouseMove.bind(this);
        this._handleTooltipAtMousePos = this._handleTooltipAtMousePos.bind(this);
        this._hideTooltip = this._hideTooltip.bind(this);
        //Bind touch events
        this._handleTouchStart = this._handleTouchStart.bind(this);
        this._handleTouchEnd = this._handleTouchEnd.bind(this);
        this.triggerElement = this;
        //Set the default values for properties.
        this.triggerManual = false;
    },

    /**
     * Displayes the tooltip with specified text at current mouse position.
     */
    showTooltip: function (tooltipText) {
        toolTipElement.show(tooltipText, this._mousePosX, this._mousePosY);
    },

    /**
     * Observer for trigger element changes.
     */
    _triggerElementChanged: function (newElement, oldElement) {
        this._setMouseEvents(newElement, oldElement, this.triggerManual);
    },

    /**
     * Observer for _triggerManual property.
     */
    _triggerManualChanged: function (newValue, oldValue) {
        this._setMouseEvents(this.triggerElement, this.triggerElement, newValue);
    },

    /**
     * Set the appropriate mouse event handlers for trigger element according to trigger policy.
     */
    _setMouseEvents: function (newTrigger, oldTrigger, manual) {
        if (oldTrigger) {
            //Unregister mouse move listener for old trigger element.
            oldTrigger.removeEventListener('mousemove', this._handleMouseMove);
            if (!manual) {
                //If tooltips were triggerd manually then don't remove anything.
                this._unregisterTooltipRelatedEvents(oldTrigger);
            }
        }
        if (newTrigger) {
            //Register mouse listeners for new trigger element.
            newTrigger.addEventListener('mousemove', this._handleMouseMove);
            if (!manual) {
                this._registerTooltipRelatedEvents(newTrigger);
            } else {
                this._unregisterTooltipRelatedEvents(newTrigger);
            }
        }
    },

    _registerTooltipRelatedEvents: function (trigger) {
        //Register mouse listener for new trigger element in order to trigger tooltips automatically
        trigger.addEventListener('mousemove', this._handleTooltipAtMousePos);
        trigger.addEventListener('mouseleave', this._hideTooltip);
        //Register touch listener for new trigger element in order to trigger tooltips automatically
        trigger.addEventListener('touchstart', this._handleTouchStart);
        trigger.addEventListener('touchend', this._handleTouchEnd);
    },

    _unregisterTooltipRelatedEvents: function (trigger) {
        //Unregister mouse listener for new trigger element in order to trigger tooltips automatically
        trigger.removeEventListener('mousemove', this._handleTooltipAtMousePos);
        trigger.removeEventListener('mouseleave', this._hideTooltip);
        //Unregister touch listener for new trigger element in order to trigger tooltips automatically
        trigger.removeEventListener('touchstart', this._handleTouchStart);
        trigger.removeEventListener('touchend', this._handleTouchEnd);
    },

    _handleTouchStart: function (event) {
        this.touchEventTriggered = true;
        this._startTooltip(event);
    },

    _handleTouchEnd: function (event) {
        this._hideTooltip();
    },

    /**
     * Saves the mouse position in _mousePosX and _mousePosY properties.
     */
    _handleMouseMove: function (event) {
        this._mousePosX = event.pageX;
        this._mousePosY = event.pageY;
    },

    /**
     * Handler that determines when to show tooltip on mouse move event amd when to hide it.
     */
    _handleTooltipAtMousePos: function (event) {
        if (!this.touchEventTriggered) {
            this._startTooltip(event);
        } else {
            this.touchEventTriggered = false;
        }
    },

    _startTooltip: function (event) {
        const currentActiveElement = extractActiveElement(event.composedPath(), this.triggerElement);

        if (currentActiveElement !== this._activeComponent) {
            this._hideTooltip();
            this._activeComponent = currentActiveElement;
        }
        const tooltipText = this._activeComponent && this._activeComponent.getAttribute("tooltip-text");
        if (tooltipText !== null && tooltipText.length > 0) {
            this.showTooltip(tooltipText);
        }
    },

    /**
     * Hides the tooltip. Used as a mouse handler for mouse leave event.
     */
    _hideTooltip: function () {
        toolTipElement.hide();
    }
};<commit_msg>#1329 Temporarily commented out adding of tooltip listeners that causes some performance problems.<commit_after>import '/resources/polymer/@polymer/polymer/polymer-legacy.js';
import '/resources/components/tg-tooltip.js';
/**
 * Provides tooltip support for component that uses this behaviour. In order to set tooltip for specific element in the component, one should
 * mark element with tooltip-text attribute.
 */

const toolTipElement = document.createElement("tg-tooltip");
/**
 * Returns the element in hierarchy that has tooltip-id attribute set otherwise returns null.
 */
const extractActiveElement = function (path, thisElement) {
    const elementPath = path.filter(node => node.nodeType === Node.ELEMENT_NODE);
    let elemIdx = 0;
    while (elemIdx < elementPath.length && !elementPath[elemIdx].hasAttribute("tooltip-text") && elementPath[elemIdx] !== thisElement) {
        elemIdx++;
    }
    return elementPath[elemIdx].hasAttribute("tooltip-text") ? elementPath[elemIdx] : null;
};
//Adds tooltip element to document's body so that it only one for all tooltips.
document.body.appendChild(toolTipElement);

export const TgTooltipBehavior = {

    properties: {
        triggerManual: {
            type: String,
            observer: "_triggerManualChanged"
        },

        /**
         * The element that will be observed for mouse move events. This element or it's children should have tooltip-text attribute set in order the tooltip to be shown.
         */
        triggerElement: {
            type: Object,
            observer: "_triggerElementChanged"
        },

        /**
         * Saved mouse positions.
         */
        _mousePosX: Number,
        _mousePosY: Number,
        /**
         * Element under mouse pointer, for which tooltip should be shown. Also this element should be marked with tooltip-id attribute.
         */
        _activeComponent: {
            type: Object
        }
    },

    ready: function () {
        //Bind mouse events
        this._handleMouseMove = this._handleMouseMove.bind(this);
        this._handleTooltipAtMousePos = this._handleTooltipAtMousePos.bind(this);
        this._hideTooltip = this._hideTooltip.bind(this);
        //Bind touch events
        this._handleTouchStart = this._handleTouchStart.bind(this);
        this._handleTouchEnd = this._handleTouchEnd.bind(this);
        this.triggerElement = this;
        //Set the default values for properties.
        this.triggerManual = false;
    },

    /**
     * Displayes the tooltip with specified text at current mouse position.
     */
    showTooltip: function (tooltipText) {
        toolTipElement.show(tooltipText, this._mousePosX, this._mousePosY);
    },

    /**
     * Observer for trigger element changes.
     */
    _triggerElementChanged: function (newElement, oldElement) {
        this._setMouseEvents(newElement, oldElement, this.triggerManual);
    },

    /**
     * Observer for _triggerManual property.
     */
    _triggerManualChanged: function (newValue, oldValue) {
        this._setMouseEvents(this.triggerElement, this.triggerElement, newValue);
    },

    /**
     * Set the appropriate mouse event handlers for trigger element according to trigger policy.
     */
    _setMouseEvents: function (newTrigger, oldTrigger, manual) {
        if (oldTrigger) {
            //Unregister mouse move listener for old trigger element.
            oldTrigger.removeEventListener('mousemove', this._handleMouseMove);
            if (!manual) {
                //If tooltips were triggerd manually then don't remove anything.
                this._unregisterTooltipRelatedEvents(oldTrigger);
            }
        }
        if (newTrigger) {
            //Register mouse listeners for new trigger element.
            newTrigger.addEventListener('mousemove', this._handleMouseMove);
            if (!manual) {
                this._registerTooltipRelatedEvents(newTrigger);
            } else {
                this._unregisterTooltipRelatedEvents(newTrigger);
            }
        }
    },

    _registerTooltipRelatedEvents: function (trigger) {
        // //Register mouse listener for new trigger element in order to trigger tooltips automatically
        // trigger.addEventListener('mousemove', this._handleTooltipAtMousePos);
        // trigger.addEventListener('mouseleave', this._hideTooltip);
        // //Register touch listener for new trigger element in order to trigger tooltips automatically
        // trigger.addEventListener('touchstart', this._handleTouchStart);
        // trigger.addEventListener('touchend', this._handleTouchEnd);
    },

    _unregisterTooltipRelatedEvents: function (trigger) {
        //Unregister mouse listener for new trigger element in order to trigger tooltips automatically
        trigger.removeEventListener('mousemove', this._handleTooltipAtMousePos);
        trigger.removeEventListener('mouseleave', this._hideTooltip);
        //Unregister touch listener for new trigger element in order to trigger tooltips automatically
        trigger.removeEventListener('touchstart', this._handleTouchStart);
        trigger.removeEventListener('touchend', this._handleTouchEnd);
    },

    _handleTouchStart: function (event) {
        this.touchEventTriggered = true;
        this._startTooltip(event);
    },

    _handleTouchEnd: function (event) {
        this._hideTooltip();
    },

    /**
     * Saves the mouse position in _mousePosX and _mousePosY properties.
     */
    _handleMouseMove: function (event) {
        this._mousePosX = event.pageX;
        this._mousePosY = event.pageY;
    },

    /**
     * Handler that determines when to show tooltip on mouse move event amd when to hide it.
     */
    _handleTooltipAtMousePos: function (event) {
        if (!this.touchEventTriggered) {
            this._startTooltip(event);
        } else {
            this.touchEventTriggered = false;
        }
    },

    _startTooltip: function (event) {
        const currentActiveElement = extractActiveElement(event.composedPath(), this.triggerElement);

        if (currentActiveElement !== this._activeComponent) {
            this._hideTooltip();
            this._activeComponent = currentActiveElement;
        }
        const tooltipText = this._activeComponent && this._activeComponent.getAttribute("tooltip-text");
        if (tooltipText !== null && tooltipText.length > 0) {
            this.showTooltip(tooltipText);
        }
    },

    /**
     * Hides the tooltip. Used as a mouse handler for mouse leave event.
     */
    _hideTooltip: function () {
        toolTipElement.hide();
    }
};<|endoftext|>


<commit_before>var Mocha = require("mocha");
var chai = require("chai");
var dir = require("node-dir");
var path = require("path");
var web3 = require("web3");
var fs = require("fs");
var temp = require("temp");
var Promise = require("bluebird");

var Contracts = require("./contracts");

var Pudding = require("ether-pudding");
var PuddingLoader = require("ether-pudding/loader");
var loadconf = require("./loadconf");
var Promise = require("bluebird");

var ExtendableError = require("./errors/extendableerror");

// Make Promise global so tests have access to it.
global.Promise = Promise;

// Use custom assertions.
global.assert = chai.assert;
chai.use(require("./assertions"));

var Test = {
  setup(config, callback) {

    global.web3 = config.web3;

    // Variables that are passed to each contract which are
    // populated by the global before() hook.
    var accounts = [];

    var BEFORE_TIMEOUT = 120000;
    var TEST_TIMEOUT = 300000;

    // Deploy all configured contracts to the chain without recompiling
    var redeploy_contracts = function(recompile, done) {
      this && this.timeout && this.timeout(BEFORE_TIMEOUT);

      Contracts.deploy(config, recompile, function(err) {
        if (err != null) {
          // Format our error messages so they print better with mocha.
          if (err instanceof ExtendableError) {
            err.formatForMocha();
          }

          done(err);
          return;
        }

        Pudding.setWeb3(web3);
        PuddingLoader.load(config.environments.current.directory, Pudding, global, function(err, contract_names) {
          for (var name in contract_names) {
            var contract = global[name];
            var inst = contract.at(contract.deployed_address);
            Truffle.log_filters.push(inst.allEvents({fromBlock: 0, toBlock: 'latest'}, function(err, results) {
              // no-op, don't worry abuot tracking events just now
            }));
          }
          done();
        });
      });
    };

    var rpc = function(method, arg, cb) {
      var req = {
        jsonrpc: "2.0",
        method: method,
        id: new Date().getTime()
      };
      if (arguments.length == 3) {
        req.params = arg;
      } else {
        cb = arg;
      }
      web3.currentProvider.sendAsync(req, cb);
    };

    global.Truffle = {

      log_filters: [],

      redeploy: redeploy_contracts,
      handle_errs: (done) => { Promise.onPossiblyUnhandledRejection(done); },

      reset: function(cb) {
        rpc("evm_reset", cb);
      },

      snapshot: function(cb) {
        rpc("evm_snapshot", cb);
      },

      revert: function(snapshot_id, cb) {
        rpc("evm_revert", [snapshot_id, true], cb);
      }
    };

    global.contract = function(name, opts, tests) {
      var reset_state = true;
      if (arguments.length === 3) {
        if (opts) {
          if (opts.reset_state) {
            reset_state = opts.reset_state;
          }
        }
      } else {
        tests = opts;
      }

      describe(`Contract: ${name}`, function() {
        this.timeout(TEST_TIMEOUT);

        var _original_contracts = {};

        before("redeploy before each suite", function(done) {
          redeploy_contracts.call(this, true, function() {

            // Store address that was first deployed, in case we redeploy
            // from within a test
            for (var name in config.contracts.classes) {
              var contract = global[name];
              _original_contracts[name] = contract.deployed_address;
            }

            done();
          });
        });

        after("clear all filters after each suite", function(done) {
          Truffle.log_filters.forEach(function(f) { f.stopWatching(); });
          Truffle.log_filters = [];
          done();
        });

        afterEach("restore contract address", function(done) {
          for (var name in _original_contracts) {
            global[name].deployed_address = _original_contracts[name];
          }
          done();
        });

        afterEach("check logs on failure", function(done) {
          if (this.currentTest.state == "failed") {

            var logs = [];
            Truffle.log_filters.forEach(function(filter) {
              try {
                logs = logs.concat(filter.get());
              } catch (e) {
                if (e.message.match(/Invalid parameters/)) {
                  // filter is invalid because the contract no longer exists
                  filter.stopWatching();
                }
              }
            });
            logs.sort(function(a, b) {
              var ret = a.blockNumber - b.blockNumber;
              if (ret == 0) {
                return a.logIndex - b.logIndex;
              }
              return ret;
            });

            if (logs.length > 0) {
              console.log("\n    Events emitted during test:");
              console.log(  "    ---------------------------");
              logs.forEach(function(log) {
                if (!log.event) {
                  return; // only want log events
                }
                if (log.event.toLowerCase() == "debug") {
                  console.log("[DEBUG]", log.args.msg);
                  return;
                }
                var data = {};
                for (var key in log.args) {
                  data[key] = log.args[key].toString();
                }
                console.log({event: log.event, data: data})
              });
              console.log(  "\n    --------------------------- END EVENTS");
            } else {
              console.log("    > No events were emitted");
            }

          }
          done();
        });

        if (reset_state) {
          var snapshot_id;
          beforeEach("snapshot state before each test", function(done) {
            Truffle.snapshot(function(err, ret) {
              snapshot_id = ret.result;
              done();
            });
          });

          afterEach("revert state after each test", function(done) {
            Truffle.revert(snapshot_id, function(err, ret) {
              done();
            });
          });
        }

        tests(accounts);
      });
    };

    // Compile all the contracts and get the available accounts.
    // We only need to do this one, and can get it outside of
    // mocha.

    console.log("Compiling contracts...");

    Contracts.compile_all(config, function(err) {
      if (err != null) {
        callback(err);
        return;
      }

      web3.eth.getAccounts(function(error, accs) {
        for (var account of accs) {
          accounts.push(account);
        }

        Pudding.defaults({
          from: accounts[0],
          gas: 3141592
        });

        callback();
      });
    });
  },

  // file must be absolute, or null.
  run(config, file, callback) {
    if (typeof file == "function") {
      callback = file;
      file = null;
      config.expect(config.tests.directory, "tests directory");
    } else {
      config.expect(file, "test file");
    }

    this.setup(config, function(err) {
      if (err != null) {
        callback(err);
        return;
      }

      // Change current working directory to that of the project.
      process.chdir(config.working_dir);
      __dirname = process.cwd();

      // If errors aren't caught in Promises, make sure they're thrown
      // and don't keep the process open.
      Promise.onPossiblyUnhandledRejection(function(e, promise) {
        throw e;
      });

      var mocha = new Mocha({
        useColors: true
      });

      var runMocha = function() {
        // TODO: Catch any errors here, and fail.
        mocha.run(function(failures) {
          callback(null, failures);
        });
      };

      if (file != null) {
        mocha.addFile(file);
        runMocha();
        return;
      }

      dir.files(config.tests.directory, function(err, files) {
        if (err != null) {
          callback(err);
          return;
        }

        // if running via the 'watch:tests' task, we want to be able to run
        // (require) our test files repeatedly, so this is a hack to make it
        // work. we copy each test file to a temp filename and load that
        // instead of the original to avoid getting cached.
        files = files.map(function(f) {
          var src = fs.readFileSync(f);
          f = temp.path({prefix: "truffle-", suffix: "-"+path.basename(f)})
          fs.writeFileSync(f, src);
          return f;
        });

        var mocha = new Mocha({
          useColors: true
        });

        for (var file of files.sort()) {
          mocha.addFile(file);
        }

        // Change current working directory to that of the project.
        process.chdir(config.working_dir);
        __dirname = process.cwd();

        // If errors aren't caught in Promises, make sure they're thrown
        // and don't keep the process open.
        Promise.onPossiblyUnhandledRejection(function(e, promise) {
          throw e;
        });

        // TODO: Catch any errors here, and fail.
        mocha.run(function(failures) {
          files.forEach(function(f) {
            fs.unlinkSync(f); // cleanup our temp files
          });
          callback(null, failures);
        });
      });
    });
  }
};

module.exports = Test;
<commit_msg>cleanup merge with master<commit_after>var Mocha = require("mocha");
var chai = require("chai");
var dir = require("node-dir");
var path = require("path");
var fs = require("fs");
var temp = require("temp");

var Contracts = require("./contracts");

var Pudding = require("ether-pudding");
var PuddingLoader = require("ether-pudding/loader");
var loadconf = require("./loadconf");
var Promise = require("bluebird");

var ExtendableError = require("./errors/extendableerror");

// Make Promise global so tests have access to it.
global.Promise = Promise;

// Use custom assertions.
global.assert = chai.assert;
chai.use(require("./assertions"));

var Test = {
  setup(config, callback) {

    global.web3 = config.web3;

    // Variables that are passed to each contract which are
    // populated by the global before() hook.
    var accounts = [];

    var BEFORE_TIMEOUT = 120000;
    var TEST_TIMEOUT = 300000;

    // Deploy all configured contracts to the chain without recompiling
    var redeploy_contracts = function(recompile, done) {
      this && this.timeout && this.timeout(BEFORE_TIMEOUT);

      Contracts.deploy(config, recompile, function(err) {
        if (err != null) {
          // Format our error messages so they print better with mocha.
          if (err instanceof ExtendableError) {
            err.formatForMocha();
          }

          done(err);
          return;
        }

        Pudding.setWeb3(web3);
        PuddingLoader.load(config.environments.current.directory, Pudding, global, function(err, contract_names) {
          for (var name in config.contracts.classes) {
            var contract = global[name];
            var inst = contract.at(contract.deployed_address);
            Truffle.log_filters.push(inst.allEvents({fromBlock: 0, toBlock: 'latest'}, function(err, results) {
              // no-op, don't worry abuot tracking events just now
            }));
          }
          done();
        });
      });
    };

    var rpc = function(method, arg, cb) {
      var req = {
        jsonrpc: "2.0",
        method: method,
        id: new Date().getTime()
      };
      if (arguments.length == 3) {
        req.params = arg;
      } else {
        cb = arg;
      }
      web3.currentProvider.sendAsync(req, cb);
    };

    global.Truffle = {

      log_filters: [],

      redeploy: redeploy_contracts,
      handle_errs: (done) => { Promise.onPossiblyUnhandledRejection(done); },

      reset: function(cb) {
        rpc("evm_reset", cb);
      },

      snapshot: function(cb) {
        rpc("evm_snapshot", cb);
      },

      revert: function(snapshot_id, cb) {
        rpc("evm_revert", [snapshot_id, true], cb);
      }
    };

    global.contract = function(name, opts, tests) {
      var reset_state = true;
      if (arguments.length === 3) {
        if (opts) {
          if (opts.reset_state) {
            reset_state = opts.reset_state;
          }
        }
      } else {
        tests = opts;
      }

      describe(`Contract: ${name}`, function() {
        this.timeout(TEST_TIMEOUT);

        var _original_contracts = {};

        before("redeploy before each suite", function(done) {
          redeploy_contracts.call(this, true, function() {

            // Store address that was first deployed, in case we redeploy
            // from within a test
            for (var name in config.contracts.classes) {
              var contract = global[name];
              _original_contracts[name] = contract.deployed_address;
            }

            done();
          });
        });

        after("clear all filters after each suite", function(done) {
          Truffle.log_filters.forEach(function(f) { f.stopWatching(); });
          Truffle.log_filters = [];
          done();
        });

        afterEach("restore contract address", function(done) {
          for (var name in _original_contracts) {
            global[name].deployed_address = _original_contracts[name];
          }
          done();
        });

        afterEach("check logs on failure", function(done) {
          if (this.currentTest.state == "failed") {

            var logs = [];
            Truffle.log_filters.forEach(function(filter) {
              try {
                logs = logs.concat(filter.get());
              } catch (e) {
                if (e.message.match(/Invalid parameters/)) {
                  // filter is invalid because the contract no longer exists
                  filter.stopWatching();
                }
              }
            });
            logs.sort(function(a, b) {
              var ret = a.blockNumber - b.blockNumber;
              if (ret == 0) {
                return a.logIndex - b.logIndex;
              }
              return ret;
            });

            if (logs.length > 0) {
              console.log("\n    Events emitted during test:");
              console.log(  "    ---------------------------");
              logs.forEach(function(log) {
                if (!log.event) {
                  return; // only want log events
                }
                if (log.event.toLowerCase() == "debug") {
                  console.log("[DEBUG]", log.args.msg);
                  return;
                }
                var data = {};
                for (var key in log.args) {
                  data[key] = log.args[key].toString();
                }
                console.log({event: log.event, data: data})
              });
              console.log(  "\n    --------------------------- END EVENTS");
            } else {
              console.log("    > No events were emitted");
            }

          }
          done();
        });

        if (reset_state) {
          var snapshot_id;
          beforeEach("snapshot state before each test", function(done) {
            Truffle.snapshot(function(err, ret) {
              snapshot_id = ret.result;
              done();
            });
          });

          afterEach("revert state after each test", function(done) {
            Truffle.revert(snapshot_id, function(err, ret) {
              done();
            });
          });
        }

        tests(accounts);
      });
    };

    // Compile all the contracts and get the available accounts.
    // We only need to do this one, and can get it outside of
    // mocha.

    console.log("Compiling contracts...");

    Contracts.compile_all(config, function(err) {
      if (err != null) {
        callback(err);
        return;
      }

      web3.eth.getAccounts(function(error, accs) {
        for (var account of accs) {
          accounts.push(account);
        }

        Pudding.defaults({
          from: accounts[0],
          gas: 3141592
        });

        callback();
      });
    });
  },

  // file must be absolute, or null.
  run(config, file, callback) {
    if (typeof file == "function") {
      callback = file;
      file = null;
      config.expect(config.tests.directory, "tests directory");
    } else {
      config.expect(file, "test file");
    }

    this.setup(config, function(err) {
      if (err != null) {
        callback(err);
        return;
      }

      // Change current working directory to that of the project.
      process.chdir(config.working_dir);
      __dirname = process.cwd();

      // If errors aren't caught in Promises, make sure they're thrown
      // and don't keep the process open.
      Promise.onPossiblyUnhandledRejection(function(e, promise) {
        throw e;
      });

      var mocha = new Mocha({
        useColors: true
      });

      var runMocha = function() {
        // TODO: Catch any errors here, and fail.
        mocha.run(function(failures) {
          callback(null, failures);
        });
      };

      if (file != null) {
        mocha.addFile(file);
        runMocha();
        return;
      }

      dir.files(config.tests.directory, function(err, files) {
        if (err != null) {
          callback(err);
          return;
        }

        // if running via the 'watch:tests' task, we want to be able to run
        // (require) our test files repeatedly, so this is a hack to make it
        // work. we copy each test file to a temp filename and load that
        // instead of the original to avoid getting cached.
        files = files.map(function(f) {
          var src = fs.readFileSync(f);
          f = temp.path({prefix: "truffle-", suffix: "-"+path.basename(f)})
          fs.writeFileSync(f, src);
          return f;
        });

        var mocha = new Mocha({
          useColors: true
        });

        for (var file of files.sort()) {
          mocha.addFile(file);
        }

        // Change current working directory to that of the project.
        process.chdir(config.working_dir);
        __dirname = process.cwd();

        // If errors aren't caught in Promises, make sure they're thrown
        // and don't keep the process open.
        Promise.onPossiblyUnhandledRejection(function(e, promise) {
          throw e;
        });

        // TODO: Catch any errors here, and fail.
        mocha.run(function(failures) {
          files.forEach(function(f) {
            fs.unlinkSync(f); // cleanup our temp files
          });
          callback(null, failures);
        });
      });
    });
  }
};

module.exports = Test;
<|endoftext|>