<commit_before>'use strict';
/**
 * @file
 * Currently the main entrypoint served on /search.
 * Provides the search functionality result view for the enduser.
 */
import React from 'react';
import QueryParser from '../../utils/QueryParser.util.js';
import {isEmpty} from 'lodash';

// import components
import AutoComplete from 'dbc-react-autocomplete';
import {TokenSearchField, FilterGuide} from 'dbc-react-querystring';
import {ResultDisplay} from 'dbc-react-resultlistview';
import SearchTabs from './SearchTabs.component.js';
import Loader from '../Loader.component.js';
// import reflux actions and stores
import AutoCompleteActions from '../../actions/AutoComplete.action.js';
import AutoCompleteStore from '../../stores/AutoComplete.store.js';
import queryAction from '../../actions/QueryUpdate.action.js';
import recommendationsAction from '../../actions/Recommendations.action.js';
import recommendationsStore from '../../stores/Recommendations.store.js';
import queryStore from '../../stores/QueryStore.store.js';
import filterStore from '../../stores/FilterStore.store.js';
import resultListStore from '../../stores/ResultList.store.js';
import coverImageStore from '../../stores/CoverImage.store.js';

/**
 * Search field wrapper component
 */
const Search = React.createClass({
  propTypes: {
    query: React.PropTypes.object,
    filterElements: React.PropTypes.array,
    resultList: React.PropTypes.array,
    recommendations: React.PropTypes.array,
    selected: React.PropTypes.string,
    coverImages: React.PropTypes.object
  },

  getInitialState() {
    const query = QueryParser.stringToObject(this.props.query || []);
    if (query.length && this.isClient()) {
      queryAction(query);
    }

    return {
      query: query,
      filterElements: this.props.filterElements || [],
      resultList: resultListStore.getStore(),
      coverImages: {images: new Map()},
      recommendations: recommendationsStore.getStore(),
      selected: null,
      autoCompleteData: {}
    };
  },

  /**
   * Get context for rendering
   */
  isClient() {
    return (typeof window !== 'undefined');
  },

  /**
   * Add a single element to the Query array
   */
    addElementToQuery(element) {
    let query = this.state.query;
    query.push(element);
    queryAction(query);
  },

  /**
   * Update the Query with a new Query
   */
    updateQuery(query) {
    this.setState({query});
    const queryString = query.length && `?${QueryParser.objectToString(query)}` || '';

    // this is a simple way of handling updates of the url
    // we might need to implement a more advanced version at some point e.g. react-router
    // but we need to figure out our needs first
    history.pushState(null, null, window.location.pathname + queryString);
  },

  updateFilters(filterElements) {
    this.setState({filterElements});
  },

  updateResultList(resultList) {
    if (resultList && resultList.result) {
      recommendationsAction(resultList.result.map(element => element.identifiers[0]));
    }
    this.setState({resultList});
  },

  updateRecommendations(recommendations) {
    this.setState({recommendations});
  },

  updateCoverImages(coverImages) {
    this.setState({coverImages});
  },

  updateSelected(selected) {
    this.setState({selected});
  },

  componentDidMount: function() {
    AutoCompleteStore.listen(this.updateAutoComplete);
    queryStore.listen(this.updateQuery);
    filterStore.listen(this.updateFilters);
    resultListStore.listen(this.updateResultList);
    coverImageStore.listen(this.updateCoverImages);
    recommendationsStore.listen(this.updateRecommendations);
    if (this.state.query.length === 0) {
      recommendationsAction(recommendationsStore.getDefaultRecommendations());
    }
  },

  updateAutoComplete(autoCompleteData) {
    this.setState({autoCompleteData: autoCompleteData[this.state.textFieldValue]});
  },

  /**
   * Callback for the text input (TokenSearchField)
   */
    onChange(textFieldValue) {
    this.setState({textFieldValue: textFieldValue});
    this.requestSuggestions();
  },

  requestSuggestions() {
    setTimeout(() => {
      AutoCompleteActions.textfieldUpdated(this.state.textFieldValue);
    }, 100);
  },

  getView() {
    const {query, selected} = this.state;
    return query.length === 0 && 'Anbefalinger' || selected;
  },

  render() {
    const {query, filterElements, resultList, recommendations, coverImages} = this.state;
    const autoCompleteData = this.state.autoCompleteData || {};
    const textFieldValue = this.state.textFieldValue || '';
    const autoCompleteVisible = (!isEmpty(autoCompleteData) && !isEmpty(textFieldValue));
    const view = this.getView();
    const results = (view === 'Anbefalinger') && recommendations || resultList;
    let filterGuide;
    let searchTabs;
    let noResults = (resultList.hasSearchBeenExecuted) && (
Søgningen gav ingen resultater
) || '';

    if (filterElements.length) {
      filterGuide =
        <FilterGuide elements={filterElements} select={this.addElementToQuery} />;
    }
    if (resultList.result.length) {
      searchTabs = <SearchTabs
        buttons={['Søgeresultat', 'Anbefalinger']}
        selected={view}
        update={this.updateSelected}
        />;
    }

    return (

        <TokenSearchField query={query} update={queryAction} change={this.onChange} />
        <AutoComplete data={autoCompleteData} visible={autoCompleteVisible} />
        {filterGuide}

          {searchTabs}
          <Loader pending={results.pending}>
            <ResultDisplay result={results.result} coverImages={coverImages.images}>
              {noResults}
            </ResultDisplay>
          </Loader>

    );
  }
});

export default Search;
<commit_msg>added debug line<commit_after>'use strict';
/**
 * @file
 * Currently the main entrypoint served on /search.
 * Provides the search functionality result view for the enduser.
 */
import React from 'react';
import QueryParser from '../../utils/QueryParser.util.js';
import {isEmpty} from 'lodash';

// import components
import AutoComplete from 'dbc-react-autocomplete';
import {TokenSearchField, FilterGuide} from 'dbc-react-querystring';
import {ResultDisplay} from 'dbc-react-resultlistview';
import SearchTabs from './SearchTabs.component.js';
import Loader from '../Loader.component.js';
// import reflux actions and stores
import AutoCompleteActions from '../../actions/AutoComplete.action.js';
import AutoCompleteStore from '../../stores/AutoComplete.store.js';
import queryAction from '../../actions/QueryUpdate.action.js';
import recommendationsAction from '../../actions/Recommendations.action.js';
import recommendationsStore from '../../stores/Recommendations.store.js';
import queryStore from '../../stores/QueryStore.store.js';
import filterStore from '../../stores/FilterStore.store.js';
import resultListStore from '../../stores/ResultList.store.js';
import coverImageStore from '../../stores/CoverImage.store.js';

/**
 * Search field wrapper component
 */
const Search = React.createClass({
  propTypes: {
    query: React.PropTypes.object,
    filterElements: React.PropTypes.array,
    resultList: React.PropTypes.array,
    recommendations: React.PropTypes.array,
    selected: React.PropTypes.string,
    coverImages: React.PropTypes.object
  },

  getInitialState() {
    const query = QueryParser.stringToObject(this.props.query || []);
    if (query.length && this.isClient()) {
      queryAction(query);
    }

    return {
      query: query,
      filterElements: this.props.filterElements || [],
      resultList: resultListStore.getStore(),
      coverImages: {images: new Map()},
      recommendations: recommendationsStore.getStore(),
      selected: null,
      autoCompleteData: {}
    };
  },

  /**
   * Get context for rendering
   */
  isClient() {
    return (typeof window !== 'undefined');
  },

  /**
   * Add a single element to the Query array
   */
    addElementToQuery(element) {
    let query = this.state.query;
    query.push(element);
    queryAction(query);
  },

  /**
   * Update the Query with a new Query
   */
    updateQuery(query) {
    this.setState({query});
    const queryString = query.length && `?${QueryParser.objectToString(query)}` || '';

    // this is a simple way of handling updates of the url
    // we might need to implement a more advanced version at some point e.g. react-router
    // but we need to figure out our needs first
    history.pushState(null, null, window.location.pathname + queryString);
  },

  updateFilters(filterElements) {
    this.setState({filterElements});
  },

  updateResultList(resultList) {
    if (resultList && resultList.result) {
      recommendationsAction(resultList.result.map(element => element.identifiers[0]));
    }
    this.setState({resultList});
  },

  updateRecommendations(recommendations) {
    this.setState({recommendations});
  },

  updateCoverImages(coverImages) {
    this.setState({coverImages});
  },

  updateSelected(selected) {
    this.setState({selected});
  },

  componentDidMount: function() {
    AutoCompleteStore.listen(this.updateAutoComplete);
    queryStore.listen(this.updateQuery);
    filterStore.listen(this.updateFilters);
    resultListStore.listen(this.updateResultList);
    coverImageStore.listen(this.updateCoverImages);
    recommendationsStore.listen(this.updateRecommendations);
    if (this.state.query.length === 0) {
      recommendationsAction(recommendationsStore.getDefaultRecommendations());
    }
  },

  updateAutoComplete(autoCompleteData) {
    this.setState({autoCompleteData: autoCompleteData[this.state.textFieldValue]});
  },

  /**
   * Callback for the text input (TokenSearchField)
   */
    onChange(textFieldValue) {
    this.setState({textFieldValue: textFieldValue});
    this.requestSuggestions();
  },

  requestSuggestions() {
    console.log('requestSuggestions'); // eslint-disable-line
    setTimeout(() => {
      AutoCompleteActions.textfieldUpdated(this.state.textFieldValue);
    }, 100);
  },

  getView() {
    const {query, selected} = this.state;
    return query.length === 0 && 'Anbefalinger' || selected;
  },

  render() {
    const {query, filterElements, resultList, recommendations, coverImages} = this.state;
    const autoCompleteData = this.state.autoCompleteData || {};
    const textFieldValue = this.state.textFieldValue || '';
    const autoCompleteVisible = (!isEmpty(autoCompleteData) && !isEmpty(textFieldValue));
    const view = this.getView();
    const results = (view === 'Anbefalinger') && recommendations || resultList;
    let filterGuide;
    let searchTabs;
    let noResults = (resultList.hasSearchBeenExecuted) && (
Søgningen gav ingen resultater
) || '';

    if (filterElements.length) {
      filterGuide =
        <FilterGuide elements={filterElements} select={this.addElementToQuery} />;
    }
    if (resultList.result.length) {
      searchTabs = <SearchTabs
        buttons={['Søgeresultat', 'Anbefalinger']}
        selected={view}
        update={this.updateSelected}
        />;
    }

    return (

        <TokenSearchField query={query} update={queryAction} change={this.onChange} />
        <AutoComplete data={autoCompleteData} visible={autoCompleteVisible} />
        {filterGuide}

          {searchTabs}
          <Loader pending={results.pending}>
            <ResultDisplay result={results.result} coverImages={coverImages.images}>
              {noResults}
            </ResultDisplay>
          </Loader>

    );
  }
});

export default Search;
<|endoftext|>


<commit_before>'use strict';

const fs     = require('fs');
const path   = require('path');
const mkdirp = require('mkdirp');
const async  = require('async');

