<commit_before>import React from "react";
import {connect} from 'react-redux';
import Textarea from 'react-textarea-autosize';
import _ from 'lodash';
import ActionsUtils from '../../actions/ActionsUtils';
import ProcessUtils from '../../common/ProcessUtils';
import ExpressionSuggester from './ExpressionSuggester'
import Autosuggest from "react-autosuggest";
import $ from "jquery";

import AceEditor from 'react-ace';
import 'brace/mode/jsx';

import 'brace/ext/language_tools'
import 'brace/ext/searchbox';

import '../../brace/mode/spel'
import '../../brace/theme/nussknacker'

//to reconsider
// - respect categories for global variables?
// - maybe ESC should be allowed to hide suggestions but leave modal open?

var inputExprIdCounter = 0
class ExpressionSuggest extends React.Component {

  static propTypes = {
    inputProps: React.PropTypes.object.isRequired
  }

  customAceEditorCompleter = {
    getCompletions: (editor, session, caretPosition2d, prefix, callback) => {
      const suggestions = this.expressionSuggester.suggestionsFor(this.state.value, caretPosition2d)
      callback(null, _.map(suggestions, (s) => {
        //unfortunately Ace treats `#` as special case, we have to remove `#` from suggestions or it will be duplicated
        //maybe it depends on language mode?
        const methodName = s.methodName.replace("#", "")
        return {name: methodName, value: methodName, score: 1, meta: ProcessUtils.humanReadableType(s.refClazzName)}
      }))
    }
  }

  constructor(props) {
    super(props);
    inputExprIdCounter+=1;
    this.state = {
      value: props.inputProps.value,
      _autosuggest_suggestions: [],
      _autosuggest_expectedCaretPosition: 0,
      id: "inputExpr" + inputExprIdCounter
    };
    this.expressionSuggester = this.createExpressionSuggester(props)
  }

  //fixme is this enough?
  //this shouldComponentUpdate is for cases when there are multiple instances of suggestion component in one view and to make them not interfere with each other
  //fixme maybe use this.state.id here?
  shouldComponentUpdate(nextProps, nextState) {
    return !(_.isEqual(this.state._autosuggest_suggestions, nextState._autosuggest_suggestions) &&
      _.isEqual(this.state._autosuggest_expectedCaretPosition, nextState._autosuggest_expectedCaretPosition) &&
      _.isEqual(this.state.value, nextState.value)
    )
  }

  componentDidUpdate(prevProps, prevState) {
    this.expressionSuggester = this.createExpressionSuggester(this.props)
    this.setCaretPosition(this.state._autosuggest_expectedCaretPosition)
    if (!_.isEqual(this.state.value, prevState.value)) {
      this.props.inputProps.onValueChange(this.state.value)
    }
  }

  createExpressionSuggester = (props) => {
    return new ExpressionSuggester(props.typesInformation, props.variables);
  }

  onChange = (newValue) => {
    this.setState({
      value: newValue,
      _autosuggest_expectedCaretPosition: this._autosuggest_getCaretPosition()
    })
  }

  render() {
    if (this.props.dataResolved) {
      const inputProps = {
        ..._.omit(this.props.inputProps, "onValueChange"), //we leave this out, because warnings
        value: this.state.value,
        onChange: (event, {newValue}) => {
          this.onChange(newValue)
        }
    }
    return this.props.advancedCodeSuggestions ? (

        <AceEditor
          mode={'spel'}
          width={"100%"}
          minLines={1}
          maxLines={10}
          theme={'nussknacker'}
          onChange={this.onChange}
          value={this.state.value}
          showPrintMargin={false}
          cursorStart={-1} //line start
          showGutter={false}
          highlightActiveLine={false}
          highlightGutterLine={false}
          setOptions={{
            enableBasicAutocompletion: [this.customAceEditorCompleter],
            enableLiveAutocompletion: false,
            enableSnippets: false,
            showLineNumbers: false,
            fontSize: 16,
            fontFamily: "'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace", //monospace font seems to be mandatory to make ace cursor work well,
            readOnly: this.props.inputProps.readOnly
          }}
        />

    ) :
      <Autosuggest
      id={"autosuggest-" + this.props.id}
      suggestions={this.state._autosuggest_suggestions}
      onSuggestionsFetchRequested={this._autosuggest_onSuggestionsFetchRequested}
      onSuggestionsClearRequested={this._autosuggest_onSuggestionsClearRequested}
      getSuggestionValue={this._autosuggest_getSuggestionValue}
      renderSuggestion={this._autosuggest_renderSuggestion}
      shouldRenderSuggestions={() => {return true}}
      renderInputComponent={this._autosuggest_renderInputComponent}
      inputProps={inputProps}
      onSuggestionSelected={this._autosuggest_onSuggestionSelected}
      />
    } else {
      return null
    }

  }

  //TODO remove autosuggest component if AceEditor will turn out to be better
  _autosuggest_onSuggestionsFetchRequested = ({value}) => {
    const caretPosition2d = {column: this._autosuggest_getCaretPosition(), row: 0 }
    const suggestions = this.expressionSuggester.suggestionsFor(value, caretPosition2d)
    this.setState({
      _autosuggest_suggestions: suggestions
    })
  }

  _autosuggest_onSuggestionsClearRequested = () => {
    this.setState({
      _autosuggest_suggestions: []
    });
  };

  _autosuggest_getSuggestionValue = suggestion => {
    return this.state.value
  }

  _autosuggest_renderSuggestion = suggestion => {
    const {start, middle, end} = this.expressionSuggester._autosuggest_extractMatchingPartFromInput(suggestion, this.state.value, this._autosuggest_getCaretPosition())
    const suggestionType = ProcessUtils.humanReadableType(suggestion.refClazzName)
    return (
      start || middle || end ?

          {start}{middle}{end}{suggestionType}

 :

{suggestion.methodName}{suggestionType}

    );
  }

  _autosuggest_renderInputComponent = inputProps => {
    return (

        <Textarea id={this.state.id} {...inputProps} />

    )
  }

  _autosuggest_onSuggestionSelected = (event, { suggestion, suggestionValue, sectionIndex, method }) => {
    event.preventDefault() //to prevent newline in textarea after choosing an option
    const suggestionApplied = this.expressionSuggester._autosuggest_applySuggestion(suggestion, this.state.value, this._autosuggest_getCaretPosition())
    this.setState({
      value: suggestionApplied.value,
      _autosuggest_expectedCaretPosition: suggestionApplied.caretPosition
    })
  }

  _autosuggest_getCaretPosition = () => {
    if (this._autosuggest_getInputExprElement()) {
      return this._autosuggest_getInputExprElement().selectionStart
    }
  }

  setCaretPosition = (position) => {
    if (this._autosuggest_getInputExprElement()) {
      this._autosuggest_getInputExprElement().setSelectionRange(position, position)
    }
  }

  //fixme change to ref?
  _autosuggest_getInputExprElement = () => {
    return $('#' + this.state.id)[0]
  }
}

function mapState(state) {
  const processDefinitionData = !_.isEmpty(state.settings.processDefinitionData) ? state.settings.processDefinitionData
    : {processDefinition: { typesInformation: []}}
  const dataResolved = !_.isEmpty(state.settings.processDefinitionData)
  const typesInformation = processDefinitionData.processDefinition.typesInformation
  const variablesForNode = state.graphReducer.nodeToDisplay.id || _.get(state.graphReducer, ".edgeToDisplay.to") || null
  const variables = ProcessUtils.findAvailableVariables(variablesForNode, state.graphReducer.processToDisplay, processDefinitionData.processDefinition)
  return {
    typesInformation: typesInformation,
    dataResolved: dataResolved,
    variables: variables,
    advancedCodeSuggestions: state.settings.featuresSettings.advancedCodeSuggestions
  };
}
export default connect(mapState, ActionsUtils.mapDispatchWithEspActions)(ExpressionSuggest);<commit_msg>[ITDEVESP-389] wrap long, single-line expression<commit_after>import React from "react";
import {connect} from 'react-redux';
import Textarea from 'react-textarea-autosize';
import _ from 'lodash';
import ActionsUtils from '../../actions/ActionsUtils';
import ProcessUtils from '../../common/ProcessUtils';
import ExpressionSuggester from './ExpressionSuggester'
import Autosuggest from "react-autosuggest";
import $ from "jquery";

import AceEditor from 'react-ace';
import 'brace/mode/jsx';

import 'brace/ext/language_tools'
import 'brace/ext/searchbox';

import '../../brace/mode/spel'
import '../../brace/theme/nussknacker'

//to reconsider
// - respect categories for global variables?
// - maybe ESC should be allowed to hide suggestions but leave modal open?

var inputExprIdCounter = 0
class ExpressionSuggest extends React.Component {

  static propTypes = {
    inputProps: React.PropTypes.object.isRequired
  }

  customAceEditorCompleter = {
    getCompletions: (editor, session, caretPosition2d, prefix, callback) => {
      const suggestions = this.expressionSuggester.suggestionsFor(this.state.value, caretPosition2d)
      callback(null, _.map(suggestions, (s) => {
        //unfortunately Ace treats `#` as special case, we have to remove `#` from suggestions or it will be duplicated
        //maybe it depends on language mode?
        const methodName = s.methodName.replace("#", "")
        return {name: methodName, value: methodName, score: 1, meta: ProcessUtils.humanReadableType(s.refClazzName)}
      }))
    }
  }

  constructor(props) {
    super(props);
    inputExprIdCounter+=1;
    this.state = {
      value: props.inputProps.value,
      _autosuggest_suggestions: [],
      _autosuggest_expectedCaretPosition: 0,
      id: "inputExpr" + inputExprIdCounter
    };
    this.expressionSuggester = this.createExpressionSuggester(props)
  }

  //fixme is this enough?
  //this shouldComponentUpdate is for cases when there are multiple instances of suggestion component in one view and to make them not interfere with each other
  //fixme maybe use this.state.id here?
  shouldComponentUpdate(nextProps, nextState) {
    return !(_.isEqual(this.state._autosuggest_suggestions, nextState._autosuggest_suggestions) &&
      _.isEqual(this.state._autosuggest_expectedCaretPosition, nextState._autosuggest_expectedCaretPosition) &&
      _.isEqual(this.state.value, nextState.value)
    )
  }