module.exports = function(params) {
  const file            = params.file;
  const files           = params.files;
  const parentDirectory = params.parentDirectory
  const dirname         = params.dirname;
  const rootPath        = process.cwd();
  var filesInformations = [];

  return new Promise((resolve, reject) => {

    if (files) {
      const filesUploaded = files.filter(file => file.originalFilename !== '');

      async.each(filesUploaded, saveFile, err => {
        if (err) {
          console.error(error)
          reject(err);
        }
        else {
          resolve(filesInformations);
        }
      });
    }
    else {
      saveFile(file, () => {
        resolve(filesInformations[0]);
      });
    }
  });

  function saveFile(file, cb) {
    const filePath     = file.path;
    const ext          = path.extname(file.originalFilename);
    const fileName     = `${file.fieldName}-${Date.now()}${ext}`;
    const dirPath      = path.join(rootPath, `uploads/${parentDirectory}/${dirname}`);
    const fileDestPath = path.join(rootPath, `uploads/${parentDirectory}/${dirname}/${fileName}`);

    mkdirp(dirPath, err => {
      if (err) {
        console.error(err);
      }
      else {
        const stream$ = fs.createReadStream(filePath).pipe(fs.createWriteStream(fileDestPath));

        stream$.on('finish', () => {
          filesInformations.push({
            fileName,
            path: fileDestPath,
          });
          cb();
        });
      }
    });
  }

};

<commit_msg>Fix path resolved<commit_after>'use strict';

const fs     = require('fs');
const path   = require('path');
const mkdirp = require('mkdirp');
const async  = require('async');

module.exports = function(params) {
  const file            = params.file;
  const files           = params.files;
  const parentDirectory = params.parentDirectory
  const dirname         = params.dirname;
  const rootPath        = process.cwd();
  var filesInformations = [];

  return new Promise((resolve, reject) => {

    if (files) {
      const filesUploaded = files.filter(file => file.originalFilename !== '');

      async.each(filesUploaded, saveFile, err => {
        if (err) {
          console.error(error)
          reject(err);
        }
        else {
          resolve(filesInformations);
        }
      });
    }
    else {
      saveFile(file, () => {
        resolve(filesInformations[0]);
      });
    }
  });

  function saveFile(file, cb) {
    const filePath     = file.path;
    const ext          = path.extname(file.originalFilename);
    const fileName     = `${file.fieldName}-${Date.now()}${ext}`;
    const dbFilePath   = `/${parentDirectory}/${dirname}/${fileName}`;
    const dirPath      = path.join(rootPath, `uploads/${parentDirectory}/${dirname}`);
    const fileDestPath = path.join(rootPath, `uploads/${dbFilePath}`);

    mkdirp(dirPath, err => {
      if (err) {
        console.error(err);
      }
      else {
        const stream$ = fs.createReadStream(filePath).pipe(fs.createWriteStream(fileDestPath));

        stream$.on('finish', () => {
          filesInformations.push({
            fileName,
            path: dbFilePath,
          });
          cb();
        });
      }
    });
  }

};

<|endoftext|>


<commit_before>(function () {
'use strict';
var miniwidth = 80;
var padwidth = 50;
var linemult = 1.25;
var fontsize = 12 * linemult;

// These must be user-supplied
var fillinFn = function () {
    alert('Must define getInitialCode and getDecompileSteps globally');
};
window.getInitialCode = window.getInitialCode || fillinFn;
window.getDecompileSteps = window.getDecompileSteps || fillinFn;

var ActionArea = React.createClass({
    getInitialState: function () {
        window.getInitialCode(function (err, data) {
            this.setState({'code': data});
            setTimeout(this.onDecompileClick, 0);
        }.bind(this));
        return {'code': ''};
    },
    handleChange: function (e) {
        this.setState({'code': e.target.value});
    },
    onDecompileClick: function () {
        this.props.decompileCB(this.state.code);
    },
    render: function () {
        return (

<button onClick={this.onDecompileClick}>Decompile!</button>

<button onClick={this.helpMe}>Help!</button>

                    <textarea onChange={this.handleChange} value={this.state.code} />

        );
    }
});

var StepPadding = React.createClass({
    shouldComponentUpdate: function (nextProps, nextState) {
        return (
            this.props.stepIdx !== nextProps.stepIdx ||
            this.props.steps !== nextProps.steps ||
            this.props.changes !== nextProps.changes ||
            this.props.offsets[0] !== nextProps.offsets[0] ||
            this.props.offsets[1] !== nextProps.offsets[1]
        );
    },
    render: function () {
        function linefromchange(change, steps, offsets) {
            var numlines, bbi, ii, step,
                c = change,
                cs = c.step, // What step is this change transforming?
                cb = c.bblock, // What block is this change transforming?
                ss = steps;

            numlines = 0;
            bbi = 0; // Which bblock are we looking at right now
            ii = 0; // Which 'line' in the bblock are we looking at right now
            step = ss[cs];
            var starty1, starty2;
            // Find the offset of source lines
            while (true) {
                if (bbi === cb && ii === c.from[0]) {
                    starty1 = numlines;
                }
                if (bbi === cb && ii === c.from[1]) {
                    starty2 = numlines;
                    break;
                }
                if (ii < step[bbi].length) {
                    numlines += step[bbi][ii].split('\n').length;
                    ii++;
                }
                else if (bbi < step.length - 1) { bbi++; ii = 0; }
                else { throw Error(); }
            }

            numlines = 0;
            bbi = 0;
            ii = 0;
            step = ss[cs+1];
            var endy1, endy2;
            // Find the offset of the target lines
            while (true) {
                if (bbi === cb && ii === c.to[0]) {
                    endy1 = numlines;
                }
                if (bbi === cb && ii === c.to[1]) {
                    endy2 = numlines;
                    break;
                }
                if (ii < step[bbi].length) {
                    numlines += step[bbi][ii].split('\n').length;
                    ii++;
                }
                else if (bbi < step.length - 1) { bbi++; ii = 0; }
                else { throw Error(); }
            }

            starty1 = (starty1 * fontsize) - offsets[0];
            starty2 = (starty2 * fontsize) - offsets[0];
            endy1 = (endy1 * fontsize) - offsets[1];
            endy2 = (endy2 * fontsize) - offsets[1];
            var p = padwidth;
            return [
                <line key={1} x1={2*p} y1={starty1} x2={3*p} y2={endy1}/>,
                <line key={2} x1={2*p} y1={starty2} x2={3*p} y2={endy2}/>,

                <line key={3} x1={0} y1={starty1} x2={2*p} y2={starty1} className='guideLine'/>,
                <line key={4} x1={0} y1={starty2} x2={2*p} y2={starty2} className='guideLine'/>,
                <line key={5} x1={3*p} y1={endy1} x2={5*p} y2={endy1}   className='guideLine'/>,
                <line key={6} x1={3*p} y1={endy2} x2={5*p} y2={endy2}   className='guideLine'/>
            ];
        }

        var stepIdx = this.props.stepIdx;
        var lines = [];
        var changes = this.props.changes.map(function (c) {
            if (c.step !== stepIdx) {
                return;
            }
            var shape = (
                <g key={lines.length}>
                    {linefromchange(c, this.props.steps, this.props.offsets)}
                </g>
            );
            lines.push(shape);
        }, this);
        return

<svg>{lines}</svg>

;
    }
});

var DecompileStepCode = React.createClass({
    shouldComponentUpdate: function (nextProps, nextState) {
        return this.props.step !== nextProps.step;
    },
    render: function () {
        var step = this.props.step;
        return (

{step.map(function (bb, bbi) {
                return bb.join('\n');
            }).join('\n')}

        );
    }
});

var DecompileSteps = React.createClass({
    handleKeyDown: function (e) {
        var key = e.keyCode || e.charCode;
        var stepIdx = this.state.stepIdx;
        // 37 <-, 38 ^, 39 ->, 40 \/
        if (key === 37) { this.changeStepIdx(-1); }
        else if (key === 38) { this.showMiniSteps(true); }
        else if (key === 39) { this.changeStepIdx(1); }
        else if (key === 40) { this.showMiniSteps(false); }
        else { return true; }
        e.preventDefault();
        return false;
    },
    showMiniSteps: function (shouldShow) {
        this.setState({'miniStepsOnly': shouldShow});
    },
    changeStepIdx: function (idxChange) {
        var stepIdx = this.state.stepIdx + idxChange;
        if (!(stepIdx < 0 || stepIdx >= this.props.steps.length)) {
            this.setState({'stepIdx': stepIdx});
        }
    },
    componentDidMount: function () {
        document.addEventListener('keydown', (function (e) {
            // Is this an event that was fired on us?
            var target = e.target;
            if (target != document && target != document.body) {
                while (target.parentNode != document.body) {
                    target = target.parentNode;
                }
                if (target != this.getDOMNode()) { return; }
            }
            return this.handleKeyDown(e);
        }).bind(this));
    },
    handleScroll: function (e) {
        var steps = this.getDOMNode().querySelectorAll('#mainsteps > .step > pre');
        var offsets = [].map.call(steps, function (e) { return e.scrollTop; });
        var ss = this.state.stepScroll;
        if (offsets.some(function(v, i) { return v !== ss[i]; })) {
            this.setState({'stepScroll': offsets});
        }
    },
    componentWillReceiveProps: function (nextProps) {
        var stepIdx = nextProps.steps.length - 2;
        if (stepIdx < 0) { stepIdx = 0; }
        this.setState({'stepIdx': stepIdx});
    },
    getInitialState: function () {
        return {'stepIdx': 0, 'stepScroll': [0, 0, 0], 'miniStepsOnly': false};
    },
    render: function () {
        var stepIdx = this.state.stepIdx;
        var stepElts = [];
        var miniStepElts = [];
        this.props.steps.map(function (step, si) {
            var className = 'step';
            if (stepIdx === si) { className += ' selected-step'; }
            var elt = (

                    <DecompileStepCode step={step} />

            );
            miniStepElts.push(elt);
            if (si < stepIdx - 1 || si > stepIdx + 1) { return; }
            stepElts.push(elt);
        }, this);
        // Add blank divs at beginning and end
        if (stepElts.length === 2) {
            var pre =

;
            if (stepElts[1].key !== 'step'+(stepIdx+1)) {
                stepElts.push(
{pre}
);
            } else if (stepElts[0].key !== 'step'+(stepIdx-1)) {
                stepElts.unshift(
{pre}
);
            }
        }

        // Add the step padding
        stepElts.splice(1, 0,
            <StepPadding key={'pad'+(stepIdx)}
            stepIdx={stepIdx - 1} steps={this.props.steps} changes={this.props.changes}
            offsets={this.state.stepScroll.slice(0, 2)}
            />
        );
        stepElts.splice(3, 0,
            <StepPadding key={'pad'+(stepIdx+1)}
            stepIdx={stepIdx} steps={this.props.steps} changes={this.props.changes}
            offsets={this.state.stepScroll.slice(1, 3)}
            />
        );

        // Fold up the main steps if we're not displaying them
        var mainstepsstyle = this.state.miniStepsOnly ? {'height': 0, 'opacity': 0} : {};

        // Put the selected ministep halfway along the bottom
        var halfway = -((stepIdx * miniwidth) + (miniwidth / 2));
        var ministepsstyle = {'left': halfway + 'px'};

        return (

{stepElts}

{miniStepElts}

        );
    }
});

window.TclDisUI = React.createClass({
    getInitialState: function () {
        return {'steps': [], 'changes': []};
    },
    getDecompileSteps: function (code) {
        window.getDecompileSteps(code, function (err, data) {
            this.setState(data);
        }.bind(this));
    },
    render: function () {
        return (

                <ActionArea decompileCB={this.getDecompileSteps} />
                <DecompileSteps steps={this.state.steps} changes={this.state.changes} />

        );
    }
});
})();
<commit_msg>Saner StepPadding key<commit_after>(function () {
'use strict';
var miniwidth = 80;
var padwidth = 50;
var linemult = 1.25;
var fontsize = 12 * linemult;

// These must be user-supplied
var fillinFn = function () {
    alert('Must define getInitialCode and getDecompileSteps globally');
};
window.getInitialCode = window.getInitialCode || fillinFn;
window.getDecompileSteps = window.getDecompileSteps || fillinFn;

var ActionArea = React.createClass({
    getInitialState: function () {
        window.getInitialCode(function (err, data) {
            this.setState({'code': data});
            setTimeout(this.onDecompileClick, 0);
        }.bind(this));
        return {'code': ''};
    },
    handleChange: function (e) {
        this.setState({'code': e.target.value});
    },
    onDecompileClick: function () {
        this.props.decompileCB(this.state.code);
    },
    render: function () {
        return (

<button onClick={this.onDecompileClick}>Decompile!</button>

<button onClick={this.helpMe}>Help!</button>

                    <textarea onChange={this.handleChange} value={this.state.code} />

        );
    }
});

var StepPadding = React.createClass({
    shouldComponentUpdate: function (nextProps, nextState) {
        return (
            this.props.stepIdx !== nextProps.stepIdx ||
            this.props.steps !== nextProps.steps ||
            this.props.changes !== nextProps.changes ||
            this.props.offsets[0] !== nextProps.offsets[0] ||
            this.props.offsets[1] !== nextProps.offsets[1]
        );
    },
    render: function () {
        function linefromchange(change, steps, offsets) {
            var numlines, bbi, ii, step,
                c = change,
                cs = c.step, // What step is this change transforming?
                cb = c.bblock, // What block is this change transforming?
                ss = steps;

            numlines = 0;
            bbi = 0; // Which bblock are we looking at right now
            ii = 0; // Which 'line' in the bblock are we looking at right now
            step = ss[cs];
            var starty1, starty2;
            // Find the offset of source lines
            while (true) {
                if (bbi === cb && ii === c.from[0]) {
                    starty1 = numlines;
                }
                if (bbi === cb && ii === c.from[1]) {
                    starty2 = numlines;
                    break;
                }
                if (ii < step[bbi].length) {
                    numlines += step[bbi][ii].split('\n').length;
                    ii++;
                }
                else if (bbi < step.length - 1) { bbi++; ii = 0; }
                else { throw Error(); }
            }

            numlines = 0;
            bbi = 0;
            ii = 0;
            step = ss[cs+1];
            var endy1, endy2;
            // Find the offset of the target lines
            while (true) {
                if (bbi === cb && ii === c.to[0]) {
                    endy1 = numlines;
                }
                if (bbi === cb && ii === c.to[1]) {
                    endy2 = numlines;
                    break;
                }
                if (ii < step[bbi].length) {
                    numlines += step[bbi][ii].split('\n').length;
                    ii++;
                }
                else if (bbi < step.length - 1) { bbi++; ii = 0; }
                else { throw Error(); }
            }

            starty1 = (starty1 * fontsize) - offsets[0];
            starty2 = (starty2 * fontsize) - offsets[0];
            endy1 = (endy1 * fontsize) - offsets[1];
            endy2 = (endy2 * fontsize) - offsets[1];
            var p = padwidth;
            return [
                <line key={1} x1={2*p} y1={starty1} x2={3*p} y2={endy1}/>,
                <line key={2} x1={2*p} y1={starty2} x2={3*p} y2={endy2}/>,

                <line key={3} x1={0} y1={starty1} x2={2*p} y2={starty1} className='guideLine'/>,
                <line key={4} x1={0} y1={starty2} x2={2*p} y2={starty2} className='guideLine'/>,
                <line key={5} x1={3*p} y1={endy1} x2={5*p} y2={endy1}   className='guideLine'/>,
                <line key={6} x1={3*p} y1={endy2} x2={5*p} y2={endy2}   className='guideLine'/>
            ];
        }

        var stepIdx = this.props.stepIdx;
        var lines = [];
        var changes = this.props.changes.map(function (c) {
            if (c.step !== stepIdx) {
                return;
            }
            var shape = (
                <g key={lines.length}>
                    {linefromchange(c, this.props.steps, this.props.offsets)}
                </g>
            );
            lines.push(shape);
        }, this);
        return

<svg>{lines}</svg>

;
    }
});

var DecompileStepCode = React.createClass({
    shouldComponentUpdate: function (nextProps, nextState) {
        return this.props.step !== nextProps.step;
    },
    render: function () {
        var step = this.props.step;
        return (

{step.map(function (bb, bbi) {
                return bb.join('\n');
            }).join('\n')}

        );
    }
});

var DecompileSteps = React.createClass({
    handleKeyDown: function (e) {
        var key = e.keyCode || e.charCode;
        var stepIdx = this.state.stepIdx;
        // 37 <-, 38 ^, 39 ->, 40 \/
        if (key === 37) { this.changeStepIdx(-1); }
        else if (key === 38) { this.showMiniSteps(true); }
        else if (key === 39) { this.changeStepIdx(1); }
        else if (key === 40) { this.showMiniSteps(false); }
        else { return true; }
        e.preventDefault();
        return false;
    },
    showMiniSteps: function (shouldShow) {
        this.setState({'miniStepsOnly': shouldShow});
    },
    changeStepIdx: function (idxChange) {
        var stepIdx = this.state.stepIdx + idxChange;
        if (!(stepIdx < 0 || stepIdx >= this.props.steps.length)) {
            this.setState({'stepIdx': stepIdx});
        }
    },
    componentDidMount: function () {
        document.addEventListener('keydown', (function (e) {
            // Is this an event that was fired on us?
            var target = e.target;
            if (target != document && target != document.body) {
                while (target.parentNode != document.body) {
                    target = target.parentNode;
                }
                if (target != this.getDOMNode()) { return; }
            }
            return this.handleKeyDown(e);
        }).bind(this));
    },
    handleScroll: function (e) {
        var steps = this.getDOMNode().querySelectorAll('#mainsteps > .step > pre');
        var offsets = [].map.call(steps, function (e) { return e.scrollTop; });
        var ss = this.state.stepScroll;
        if (offsets.some(function(v, i) { return v !== ss[i]; })) {
            this.setState({'stepScroll': offsets});
        }
    },
    componentWillReceiveProps: function (nextProps) {
        var stepIdx = nextProps.steps.length - 2;
        if (stepIdx < 0) { stepIdx = 0; }
        this.setState({'stepIdx': stepIdx});
    },
    getInitialState: function () {
        return {'stepIdx': 0, 'stepScroll': [0, 0, 0], 'miniStepsOnly': false};
    },
    render: function () {
        var stepIdx = this.state.stepIdx;
        var stepElts = [];
        var miniStepElts = [];
        this.props.steps.map(function (step, si) {
            var className = 'step';
            if (stepIdx === si) { className += ' selected-step'; }
            var elt = (

                    <DecompileStepCode step={step} />

            );
            miniStepElts.push(elt);
            if (si < stepIdx - 1 || si > stepIdx + 1) { return; }
            stepElts.push(elt);
        }, this);
        // Add blank divs at beginning and end
        if (stepElts.length === 2) {
            var pre =

;
            if (stepElts[1].key !== 'step'+(stepIdx+1)) {
                stepElts.push(
{pre}
);
            } else if (stepElts[0].key !== 'step'+(stepIdx-1)) {
                stepElts.unshift(
{pre}
);
            }
        }

        // Add the step padding
        stepElts.splice(1, 0,
            <StepPadding key={'pad'+(stepIdx-1)}
            stepIdx={stepIdx-1} steps={this.props.steps} changes={this.props.changes}
            offsets={this.state.stepScroll.slice(0, 2)}
            />
        );
        stepElts.splice(3, 0,
            <StepPadding key={'pad'+(stepIdx)}
            stepIdx={stepIdx} steps={this.props.steps} changes={this.props.changes}
            offsets={this.state.stepScroll.slice(1, 3)}
            />
        );

        // Fold up the main steps if we're not displaying them
        var mainstepsstyle = this.state.miniStepsOnly ? {'height': 0, 'opacity': 0} : {};

        // Put the selected ministep halfway along the bottom
        var halfway = -((stepIdx * miniwidth) + (miniwidth / 2));
        var ministepsstyle = {'left': halfway + 'px'};

        return (

{stepElts}

{miniStepElts}

        );
    }
});

window.TclDisUI = React.createClass({
    getInitialState: function () {
        return {'steps': [], 'changes': []};
    },
    getDecompileSteps: function (code) {
        window.getDecompileSteps(code, function (err, data) {
            this.setState(data);
        }.bind(this));
    },
    render: function () {
        return (

                <ActionArea decompileCB={this.getDecompileSteps} />
                <DecompileSteps steps={this.state.steps} changes={this.state.changes} />

        );
    }
});
})();
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import tagComponent from '../../utils/helpers/tags/tags';
import CreateStyle from './create.style';