  componentDidUpdate(prevProps, prevState) {
    this.expressionSuggester = this.createExpressionSuggester(this.props)
    this.setCaretPosition(this.state._autosuggest_expectedCaretPosition)
    if (!_.isEqual(this.state.value, prevState.value)) {
      this.props.inputProps.onValueChange(this.state.value)
    }
  }

  createExpressionSuggester = (props) => {
    return new ExpressionSuggester(props.typesInformation, props.variables);
  }

  onChange = (newValue) => {
    this.setState({
      value: newValue,
      _autosuggest_expectedCaretPosition: this._autosuggest_getCaretPosition()
    })
  }

  render() {
    if (this.props.dataResolved) {
      const inputProps = {
        ..._.omit(this.props.inputProps, "onValueChange"), //we leave this out, because warnings
        value: this.state.value,
        onChange: (event, {newValue}) => {
          this.onChange(newValue)
        }
    }
    return this.props.advancedCodeSuggestions ? (

        <AceEditor
          mode={'spel'}
          width={"100%"}
          minLines={1}
          maxLines={50}
          theme={'nussknacker'}
          onChange={this.onChange}
          value={this.state.value}
          showPrintMargin={false}
          cursorStart={-1} //line start
          showGutter={false}
          highlightActiveLine={false}
          highlightGutterLine={false}
          wrapEnabled={true}
          setOptions={{
            indentedSoftWrap: false, //removes weird spaces for multiline strings when wrapEnabled=true
            enableBasicAutocompletion: [this.customAceEditorCompleter],
            enableLiveAutocompletion: false,
            enableSnippets: false,
            showLineNumbers: false,
            fontSize: 16,
            fontFamily: "'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace", //monospace font seems to be mandatory to make ace cursor work well,
            readOnly: this.props.inputProps.readOnly
          }}
        />

    ) :
      <Autosuggest
      id={"autosuggest-" + this.props.id}
      suggestions={this.state._autosuggest_suggestions}
      onSuggestionsFetchRequested={this._autosuggest_onSuggestionsFetchRequested}
      onSuggestionsClearRequested={this._autosuggest_onSuggestionsClearRequested}
      getSuggestionValue={this._autosuggest_getSuggestionValue}
      renderSuggestion={this._autosuggest_renderSuggestion}
      shouldRenderSuggestions={() => {return true}}
      renderInputComponent={this._autosuggest_renderInputComponent}
      inputProps={inputProps}
      onSuggestionSelected={this._autosuggest_onSuggestionSelected}
      />
    } else {
      return null
    }

  }

  //TODO remove autosuggest component if AceEditor will turn out to be better
  _autosuggest_onSuggestionsFetchRequested = ({value}) => {
    const caretPosition2d = {column: this._autosuggest_getCaretPosition(), row: 0 }
    const suggestions = this.expressionSuggester.suggestionsFor(value, caretPosition2d)
    this.setState({
      _autosuggest_suggestions: suggestions
    })
  }

  _autosuggest_onSuggestionsClearRequested = () => {
    this.setState({
      _autosuggest_suggestions: []
    });
  };

  _autosuggest_getSuggestionValue = suggestion => {
    return this.state.value
  }

  _autosuggest_renderSuggestion = suggestion => {
    const {start, middle, end} = this.expressionSuggester._autosuggest_extractMatchingPartFromInput(suggestion, this.state.value, this._autosuggest_getCaretPosition())
    const suggestionType = ProcessUtils.humanReadableType(suggestion.refClazzName)
    return (
      start || middle || end ?

          {start}{middle}{end}{suggestionType}

 :

{suggestion.methodName}{suggestionType}

    );
  }

  _autosuggest_renderInputComponent = inputProps => {
    return (

        <Textarea id={this.state.id} {...inputProps} />

    )
  }

  _autosuggest_onSuggestionSelected = (event, { suggestion, suggestionValue, sectionIndex, method }) => {
    event.preventDefault() //to prevent newline in textarea after choosing an option
    const suggestionApplied = this.expressionSuggester._autosuggest_applySuggestion(suggestion, this.state.value, this._autosuggest_getCaretPosition())
    this.setState({
      value: suggestionApplied.value,
      _autosuggest_expectedCaretPosition: suggestionApplied.caretPosition
    })
  }

  _autosuggest_getCaretPosition = () => {
    if (this._autosuggest_getInputExprElement()) {
      return this._autosuggest_getInputExprElement().selectionStart
    }
  }

  setCaretPosition = (position) => {
    if (this._autosuggest_getInputExprElement()) {
      this._autosuggest_getInputExprElement().setSelectionRange(position, position)
    }
  }

  //fixme change to ref?
  _autosuggest_getInputExprElement = () => {
    return $('#' + this.state.id)[0]
  }
}

function mapState(state) {
  const processDefinitionData = !_.isEmpty(state.settings.processDefinitionData) ? state.settings.processDefinitionData
    : {processDefinition: { typesInformation: []}}
  const dataResolved = !_.isEmpty(state.settings.processDefinitionData)
  const typesInformation = processDefinitionData.processDefinition.typesInformation
  const variablesForNode = state.graphReducer.nodeToDisplay.id || _.get(state.graphReducer, ".edgeToDisplay.to") || null
  const variables = ProcessUtils.findAvailableVariables(variablesForNode, state.graphReducer.processToDisplay, processDefinitionData.processDefinition)
  return {
    typesInformation: typesInformation,
    dataResolved: dataResolved,
    variables: variables,
    advancedCodeSuggestions: state.settings.featuresSettings.advancedCodeSuggestions
  };
}
export default connect(mapState, ActionsUtils.mapDispatchWithEspActions)(ExpressionSuggest);<|endoftext|>


<commit_before>'use strict'

/**
 * adonis-framework
 *
 * (c) Harminder Virk <virk@adonisjs.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
*/

const crypto = require('crypto')

/**
 * Encrypt and decrypt values using nodeJs crypto, make
 * sure to set APP_KEY inside .env file.
 * @class
 */
class Encryption {

  constructor (Config) {
    this.appKey = Config.get('app.appKey')
    this.algorithm = Config.get('app.encryption.algorithm', 'aes-256-cbc')

    /**
     * throwing error when APP_KEY is not defined as encryption
     * does not make sense without a key
     */
    if (!this.appKey) {
      throw new Error('Encryption cannot work without application key. Define appKey inside app config')
    }
  }

  /**
   * encrypts a given value
   *
   * @param  {Mixed} value - value to be encrypted
   * @param  {String} [encoding=utf8] encoding to be used for input value
   * @return {String}
   *
   * @example
   * Encryption.encrypt('somevalue')
   *
   * @public
   */
  encrypt (value, encoding) {
    encoding = encoding || 'utf8'
    const cipher = crypto.createCipher(this.algorithm, this.appKey)
    cipher.update(value, encoding, 'hex')
    return cipher.final('hex')
  }

  /**
   * decrypts encrypted value
   *
   * @param  {String} value - value to decrypt
   * @param  {String} [encoding=utf8] encoding to be used for output value
   * @return {Mixed}
   *
   * @example
   * Encryption.decrypt('somevalue')
   *
   * @public
   */
  decrypt (value, encoding) {
    encoding = encoding || 'utf8'
    const decipher = crypto.createDecipher(this.algorithm, this.appKey)
    decipher.update(value, 'hex', encoding)
    return decipher.final(encoding)
  }

}

module.exports = Encryption
<commit_msg>Add initialization vector to Encryption<commit_after>'use strict'

/**
 * adonis-framework
 *
 * (c) Harminder Virk <virk@adonisjs.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
*/

const crypto = require('crypto')

/**
 * Encrypt and decrypt values using nodeJs crypto, make
 * sure to set APP_KEY inside .env file.
 * @class
 */
class Encryption {

  constructor (Config) {
    this.appKey = Config.get('app.appKey')
    this.algorithm = Config.get('app.encryption.algorithm', 'aes-256-cbc')

    /**
     * throwing error when APP_KEY is not defined as encryption
     * does not make sense without a key
     */
    if (!this.appKey) {
      throw new Error('Encryption cannot work without application key. Define appKey inside app config')
    }
  }

  /**
   * encrypts a given value
   *
   * @param  {Mixed} value - value to be encrypted
   * @param  {String} [encoding=utf8] encoding to be used for input value
   * @return {String}
   *
   * @example
   * Encryption.encrypt('somevalue')
   *
   * @public
   */
  encrypt (value, encoding) {
    encoding = encoding || 'utf8'

    let iv = crypto.randomBytes(this.getIvSize())

    let cipher = crypto.createCipheriv(this.algorithm, this.appKey, iv)
    value = cipher.update(value, encoding, 'base64')
    value += cipher.final('base64')

    if (!value) {
      throw new Error('Could not encrypt the data.')
    }

    // Once we have the encrypted value we will go ahead base64_encode the input
    // vector and create the MAC for the encrypted value so we can verify its
    // authenticity. Then, we'll JSON encode the data in a "payload" array.
    let mac = this.hash(iv = this.base64Encode(iv), value)
    let json = JSON.stringify({iv: iv, value: value, mac: mac})
    return this.base64Encode(json)
  }

  /**
   * decrypts encrypted value
   *
   * @param  {String} value - value to decrypt
   * @param  {String} [encoding=utf8] encoding to be used for output value
   * @return {Mixed}
   *
   * @example
   * Encryption.decrypt('somevalue')
   *
   * @public
   */
  decrypt (payload, encoding) {
    encoding = encoding || 'utf8'
    payload = this.getJsonPayload(payload)

    let iv = this.base64Decode(payload.iv, true)

    let decipher = crypto.createDecipheriv(this.algorithm, this.appKey, iv)
    var decrypted = decipher.update(payload.value, 'base64', encoding)
    decrypted += decipher.final(encoding)

    if (!decrypted) {
      throw new Error('Could not decrypt the data.')
    }
    return decrypted
  }