class Create extends React.Component {
  /** Returns the props for the component. */
  linkProps = () => {
    const { ...props } = this.props;

    props.iconAlign = 'right';
    props.icon = 'add';
    return props;
  }

  render() {
    return (
      <CreateStyle
        { ...this.linkProps() } { ...tagComponent('create', this.props) }
      >
        { this.props.children }
      </CreateStyle>
    );
  }
}

Create.propTypes = {
  /** Children elements */
  children: PropTypes.node,
  /** Custom className */
  className: PropTypes.string,
  /** Props for the link */
  linkProps: PropTypes.object
};

export default Create;
<commit_msg>change class-based component to be functional based component<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import tagComponent from '../../utils/helpers/tags/tags';
import CreateStyle from './create.style';

const Create = (props) => {
  /** Returns the props for the component. */
  const linkProps = () => {
    const { ...componentProps } = props;

    componentProps.iconAlign = 'right';
    componentProps.icon = 'add';
    return componentProps;
  };

  return (
    <CreateStyle
      { ...linkProps() } { ...tagComponent('create', props) }
    >
      { props.children }
    </CreateStyle>
  );
};

Create.propTypes = {
  /** Children elements */
  children: PropTypes.node,
  /** Custom className */
  className: PropTypes.string,
  /** Props for the link */
  linkProps: PropTypes.object
};

export default Create;
<|endoftext|>


<commit_before>'use strict';

var docker = require('dockerode')();

/**
 * Run Docker SwellRT instances from Gulp
 *
 * @package gulp-docker-swellrt
 * @author P2Pvalue UCM team <p2pv@ucm.es>
 */

var defaultOpts = {
  image: 'p2pvalue/swellrt',
  host:  'swellrt',
  name:  'gulp-swellrt',
  port:  '9898',
  tag:   'latest'
};

var start = function start(options, cb) {
  docker.pull(options.taggedImage, function(err, stream) {
    if (err) { throw err; }

    docker.modem.followProgress(stream, function(err) {
      if (err) { throw err; }

      var containerOpts = {
        Image: options.taggedImage,
        Hostname: options.host,
        name: options.name,
        HostConfig: {
          PortBindings: {
            '9898/tcp': [{
              HostIp:   '0.0.0.0',
              HostPort: options.port
            }]
          }
        }
      };

      docker.createContainer(containerOpts, function(err, container) {
        if (err) { throw err; }

        container.start(function(err, data) {
          if (err) { throw err; }

          console.log(data);

          if (cb) { cb(); }
        });
      });
    });
  });
};

var check = function(options, cb) {
  docker.listContainers(function(err, containers) {
    if (err) {
      if (err.code === 'EACCES') {
        console.error('Error: Cannot access docker. You probably need to add your user to the docker group');
        console.error('Error: Try: sudo adduser <your_user> docker and restart');

        throw err;
      }
    }

    for (var i in containers) {
      var c = containers[i];

      if (c.Names[0] === '/' + options.name) {
        if (c.Image === options.taggedImage) {
          // Image already running
          if (cb) { cb(); }

        } else {
          // Stop and start image
          docker.getContainer(c.Id).remove({ force: true }, function(err, data) {
            if (err) { throw err; }

            console.log(data);

            start(options, cb);
          });
        }

        // Don't look other containers, we already found the named one
        return;
      }
    }

    // We couldn't found the container
    start(options, cb);
  });
};

var gulpDockerSwellrt = function gulpDockerSwellrt(options, cb) {
  var mergedOpts = {};

  for (var attr in defaultOpts) {
    if (options[attr]) {
      mergedOpts[attr] = options[attr];
    } else {
      mergedOpts[attr] = defaultOpts[attr];
    }
  }

  mergedOpts.taggedImage = mergedOpts.image + ':' + mergedOpts.tag;

  check(mergedOpts, cb);
};

module.exports = gulpDockerSwellrt;
<commit_msg>Deal with stopped containers<commit_after>'use strict';

var docker = require('dockerode')();

/**
 * Run Docker SwellRT instances from Gulp
 *
 * @package gulp-docker-swellrt
 * @author P2Pvalue UCM team <p2pv@ucm.es>
 */

var defaultOpts = {
  image: 'p2pvalue/swellrt',
  host:  'swellrt',
  name:  'gulp-swellrt',
  port:  '9898',
  tag:   'latest'
};

var start = function start(options, cb) {
  docker.pull(options.taggedImage, function(err, stream) {
    if (err) { throw err; }

    docker.modem.followProgress(stream, function(err) {
      if (err) { throw err; }

      var containerOpts = {
        Image: options.taggedImage,
        Hostname: options.host,
        name: options.name,
        HostConfig: {
          PortBindings: {
            '9898/tcp': [{
              HostIp:   '0.0.0.0',
              HostPort: options.port
            }]
          }
        }
      };

      docker.createContainer(containerOpts, function(err, container) {
        if (err) { throw err; }

        container.start(function(err, data) {
          if (err) { throw err; }

          console.log(data);

          if (cb) { cb(); }
        });
      });
    });
  });
};

var check = function(options, cb) {
  docker.listContainers({ all: true }, function(err, containers) {
    if (err) {
      if (err.code === 'EACCES') {
        console.error('Error: Cannot access docker. You probably need to add your user to the docker group');
        console.error('Error: Try: sudo adduser <your_user> docker and restart');

        throw err;
      }
    }

    for (var i in containers) {
      var c = containers[i];

      if (c.Names[0] === '/' + options.name) {
        if (c.Image === options.taggedImage && /^Up/.test(c.Status)) {
          // Image already running
          if (cb) { cb(); }
        } else {
          // Stop and start image
          docker.getContainer(c.Id).remove({ force: true }, function(err, data) {
            if (err) { throw err; }

            console.log(data);

            start(options, cb);
          });
        }

        // Don't look other containers, we already found the named one
        return;
      }
    }

    // We couldn't found the container
    start(options, cb);
  });
};

var gulpDockerSwellrt = function gulpDockerSwellrt(options, cb) {
  var mergedOpts = {};

  for (var attr in defaultOpts) {
    if (options[attr]) {
      mergedOpts[attr] = options[attr];
    } else {
      mergedOpts[attr] = defaultOpts[attr];
    }
  }

  mergedOpts.taggedImage = mergedOpts.image + ':' + mergedOpts.tag;

  check(mergedOpts, cb);
};

module.exports = gulpDockerSwellrt;
<|endoftext|>


<commit_before>var canvas = document.getElementById('game');
var context = canvas.getContext('2d');

var Ball = function(x, y, radius, context) {
  this.x = x;
  this.y = y;
  this.radius = radius || 10;
  this.startAngle = 0;
  this.endAngle = (Math.PI / 180) * 360;
  this.context = context
  this.speed = 0
}

Ball.prototype.draw = function () {
  context.arc(this.x, this.y, this.radius, this.startAngle, this.endAngle);
  context.fillStyle = "white"
  context.fill();
  return this;
};

Ball.prototype.move = function () {
  if (this.x < canvas.width - this.radius && this.y < canvas.height - this.radius ){
    this.x += this.speed;
    // this.y += this.speed;
  }
  return this;
};

var golfBall = new Ball(350, 450, 5, 0);

requestAnimationFrame(function gameLoop() {
  context.beginPath();
  context.clearRect(0, 0, canvas.width, canvas.height);
  context.closePath();
  golfBall.draw().move();
  requestAnimationFrame(gameLoop);
});

module.exports = Ball;
<commit_msg>ball moves on mouse down event<commit_after>var canvas = document.getElementById('game');
var context = canvas.getContext('2d');
canvas.addEventListener('mousedown', function(event) {
    var that = this
    var mousePos = getMousePosition(that, event)
    moveBall(mousePos)
    console.log(mousePos)
}
, false)

var Ball = function(x, y, radius) {
  this.x = x;
  this.y = y;
  this.radius = radius || 10;
  this.startAngle = 0;
  this.endAngle = (Math.PI / 180) * 360;
  this.speed = 3
  this.moving = false
}

Ball.prototype.draw = function () {
  context.arc(this.x, this.y, this.radius, this.startAngle, this.endAngle);
  context.fillStyle = "white"
  context.fill();
  return this;
};

Ball.prototype.move = function () {
  if (this.x < canvas.width - this.radius && this.y < canvas.height - this.radius ){
      //this.x += this.speed;
      this.y -= this.speed;
    console.log('ballx= ' + this.x + 'bally= ' + this.y)
  }
  return this;
};

function getMousePosition(that, event) {
    return event.clientX - that.offsetLeft
}

function moveBall(mousePos) {
    if(mousePos <= ballPos.x + ballPos.radius && mousePos >= ballPos.x - ballPos.radius)
        golfBall.moving = true
}

var golfBall = new Ball(350, 450, 5);
var ballPos = golfBall.draw()

requestAnimationFrame(function gameLoop() {
  context.beginPath();
  context.clearRect(0, 0, canvas.width, canvas.height);
  context.closePath();
  golfBall.draw()
  if(golfBall.moving) {
      golfBall.move()
  }
 requestAnimationFrame(gameLoop);
});

module.exports = Ball;
<|endoftext|>


<commit_before>// https://github.com/nDmitry/grunt-postcss
var autoprefixer = require('autoprefixer');
var pxtorem = require('postcss-pxtorem');

module.exports = {
  options: {
    map: false,
    processors: [
      pxtorem({
        rootValue: 16,
        propWhiteList: [
          'font',
          'font-size',
          'line-height',
          'letter-spacing',
          'margin',
          'margin-left',
          'margin-right',
          'margin-top',
          'margin-bottom',
          'padding',
          'padding-left',
          'padding-right',
          'padding-top',
          'padding-bottom'
        ],
        selectorBlackList: [
          'html',
          'body'
        ],
        minPixelValue: 2
      }),
      autoprefixer({
        browsers: [
          'Android >= 2.1',
          'Chrome >= 21',
          'Edge >= 12',
          'Explorer >= 8',
          'Firefox >= 17',
          'Opera >= 12.1',
          'Safari >= 6.0'
        ]
      })
    ]
  },
  style: {
    src: '<%= paths.tmp %>style.css',
    dest: '<%= paths.tmp %>style.css'
  },
  editorStyle: {
    src: '<%= paths.tmp %>editor-style.css',
    dest: '<%= paths.tmp %>editor-style.css'
  }
};<commit_msg>add fallback for rem font-size as it doesn't hurt and since its 2016 target IE9+.<commit_after>// https://github.com/nDmitry/grunt-postcss
var autoprefixer = require('autoprefixer');
var pxtorem = require('postcss-pxtorem');

module.exports = {
  options: {
    map: false,
    processors: [
      pxtorem({
        replace: false,
        rootValue: 16,
        propWhiteList: [
          'font',
          'font-size',
          'line-height',
          'letter-spacing',
          'margin',
          'margin-left',
          'margin-right',
          'margin-top',
          'margin-bottom',
          'padding',
          'padding-left',
          'padding-right',
          'padding-top',
          'padding-bottom'
        ],
        selectorBlackList: [
          'html',
          'body'
        ],
        minPixelValue: 2
      }),
      autoprefixer({
        browsers: [
          'Android >= 2.1',
          'Chrome >= 21',
          'Edge >= 12',
          'Explorer >= 9',
          'Firefox >= 17',
          'Opera >= 12.1',
          'Safari >= 6.0'
        ]
      })
    ]
  },
  style: {
    src: '<%= paths.tmp %>style.css',
    dest: '<%= paths.tmp %>style.css'
  },
  editorStyle: {
    src: '<%= paths.tmp %>editor-style.css',
    dest: '<%= paths.tmp %>editor-style.css'
  }
};<|endoftext|>


<commit_before>// Load in dependencies
var gui = require('nw.gui');
var chalk = require('chalk');
var GoogleMusic = require('./google-music');

// Set up app
var app = gui.App;
var googleMusic = null;

// Generate a window for Google Music
// https://github.com/rogerwang/node-webkit/wiki/Window
// TODO: Can we change window.location for the same effect?
// TODO: Probably but the reason they usually open a subwindow is so we can act on a close event instead of exiting immediately
var googleWindow = window.open('https://play.google.com/music');
var googleWebkit = gui.Window.get(googleWindow);

// TODO: Set up `tray.tooltip` to update on song change
// TODO: Fill out package.json in full
// TODO: Add tray menu for all methods we want
// TODO: Look into icon for taskbar item https://github.com/rogerwang/node-webkit/wiki/Icons

// Create our tray icon
// DEV: Icon via http://lorempixel.com/32/32/abstract/3/
// https://github.com/rogerwang/node-webkit/wiki/Tray
var tray = new gui.Tray({icon: 'lib/icon.png'});

// Add a menu to our tray with bindings for GoogleMusic
var trayMenu = new gui.Menu();
// TODO: Add/remove play/pause depending on if music is playing or not
trayMenu.append(new gui.MenuItem({
  label: 'Play/Pause',
  click: function () {
    // TODO: If we are not logged in, prompt for login
    if (googleMusic) {
      googleMusic.playPause();
    }
  }
}));
trayMenu.append(new gui.MenuItem({
  label: 'Quit',
  click: function () {
    app.quit();
  }
}));
tray.menu = trayMenu;

// When the tray icon is clicked, toggle the window state
googleWebkit.isVisible = true;
tray.on('click', function maximizeFromTray () {
  // If it is visible, hide it
  if (googleWebkit.isVisible) {
    googleWebkit.minimize();
    googleWebkit.isVisible = false;
  // Otherwise, show it
  } else {
    googleWebkit.restore();
    googleWebkit.isVisible = true;
  }
});

// When we minimize/restore our window, remember the state
googleWebkit.on('minimize', function minimizeToTray () {
  googleWebkit.isVisible = false;
});

// When we close the googleWebkit window, close the app
// https://github.com/rogerwang/node-webkit/wiki/Window#closed
googleWebkit.on('closed', function closeApp () {
  app.quit();
});