  /**
   * get the JSON object from the given payload
   *
   * @param  {String} payload
   * @return {Mixed}
   */
  getJsonPayload (payload) {
    payload = JSON.parse(this.base64Decode(payload))
    // If the payload is not valid JSON or does not have the proper keys set we will
    // assume it is invalid and bail out of the routine since we will not be able
    // to decrypt the given value. We'll also check the MAC for this encryption.
    if (!payload || this.invalidPayload(payload)) {
      throw new Error('The payload is invalid.')
    }
    if (!this.validMac(payload)) {
      throw new Error('The MAC is invalid.')
    }
    return payload
  }

  /**
   * Create a MAC for the given value
   *
   * @param  {String} iv
   * @param  {String} value
   * @return {String}
   */
  hash (iv, value) {
    return this.hashHmac('sha256', iv + value, this.appKey)
  }

  /**
   * Generate a keyed hash value using the HMAC method
   *
   * @param  {String} algo
   * @param  {String} data
   * @param  {String} key
   * @return {String}
   */
  hashHmac (algo, data, key) {
    return crypto.createHmac(algo, key).update(data).digest('hex')
  }

  /**
   * returns encoded base64 string
   *
   * @param  {String} unencoded
   * @return {String}
   */
  base64Encode (unencoded) {
    return new Buffer(unencoded || '').toString('base64')
  }

  /**
   * returns decoded base64 string/buffer
   *
   * @param  {String} encoded
   * @param  {Boolean} raw
   * @return {Mixed}
   */
  base64Decode (encoded, raw) {
    if (raw) {
      return new Buffer(encoded || '', 'base64')
    }
    return new Buffer(encoded || '', 'base64').toString('utf8')
  }

  /**
   * Verify that the encryption payload is valid.
   *
   * @param  {Mixed} data
   * @return {Boolean}
   */
  invalidPayload (data) {
    return typeof data !== 'object' || !data.hasOwnProperty('iv') || !data.hasOwnProperty('value') || !data.hasOwnProperty('mac')
  }

  /**
   * Determine if the MAC for the given payload is valid
   *
   * @param  object payload
   * @return {Boolean}
   */
  validMac (payload) {
    let bytes = crypto.randomBytes(this.getIvSize())
    let calcMac = this.hashHmac('sha256', this.hash(payload.iv, payload.value), bytes)
    return this.hashHmac('sha256', payload.mac, bytes) === calcMac
  }

  /**
   * Get the IV size for the cipher
   *
   * @return {Integer}
   */
  getIvSize () {
    return 16
  }

}

module.exports = Encryption
<|endoftext|>


<commit_before>"use strict";
const exec = require('../util/exec-promise');
const Pod = require('./Pod');

class Node {

	constructor(name) {
		this.name = name;
	}

	getAllPods() {
		const cmd = [
			`kubectl get pods --all-namespaces`,
			`-o jsonpath='{range .items[?(.spec.nodeName=="${this.name}")]}{.metadata.namespace} {.metadata.name} {end}'`,
			`| xargs -n 2`
		].join(' ');

		return exec(cmd)
			.then(out => out
				.split('\n')
				.filter(line => line.length)
				.map(line => {
					const podName = line.split(' ')[1];
					const podNamespace = line.split(' ')[0];

					return new Pod(podName, podNamespace);
				})
			);
	}

	getJson() {
		return exec(`kubectl get nodes ${this.name} -o json`)
			.then(out => JSON.parse(out));
	}

	makeUnschedulable() {
		return exec(`kubectl patch node ${this.name} -p '{"spec":{"unschedulable":true}}'`);
	}

	makeSchedulable() {
		return exec(`kubectl patch node ${this.name} -p '{"spec":{"unschedulable":false}}'`);
	}
}

module.exports = Node;<commit_msg>Node#isReady<commit_after>"use strict";
const exec = require('../util/exec-promise');
const Pod = require('./Pod');
const parseKubectlFields = require('../util/parseKubectlFields');
const waitUntil = require('../util/waitUntil');

class Node {

	constructor(name) {
		this.name = name;
	}

	getAllPods() {
		const cmd = [
			`kubectl get pods --all-namespaces`,
			`-o jsonpath='{range .items[?(.spec.nodeName=="${this.name}")]}{.metadata.namespace} {.metadata.name} {end}'`,
			`| xargs -n 2`
		].join(' ');

		return exec(cmd)
			.then(out => out
				.split('\n')
				.filter(line => line.length)
				.map(line => {
					const podName = line.split(' ')[1];
					const podNamespace = line.split(' ')[0];

					return new Pod(podName, podNamespace);
				})
			);
	}

	getJson() {
		return exec(`kubectl get nodes ${this.name} -o json`)
			.then(out => JSON.parse(out));
	}

	makeUnschedulable() {
		return exec(`kubectl patch node ${this.name} -p '{"spec":{"unschedulable":true}}'`);
	}

	makeSchedulable() {
		return exec(`kubectl patch node ${this.name} -p '{"spec":{"unschedulable":false}}'`);
	}

	isReady() {
		return exec(`kubectl get node ${this.name}`)
			.then(out => parseKubectlFields(out))
			.then(fields => fields[0].STATUS === 'READY')
	}
}

module.exports = Node;<|endoftext|>


<commit_before>/*
This file in the main entry point for defining Gulp tasks and using Gulp plugins.
Click here to learn more. http://go.microsoft.com/fwlink/?LinkId=518007
*/

var gulp = require('gulp');

/**
 * PostCSS - Tool for transforming styles with JS plugins
 */
var postcss = require("gulp-postcss");

/**
 * PostCSS Ordered Values - Ensure values are ordered consistently in your CSS.
 */
var orderedValues = require('postcss-ordered-values');

/**
 * PostCSS Reporter - Log PostCSS messages in the console
 */
var reporter = require('postcss-reporter');

/**
 * Stylelint - Modern CSS linter
 */
var stylelint = require('stylelint');

/**
 * gulp-parker - Gulp plugin for parker, a stylesheet analysis tool.
 */
var parker = require('gulp-parker');

/**
 * del - Delete files/folders using globs
 */

var del = require('del');

/**
 * CSSComb - Sorts CSS Properties within each selector declaration in a predefined order to improve maintenance.
 */
var csscomb = require('gulp-csscomb');

/**
 * Gulp Log2 - Support for simple log statements in the gulp pipeline
 */
var log = require('gulp-log2');

/**
 * PostCSS Discard Duplicates - Discard duplicate rules in your CSS files with PostCSS.
 */
var discardDuplicates = require('postcss-discard-duplicates');

/**
 * Custom Properties - PostCSS plugin to transform W3C CSS Custom Properties for cascading variables
 */
var customProperties = require("postcss-custom-properties");
var fs = require("fs"); /* fs is a dependency of Custom Properties*/

/**
 * glob - Matches files using the patterns the shell uses.
 */
var glob = require('glob');

/**
 * q - A library for promises
 */
var Q = require('q');

/**
 * gulp-concat - Concatenates files
 */
var concat = require('gulp-concat');

/**
 * cssnano - a CSS modular minifier
 */
var cssnano = require('cssnano');

/**
 * autoprefixer - Parse CSS and add vendor prefixes to rules by Can I Use
 */
var autoprefixer = require('autoprefixer');

/**********************************************************************
 * Bundle Config
 */

var stylesheetBundle = [
    // Default Base
    'base/00-Normalize.css'
    , 'base/01-Base.css'
    , 'base/02-Typography.css'
    , 'base/03-List.css'
    , 'base/04-Forms.css'

    // Client Generic Rules
    , 'theme/theme.project.css'

    // Layout
    , 'layout/00-Layout.css'
    , 'layout/01-csswizardary-grids.css'

    // Objects
    , 'objects/*.css'

    // Vendor & Vendor Themes
    //, 'vendor/vendorsubfolder/vendor-theme.css'
    //, 'theme/vendor.project.css'

    // Components
    , 'components/*.css'
    , 'components/**/*.css'

    // WARNING: scope should be used sparingly - these should be used for a new styling context but may not necessarily cosmetic.
    // Scope can be open to abuse and lead to poor CSS if not used wisely. If you find yourself having to call a certain scope style in multiple locations
    // then it probably should be re-factored and moved outside of scope.
    // Normally what would be used in scope would be things that we don't have control over - such as if we were using some third party content management system
    // another case might be if we are needing to do certain styles that might be considered a 'one-off' or if it's a certain section of the site that needs to
    // be styled a certain way that for certain pages but the markup remains the same with the exception of the class that get's appended which would be used as the
    // scope hook.

    , 'scope/*.css'

    // Utilities

    //Uncomment when checking for potentially broken, malformed or legacy (X)HTML.
    // Note: Not all "errors" are created equally, so they are color coded to highlight severity. Green is "probably not a big problem",
    // yellow is "worth looking at" and red is "you really should fix this."

    //,'utilities/debug.css'

    , 'utilities/animations/*.css'

    // Strip-margin would conflict if moved below the 'aside' classes as it would now
    // override the aside rather than allowing the aside to be used after clearing the existing margin
    , 'utilities/overrides/strip-margin.css'
    , 'utilities/overrides/strip-border.css'
    , 'utilities/overrides/strip-padding.css'

    // Floats force a display of block so anything forcing display should come after it
    , 'utilities/overrides/floats.css'
    , 'utilities/overrides/force-display-visibility.css'
    , 'utilities/overrides/clearfix.css'

    // Alignments depends on the display type (and stripping margin may conflict with it)
    , 'utilities/overrides/align.css'

    // Aside uses margin so it would need to come after the strip-margin classes to work.
    , 'utilities/overrides/aside.css'

    // Flex utilities shouldn't be overridden by a force display
    , 'utilities/overrides/flex-utilities.css'

    // Overflow could conflict with Text-decoration on `u-text-ellipsis`
    , 'utilities/overrides/overflow.css'

    , 'utilities/overrides/cursor.css'
    , 'utilities/overrides/height.css'
    , 'utilities/overrides/line-height.css'
    , 'utilities/overrides/width.css'

    , 'utilities/overrides/font-size.css'
    , 'utilities/overrides/font-weight.css'
    , 'utilities/overrides/text-alignment.css'

    // Text decoration `u-text-ellipsis` (overflow) shouldn't be allow to be overridden as it will break
    , 'utilities/overrides/text-decoration.css'
    , 'utilities/overrides/text-transform.css'

    , 'utilities/overrides/position.css'
    , 'utilities/overrides/depth.css'
    , 'utilities/overrides/links.css'
    , 'utilities/overrides/box-sizing.css'
    , 'utilities/overrides/rotation.css'
    , 'utilities/shame.css'
    , 'utilities/print.css'
]