// When our url changes
// Create a GoogleMusic instance for our window
googleWebkit.on('loaded', function () {
  // If we have arrived to a logged in state in Google Music
  var loc = googleWindow.location;
  if (loc.href.indexOf('//play.google.com/music') !== -1 && loc.hash !== '') {
    // If we need to add scripts
    if (!googleMusic) {
      // Bind to the window
      googleMusic = new GoogleMusic(googleWindow);

      // When the song changes, update the tooltip
      // Data looks like: {title, artist, album, art, duration}
      googleMusic.on('song:changed', function (data) {
        tray.tooltip = [
          'Title: ' + data.title,
          'Artist: ' + data.artist,
          'Album: ' + data.album,
        ].join('\n');
      });
    }
  // If we have left Google Music (e.g. left Google Music), clean up google music
  } else {
    delete tray.tooltip;
    if (googleMusic) {
      googleMusic.removeAllListeners();
    }
    googleMusic = null;
  }
});
<commit_msg>Removing dependency on chalk<commit_after>// Load in dependencies
var gui = require('nw.gui');
var GoogleMusic = require('./google-music');

// Set up app
var app = gui.App;
var googleMusic = null;

// Generate a window for Google Music
// https://github.com/rogerwang/node-webkit/wiki/Window
// TODO: Can we change window.location for the same effect?
// TODO: Probably but the reason they usually open a subwindow is so we can act on a close event instead of exiting immediately
var googleWindow = window.open('https://play.google.com/music');
var googleWebkit = gui.Window.get(googleWindow);

// TODO: Set up `tray.tooltip` to update on song change
// TODO: Fill out package.json in full
// TODO: Add tray menu for all methods we want
// TODO: Look into icon for taskbar item https://github.com/rogerwang/node-webkit/wiki/Icons

// Create our tray icon
// DEV: Icon via http://lorempixel.com/32/32/abstract/3/
// https://github.com/rogerwang/node-webkit/wiki/Tray
var tray = new gui.Tray({icon: 'lib/icon.png'});

// Add a menu to our tray with bindings for GoogleMusic
var trayMenu = new gui.Menu();
// TODO: Add/remove play/pause depending on if music is playing or not
trayMenu.append(new gui.MenuItem({
  label: 'Play/Pause',
  click: function () {
    // TODO: If we are not logged in, prompt for login
    if (googleMusic) {
      googleMusic.playPause();
    }
  }
}));
trayMenu.append(new gui.MenuItem({
  label: 'Quit',
  click: function () {
    app.quit();
  }
}));
tray.menu = trayMenu;

// When the tray icon is clicked, toggle the window state
googleWebkit.isVisible = true;
tray.on('click', function maximizeFromTray () {
  // If it is visible, hide it
  if (googleWebkit.isVisible) {
    googleWebkit.minimize();
    googleWebkit.isVisible = false;
  // Otherwise, show it
  } else {
    googleWebkit.restore();
    googleWebkit.isVisible = true;
  }
});

// When we minimize/restore our window, remember the state
googleWebkit.on('minimize', function minimizeToTray () {
  googleWebkit.isVisible = false;
});

// When we close the googleWebkit window, close the app
// https://github.com/rogerwang/node-webkit/wiki/Window#closed
googleWebkit.on('closed', function closeApp () {
  app.quit();
});

// When our url changes
// Create a GoogleMusic instance for our window
googleWebkit.on('loaded', function () {
  // If we have arrived to a logged in state in Google Music
  var loc = googleWindow.location;
  if (loc.href.indexOf('//play.google.com/music') !== -1 && loc.hash !== '') {
    // If we need to add scripts
    if (!googleMusic) {
      // Bind to the window
      googleMusic = new GoogleMusic(googleWindow);

      // When the song changes, update the tooltip
      // Data looks like: {title, artist, album, art, duration}
      googleMusic.on('song:changed', function (data) {
        tray.tooltip = [
          'Title: ' + data.title,
          'Artist: ' + data.artist,
          'Album: ' + data.album,
        ].join('\n');
      });
    }
  // If we have left Google Music (e.g. left Google Music), clean up google music
  } else {
    delete tray.tooltip;
    if (googleMusic) {
      googleMusic.removeAllListeners();
    }
    googleMusic = null;
  }
});
<|endoftext|>


<commit_before>import _ from 'lodash';
import RadioComponent from '../radio/Radio';

export default class SelectBoxesComponent extends RadioComponent {
  static schema(...extend) {
    return RadioComponent.schema({
      type: 'selectboxes',
      label: 'Select Boxes',
      key: 'selectBoxes',
      inline: false
    }, ...extend);
  }

  static get builderInfo() {
    return {
      title: 'Select Boxes',
      group: 'basic',
      icon: 'plus-square',
      weight: 60,
      documentation: 'http://help.form.io/userguide/#selectboxes',
      schema: SelectBoxesComponent.schema()
    };
  }

  constructor(...args) {
    super(...args);
    this.validators = this.validators.concat('minSelectedCount', 'maxSelectedCount');
  }

  init() {
    super.init();
    this.component.inputType = 'checkbox';
  }

  get defaultSchema() {
    return SelectBoxesComponent.schema();
  }

  get inputInfo() {
    const info = super.elementInfo();
    info.attr.name += '[]';
    info.attr.type = 'checkbox';
    info.attr.class = 'form-check-input';
    return info;
  }

  get emptyValue() {
    return this.component.values.reduce((prev, value) => {
      prev[value.value] = false;
      return prev;
    }, {});
  }

  /**
   * Only empty if the values are all false.
   *
   * @param value
   * @return {boolean}
   */
  isEmpty(value = this.dataValue) {
    let empty = true;
    for (const key in value) {
      if (value.hasOwnProperty(key) && value[key]) {
        empty = false;
        break;
      }
    }

    return empty;
  }

  getValue() {
    if (this.viewOnly || !this.refs.input || !this.refs.input.length) {
      return this.dataValue;
    }
    const value = {};
    _.each(this.refs.input, (input) => {
      value[input.value] = !!input.checked;
    });
    return value;
  }

  /**
   * Normalize values coming into updateValue.
   *
   * @param value
   * @return {*}
   */
  normalizeValue(value) {
    value = value || {};
    if (typeof value !== 'object') {
      if (typeof value === 'string') {
        value = {
          [value]: true
        };
      }
      else {
        value = {};
      }
    }
    if (Array.isArray(value)) {
      _.each(value, (val) => {
        value[val] = true;
      });
    }

    return value;
  }

  /**
   * Set the value of this component.
   *
   * @param value
   * @param flags
   */
  setValue(value, flags) {
    const changed = this.updateValue(value, flags);
    value = this.dataValue;
    _.each(this.refs.input, (input) => {
      if (_.isUndefined(value[input.value])) {
        value[input.value] = false;
      }
      input.checked = !!value[input.value];
    });
    return changed;
  }

  getValueAsString(value) {
    if (!value) {
      return '';
    }
    return _(this.component.values || [])
      .filter((v) => value[v.value])
      .map('label')
      .join(', ');
  }

  checkComponentValidity(data, dirty, rowData) {
    const minCount = this.component.validate.minSelectedCount;
    const maxCount = this.component.validate.maxSelectedCount;

    if (maxCount || minCount) {
      const count = Object.keys(this.validationValue).reduce((total, key) => {
        if (this.validationValue[key]) {
          total++;
        }
        return total;
      }, 0);

      if (maxCount && count >= maxCount) {
        if (this.refs.input) {
          this.refs.input.forEach(item => {
            if (!item.checked) {
              item.disabled = true;
            }
          });
        }
        if (maxCount && count > maxCount) {
          const message = this.component.maxSelectedCountMessage
            ? this.component.maxSelectedCountMessage
            : `You can only select up to ${maxCount} items.`;
          this.setCustomValidity(message, dirty);
          return false;
        }
      }
      else if (minCount && count < minCount) {
        if (this.refs.input) {
          this.refs.input.forEach(item => {
            item.disabled = false;
          });
        }
        const message = this.component.minSelectedCountMessage
          ? this.component.minSelectedCountMessage
          : `You must select at least ${minCount} items.`;
        this.setCustomValidity(message, dirty);
        return false;
      }
      else {
        if (this.refs.input) {
          this.refs.input.forEach(item => {
            item.disabled = false;
          });
        }
      }
    }

    return super.checkComponentValidity(data, dirty, rowData);
  }
}
<commit_msg>Fixed unexpected validation for selectboxes on pristine form<commit_after>import _ from 'lodash';
import RadioComponent from '../radio/Radio';

export default class SelectBoxesComponent extends RadioComponent {
  static schema(...extend) {
    return RadioComponent.schema({
      type: 'selectboxes',
      label: 'Select Boxes',
      key: 'selectBoxes',
      inline: false
    }, ...extend);
  }

  static get builderInfo() {
    return {
      title: 'Select Boxes',
      group: 'basic',
      icon: 'plus-square',
      weight: 60,
      documentation: 'http://help.form.io/userguide/#selectboxes',
      schema: SelectBoxesComponent.schema()
    };
  }

  constructor(...args) {
    super(...args);
    this.validators = this.validators.concat('minSelectedCount', 'maxSelectedCount');
  }

  init() {
    super.init();
    this.component.inputType = 'checkbox';
  }

  get defaultSchema() {
    return SelectBoxesComponent.schema();
  }

  get inputInfo() {
    const info = super.elementInfo();
    info.attr.name += '[]';
    info.attr.type = 'checkbox';
    info.attr.class = 'form-check-input';
    return info;
  }

  get emptyValue() {
    return this.component.values.reduce((prev, value) => {
      prev[value.value] = false;
      return prev;
    }, {});
  }

  /**
   * Only empty if the values are all false.
   *
   * @param value
   * @return {boolean}
   */
  isEmpty(value = this.dataValue) {
    let empty = true;
    for (const key in value) {
      if (value.hasOwnProperty(key) && value[key]) {
        empty = false;
        break;
      }
    }

    return empty;
  }

  getValue() {
    if (this.viewOnly || !this.refs.input || !this.refs.input.length) {
      return this.dataValue;
    }
    const value = {};
    _.each(this.refs.input, (input) => {
      value[input.value] = !!input.checked;
    });
    return value;
  }

  /**
   * Normalize values coming into updateValue.
   *
   * @param value
   * @return {*}
   */
  normalizeValue(value) {
    value = value || {};
    if (typeof value !== 'object') {
      if (typeof value === 'string') {
        value = {
          [value]: true
        };
      }
      else {
        value = {};
      }
    }
    if (Array.isArray(value)) {
      _.each(value, (val) => {
        value[val] = true;
      });
    }

    return value;
  }

  /**
   * Set the value of this component.
   *
   * @param value
   * @param flags
   */
  setValue(value, flags) {
    const changed = this.updateValue(value, flags);
    value = this.dataValue;
    _.each(this.refs.input, (input) => {
      if (_.isUndefined(value[input.value])) {
        value[input.value] = false;
      }
      input.checked = !!value[input.value];
    });
    return changed;
  }

  getValueAsString(value) {
    if (!value) {
      return '';
    }
    return _(this.component.values || [])
      .filter((v) => value[v.value])
      .map('label')
      .join(', ');
  }

  checkComponentValidity(data, dirty, rowData) {
    const minCount = this.component.validate.minSelectedCount;
    const maxCount = this.component.validate.maxSelectedCount;

    if ((maxCount || minCount) && !this.isValid(data, dirty)) {
      const count = Object.keys(this.validationValue).reduce((total, key) => {
        if (this.validationValue[key]) {
          total++;
        }
        return total;
      }, 0);

      if (maxCount && count >= maxCount) {
        if (this.refs.input) {
          this.refs.input.forEach(item => {
            if (!item.checked) {
              item.disabled = true;
            }
          });
        }
        if (maxCount && count > maxCount) {
          const message = this.component.maxSelectedCountMessage
            ? this.component.maxSelectedCountMessage
            : `You can only select up to ${maxCount} items.`;
          this.setCustomValidity(message, dirty);
          return false;
        }
      }
      else if (minCount && count < minCount) {
        if (this.refs.input) {
          this.refs.input.forEach(item => {
            item.disabled = false;
          });
        }
        const message = this.component.minSelectedCountMessage
          ? this.component.minSelectedCountMessage
          : `You must select at least ${minCount} items.`;
        this.setCustomValidity(message, dirty);
        return false;
      }
      else {
        if (this.refs.input) {
          this.refs.input.forEach(item => {
            item.disabled = false;
          });
        }
      }
    }

    return super.checkComponentValidity(data, dirty, rowData);
  }
}
<|endoftext|>


<commit_before>var mongoose = require('mongoose'),
    _ = require('underscore'),
    async = require('async');

var find = function (query, depth, fieldsFilter, cb) {

  if(_.isUndefined(this.schema.fieldsFilter)) return cb('Model ' + that.modelName + ' is missing a filter');

  // If fields filter is empty, use model default
  if(fieldsFilter.length === 0)
    fieldsFilter = this.schema.fieldsFilter;

  var schemaFields = this.schema.paths;

  var currentFields = _getCurrentFields(fieldsFilter);
  var modelRefFields = _getModelReferences(this);

  // Find the fields that need to be populated
  var populateFields = _.filter(modelRefFields, function(item){ return _.contains(currentFields, item.field) && item.field !== '_id'; });

  this.find(query).lean().exec(function(err, results) {
    if(results == null || results.length === 0) return cb(null);

    //  Our recursion check point
    if(depth > 0 && populateFields.length > 0) {
      _queryChildren(results, depth, fieldsFilter, populateFields, schemaFields, function(err, results){
        cb(err, _filterResults(results, currentFields));
      });
    } else {
      cb(err, _filterResults(results, currentFields));
    }
  });
};

var findOne = function (query, depth, fieldsFilter, cb) {

  if(_.isUndefined(this.schema.fieldsFilter)) return cb('Model ' + that.modelName + ' is missing a filter');

  // If fields filter is empty, use model default
  if(fieldsFilter.length === 0)
    fieldsFilter = this.schema.fieldsFilter;

  var schemaFields = this.schema.paths;

  var currentFields = _getCurrentFields(fieldsFilter);
  var modelRefFields = _getModelReferences(this);

  // Find the fields that need to be populated
  var populateFields = _.filter(modelRefFields, function(item){ return _.contains(currentFields, item.field) && item.field !== '_id'; });

  this.findOne(query).lean().exec(function(err, result) {
    if(results == null || results.length === 0) return cb(null);

    //  Our recursion check point
    if(depth > 0 && populateFields.length > 0) {
      _queryChildren(results, depth, fieldsFilter, populateFields, schemaFields, function(err, results){
        cb(err, _filterResult(result, currentFields));
      });
    } else {
      cb(err, _filterResult(result, currentFields));
    }
  });
};

var save = function(callback) {

  var filterFields = this.schema.fieldsFilter;

  this.save (function (err, instance) {
    if (err) return callback (err, null);
    var lean = instance.toObject();
    return callback (null, _filterResult(lean, filterFields));
  });
}

var _queryChildren = function(results, depth, fieldsFilter, populateFields, parentSchema, callback) {

  async.map(results, function(result, callback){
    async.each(populateFields, function(field, callback){
      if(_.isUndefined(mongoose.model(field.model).schema.fieldsFilter)) return callback('Model ' + field.model + ' is missing a filter');

      //  Get child fields of current Model field, and use default filter for Model if none defined
      var childFields = _getChildFields(fieldsFilter, field.field);
      if(childFields.length === 0) childFields = mongoose.model(field.model).schema.fieldsFilter;

      mongoose.model(field.model).filterPopulate().find({ _id : result[field.field] }, depth - 1, childFields, function(err, instances){

        result[field.field] = _.map(instances, function(instance){ return _.pick(instance, childFields); });
        // If it is not an array, return single instance
        if(!_isModelRefArray(parentSchema[field.field])) result[field.field] = result[field.field][0];

        callback(null);
      })
    }, function(err){
      callback(err, result)
    });
  }, function(err, results){
    callback(err, results);
  });
};

var _filterResults = function(results, filterFields) {
  return _.map(results, function(result){
    if(result.id == null)
      result.id = result._id;

    return _.pick(result, filterFields);
  });
};

var _filterResult = function(result, filterFields) {
  if(result.id == null)
    result.id = result._id;
  return _.pick(result, filterFields);
};

var _getCurrentFields = function(dotNotionFields) {
  return _.uniq(_.compact(_.map(dotNotionFields, function(field){
    var field = field.split('.');
    return field[0];
  })));
};

var _getChildFields = function(dotNotionFields, parentField) {
  return _.compact(_.map(dotNotionFields, function(field){
    var fields = field.split('.');
    if(fields[0] === parentField) {
      fields.shift();
      return fields.join('.');
    } else {
      return null;
    }
  }));
};

//  Check if the model ref an array
var _isModelRefArray = function(schemaField) {
  return _.isObject(schemaField.caster);
};

var _getModelReferences = function (model) {

  var fields = model.schema.paths;

  return _.compact(_.map(_.pairs(fields), function(field) {
    //  If it's an array, get actual field attrs
    if(_isModelRefArray(field[1])) field = [field[0], field[1].caster];
    //  We don't want type mixed fields, or non Model Refs or _id
    if(_.isUndefined(field[1].instance) || field[1].instance !== 'ObjectID' || field[0] === '_id') return null;

    return { field: field[0], model: field[1].options.ref };
  }));
};

module.exports = exports = function mongooseExpandPlugin(schema, options) {

  schema.statics.filterPopulate = function(){
    return {
      find: find.bind(this),
      findOne: findOne.bind(this)
    }
  };

  schema.methods.filterPopulate = function(){
    return {
      save: save.bind(this)
    }
  };
};

<commit_msg>refactor<commit_after>var mongoose = require('mongoose'),
    _ = require('underscore'),
    async = require('async');

var options = {
  depth: 1,
  filterFields: []
};

var find = function (query, opts, cb) {

  var opts = _.defaults(opts, options);

  if(_.isUndefined(this.schema.filterFields)) return cb('Model ' + that.modelName + ' is missing a filter');

  // If fields filter is empty, use model default
  if(opts.filterFields.length === 0)
    opts.filterFields = this.schema.filterFields;

  var schemaFields = this.schema.paths;
  var currentFields = _getCurrentFields(opts.filterFields);
  var modelRefFields = _getModelReferences(this);

  // Find the fields that need to be populated
  var populateFields = _.filter(modelRefFields, function(item){ return _.contains(currentFields, item.field) && item.field !== '_id'; });

  this.find(query).lean().exec(function(err, results) {
    if(results == null || results.length === 0) return cb(null);

    //  Our recursion check point
    if(opts.depth > 0 && populateFields.length > 0) {

      _queryChildren(results, opts.depth, opts.filterFields, populateFields, schemaFields, function(err, results){
        cb(err, _filterResults(results, currentFields));
      });
    } else {
      cb(err, _filterResults(results, currentFields));
    }
  });
};

var findOne = function (query, opts, cb) {

  var opts = _.defaults(opts, options);

  if(_.isUndefined(this.schema.filterFields)) return cb('Model ' + that.modelName + ' is missing a filter');

  // If fields filter is empty, use model default
  if(opts.filterFields.length === 0)
    opts.filterFields = this.schema.filterFields;

  var schemaFields = this.schema.paths;
  var currentFields = _getCurrentFields(opts.filterFields);
  var modelRefFields = _getModelReferences(this);

  // Find the fields that need to be populated
  var populateFields = _.filter(modelRefFields, function(item){ return _.contains(currentFields, item.field) && item.field !== '_id'; });

  this.findOne(query).lean().exec(function(err, result) {

    if(result == null || result.length === 0) return cb(null);

    //  Our recursion check point
    if(opts.depth > 0 && populateFields.length > 0) {

      _queryChildren([result], opts.depth, opts.filterFields, populateFields, schemaFields, function(err, results){
        cb(err, _filterResults(results, currentFields)[0]);
      });
    } else {
      cb(err, _filterResult(result, currentFields));
    }
  });
};

var save = function(opts, callback) {

  var opts = _.defaults(opts, options);

  var filterFields = this.schema.filterFields;

  this.save (function (err, instance) {
    if (err) return callback (err, null);
    var lean = instance.toObject();
    return callback (null, _filterResult(lean, filterFields));
  });
}

var _queryChildren = function(results, depth, filterFields, populateFields, parentSchema, callback) {

  async.map(results, function(result, callback){
    async.each(populateFields, function(field, callback){
      if(_.isUndefined(mongoose.model(field.model).schema.filterFields)) return callback('Model ' + field.model + ' is missing a filter');

      //  Get child fields of current Model field, and use default filter for Model if none defined
      var childFields = _getChildFields(filterFields, field.field);
      if(childFields.length === 0) childFields = mongoose.model(field.model).schema.filterFields;

      var options = {
        depth: depth - 1,
        filterFields: childFields
      };

      mongoose.model(field.model).findFP({ _id : result[field.field] }, options, function(err, instances){

        result[field.field] = _.map(instances, function(instance){ return _.pick(instance, childFields); });
        // If it is not an array, return single instance
        if(!_isModelRefArray(parentSchema[field.field])) result[field.field] = result[field.field][0];

        callback(null);
      })
    }, function(err){
      callback(err, result)
    });
  }, function(err, results){
    callback(err, results);
  });
};

var _filterResults = function(results, filterFields) {

  return _.map(results, function(result){
    if(result.id == null)
      result.id = result._id;

    return _.pick(result, filterFields);
  });
};

var _filterResult = function(result, filterFields) {
  if(result.id == null)
    result.id = result._id;
  return _.pick(result, filterFields);
};

var _getCurrentFields = function(dotNotionFields) {
  return _.uniq(_.compact(_.map(dotNotionFields, function(field){
    var field = field.split('.');
    return field[0];
  })));
};

var _getChildFields = function(dotNotionFields, parentField) {
  return _.compact(_.map(dotNotionFields, function(field){
    var fields = field.split('.');
    if(fields[0] === parentField) {
      fields.shift();
      return fields.join('.');
    } else {
      return null;
    }
  }));
};

//  Check if the model ref an array
var _isModelRefArray = function(schemaField) {
  return _.isObject(schemaField.caster);
};

var _getModelReferences = function (model) {

  var fields = model.schema.paths;

  return _.compact(_.map(_.pairs(fields), function(field) {
    //  If it's an array, get actual field attrs
    if(_isModelRefArray(field[1])) field = [field[0], field[1].caster];
    //  We don't want type mixed fields, or non Model Refs or _id
    if(_.isUndefined(field[1].instance) || field[1].instance !== 'ObjectID' || field[0] === '_id') return null;

    return { field: field[0], model: field[1].options.ref };
  }));
};

module.exports = exports = function mongooseExpandPlugin(schema, options) {

  schema.statics.findFP = find;
  schema.statics.findOneFP = findOne;
  schema.methods.saveFP = save;

};