/*
 * End Bundle Config
 *********************************************************************/

gulp.task('production:Clean', done => {
    console.log('Deleting Old Preparation/Build/Etc Folders');
    del(['./Content/css/preparation']);
    del(['./Content/css/optimization']);
    del(['./Content/css/minification']);
    del(['./Content/css/dist']);

    done();
});

gulp.task('production:Prepare-custom-properties', gulp.series('production:Clean', function () {

    console.log("Since the custom properties get's concatenated with each stylesheet during polyfilling " +
        " strip out the comments to avoid it being added to each stylesheet.");

    return gulp.src('./Content/css/src/_settings/project.css')
        .pipe(log("Starting Minification on project settings"))
        // cssnano also does what orderValues does
        .pipe(postcss([
            autoprefixer(),
            cssnano({
                safe: true,
                discardComments: {
                    removeAll: true
                },
                normalizeCharset: true,
            }),
        ]))
        .pipe(log("Project Settings Minification Complete"))
        .pipe(gulp.dest('./Content/css/preparation/_settings/'));

}));

gulp.task('production:Polyfilling-custom-properties', gulp.series('production:Prepare-custom-properties', function () {

    console.log("Polyfilling - Custom Properties: Since Css Variables (Css Properties) are supported in Firefox, Chrome & Safari." +
        " Use a polyfill (customProperties) to get it rendered for IE & Edge");

    var cssSrc = './Content/css/src/{base,components,components/**,layout,objects,scope,theme,utilities,utilities/**,vendor,vendor/**}/*.css';

    var projectSettings = './Content/css/preparation/_settings/project.css';

    var promises = []; // Used for looping through each file within _settings

    //Iterating over the files
    //Original logic based off of the following - http://stackoverflow.com/a/22159951
    glob.sync(cssSrc).forEach(function (filePath) {

        var polyfillStylesheets = []

        polyfillStylesheets.push(projectSettings);
        polyfillStylesheets.push(filePath);

        if (fs.statSync(polyfillStylesheets[1]).isFile()) {

            var defer = Q.defer();
            var pipeline = gulp.src(polyfillStylesheets)
                .pipe(concat(polyfillStylesheets[1].replace("./Content/css/src/", "")))
                //TODO: Fix
                //.pipe(postcss([customProperties]))
                .pipe(gulp.dest('./Content/css/preparation/'));

            pipeline.on('end', function () {
                defer.resolve();
            });
            promises.push(defer.promise);
        }
    });

    return Q.all(promises);

}));

gulp.task('production:Optimization', gulp.series('production:Polyfilling-custom-properties', function () {

    var cssSrc = './Content/css/preparation/{base,components,components/**,layout,objects,scope,theme,utilities,utilities/**,vendor,vendor/**}/*.css';

    console.warn("TODO: Make sure you have completed any polyfilling steps needed.")
    console.log("This task is similar to `dev:normalize-css-styles` however it is assumed that any polyfilling steps would be completed before running this task.");

    return gulp.src(cssSrc)
        .pipe(log("Starting PostCSS"))
        .pipe(log(" - Optimization - Discard Duplicates: Discard duplicate rules in your CSS files."))
        .pipe(log(" - Optimization - Ordered Values: Ensure values are ordered consistently in your CSS."))
        .pipe(postcss([
            discardDuplicates(),
            orderedValues()
        ]))
        .pipe(log("Ending PostCSS"))
        .pipe(log("Starting CSSComb - sorting CSS Properties within each selector declaration"))
        .pipe(csscomb())
        .pipe(log("Ending CSSComb"))
        .pipe(gulp.dest('./Content/css/optimization/'))
}));

gulp.task('production:Minification', gulp.series('production:Optimization', function () {

    var cssSrc = './Content/css/optimization/{base,components,components/**,layout,objects,scope,theme,utilities,utilities/**,vendor,vendor/**}/*.css';

    return gulp.src(cssSrc)
        .pipe(log("Starting PostCSS"))
        .pipe(log(" - Minification - CSSNano: Modular Minifier"))
        .pipe(postcss([
            autoprefixer(),
            cssnano({
                safe: true,
                discardComments: {
                    removeAll: true
                },
                normalizeCharset: true,
            })
        ]))
        .pipe(log("Ending PostCSS"))
        .pipe(gulp.dest('./Content/css/minification/'))
}));

gulp.task('dev:stylelint', function ()
{

    var processors = [
      stylelint(),
      // Pretty reporting config
      reporter({
          clearAllMessages: true,
          throwError: false
      })
    ];

    return gulp.src(
    // Stylesheet source:
        ['./Content/css/src/**/*.css',
        // Ignore linting vendor assets:
            '!./Content/css/src/vendor/**/*.css',
            '!./Content/css/src/utilities/debug.css',
            '!./Content/css/src/base/00-Normalize.css']
        //, '!./Content/css/src/_settings/**/*.css']
  )
  .pipe(postcss(processors));
});

gulp.task('dev:parker', function () {

    del(['./report.md']);

    return gulp.src(
        // Stylesheet source:
        ['./Content/css/src/**/*.css',
            // Ignore linting vendor assets:
            '!./Content/css/src/vendor/**/*.css',
            '!./Content/css/src/utilities/debug.css',
            '!./Content/css/src/base/00-Normalize.css']
    )
        .pipe(parker({
            file: 'report.md'
        }))
        .pipe(log("Markdown File (report.md) is located at the root of the Solution"));
});

gulp.task('dev:normalize-css-styles', function () {

    del(['./Content/css/normalized']);

    var cssSrc = ['./Content/css/src/{base,components,components/**,layout,objects,scope,theme,utilities,utilities/**,vendor,vendor/**}/*.css',
        // Ignore Normalizing third party assets
        '!./Content/css/src/vendor/**/*.css',
        '!./Content/css/src/utilities/debug.css',
        '!./Content/css/src/base/00-Normalize.css'
    ];

    return gulp.src(cssSrc)
        .pipe(log("Starting PostCSS"))
        .pipe(postcss([
            discardDuplicates(),
            orderedValues()
        ]))
        .pipe(log("Ending PostCSS"))
        .pipe(log("Starting CSSComb - sorting CSS Properties within each selector declaration"))
        .pipe(csscomb())
        .pipe(log("Ending CSSComb"))
        .pipe(gulp.dest('./Content/css/normalized'))
        .pipe(log("CSS Normalization is complete, compare changes in src against normalized"));
});

gulp.task('isolated-debug:node-version', done => {
    /**
     * Isolated gulp task which returns Node Version installed/is being used. Useful when debugging possible issues.
     */
    console.log('Version: ' + process.version)
    done();
});

gulp.task('default-example', gulp.series('production:Minification', function (done) {
    console.log('You are running Node Version: ' + process.version);
    console.log("Update default to run what task you need it to run." +
        " For Example: If you plan to have a different build process for your minification or bundling - then you probably only need to run `production: Optimization`" +
        " HOWEVER - if you need don't need bundling but need the minification - then you only need to run `production:Minification` such as in the cases of HTTP2 support where bundling may not be as useful." +
        " but maybe you can't support HTTP2 yet so you still need to bundle - then you can update this default task to do that after it's completed the `production:Minification` build and use the " +
        " stylesheetBundle located above the default task.");

    var cssSrc = './Content/css/minification/{base,components,components/**,layout,objects,scope,theme,utilities,utilities/**,vendor,vendor/**}/*.css';
    return gulp.src(cssSrc)
        .pipe(log("This is an example where [default-example] is only using the `'production:Minification` task and is outputting to the dist folder as gulp is being used for the full build process. You will want to have a clean up task that cleans up everything except your dist folder in this case."))
        .pipe(gulp.dest('./Content/css/dist/'))
}));

gulp.task('default', done => {
    console.log('You are running Node Version: ' + process.version);
    console.log("Update default to run what task you need it to run." +
        " For Example: If you plan to have a different build process for your minification or bundling - then you probably only need to run `production: Optimization`" +
        " HOWEVER - if you need don't need bundling but need the minification - then you only need to run `production:Minification` such as in the cases of HTTP2 support where bundling may not be as useful." +
        " but maybe you can't support HTTP2 yet so you still need to bundle - then you can update this default task to do that after it's completed the `production:Minification` build and use the " +
        " stylesheetBundle located above the default task.");

    done();
});

<commit_msg>Add missing semicolons<commit_after>/*
This file in the main entry point for defining Gulp tasks and using Gulp plugins.
Click here to learn more. http://go.microsoft.com/fwlink/?LinkId=518007
*/

var gulp = require('gulp');

/**
 * PostCSS - Tool for transforming styles with JS plugins
 */
var postcss = require("gulp-postcss");

/**
 * PostCSS Ordered Values - Ensure values are ordered consistently in your CSS.
 */
var orderedValues = require('postcss-ordered-values');

/**
 * PostCSS Reporter - Log PostCSS messages in the console
 */
var reporter = require('postcss-reporter');

/**
 * Stylelint - Modern CSS linter
 */
var stylelint = require('stylelint');

/**
 * gulp-parker - Gulp plugin for parker, a stylesheet analysis tool.
 */
var parker = require('gulp-parker');

/**
 * del - Delete files/folders using globs
 */

var del = require('del');

/**
 * CSSComb - Sorts CSS Properties within each selector declaration in a predefined order to improve maintenance.
 */
var csscomb = require('gulp-csscomb');