<|endoftext|>


<commit_before>import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import Box, { pickBoxProps } from '../box';
import Button, { ButtonGroup } from '../button';
import { Menu } from '../menu';
import Popover from '../popover';
import { IconChevronDownSmallOutline } from '@teamleader/ui-icons';
import pick from 'lodash.pick';

class SplitButton extends PureComponent {
  firstChild = pick(this.props.children[0].props, ['label']);

  state = {
    buttonLabel: this.firstChild.label,
    popoverActive: false,
    popoverAnchorEl: null,
  };

  handleMainButtonClick = (event) => {
    this.props.onButtonClick(event);
  };

  handleSecondaryButtonClick = (event) => {
    const { onSecondaryButtonClick } = this.props;
    this.setState({ popoverActive: true, popoverAnchorEl: event.currentTarget });
    if (onSecondaryButtonClick) {
      onSecondaryButtonClick(event);
    }
  };

  handleMenuItemClick = (child, event) => {
    const childProps = child.props;
    this.setState({
      popoverActive: false,
    });
    childProps.onClick(event);
  };

  handleCloseClick = () => {
    this.setState({ popoverActive: false });
  };

  render() {
    const { children, level, size, disabled, ...others } = this.props;
    const { buttonLabel, popoverActive, popoverAnchorEl } = this.state;
    const boxProps = pickBoxProps(others);
    return (
      <Box display="flex" justifyContent="center" {...boxProps} data-teamleader-ui="split-menu">
        <ButtonGroup segmented>
          <Button
            label={buttonLabel}
            level={level}
            size={size}
            disabled={disabled}
            onClick={this.handleMainButtonClick}
          />
          <Button
            icon={<IconChevronDownSmallOutline />}
            level={level}
            size={size}
            disabled={disabled}
            onClick={this.handleSecondaryButtonClick}
          />
        </ButtonGroup>
        <Popover
          active={popoverActive}
          anchorEl={popoverAnchorEl}
          backdrop="transparent"
          onEscKeyDown={this.handleCloseClick}
          onOverlayClick={this.handleCloseClick}
          position="start"
        >
          <Menu>
            {React.Children.map(children, (child) => {
              if (child.props.label !== buttonLabel) {
                return React.cloneElement(child, {
                  onClick: (event) => this.handleMenuItemClick(child, event),
                });
              }
            })}
          </Menu>
        </Popover>
      </Box>
    );
  }
}

SplitButton.defaultProps = {
  level: 'primary',
  size: 'medium',
};

SplitButton.propTypes = {
  /** The MenuItems we pass to our component. */
  children: PropTypes.node.isRequired,
  /** Determines which kind of button to be rendered. */
  level: PropTypes.oneOf(['primary', 'secondary', 'destructive']),
  /** Size of the button. */
  size: PropTypes.oneOf(['small', 'medium', 'large']),
  /** The function executed, when we click on the main button. */
  onButtonClick: PropTypes.func.isRequired,
  /** The function executed, when we click on the secondary button. */
  onSecondaryButtonClick: PropTypes.func,
  /** If true, component will be disabled. */
  disabled: PropTypes.bool,
};

export default SplitButton;
<commit_msg>Prevent scroll lock<commit_after>import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import Box, { pickBoxProps } from '../box';
import Button, { ButtonGroup } from '../button';
import { Menu } from '../menu';
import Popover from '../popover';
import { IconChevronDownSmallOutline } from '@teamleader/ui-icons';
import pick from 'lodash.pick';

class SplitButton extends PureComponent {
  firstChild = pick(this.props.children[0].props, ['label']);

  state = {
    buttonLabel: this.firstChild.label,
    popoverActive: false,
    popoverAnchorEl: null,
  };

  handleMainButtonClick = (event) => {
    this.props.onButtonClick(event);
  };

  handleSecondaryButtonClick = (event) => {
    const { onSecondaryButtonClick } = this.props;
    this.setState({ popoverActive: true, popoverAnchorEl: event.currentTarget });
    if (onSecondaryButtonClick) {
      onSecondaryButtonClick(event);
    }
  };

  handleMenuItemClick = (child, event) => {
    const childProps = child.props;
    this.setState({
      popoverActive: false,
    });
    childProps.onClick(event);
  };

  handleCloseClick = () => {
    this.setState({ popoverActive: false });
  };

  render() {
    const { children, level, size, disabled, ...others } = this.props;
    const { buttonLabel, popoverActive, popoverAnchorEl } = this.state;
    const boxProps = pickBoxProps(others);
    return (
      <Box display="flex" justifyContent="center" {...boxProps} data-teamleader-ui="split-menu">
        <ButtonGroup segmented>
          <Button
            label={buttonLabel}
            level={level}
            size={size}
            disabled={disabled}
            onClick={this.handleMainButtonClick}
          />
          <Button
            icon={<IconChevronDownSmallOutline />}
            level={level}
            size={size}
            disabled={disabled}
            onClick={this.handleSecondaryButtonClick}
          />
        </ButtonGroup>
        <Popover
          active={popoverActive}
          anchorEl={popoverAnchorEl}
          backdrop="transparent"
          lockScroll={false}
          onEscKeyDown={this.handleCloseClick}
          onOverlayClick={this.handleCloseClick}
          position="start"
        >
          <Menu>
            {React.Children.map(children, (child) => {
              if (child.props.label !== buttonLabel) {
                return React.cloneElement(child, {
                  onClick: (event) => this.handleMenuItemClick(child, event),
                });
              }
            })}
          </Menu>
        </Popover>
      </Box>
    );
  }
}

SplitButton.defaultProps = {
  level: 'primary',
  size: 'medium',
};

SplitButton.propTypes = {
  /** The MenuItems we pass to our component. */
  children: PropTypes.node.isRequired,
  /** Determines which kind of button to be rendered. */
  level: PropTypes.oneOf(['primary', 'secondary', 'destructive']),
  /** Size of the button. */
  size: PropTypes.oneOf(['small', 'medium', 'large']),
  /** The function executed, when we click on the main button. */
  onButtonClick: PropTypes.func.isRequired,
  /** The function executed, when we click on the secondary button. */
  onSecondaryButtonClick: PropTypes.func,
  /** If true, component will be disabled. */
  disabled: PropTypes.bool,
};

export default SplitButton;
<|endoftext|>


<commit_before>"use strict";

var WebdriverIO = require('webdriverio/lib/webdriverio')
var MultiDevice = require('./multidevice')
var Flow = require('./flow/flow')
var Device = require('./flow/device')
var Checkpoint = require('./flow/checkpoint')
var Step = require('./flow/step')
var templates = require('./templates')
var path = require('path')
var fs = require('fs')

/**
 * @param options
 * @param modifier
 * @param {Flow|null} flow Required for and only for instances of a multi device, but not for the multi device collection itself.
 * @returns {WebdriverIO.Client}
 */
function remote(options, modifier, flow) {

    options = options || {};

    let flowDevice = new Device(options)
    if (flow) {
        flow.addDevices([flowDevice])
    }

    let screenshotBlackList = {
        protocolQueries: ['element', 'elementActive', 'elementIdAttribute', 'elementIdCssProperty', 'elementIdDisplayed', 'elementIdElement', 'elementIdElements', 'elementIdEnabled', 'elementIdLocation', 'elementIdLocationInView', 'elementIdName', 'elementIdSelected', 'elementIdSize', 'elementIdText', 'elements', 'title', 'windowHandle', 'windowHandles'],
        stateQueries: ['isEnabled', 'isExisting', 'isSelected', 'isVisible', 'isVisibleWithinViewport'],
        cookieCommands: ['deleteCookie', 'getCookie', 'setCookie', 'cookie'],
        elementPropertyQueries: ['getAttribute', 'getCssProperty', 'getElementSize', 'getHTML', 'getLocation', 'getLocationInView', 'getSource', 'getTagName', 'getText', 'getTitle', 'getUrl', 'getValue'],
        interferingCommands: ['init', 'session', 'sessions', 'saveScreenshot', 'screenshot'],
        otherCommandsWithoutSideEffects: ['checkpoint', 'timeouts', 'timeoutsAsyncScript', 'timeoutsImplicitWait', 'windowHandlePosition', 'getCommandHistory', 'getTabIds', 'getViewportSize'],
        internalWebElementCommands: ['elementIdAttribute', 'elementIdClear', 'elementIdClick', 'elementIdCssProperty', 'elementIdDisplayed', 'elementIdElement', 'elementIdElements', 'elementIdEnabled', 'elementIdLocation', 'elementIdLocationInView', 'elementIdName', 'elementIdSelected', 'elementIdSize', 'elementIdText', 'elementIdValue'],
        xdTesting: ['windowHandleSize']
    };
    // Merge all blacklists into one list
    screenshotBlackList = Object.keys(screenshotBlackList).map(key => screenshotBlackList[key]).reduce((previous, current) => previous.concat(current), [])

    /**
     * Initialise monad
     */
    var unit = WebdriverIO(options, modifier)

    unit.lift('checkpoint', function (checkpointName) {
        return this.saveScreenshot(function (err, screenshot, response) {
            if (err) {
                // Log screenshot error encoded as command step
                console.log('could not create screenshot', err)
                flowDevice.addStep(new Step(flow.generateStepId(), '[ERROR: could not create screenshot]', null))
            } else {
                // Create new step with screenshot
                flowDevice.addStep(new Step(flow.generateStepId(), [], screenshot))
            }

            // Create new checkpoint
            flowDevice.addCheckpoint(new Checkpoint(flow.generateCheckpointId(), checkpointName))
        })
    })

    unit.lift('name', function (name) {
        // For some reasons this.then(...) is not working, but the screenshot is.
        // Except for the overhead there should be no drawback
        return this.saveScreenshot(function () {
            flow.setName(name)
        })
    })

    unit.lift('getFlow', function () {
        // For some reasons this.then(...) is not working, but the screenshot is.
        // Except for the overhead there should be no drawback
        return this.saveScreenshot(function () {
            return flow
        })
    })

    /**
     * Add the webdriverIO commands
     * Build prototype: commands
     */
    ;[
        '../node_modules/webdriverio/lib/protocol',
        '../node_modules/webdriverio/lib/commands',
        'commands'
    ].forEach(function (dir) {
        dir = path.join(__dirname, dir)
        var files = fs.readdirSync(dir)

        files.forEach(function (filename) {
            let commandName = filename.slice(0, -3)
            let commandFunc = require(path.join(dir, filename))

            /**
             * Command proxy: Take a screenshot between commands.
             * Proxy is executed for each device
             * @returns {WebdriverIO.Client}
             */
            let proxyCommandFunc = function () {
                // Default: no additional behaviour
                let intermediate = this

                if (screenshotBlackList.indexOf(commandName) === -1) {

                    let commandArgs = arguments

                    // Take a screenshot before each command
                    // This does not override the original commands return value
                    intermediate = intermediate.saveScreenshot(function (err, screenshot, response) {
                        if (err) {
                            // Log screenshot error encoded as command step
                            console.log('could not create screenshot', err)
                            flowDevice.addStep(new Step(flow.generateStepId(), '[ERROR: could not create screenshot]', null))
                        } else {
                            // Create new step with screenshot
                            flowDevice.addStep(new Step(flow.generateStepId(), [], screenshot))
                        }
                    })

                    // Automatically insert a checkpoint before the browser session is closed
                    if (commandName === 'end' || commandName === 'endAll') {
                        // Skip checkpoint if it already exists
                        if (flowDevice.checkpoints.filter(checkpoint => checkpoint.name === commandName).length === 0) {
                            intermediate = intermediate.checkpoint(commandName)
                        }
                    }

                    intermediate = intermediate.then(function() {
                        // Add command in a seperate step after the screenshot
                        let commandLabel = commandName
                        if (typeof commandArgs[0] == "string") {
                            commandLabel += "('" + commandArgs[0] + "')"
                        }
                        flowDevice.addStep(new Step(flow.generateStepId(), commandLabel, null))

                        // Serialize and store the steps and screenshots
                        flow.storeIfChanged()
                    })
                }

                // Execute function after the intermediate
                return commandFunc.apply(intermediate, arguments);
            }

            unit.lift(commandName, proxyCommandFunc);
        })
    })

    var prototype = unit()
    prototype.defer.resolve()

    return prototype
}

module.exports.remote = remote

/**
 * @param options
 * @returns {WebdriverIO.Client}
 */
function multiremote(options) {

    // Add id key to options
    Object.keys(options).forEach(function (id) {
        options[id]['id'] = id
        options[id]['waitforTimeout'] = options[id]['waitforTimeout'] || waitForTimeout
    })

    var multiDevice = new MultiDevice(options)
    var flow = new Flow()

    Object.keys(options).forEach(function (browserName) {
        multiDevice.addInstance(
            browserName,
            remote(options[browserName], multiDevice.getInstanceModifier(), flow)
        )
    })

    return remote(options, multiDevice.getModifier(null, flow), null)
}

module.exports.multiremote = multiremote

/**
 * Load setups from the setup file.
 * Replace device string references by the full options.
 * @returns {Array.<Object.<String, Object>>}
 */
function loadSetups() {
    var config = require(path.join(process.cwd(), setupsFile))

    // Resolve templates: Replace device string references by the full options
    config['setups'].forEach(setup => {
        Object.keys(setup.devices).forEach(id => {
            var deviceConfig = setup.devices[id]
            if (typeof deviceConfig == "string" && templates.devices[deviceConfig]) {
                // Replace template reference
                setup.devices[id] = templates.devices[deviceConfig]()
            }
        })
    })
    return config['setups']
}

module.exports.templates = templates
module.exports.loadSetups = loadSetups

/**
 * Config
 */

/**
 * @type {number}
 */
var waitForTimeout = 60 * 1000
module.exports.waitForTimeout = waitForTimeout

/**
 * @type {string}
 */
const setupsFile = 'xd-testing.json'
module.exports.setupsFile = setupsFile

/**
 * Url to load on initialization of multiremote instances.
 * @type {string|null}
 */
module.exports.baseUrl = null

/*
 * Default adapters
 */
module.exports.adapter = {
    /**
     * @return {xdmvc}
     */
    xdmvc: require('./adapter/xdmvc')
}

/**
 * @return {Function|Function.<xdmvc>}
 */
module.exports.appFramework = null

module.exports.reset = () => {
    module.exports.appFramework = null
    module.exports.baseUrl = null
    module.exports.setupsFile = setupsFile
    module.exports.waitForTimeout = waitForTimeout
}

module.exports.reset()
<commit_msg>Renaming<commit_after>"use strict";

var WebdriverIO = require('webdriverio/lib/webdriverio')
var MultiDevice = require('./multidevice')
var Flow = require('./flow/flow')
var Device = require('./flow/device')
var Checkpoint = require('./flow/checkpoint')
var Step = require('./flow/step')
var templates = require('./templates')
var path = require('path')
var fs = require('fs')

/**
 * @param options
 * @param modifier
 * @param {Flow|null} flow Required for and only for instances of a multi device, but not for the multi device collection itself.
 * @returns {WebdriverIO.Client}
 */
function remote(options, modifier, flow) {

    options = options || {};

    let flowDevice = new Device(options)
    if (flow) {
        flow.addDevices([flowDevice])
    }

    let screenshotBlackList = {
        protocolQueries: ['element', 'elementActive', 'elementIdAttribute', 'elementIdCssProperty', 'elementIdDisplayed', 'elementIdElement', 'elementIdElements', 'elementIdEnabled', 'elementIdLocation', 'elementIdLocationInView', 'elementIdName', 'elementIdSelected', 'elementIdSize', 'elementIdText', 'elements', 'title', 'windowHandle', 'windowHandles'],
        stateQueries: ['isEnabled', 'isExisting', 'isSelected', 'isVisible', 'isVisibleWithinViewport'],
        cookieCommands: ['deleteCookie', 'getCookie', 'setCookie', 'cookie'],
        elementPropertyQueries: ['getAttribute', 'getCssProperty', 'getElementSize', 'getHTML', 'getLocation', 'getLocationInView', 'getSource', 'getTagName', 'getText', 'getTitle', 'getUrl', 'getValue'],
        interferingCommands: ['init', 'session', 'sessions', 'saveScreenshot', 'screenshot'],
        otherCommandsWithoutSideEffects: ['checkpoint', 'timeouts', 'timeoutsAsyncScript', 'timeoutsImplicitWait', 'windowHandlePosition', 'getCommandHistory', 'getTabIds', 'getViewportSize'],
        internalWebElementCommands: ['elementIdAttribute', 'elementIdClear', 'elementIdClick', 'elementIdCssProperty', 'elementIdDisplayed', 'elementIdElement', 'elementIdElements', 'elementIdEnabled', 'elementIdLocation', 'elementIdLocationInView', 'elementIdName', 'elementIdSelected', 'elementIdSize', 'elementIdText', 'elementIdValue'],
        xdTesting: ['windowHandleSize']
    };
    // Merge all blacklists into one list
    screenshotBlackList = Object.keys(screenshotBlackList).map(key => screenshotBlackList[key]).reduce((previous, current) => previous.concat(current), [])

    /**
     * Initialise monad
     */
    var unit = WebdriverIO(options, modifier)

    unit.lift('checkpoint', function (checkpointName) {
        return this.saveScreenshot(function (err, screenshot, response) {
            if (err) {
                // Log screenshot error encoded as command step
                console.log('could not create screenshot', err)
                flowDevice.addStep(new Step(flow.generateStepId(), '[ERROR: could not create screenshot]', null))
            } else {
                // Create new step with screenshot
                flowDevice.addStep(new Step(flow.generateStepId(), [], screenshot))
            }

            // Create new checkpoint
            flowDevice.addCheckpoint(new Checkpoint(flow.generateCheckpointId(), checkpointName))
        })
    })

    unit.lift('name', function (name) {
        // For some reasons this.then(...) is not working, but the screenshot is.
        // Except for the overhead there should be no drawback
        return this.saveScreenshot(function () {
            flow.setName(name)
        })
    })

    unit.lift('getFlow', function () {
        // For some reasons this.then(...) is not working, but the screenshot is.
        // Except for the overhead there should be no drawback
        return this.saveScreenshot(function () {
            return flow
        })
    })

    /**
     * Add the webdriverIO commands
     * Build prototype: commands
     */
    ;[
        '../node_modules/webdriverio/lib/protocol',
        '../node_modules/webdriverio/lib/commands',
        'commands'
    ].forEach(function (dir) {
        dir = path.join(__dirname, dir)
        var files = fs.readdirSync(dir)

        files.forEach(function (filename) {
            let commandName = filename.slice(0, -3)
            let commandFunc = require(path.join(dir, filename))

            /**
             * Command proxy: Take a screenshot between commands.
             * Proxy is executed for each device
             * @returns {WebdriverIO.Client}
             */
            let proxyCommandFunc = function () {
                // Default: no additional behaviour
                let intermediate = this

                if (screenshotBlackList.indexOf(commandName) === -1) {

                    let commandArgs = arguments

                    // Take a screenshot before each command
                    // This does not override the original commands return value
                    intermediate = intermediate.saveScreenshot(function (err, screenshot, response) {
                        if (err) {
                            // Log screenshot error encoded as command step
                            console.log('could not create screenshot', err)
                            flowDevice.addStep(new Step(flow.generateStepId(), '[ERROR: could not create screenshot]', null))
                        } else {
                            // Create new step with screenshot
                            flowDevice.addStep(new Step(flow.generateStepId(), [], screenshot))
                        }
                    })

                    // Automatically insert a checkpoint before the browser session is closed
                    if (commandName === 'end' || commandName === 'endAll') {
                        // Skip checkpoint if it already exists
                        if (flowDevice.checkpoints.filter(checkpoint => checkpoint.name === commandName).length === 0) {
                            intermediate = intermediate.checkpoint(commandName)
                        }
                    }

                    intermediate = intermediate.then(function() {
                        // Add command in a seperate step after the screenshot
                        let commandLabel = commandName
                        if (typeof commandArgs[0] == "string") {
                            commandLabel += "('" + commandArgs[0] + "')"
                        }
                        flowDevice.addStep(new Step(flow.generateStepId(), commandLabel, null))

                        // Serialize and store the steps and screenshots
                        flow.storeIfChanged()
                    })
                }

                // Execute function after the intermediate
                return commandFunc.apply(intermediate, arguments);
            }

            unit.lift(commandName, proxyCommandFunc);
        })
    })

    var prototype = unit()
    prototype.defer.resolve()

    return prototype
}

module.exports.remote = remote

/**
 * @param options
 * @returns {WebdriverIO.Client}
 */
function multiremote(options) {

    // Add id key to options
    Object.keys(options).forEach(function (id) {
        options[id]['id'] = id
        options[id]['waitforTimeout'] = options[id]['waitforTimeout'] || waitForTimeout
    })

    var multiDevice = new MultiDevice(options)
    var flow = new Flow()

    Object.keys(options).forEach(function (browserName) {
        multiDevice.addInstance(
            browserName,
            remote(options[browserName], multiDevice.getInstanceModifier(), flow)
        )
    })

    return remote(options, multiDevice.getModifier(null, flow), null)
}

module.exports.multiremote = multiremote

/**
 * Load scenarios from the config file.
 * Replace device string references by the full options.
 * @returns {Array.<Object.<String, Object>>}
 */
function loadScenarios() {
    var config = require(path.join(process.cwd(), scenariosFile))

    // Resolve templates: Replace device string references by the full options
    config['scenarios'].forEach(setup => {
        Object.keys(setup.devices).forEach(id => {
            var deviceConfig = setup.devices[id]
            if (typeof deviceConfig == "string" && templates.devices[deviceConfig]) {
                // Replace template reference
                setup.devices[id] = templates.devices[deviceConfig]()
            }
        })
    })
    return config['scenarios']
}

module.exports.templates = templates
module.exports.loadScenarios = loadScenarios

/**
 * Config
 */

/**
 * @type {number}
 */
var waitForTimeout = 60 * 1000
module.exports.waitForTimeout = waitForTimeout

/**
 * @type {string}
 */
const scenariosFile = 'xd-testing.json'
module.exports.scenariosFile = scenariosFile

/**
 * Url to load on initialization of multiremote instances.
 * @type {string|null}
 */
module.exports.baseUrl = null

/*
 * Default adapters
 */
module.exports.adapter = {
    /**
     * @return {xdmvc}
     */
    xdmvc: require('./adapter/xdmvc')
}

/**
 * @return {Function|Function.<xdmvc>}
 */
module.exports.appFramework = null

module.exports.reset = () => {
    module.exports.appFramework = null
    module.exports.baseUrl = null
    module.exports.scenariosFile = scenariosFile
    module.exports.waitForTimeout = waitForTimeout
}

module.exports.reset()
<|endoftext|>