/**
 * Gulp Log2 - Support for simple log statements in the gulp pipeline
 */
var log = require('gulp-log2');

/**
 * PostCSS Discard Duplicates - Discard duplicate rules in your CSS files with PostCSS.
 */
var discardDuplicates = require('postcss-discard-duplicates');

/**
 * Custom Properties - PostCSS plugin to transform W3C CSS Custom Properties for cascading variables
 */
var customProperties = require("postcss-custom-properties");
var fs = require("fs"); /* fs is a dependency of Custom Properties*/

/**
 * glob - Matches files using the patterns the shell uses.
 */
var glob = require('glob');

/**
 * q - A library for promises
 */
var Q = require('q');

/**
 * gulp-concat - Concatenates files
 */
var concat = require('gulp-concat');

/**
 * cssnano - a CSS modular minifier
 */
var cssnano = require('cssnano');

/**
 * autoprefixer - Parse CSS and add vendor prefixes to rules by Can I Use
 */
var autoprefixer = require('autoprefixer');

/**********************************************************************
 * Bundle Config
 */

var stylesheetBundle = [
    // Default Base
    'base/00-Normalize.css'
    , 'base/01-Base.css'
    , 'base/02-Typography.css'
    , 'base/03-List.css'
    , 'base/04-Forms.css'

    // Client Generic Rules
    , 'theme/theme.project.css'

    // Layout
    , 'layout/00-Layout.css'
    , 'layout/01-csswizardary-grids.css'

    // Objects
    , 'objects/*.css'

    // Vendor & Vendor Themes
    //, 'vendor/vendorsubfolder/vendor-theme.css'
    //, 'theme/vendor.project.css'

    // Components
    , 'components/*.css'
    , 'components/**/*.css'

    // WARNING: scope should be used sparingly - these should be used for a new styling context but may not necessarily cosmetic.
    // Scope can be open to abuse and lead to poor CSS if not used wisely. If you find yourself having to call a certain scope style in multiple locations
    // then it probably should be re-factored and moved outside of scope.
    // Normally what would be used in scope would be things that we don't have control over - such as if we were using some third party content management system
    // another case might be if we are needing to do certain styles that might be considered a 'one-off' or if it's a certain section of the site that needs to
    // be styled a certain way that for certain pages but the markup remains the same with the exception of the class that get's appended which would be used as the
    // scope hook.

    , 'scope/*.css'

    // Utilities

    //Uncomment when checking for potentially broken, malformed or legacy (X)HTML.
    // Note: Not all "errors" are created equally, so they are color coded to highlight severity. Green is "probably not a big problem",
    // yellow is "worth looking at" and red is "you really should fix this."

    //,'utilities/debug.css'

    , 'utilities/animations/*.css'

    // Strip-margin would conflict if moved below the 'aside' classes as it would now
    // override the aside rather than allowing the aside to be used after clearing the existing margin
    , 'utilities/overrides/strip-margin.css'
    , 'utilities/overrides/strip-border.css'
    , 'utilities/overrides/strip-padding.css'

    // Floats force a display of block so anything forcing display should come after it
    , 'utilities/overrides/floats.css'
    , 'utilities/overrides/force-display-visibility.css'
    , 'utilities/overrides/clearfix.css'

    // Alignments depends on the display type (and stripping margin may conflict with it)
    , 'utilities/overrides/align.css'

    // Aside uses margin so it would need to come after the strip-margin classes to work.
    , 'utilities/overrides/aside.css'

    // Flex utilities shouldn't be overridden by a force display
    , 'utilities/overrides/flex-utilities.css'

    // Overflow could conflict with Text-decoration on `u-text-ellipsis`
    , 'utilities/overrides/overflow.css'

    , 'utilities/overrides/cursor.css'
    , 'utilities/overrides/height.css'
    , 'utilities/overrides/line-height.css'
    , 'utilities/overrides/width.css'

    , 'utilities/overrides/font-size.css'
    , 'utilities/overrides/font-weight.css'
    , 'utilities/overrides/text-alignment.css'

    // Text decoration `u-text-ellipsis` (overflow) shouldn't be allow to be overridden as it will break
    , 'utilities/overrides/text-decoration.css'
    , 'utilities/overrides/text-transform.css'

    , 'utilities/overrides/position.css'
    , 'utilities/overrides/depth.css'
    , 'utilities/overrides/links.css'
    , 'utilities/overrides/box-sizing.css'
    , 'utilities/overrides/rotation.css'
    , 'utilities/shame.css'
    , 'utilities/print.css'
]

/*
 * End Bundle Config
 *********************************************************************/

gulp.task('production:Clean', done => {
    console.log('Deleting Old Preparation/Build/Etc Folders');
    del(['./Content/css/preparation']);
    del(['./Content/css/optimization']);
    del(['./Content/css/minification']);
    del(['./Content/css/dist']);

    done();
});

gulp.task('production:Prepare-custom-properties', gulp.series('production:Clean', function () {

    console.log("Since the custom properties get's concatenated with each stylesheet during polyfilling " +
        " strip out the comments to avoid it being added to each stylesheet.");

    return gulp.src('./Content/css/src/_settings/project.css')
        .pipe(log("Starting Minification on project settings"))
        // cssnano also does what orderValues does
        .pipe(postcss([
            autoprefixer(),
            cssnano({
                safe: true,
                discardComments: {
                    removeAll: true
                },
                normalizeCharset: true,
            }),
        ]))
        .pipe(log("Project Settings Minification Complete"))
        .pipe(gulp.dest('./Content/css/preparation/_settings/'));

}));

gulp.task('production:Polyfilling-custom-properties', gulp.series('production:Prepare-custom-properties', function () {

    console.log("Polyfilling - Custom Properties: Since Css Variables (Css Properties) are supported in Firefox, Chrome & Safari." +
        " Use a polyfill (customProperties) to get it rendered for IE & Edge");

    var cssSrc = './Content/css/src/{base,components,components/**,layout,objects,scope,theme,utilities,utilities/**,vendor,vendor/**}/*.css';

    var projectSettings = './Content/css/preparation/_settings/project.css';

    var promises = []; // Used for looping through each file within _settings

    //Iterating over the files
    //Original logic based off of the following - http://stackoverflow.com/a/22159951
    glob.sync(cssSrc).forEach(function (filePath) {

        var polyfillStylesheets = [];

        polyfillStylesheets.push(projectSettings);
        polyfillStylesheets.push(filePath);

        if (fs.statSync(polyfillStylesheets[1]).isFile()) {

            var defer = Q.defer();
            var pipeline = gulp.src(polyfillStylesheets)
                .pipe(concat(polyfillStylesheets[1].replace("./Content/css/src/", "")))
                //TODO: Fix
                //.pipe(postcss([customProperties]))
                .pipe(gulp.dest('./Content/css/preparation/'));

            pipeline.on('end', function () {
                defer.resolve();
            });
            promises.push(defer.promise);
        }
    });

    return Q.all(promises);

}));

gulp.task('production:Optimization', gulp.series('production:Polyfilling-custom-properties', function () {

    var cssSrc = './Content/css/preparation/{base,components,components/**,layout,objects,scope,theme,utilities,utilities/**,vendor,vendor/**}/*.css';

    console.warn("TODO: Make sure you have completed any polyfilling steps needed.")
    console.log("This task is similar to `dev:normalize-css-styles` however it is assumed that any polyfilling steps would be completed before running this task.");

    return gulp.src(cssSrc)
        .pipe(log("Starting PostCSS"))
        .pipe(log(" - Optimization - Discard Duplicates: Discard duplicate rules in your CSS files."))
        .pipe(log(" - Optimization - Ordered Values: Ensure values are ordered consistently in your CSS."))
        .pipe(postcss([
            discardDuplicates(),
            orderedValues()
        ]))
        .pipe(log("Ending PostCSS"))
        .pipe(log("Starting CSSComb - sorting CSS Properties within each selector declaration"))
        .pipe(csscomb())
        .pipe(log("Ending CSSComb"))
        .pipe(gulp.dest('./Content/css/optimization/'));
}));

gulp.task('production:Minification', gulp.series('production:Optimization', function () {

    var cssSrc = './Content/css/optimization/{base,components,components/**,layout,objects,scope,theme,utilities,utilities/**,vendor,vendor/**}/*.css';

    return gulp.src(cssSrc)
        .pipe(log("Starting PostCSS"))
        .pipe(log(" - Minification - CSSNano: Modular Minifier"))
        .pipe(postcss([
            autoprefixer(),
            cssnano({
                safe: true,
                discardComments: {
                    removeAll: true
                },
                normalizeCharset: true,
            })
        ]))
        .pipe(log("Ending PostCSS"))
        .pipe(gulp.dest('./Content/css/minification/'));
}));

gulp.task('dev:stylelint', function ()
{

    var processors = [
      stylelint(),
      // Pretty reporting config
      reporter({
          clearAllMessages: true,
          throwError: false
      })
    ];

    return gulp.src(
    // Stylesheet source:
        ['./Content/css/src/**/*.css',
        // Ignore linting vendor assets:
            '!./Content/css/src/vendor/**/*.css',
            '!./Content/css/src/utilities/debug.css',
            '!./Content/css/src/base/00-Normalize.css']
        //, '!./Content/css/src/_settings/**/*.css']
  )
  .pipe(postcss(processors));
});

gulp.task('dev:parker', function () {

    del(['./report.md']);

    return gulp.src(
        // Stylesheet source:
        ['./Content/css/src/**/*.css',
            // Ignore linting vendor assets:
            '!./Content/css/src/vendor/**/*.css',
            '!./Content/css/src/utilities/debug.css',
            '!./Content/css/src/base/00-Normalize.css']
    )
        .pipe(parker({
            file: 'report.md'
        }))
        .pipe(log("Markdown File (report.md) is located at the root of the Solution"));
});

gulp.task('dev:normalize-css-styles', function () {

    del(['./Content/css/normalized']);

    var cssSrc = ['./Content/css/src/{base,components,components/**,layout,objects,scope,theme,utilities,utilities/**,vendor,vendor/**}/*.css',
        // Ignore Normalizing third party assets
        '!./Content/css/src/vendor/**/*.css',
        '!./Content/css/src/utilities/debug.css',
        '!./Content/css/src/base/00-Normalize.css'
    ];

    return gulp.src(cssSrc)
        .pipe(log("Starting PostCSS"))
        .pipe(postcss([
            discardDuplicates(),
            orderedValues()
        ]))
        .pipe(log("Ending PostCSS"))
        .pipe(log("Starting CSSComb - sorting CSS Properties within each selector declaration"))
        .pipe(csscomb())
        .pipe(log("Ending CSSComb"))
        .pipe(gulp.dest('./Content/css/normalized'))
        .pipe(log("CSS Normalization is complete, compare changes in src against normalized"));
});

gulp.task('isolated-debug:node-version', done => {
    /**
     * Isolated gulp task which returns Node Version installed/is being used. Useful when debugging possible issues.
     */
    console.log('Version: ' + process.version);
    done();
});

gulp.task('default-example', gulp.series('production:Minification', function (done) {
    console.log('You are running Node Version: ' + process.version);
    console.log("Update default to run what task you need it to run." +
        " For Example: If you plan to have a different build process for your minification or bundling - then you probably only need to run `production: Optimization`" +
        " HOWEVER - if you need don't need bundling but need the minification - then you only need to run `production:Minification` such as in the cases of HTTP2 support where bundling may not be as useful." +
        " but maybe you can't support HTTP2 yet so you still need to bundle - then you can update this default task to do that after it's completed the `production:Minification` build and use the " +
        " stylesheetBundle located above the default task.");

    var cssSrc = './Content/css/minification/{base,components,components/**,layout,objects,scope,theme,utilities,utilities/**,vendor,vendor/**}/*.css';
    return gulp.src(cssSrc)
        .pipe(log("This is an example where [default-example] is only using the `'production:Minification` task and is outputting to the dist folder as gulp is being used for the full build process. You will want to have a clean up task that cleans up everything except your dist folder in this case."))
        .pipe(gulp.dest('./Content/css/dist/'));
}));

gulp.task('default', done => {
    console.log('You are running Node Version: ' + process.version);
    console.log("Update default to run what task you need it to run." +
        " For Example: If you plan to have a different build process for your minification or bundling - then you probably only need to run `production: Optimization`" +
        " HOWEVER - if you need don't need bundling but need the minification - then you only need to run `production:Minification` such as in the cases of HTTP2 support where bundling may not be as useful." +
        " but maybe you can't support HTTP2 yet so you still need to bundle - then you can update this default task to do that after it's completed the `production:Minification` build and use the " +
        " stylesheetBundle located above the default task.");

    done();
});

<|endoftext|>


<commit_before>var RSH = require('./RequestSignatureHelper').RequestSignatureHelper,
    http = require('http'),
    xml2js = require('xml2js');

var OperationHelper = function(params) {
    this.init(params);
};

OperationHelper.version = '2011-08-01';
OperationHelper.service = 'AWSECommerceService';
OperationHelper.defaultEndPoint = 'ecs.amazonaws.com';
OperationHelper.defaultBaseUri = '/onca/xml';

OperationHelper.prototype.init = function(params) {
    params = params || {};

    // check requried params
    if (typeof(params.awsId)     === 'undefined') { throw 'Missing AWS Id param' }
    if (typeof(params.awsSecret) === 'undefined') { throw 'Missing AWS Secret param' }
    if (typeof(params.assocId)   === 'undefined') { throw 'Missing Associate Id param' }

    // set instance variables from params
    this.awsId     = params.awsId;
    this.awsSecret = params.awsSecret;
    this.assocId   = params.assocId;
    this.endPoint  = params.endPoint || OperationHelper.defaultEndPoint;
    this.baseUri   = params.baseUri || OperationHelper.defaultBaseUri;
};

OperationHelper.prototype.getSignatureHelper = function() {
    if (typeof(this.signatureHelper) === 'undefined') {
        var params = {};
        params[RSH.kAWSAccessKeyId] = this.awsId;
        params[RSH.kAWSSecretKey]   = this.awsSecret;
        params[RSH.kEndPoint]       = this.endPoint;
        this.signatureHelper = new RSH(params);
    }
    return this.signatureHelper;
};

OperationHelper.prototype.generateParams = function(operation, params) {
    params.Service = OperationHelper.service;
    params.Version = OperationHelper.version;
    params.Operation = operation;
    params.AWSAccessKeyId = this.awsId;
    params.AssociateTag = this.assocId;
    return params;
};

OperationHelper.prototype.generateUri = function(operation, params) {
    params = this.generateParams(operation, params);
    var helper = this.getSignatureHelper();
    params = helper.sign(params);
    var queryString = helper.canonicalize(params);
    var uri = this.baseUri + '?' + queryString;
    return uri;
}

OperationHelper.prototype.execute = function(operation, params, callback) {
    if (typeof(operation) === 'undefined') { throw 'Missing operation parameter' }
    if (typeof(params) === 'undefined') { params = {} }
    var uri = this.generateUri(operation, params);
    var host = this.endPoint;

    var options = {
        hostname: host,
        path: uri,
        method: 'GET'
    };

    var responseBody = '';

    var request = http.request(options, function (response) {
        response.setEncoding('utf8');

        response.on('data', function (chunk) {
            responseBody += chunk;
        });

        response.on('end', function() {
            xml2js.parseString(responseBody, function(err, result){
                if (err) {console.log(err);}
                callback(result);
            });
        });

    });

    request.on('error', function (err) {
        console.log(err.message);
    });

    request.end();
};

exports.OperationHelper = OperationHelper;
<commit_msg>Enabling an option to return xml as an extra option<commit_after>var RSH = require('./RequestSignatureHelper').RequestSignatureHelper,
    http = require('http'),
    xml2js = require('xml2js');

var OperationHelper = function(params) {
    this.init(params);
};

OperationHelper.version = '2011-08-01';
OperationHelper.service = 'AWSECommerceService';
OperationHelper.defaultEndPoint = 'ecs.amazonaws.com';
OperationHelper.defaultBaseUri = '/onca/xml';

OperationHelper.prototype.init = function(params) {
    params = params || {};

    // check requried params
    if (typeof(params.awsId)     === 'undefined') { throw 'Missing AWS Id param' }
    if (typeof(params.awsSecret) === 'undefined') { throw 'Missing AWS Secret param' }
    if (typeof(params.assocId)   === 'undefined') { throw 'Missing Associate Id param' }

    // set instance variables from params
    this.awsId     = params.awsId;
    this.awsSecret = params.awsSecret;
    this.assocId   = params.assocId;
    this.endPoint  = params.endPoint || OperationHelper.defaultEndPoint;
    this.baseUri   = params.baseUri || OperationHelper.defaultBaseUri;
};

OperationHelper.prototype.getSignatureHelper = function() {
    if (typeof(this.signatureHelper) === 'undefined') {
        var params = {};
        params[RSH.kAWSAccessKeyId] = this.awsId;
        params[RSH.kAWSSecretKey]   = this.awsSecret;
        params[RSH.kEndPoint]       = this.endPoint;
        this.signatureHelper = new RSH(params);
    }
    return this.signatureHelper;
};

OperationHelper.prototype.generateParams = function(operation, params) {
    params.Service = OperationHelper.service;
    params.Version = OperationHelper.version;
    params.Operation = operation;
    params.AWSAccessKeyId = this.awsId;
    params.AssociateTag = this.assocId;
    return params;
};

OperationHelper.prototype.generateUri = function(operation, params) {
    params = this.generateParams(operation, params);
    var helper = this.getSignatureHelper();
    params = helper.sign(params);
    var queryString = helper.canonicalize(params);
    var uri = this.baseUri + '?' + queryString;
    return uri;
}

OperationHelper.prototype.execute = function(operation, params, extraOptions, callback) {
    if (typeof(operation) === 'undefined') { throw 'Missing operation parameter' }
    if (typeof(params) === 'undefined') { params = {} }
    if (typeof(extraOptions) === 'function') {
        callback = extraOptions;
        extraOptions = {};
    }
    var uri = this.generateUri(operation, params);
    var host = this.endPoint;

    var options = {
        hostname: host,
        path: uri,
        method: 'GET'
    };

    var responseBody = '';

    var request = http.request(options, function (response) {
        response.setEncoding('utf8');

        response.on('data', function (chunk) {
            responseBody += chunk;
        });

        response.on('end', function() {
            if (extraOptions.useXml) {
                callback(responseBody);
            } else {
                xml2js.parseString(responseBody, function(err, result){
                    if (err) {console.log(err);}
                    callback(result);
                });
            }
        });

    });

    request.on('error', function (err) {
        console.log(err.message);
    });

    request.end();
};

exports.OperationHelper = OperationHelper;
<|endoftext|>


<commit_before>var Pool          = require('./Pool');
var PoolConfig    = require('./PoolConfig');
var PoolNamespace = require('./PoolNamespace');
var PoolSelector  = require('./PoolSelector');
var Util          = require('util');
var EventEmitter  = require('events').EventEmitter;

/**
 * PoolClusterFarm
 * @constructor
 * @param {object} [config] The pool cluster farm configuration
 * @public
 */
function PoolClusterFarm(config) {
  EventEmitter.call(this);

  config = config || {};
  this._servers = [];
  this._numMasters = 0;
  this._numSlaves = 0;
  this._last_index = 0;
  this.debug = config.debug || false
}

var isFunction = function(func) {
	return func && typeof func == 'function'
}

Util.inherits(PoolClusterFarm,EventEmitter);

PoolClusterFarm.prototype.query = function query(sql,values,cb) {
	var self = this

	if (!this._numMasters || this._numMasters < 1) {
		throw new Error('No servers assigned to PoolClusterFarm. Please verify...')
	}

	if (typeof sql != 'string') {
		throw new Error('Invalid sql statement. Please verify...')
	}

	if (sql.match(/select/i) || sql.match(/call/i)) {
		var index_to_use = this._last_index;
		if (index_to_use >= (this._servers.length)) { this._last_index = index_to_use = 0; }
		else { this._last_index++; }
		if (this.debug) { console.log('Using server',(index_to_use+1),'of',(this._servers.length)); }
		this._servers[index_to_use].pool.query(sql,values,cb);
	} else {
		var num = this._numMasters;
		var error_encountered = false

		var updateMasters = function(i) {
			if (error_encountered) { return }
			self._servers[i].pool.query(sql,values,function(err,rows) {
				if (error_encountered) { return }
				if (err) {
					error_encountered = true
					if (isFunction(cb)) { return cb(err,rows); }
					else if (values && typeof values == 'function') { return values(err,rows); }
				}
				if (!(--num)) {
					if (isFunction(cb)) { return cb(err,rows); }
					else if (values && typeof values == 'function') { return values(err,rows); }
				}
			});
		}

		for (var i = 0; i < this._servers.length; i++) {
			if (this._servers[i].type == 'master') {
				updateMasters(i);
			}
		}

	}
}

PoolClusterFarm.prototype.addMaster = function(id,config,respond) {
	if (!respond || typeof respond != 'function') { respond = function(err){ throw new Error(err) }; }
	var server_name = typeof id == 'string' ? id : this._makeId();
	var server_type = 'master';
	var server_config = {};
	if (config && typeof config == 'object') {
		server_config = config;
	} else if (type && typeof type == 'object') {
		server_config = type;
	} else if (id && typeof id == 'object') {
		server_config = id;
	} else {
		throw new Error('No server configuration passed in. Please look at the documentation.');
	}
	var poolConfig = new PoolConfig(server_config)
	this._servers.push({
		id:server_name,
		type:server_type,
		master_id:null
		pool:new Pool({config:poolConfig}),
		online:1
	})
	this._calculateNumServers()
}

PoolClusterFarm.prototype.addSlave = function(id,master,config,respon) {
	if (!respond || typeof respond != 'function') { respond = function(err){ throw new Error(err) }; }
	var server_name = typeof id == 'string' ? id : this._makeId();
	var server_type = 'slave';
	var server_master_id = master;
	var server_config = {};
	if (config && typeof config == 'object') {
		server_config = config;
	} else if (type && typeof type == 'object') {
		server_config = type;
	} else if (id && typeof id == 'object') {
		server_config = id;
	} else {
		throw new Error('No server configuration passed in. Please look at the documentation.');
	}
	var poolConfig = new PoolConfig(server_config)
	this._servers.push({
		id:server_name,
		type:server_type,
		master_id:server_master_id
		pool:new Pool({config:poolConfig}),
		online:1
	})
	this._calculateNumServers()
}

PoolClusterFarm.prototype._makeId = function _makeId() {
	return Math.random().toString(36).substring(5);
}

PoolClusterFarm.prototype._calculateNumServers = function _calculateNumServers() {
	this._numMasters = 0;
	this._numSlaves = 0;
	for (var i = 0; i < this._servers.length; i++) {
		if (this._servers[i].type == 'master') { this._numMasters++; }
		else { this._numSlaves++; }
	}
}

module.exports = PoolClusterFarm;<commit_msg>finsihed<commit_after>var Pool          = require('./Pool');
var PoolConfig    = require('./PoolConfig');
var PoolNamespace = require('./PoolNamespace');
var PoolSelector  = require('./PoolSelector');
var Util          = require('util');
var EventEmitter  = require('events').EventEmitter;

/**
 * PoolClusterFarm
 * @constructor
 * @param {object} [config] The pool cluster farm configuration
 * @public
 */
function PoolClusterFarm(config) {
  EventEmitter.call(this);

  config = config || {};
  this._servers = [];
  this._numMasters = 0;
  this._numSlaves = 0;
  this._last_index = 0;
  this.debug = config.debug || false
}

var isFunction = function(func) {
	return func && typeof func == 'function'
}

Util.inherits(PoolClusterFarm,EventEmitter);

PoolClusterFarm.prototype.query = function query(sql,values,cb) {
	var self = this

	if (!this._numMasters || this._numMasters < 1) {
		throw new Error('No servers assigned to PoolClusterFarm. Please verify...')
	}

	if (typeof sql != 'string') {
		throw new Error('Invalid sql statement. Please verify...')
	}

	if (sql.match(/select/i) || sql.match(/call/i)) {
		var index_to_use = this._last_index;
		if (index_to_use >= (this._servers.length)) { this._last_index = index_to_use = 0; }
		else { this._last_index++; }
		if (this.debug) { console.log('Using server',(index_to_use+1),'of',(this._servers.length)); }
		this._servers[index_to_use].pool.query(sql,values,cb);
	} else {
		var num = this._numMasters;
		var error_encountered = false

		var updateMasters = function(i) {
			if (error_encountered) { return }
			self._servers[i].pool.query(sql,values,function(err,rows) {
				if (error_encountered) { return }
				if (err) {
					error_encountered = true
					if (isFunction(cb)) { return cb(err,rows); }
					else if (values && typeof values == 'function') { return values(err,rows); }
				}
				if (!(--num)) {
					if (isFunction(cb)) { return cb(err,rows); }
					else if (values && typeof values == 'function') { return values(err,rows); }
				}
			});
		}

		for (var i = 0; i < this._servers.length; i++) {
			if (this._servers[i].type == 'master') {
				updateMasters(i);
			}
		}

	}
}

PoolClusterFarm.prototype.addMaster = function(id,config,respond) {
	if (!respond || typeof respond != 'function') { respond = function(err){ throw new Error(err) }; }
	var server_name = typeof id == 'string' ? id : this._makeId();
	var server_type = 'master';
	var server_config = {};
	if (config && typeof config == 'object') {
		server_config = config;
	} else if (type && typeof type == 'object') {
		server_config = type;
	} else if (id && typeof id == 'object') {
		server_config = id;
	} else {
		throw new Error('No server configuration passed in. Please look at the documentation.');
	}
	var poolConfig = new PoolConfig(server_config)
	this._servers.push({
		id:server_name,
		type:server_type,
		master_id:null,
		pool:new Pool({config:poolConfig}),
		online:1
	})
	this._calculateNumServers()
}

PoolClusterFarm.prototype.addSlave = function(id,master,config,respon) {
	if (!respond || typeof respond != 'function') { respond = function(err){ throw new Error(err) }; }
	var server_name = typeof id == 'string' ? id : this._makeId();
	var server_type = 'slave';
	var server_master_id = master;
	var server_config = {};
	if (config && typeof config == 'object') {
		server_config = config;
	} else if (type && typeof type == 'object') {
		server_config = type;
	} else if (id && typeof id == 'object') {
		server_config = id;
	} else {
		throw new Error('No server configuration passed in. Please look at the documentation.');
	}
	var poolConfig = new PoolConfig(server_config)
	this._servers.push({
		id:server_name,
		type:server_type,
		master_id:server_master_id,
		pool:new Pool({config:poolConfig}),
		online:1
	})
	this._calculateNumServers()
}

PoolClusterFarm.prototype._makeId = function _makeId() {
	return Math.random().toString(36).substring(5);
}

PoolClusterFarm.prototype._calculateNumServers = function _calculateNumServers() {
	this._numMasters = 0;
	this._numSlaves = 0;
	for (var i = 0; i < this._servers.length; i++) {
		if (this._servers[i].type == 'master') { this._numMasters++; }
		else { this._numSlaves++; }
	}
}

module.exports = PoolClusterFarm;<|endoftext|>


<commit_before>/**
 * Tooltip
 */

import React, { Component, createRef } from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';

import get from 'lodash/get';
import childrenOf from '../../util/childrenOf';
import Popper, { AVAILABLE_PLACEMENTS } from '../Popper/Popper';
import css from './Tooltip.css';

const PORTAL_ELEMENT = 'OverlayContainer';
const SHOW_EVENT_LISTENERS = ['mouseover', 'focus'];
const HIDE_EVENT_LISTENERS = ['mouseout', 'focusout'];

export default class Tooltip extends Component {
  static propTypes = {
    children: PropTypes.func,
    hideOnTouch: PropTypes.bool,
    id: PropTypes.string.isRequired,
    modifiers: PropTypes.object,
    placement: PropTypes.oneOf(AVAILABLE_PLACEMENTS),
    sub: PropTypes.oneOfType([
      PropTypes.string,
      childrenOf(FormattedMessage)
    ]),
    text: PropTypes.oneOfType([
      PropTypes.string.isRequired,
      childrenOf(FormattedMessage).isRequired
    ]).isRequired,
    triggerRef: PropTypes.oneOfType([
      PropTypes.func,
      PropTypes.shape({ current: PropTypes.elementType }),
      PropTypes.shape({ current: PropTypes.instanceOf(Element) })
    ]),
  }

  static defaultProps = {
    placement: 'bottom',
    modifiers: {
      flip: { boundariesElement: 'viewport', padding: 10 },
      preventOverflow: { boundariesElement: 'viewport', padding: 10 }
    }
  }

  constructor(props) {
    super(props);
    this.triggerRef = props.triggerRef || createRef(null);
  }

  state = {
    open: false,
  }

  componentDidMount() {
    this.portal = document.getElementById(PORTAL_ELEMENT);

    if (this.triggerRef && this.triggerRef.current) {
      this.triggerEl = this.triggerRef.current;

      SHOW_EVENT_LISTENERS.forEach(listener => {
        this.triggerEl.addEventListener(listener, this.show, true);
      });

      HIDE_EVENT_LISTENERS.forEach(listener => {
        this.triggerEl.addEventListener(listener, this.hide, true);
      });

      this.validateTriggerElement();
    }
  }

  componentDidUpdate(_, { open: prevOpen }) {
    const { open } = this.state;

    if (open && !prevOpen) {
      this.triggerEl.addEventListener('keydown', this.handleKeyDown, true);
    } else if (!open && prevOpen) {
      this.triggerEl.removeEventListener('keydown', this.handleKeyDown, true);
    }
  }

  componentWillUnmount() {
    clearTimeout(this.timeout);
    if (this.triggerEl) {
      SHOW_EVENT_LISTENERS.forEach(listener => {
        this.triggerEl.removeEventListener(listener, this.show, true);
      });

      HIDE_EVENT_LISTENERS.forEach(listener => {
        this.triggerEl.removeEventListener(listener, this.hide, true);
      });

      this.triggerEl.removeEventListener('keydown', this.handleKeyDown, true);
    }
  }

  /**
   * Show, hide and toggle tooltip
   */
  toggle = (bool) => {
    const { hideOnTouch } = this.props;
    const { open } = this.state;
    const isTouch = matchMedia('(hover: none)').matches;
    const disable = hideOnTouch && isTouch;

    if (bool !== open && !disable) {
      this.setState({
        open: bool,
      });
    }
  }

  show = () => {
    // A small delay ensures that the tooltip doesn't pop up unnecessarily
    this.timeout = setTimeout(() => this.toggle(true), 70);
  }

  hide = () => {
    clearTimeout(this.timeout);
    this.toggle(false);
  }

  /**
   * Close tooltip on escape
   */
  handleKeyDown = ({ key }) => {
    const { open } = this.state;

    if (open && key === 'Escape') {
      this.hide();
    }
  }

  /**
   * Render proximity element – only available for screen readers
   */
  renderProximityElement = () => {
    const { text, sub } = this.props;
    const ariaIds = this.getAriaIds();

    return (

        { text && {text} }
        { sub && {sub} }

    );
  }

  /**
   * Get assistive ID's used for aria-labelledby/aria-describedby
   */
  getAriaIds = () => {
    const { id, text, sub } = this.props;
    const ids = {};

    if (text) {
      ids.text = `${id}-text`;
    }

    if (sub) {
      ids.sub = `${id}-sub`;
    }

    return ids;
  }

  /**
   * Validate the trigger element
   * Here we validate that the correct assitive ID's are present
   */
  validateTriggerElement = () => {
    // Get array of aria IDs we expect to be present on the trigger element
    const ariaIds = Object.keys(this.getAriaIds()).map(key => this.getAriaIds()[key]);

    // Get list of all aria values on the trigger as a string
    const triggerAriaValues = Array.from(get(this.triggerEl, 'attributes', []))
      .filter(attr => attr.name.includes('aria'))
      .map(attr => attr.value)
      .join(' ');

    // Check if the correct ID's are present
    const hasCorrectIds = ariaIds.filter(id => triggerAriaValues.includes(id)).length === ariaIds.length;

    // // Make it clear for the developer that something is wrong
    if (!hasCorrectIds) {
      console.warn(
        'For accessibility reasons, your <Tooltip> trigger element:', this.triggerEl,
        'should include one or both of the following attributes: aria-labelledby',
        'and/or aria-describedby with the relevant ID(s):', ariaIds, '.',
        'See the <Tooltip> readme for more information.'
      );
      this.triggerEl.style.backgroundColor = 'red';
    }
  }

  /**
   * Get the props for the trigger element
   * Note: These props will be spread onto the trigger element
   */
  getRenderProps = () => {
    return {
      ref: this.triggerRef,
      ariaIds: this.getAriaIds(),
    };
  }

  render() {
    const { text, placement, sub, children, modifiers } = this.props;
    const { open } = this.state;
    const renderProps = this.getRenderProps();

    return (
      <>
        <Popper
          isOpen={open}
          anchorRef={this.triggerRef}
          portal={this.portal}
          placement={placement}
          modifiers={modifiers}
        >

            {text && (

                {text}

            )}
            {sub && (

                {sub}

            )}

        </Popper>
        {typeof children === 'function' ? children(renderProps) : null}
        {this.renderProximityElement()}
      </>
    );
  }
}
<commit_msg>Clear timeout before showing the tooltip to avoid setting state on unmounted component. (#1609)<commit_after>/**
 * Tooltip
 */

import React, { Component, createRef } from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';

import get from 'lodash/get';
import childrenOf from '../../util/childrenOf';
import Popper, { AVAILABLE_PLACEMENTS } from '../Popper/Popper';
import css from './Tooltip.css';

const PORTAL_ELEMENT = 'OverlayContainer';
const SHOW_EVENT_LISTENERS = ['mouseover', 'focus'];
const HIDE_EVENT_LISTENERS = ['mouseout', 'focusout'];

export default class Tooltip extends Component {
  static propTypes = {
    children: PropTypes.func,
    hideOnTouch: PropTypes.bool,
    id: PropTypes.string.isRequired,
    modifiers: PropTypes.object,
    placement: PropTypes.oneOf(AVAILABLE_PLACEMENTS),
    sub: PropTypes.oneOfType([
      PropTypes.string,
      childrenOf(FormattedMessage)
    ]),
    text: PropTypes.oneOfType([
      PropTypes.string.isRequired,
      childrenOf(FormattedMessage).isRequired
    ]).isRequired,
    triggerRef: PropTypes.oneOfType([
      PropTypes.func,
      PropTypes.shape({ current: PropTypes.elementType }),
      PropTypes.shape({ current: PropTypes.instanceOf(Element) })
    ]),
  }

  static defaultProps = {
    placement: 'bottom',
    modifiers: {
      flip: { boundariesElement: 'viewport', padding: 10 },
      preventOverflow: { boundariesElement: 'viewport', padding: 10 }
    }
  }

  constructor(props) {
    super(props);
    this.triggerRef = props.triggerRef || createRef(null);
  }

  state = {
    open: false,
  }

  componentDidMount() {
    this.portal = document.getElementById(PORTAL_ELEMENT);

    if (this.triggerRef && this.triggerRef.current) {
      this.triggerEl = this.triggerRef.current;

      SHOW_EVENT_LISTENERS.forEach(listener => {
        this.triggerEl.addEventListener(listener, this.show, true);
      });

      HIDE_EVENT_LISTENERS.forEach(listener => {
        this.triggerEl.addEventListener(listener, this.hide, true);
      });

      this.validateTriggerElement();
    }
  }

  componentDidUpdate(_, { open: prevOpen }) {
    const { open } = this.state;

    if (open && !prevOpen) {
      this.triggerEl.addEventListener('keydown', this.handleKeyDown, true);
    } else if (!open && prevOpen) {
      this.triggerEl.removeEventListener('keydown', this.handleKeyDown, true);
    }
  }

  componentWillUnmount() {
    clearTimeout(this.timeout);
    if (this.triggerEl) {
      SHOW_EVENT_LISTENERS.forEach(listener => {
        this.triggerEl.removeEventListener(listener, this.show, true);
      });

      HIDE_EVENT_LISTENERS.forEach(listener => {
        this.triggerEl.removeEventListener(listener, this.hide, true);
      });

      this.triggerEl.removeEventListener('keydown', this.handleKeyDown, true);
    }
  }

  /**
   * Show, hide and toggle tooltip
   */
  toggle = (bool) => {
    const { hideOnTouch } = this.props;
    const { open } = this.state;
    const isTouch = matchMedia('(hover: none)').matches;
    const disable = hideOnTouch && isTouch;

    if (bool !== open && !disable) {
      this.setState({
        open: bool,
      });
    }
  }

  show = () => {
    clearTimeout(this.timeout);
    // A small delay ensures that the tooltip doesn't pop up unnecessarily
    this.timeout = setTimeout(() => this.toggle(true), 70);
  }

  hide = () => {
    clearTimeout(this.timeout);
    this.toggle(false);
  }

  /**
   * Close tooltip on escape
   */
  handleKeyDown = ({ key }) => {
    const { open } = this.state;

    if (open && key === 'Escape') {
      this.hide();
    }
  }

  /**
   * Render proximity element – only available for screen readers
   */
  renderProximityElement = () => {
    const { text, sub } = this.props;
    const ariaIds = this.getAriaIds();

    return (

        {text && {text}}
        {sub && {sub}}

    );
  }

  /**
   * Get assistive ID's used for aria-labelledby/aria-describedby
   */
  getAriaIds = () => {
    const { id, text, sub } = this.props;
    const ids = {};

    if (text) {
      ids.text = `${id}-text`;
    }

    if (sub) {
      ids.sub = `${id}-sub`;
    }

    return ids;
  }

  /**
   * Validate the trigger element
   * Here we validate that the correct assitive ID's are present
   */
  validateTriggerElement = () => {
    // Get array of aria IDs we expect to be present on the trigger element
    const ariaIds = Object.keys(this.getAriaIds()).map(key => this.getAriaIds()[key]);

    // Get list of all aria values on the trigger as a string
    const triggerAriaValues = Array.from(get(this.triggerEl, 'attributes', []))
      .filter(attr => attr.name.includes('aria'))
      .map(attr => attr.value)
      .join(' ');

    // Check if the correct ID's are present
    const hasCorrectIds = ariaIds.filter(id => triggerAriaValues.includes(id)).length === ariaIds.length;

    // // Make it clear for the developer that something is wrong
    if (!hasCorrectIds) {
      console.warn(
        'For accessibility reasons, your <Tooltip> trigger element:', this.triggerEl,
        'should include one or both of the following attributes: aria-labelledby',
        'and/or aria-describedby with the relevant ID(s):', ariaIds, '.',
        'See the <Tooltip> readme for more information.'
      );
      this.triggerEl.style.backgroundColor = 'red';
    }
  }

  /**
   * Get the props for the trigger element
   * Note: These props will be spread onto the trigger element
   */
  getRenderProps = () => {
    return {
      ref: this.triggerRef,
      ariaIds: this.getAriaIds(),
    };
  }

  render() {
    const { text, placement, sub, children, modifiers } = this.props;
    const { open } = this.state;
    const renderProps = this.getRenderProps();

    return (
      <>
        <Popper
          isOpen={open}
          anchorRef={this.triggerRef}
          portal={this.portal}
          placement={placement}
          modifiers={modifiers}
        >

            {text && (

                {text}

            )}
            {sub && (

                {sub}

            )}

        </Popper>
        {typeof children === 'function' ? children(renderProps) : null}
        {this.renderProximityElement()}
      </>
    );
  }
}
<|endoftext|>