<commit_before>const Ajv = require('ajv');
const predefined = require('./predefined');

const ajv = new Ajv({
  schemas: predefined,
  useDefaults: true
});

const buildKey = (type, name) => `${type}:${name}`;

const registeredKeys = predefined.map((predefinedSchema) => ({
  type: 'core',
  key: predefinedSchema.$id,
  name: predefinedSchema.$id
}));

function register (type, name, schema) {
  const key = buildKey(type, name);
  /*
    This piece of code is checking that the schema isn't already registered.
    This is not optimal and it's happening because the testing helpers aren't
    removing the schemas when we're shutting down the gateway. Hopefully we'll
    get back to this once we'll have a better story for programmatic startup/shutdown
  */
  if (schema && registeredKeys.findIndex(keys => keys.key === key) === -1) {
    ajv.addSchema(schema, key);
    registeredKeys.push({ type, name, key });
  }

  return (data) => validate(type, name, data);
}

function validate (type, name, data) {
  const key = buildKey(type, name);
  const compiled = ajv.getSchema(key);
  if (compiled) {
    const isValid = compiled(data);
    return { isValid, error: ajv.errorsText(compiled.errors) };
  }

  return { isValid: true };
}

function find (type = null, name = null) {
  if (type && name) {
    const item = ajv.getSchema(buildKey(type, name));
    if (item) {
      return { type, name, schema: item.schema };
    }
  } else {
    return registeredKeys
      .filter(item => !type || item.type === type)
      .map(key => ({ key: key.key, type: key.type, name: key.name, schema: ajv.getSchema(key.key).schema }));
  }
}

module.exports = {
  register,
  validate,
  find
};
<commit_msg>Send a warning when a JSON Schema hasn't been provided.<commit_after>const Ajv = require('ajv');
const predefined = require('./predefined');
const logger = require('../../lib/logger').gateway;

const ajv = new Ajv({
  schemas: predefined,
  useDefaults: true
});

const buildKey = (type, name) => `${type}:${name}`;

const registeredKeys = predefined.map((predefinedSchema) => ({
  type: 'core',
  key: predefinedSchema.$id,
  name: predefinedSchema.$id
}));

function register (type, name, schema) {
  const key = buildKey(type, name);
  /*
    This piece of code is checking that the schema isn't already registered.
    This is not optimal and it's happening because the testing helpers aren't
    removing the schemas when we're shutting down the gateway. Hopefully we'll
    get back to this once we'll have a better story for programmatic startup/shutdown
  */

  if (!schema) {
    logger.warn(`${name} ${type} hasn't provided a schema. Validation for this ${type} will be skipped.`);
  } else if (registeredKeys.findIndex(keys => keys.key === key) === -1) {
    ajv.addSchema(schema, key);
    registeredKeys.push({ type, name, key });
  }
  return (data) => validate(type, name, data);
}

function validate (type, name, data) {
  const key = buildKey(type, name);
  const compiled = ajv.getSchema(key);
  if (compiled) {
    const isValid = compiled(data);
    return { isValid, error: ajv.errorsText(compiled.errors) };
  }

  return { isValid: true };
}

function find (type = null, name = null) {
  if (type && name) {
    const item = ajv.getSchema(buildKey(type, name));
    if (item) {
      return { type, name, schema: item.schema };
    }
  } else {
    return registeredKeys
      .filter(item => !type || item.type === type)
      .map(key => ({ key: key.key, type: key.type, name: key.name, schema: ajv.getSchema(key.key).schema }));
  }
}

module.exports = {
  register,
  validate,
  find
};
<|endoftext|>


<commit_before>var React          = require('react'),
    ReactDOM       = require('react-dom'),
    ReactDOMServer = require('react-dom/server'),
    scroll         = require('scroll'),
    Beacon         = require('./Beacon'),
    Tooltip        = require('./Tooltip');

var tooltipper = {
    browser: undefined, // useful for cross-browser styling
    initialized: false,
    listeners: {},
    mixin: null,
    options: {
        closeOnAnyClick: false,
        completeCallback: undefined,
        doDebounceResize: true,
        overridePosition: false,
        scrollToTooltip: true,
        scrollOffset: 20,
        showOverlay: true,
        tooltipOffset: 15,
        type: 'single'
    },
    tooltipData: {},

    /**
     * Returns the current browser
     * @returns {String}
     */
    getBrowser: function () {
        // Return cached result if available, else get result then cache it.
        if (this.browser) {
            return this.browser;
        }

        var isOpera = Boolean(window.opera) || navigator.userAgent.indexOf(' OPR/') >= 0;
        // Opera 8.0+ (UA detection to detect Blink/v8-powered Opera)
        var isFirefox = typeof InstallTrigger !== 'undefined';// Firefox 1.0+
        var isSafari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0;
        // At least Safari 3+: "[object HTMLElementConstructor]"
        var isChrome = Boolean(window.chrome) && !isOpera;// Chrome 1+
        var isIE = /*@cc_on!@*/false || Boolean(document.documentMode); // At least IE6

        return (this.browser =
            isOpera ? 'opera' :
                isFirefox ? 'firefox' :
                    isSafari ? 'safari' :
                        isChrome ? 'chrome' :
                            isIE ? 'ie' :
                                '');
    },

    /**
     * Get an element actual dimensions with margin
     * @param {String|DOMElement} el - Element node or selector
     * @returns {{height: number, width: number}}
     */
    getElementDimensions: function (el) {
        // Get the DOM Node if you pass in a string
        el = (typeof el === 'string') ? document.querySelector(el) : el;

        var styles = window.getComputedStyle(el),
            height = el.clientHeight + parseInt(styles.marginTop, 10) + parseInt(styles.marginBottom, 10),
            width  = el.clientWidth + parseInt(styles.marginLeft, 10) + parseInt(styles.marginRight, 10);

        return {
            height: height,
            width: width
        };
    },

    /**
     * Get the scrollTop position
     * @returns {number}
     */
    getScrollTop: function () {
        var state     = this.mixin.state,
            tooltipData      = tooltipper.tooltipData,
            position  = tooltipper.options.overridePosition || tooltipData.position,
            target    = document.querySelector(tooltipData.selector),
            targetTop = target.getBoundingClientRect().top + document.body.scrollTop,
            scrollTop = 0;

        if (/^top/.test(position)) {
            scrollTop = Math.floor(state._tooltipperYPos - tooltipper.options.scrollOffset);
        }
        else if (/^bottom|^left|^right/.test(position)) {
            scrollTop = Math.floor(targetTop - tooltipper.options.scrollOffset);
        }

        return scrollTop;
    },

    /**
     * Keydown event listener
     * @this Mixin
     * @param {Event} e - Keyboard event
     */
    keyboardNavigation: function (e) {
        var intKey = (window.Event) ? e.which : e.keyCode;

        if (this.state._tooltipperShowTooltip && intKey === 27) { // escape
            tooltipper.toggleTooltip(false, true);
        }
    },

    /**
     * Beacon click event listener
     * @this Mixin
     * @param {Event} e - Keyboard event
     */
    onClickBeacon: function (e) {
        e.preventDefault();
        tooltipper.toggleTooltip(true);
    },

    /**
     * Tooltip click event listener
     * @this Mixin
     * @param {Event} e - Keyboard event
     */
    onClickTooltip: function (e) {
        e.preventDefault();
        e.stopPropagation();

        var type = e.currentTarget.getAttribute('data-type');
        tooltipper.toggleTooltip(type !== 'close', type === 'close');
    },

    /**
     * Position absolute elements next to its target based on
     * the tooltip position and window size
     * @this this
     * @this Mixin
     */
    calcPlacement: function () {
        var mixin     = this.mixin || this,
            state     = mixin.state,
            tooltipData      = tooltipper.tooltipData,
            component = document.querySelector((state._tooltipperShowTooltip ? '.tooltipper-tooltip' : '.tooltipper-beacon')),
            position,
            body,
            target,
            placement = {
                x: -1000,
                y: -1000
            };

        if (Object.keys(tooltipData).length && component) {
            position = tooltipData.position;
            body = document.body.getBoundingClientRect();
            target = document.querySelector(tooltipData.selector).getBoundingClientRect();
            component = tooltipper.getElementDimensions((state._tooltipperShowTooltip ? '.tooltipper-tooltip' : '.tooltipper-beacon'));

            // Change the tooltip position if the tooltip won't fit in the window
            if (/^left/.test(position) && target.left - (component.width + tooltipper.options.tooltipOffset) < 0) {
                position = 'top';
            }
            else if (/^right/.test(position) && target.left + target.width + (component.width + tooltipper.options.tooltipOffset) > body.width) {
                position = 'bottom';
            }

            // Calculate x position
            if (/^left/.test(position)) {
                placement.x = target.left - (state._tooltipperShowTooltip ? component.width + tooltipper.options.tooltipOffset : component.width / 2);
            }
            else if (/^right/.test(position)) {
                placement.x = target.left + target.width - (state._tooltipperShowTooltip ? -tooltipper.options.tooltipOffset : component.width / 2);
            }
            else {
                placement.x = target.left + target.width / 2 - component.width / 2;
            }

            // Calculate y position
            if (/^top/.test(position)) {
                placement.y = (target.top - body.top) - (state._tooltipperShowTooltip ? component.height + tooltipper.options.tooltipOffset : component.height / 2);
            }
            else if (/^bottom/.test(position)) {
                placement.y = (target.top - body.top) + target.height - (state._tooltipperShowTooltip ? -tooltipper.options.tooltipOffset : component.height / 2);
            }
            else {
                placement.y = (target.top - body.top) + target.height / 2 - component.height / 2 + (state._tooltipperShowTooltip ? tooltipper.options.tooltipOffset : 0);
            }

            if (/^bottom|^top/.test(position)) {
                if (/left/.test(position)) {
                    placement.x = target.left - (state._tooltipperShowTooltip ? tooltipper.options.tooltipOffset : component.width / 2);
                }
                else if (/right/.test(position)) {
                    placement.x = target.left + target.width - (state._tooltipperShowTooltip ? component.width - tooltipper.options.tooltipOffset : component.width / 2);
                }
            }

            mixin.setState({
                _tooltipperXPos: tooltipper.preventWindowOverflow(Math.ceil(placement.x), 'x', component.width, component.height),
                _tooltipperYPos: tooltipper.preventWindowOverflow(Math.ceil(placement.y), 'y', component.width, component.height),
                tooltipperOverridePosition: tooltipData.position !== position ? position : false
            });
        }
    },

    /**
     * Prevent tooltip to render outside the window
     * @param {Number} value - The axis position
     * @param {String} axis - The Axis X or Y
     * @param {Number} elWidth - The target element width
     * @param {Number} elHeight - The target element height
     * @returns {Number}
     */
    preventWindowOverflow: function (value, axis, elWidth, elHeight) {
        var winWidth  = window.innerWidth,
            docHeight = document.body.offsetHeight,
            newValue  = value;

        if (axis === 'x') {
            if (value + elWidth >= winWidth) {
                newValue = winWidth - elWidth - 15;
            }
            else if (value < 0) {
                newValue = 15;
            }
        }
        else if (axis === 'y') {
            if (value + elHeight >= docHeight) {
                newValue = docHeight - elHeight - 15;
            }
            else if (value < 0) {
                newValue = 15;
            }
        }

        return newValue;
    },

    /**
     * Toggle Tooltip's visibility and deactivate Tooltipper if necessary
     * @param {Boolean} show Render the tooltip directly (true) or just the beacon (false)
     * @param {boolean} shouldDeactivate Deactivate Tooltipper if true (unmount and remove listeners)
     */
    toggleTooltip: function (show, shouldDeactivate) {
        this.mixin.setState({
            _tooltipperShouldBeActive: !shouldDeactivate,
            _tooltipperShowTooltip: show,
            _tooltipperXPos: -1000,
            _tooltipperYPos: -1000,
            _tooltipperTooltipSeenAndClosed: shouldDeactivate
        });
    }
};

/**
 * @constructor
 */
var Mixin = {
    getInitialState: function () {
        return {
            _tooltipperShouldBeActive: false,
            _tooltipperShowTooltip: false,
            _tooltipperXPos: -1000,
            _tooltipperYPos: -1000,
            _tooltipperTooltipSeenAndClosed: false
        };
    },

    componentDidMount: function () {
        var state = this.state;

        this._target = document.createElement('div');
        this._target.className = 'tooltipper';
        document.body.appendChild(this._target);

        if (Object.keys(tooltipper.tooltipData).length) {
            this._tooltipperRenderLayer();
        }

        tooltipper.mixin = this;

        if (!tooltipper.options.doDebounceResize) {
            tooltipper.listeners.resize = tooltipper.calcPlacement.bind(this);
        } else {
            tooltipper.listeners.resize = (function() {
                var timeoutId;
                return function() {
                    clearTimeout(timeoutId);
                    timeoutId = setTimeout(function() {
                        timeoutId = null;
                        tooltipper.calcPlacement.call(tooltipper.mixin);
                    }, 200);
                }
            }());
        }
        window.addEventListener('resize', tooltipper.listeners.resize);

        tooltipper.listeners.keyboard = tooltipper.keyboardNavigation.bind(this);
        document.body.addEventListener('keydown', tooltipper.listeners.keyboard);
    },

    componentWillUnmount: function () {
        this._tooltipperUnrenderLayer();
        document.body.removeChild(this._target);

        window.removeEventListener('resize', tooltipper.listeners.resize);
        if (tooltipper.options.keyboardNavigation) {
            document.body.removeEventListener('keydown', tooltipper.listeners.keyboard);
        }
    },

    componentDidUpdate: function (prevProps, prevState) {
        var state = this.state,
            opt   = {
                newX: state._tooltipperXPos !== prevState._tooltipperXPos,
                newY: state._tooltipperYPos !== prevState._tooltipperYPos,
                hasTooltipData: Object.keys(tooltipper.tooltipData).length,
                toggleTooltip: true, // FIXME
                tooltipSeenAndClosed: state._tooltipperTooltipSeenAndClosed
            };

        if (opt.tooltipSeenAndClosed && state._tooltipperTooltipSeenAndClosed !== prevState._tooltipperTooltipSeenAndClosed) {
            if (typeof tooltipper.options.completeCallback === 'function') {
                tooltipper.options.completeCallback();
            }
            this._tooltipperUnrenderLayer();
        } else if (state._tooltipperShouldBeActive && (opt.toggleTooltip || opt.newX || opt.newY) && opt.hasTooltipData) {
            this._tooltipperRenderLayer();
        }
    },

    /**
     * Triggers (displays) a tooltip or its beacon
     * @param {boolean} [autorun]- Starts with the tooltip opened if true
     */
    tooltipperTrigger: function (autorun) {
        autorun = autorun || false;

        this.setState({
            _tooltipperShowTooltip: autorun,
            _tooltipperShouldBeActive: true,
            _tooltipperTooltipSeenAndClosed: false
        });
    },

    /**
     * Set the tooltip data to be used for the tooltip.
     *
     * @param {object} tooltipData A flat object specifying title, text, selector, and position.
     */
    tooltipperSetTooltipData: function(tooltipData) {
        // clone the data so that we don't accidentally mutate whatever was passed
        Object.keys(tooltipData).forEach(function(key) {
            tooltipper.tooltipData[key] = tooltipData[key];
        });
    },

    /**
     * Change the default options
     */
    tooltipperSetOptions: function (opts) {
        Object.keys(tooltipper.options).forEach(function (o) {
            if (opts[o] !== undefined) {
                tooltipper.options[o] = opts[o];
            }
        })
    },

    /**
     * Force the tooltipper to update its positioning. This should only be
     * necessary when an element has been removed from or added to the DOM and
     * the tooltip's position should be effected.
     */
    tooltipperForceCalcPlacement: function() {
        tooltipper.calcPlacement.call(this);
    },

    _tooltipperRenderLayer: function () {
        var component = this._tooltipperRenderTooltip();
        if (component) {
            if (!tooltipper.initialized) {
                tooltipper.initialized = true;
                ReactDOMServer.renderToString(component);
            }

            ReactDOM.render(component, this._target, function () {
                tooltipper.calcPlacement();
                if (tooltipper.options.scrollToTooltip) {
                    scroll.top(document.body, tooltipper.getScrollTop());
                }
            }.bind(this));
        }
    },

    _tooltipperUnrenderLayer: function () {
        ReactDOM.unmountComponentAtNode(this._target);
    },

    _tooltipperRenderTooltip: function () {
        var state       = this.state,
            component,
            tooltipData = !this.state._tooltipperTooltipSeenAndClosed && tooltipper.tooltipData,
            target      = tooltipData && tooltipData.selector ? document.querySelector(tooltipData.selector) : null,
            cssPosition = tooltipper.options.showOverlay ?
                (target ? target.style.position : null) :
                (tooltipper.options.closeOnAnyClick ? 'absolute' : 'fixed');

        if (target) {
            if (state._tooltipperShowTooltip) {
                component = React.createElement(Tooltip, {
                    animate: state._tooltipperXPos > -1,
                    browser: tooltipper.getBrowser(),
                    closeOnAnyClick: tooltipper.options.closeOnAnyClick,
                    cssPosition: cssPosition,
                    overridePosition: tooltipper.options.overridePosition,
                    showOverlay: tooltipper.options.showOverlay,
                    tooltipData: tooltipData,
                    xPos: state._tooltipperXPos,
                    yPos: state._tooltipperYPos,
                    onClick: tooltipper.onClickTooltip.bind(this)
                });
            }
            else {
                component = React.createElement(Beacon, {
                    cssPosition: cssPosition,
                    xPos: state._tooltipperXPos,
                    yPos: state._tooltipperYPos,
                    onClick: tooltipper.onClickBeacon.bind(this)
                });
            }
        }

        return component;
    }
};

module.exports = Mixin;
<commit_msg>Ensure that old tooltipData is cleared when new tooltip data is set (allows resetting with empty object)<commit_after>var React          = require('react'),
    ReactDOM       = require('react-dom'),
    ReactDOMServer = require('react-dom/server'),
    scroll         = require('scroll'),
    Beacon         = require('./Beacon'),
    Tooltip        = require('./Tooltip');

var tooltipper = {
    browser: undefined, // useful for cross-browser styling
    initialized: false,
    listeners: {},
    mixin: null,
    options: {
        closeOnAnyClick: false,
        completeCallback: undefined,
        doDebounceResize: true,
        overridePosition: false,
        scrollToTooltip: true,
        scrollOffset: 20,
        showOverlay: true,
        tooltipOffset: 15,
        type: 'single'
    },
    tooltipData: {},

    /**
     * Returns the current browser
     * @returns {String}
     */
    getBrowser: function () {
        // Return cached result if available, else get result then cache it.
        if (this.browser) {
            return this.browser;
        }

        var isOpera = Boolean(window.opera) || navigator.userAgent.indexOf(' OPR/') >= 0;
        // Opera 8.0+ (UA detection to detect Blink/v8-powered Opera)
        var isFirefox = typeof InstallTrigger !== 'undefined';// Firefox 1.0+
        var isSafari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0;
        // At least Safari 3+: "[object HTMLElementConstructor]"
        var isChrome = Boolean(window.chrome) && !isOpera;// Chrome 1+
        var isIE = /*@cc_on!@*/false || Boolean(document.documentMode); // At least IE6

        return (this.browser =
            isOpera ? 'opera' :
                isFirefox ? 'firefox' :
                    isSafari ? 'safari' :
                        isChrome ? 'chrome' :
                            isIE ? 'ie' :
                                '');
    },

    /**
     * Get an element actual dimensions with margin
     * @param {String|DOMElement} el - Element node or selector
     * @returns {{height: number, width: number}}
     */
    getElementDimensions: function (el) {
        // Get the DOM Node if you pass in a string
        el = (typeof el === 'string') ? document.querySelector(el) : el;

        var styles = window.getComputedStyle(el),
            height = el.clientHeight + parseInt(styles.marginTop, 10) + parseInt(styles.marginBottom, 10),
            width  = el.clientWidth + parseInt(styles.marginLeft, 10) + parseInt(styles.marginRight, 10);

        return {
            height: height,
            width: width
        };
    },

    /**
     * Get the scrollTop position
     * @returns {number}
     */
    getScrollTop: function () {
        var state     = this.mixin.state,
            tooltipData      = tooltipper.tooltipData,
            position  = tooltipper.options.overridePosition || tooltipData.position,
            target    = document.querySelector(tooltipData.selector),
            targetTop = target.getBoundingClientRect().top + document.body.scrollTop,
            scrollTop = 0;

        if (/^top/.test(position)) {
            scrollTop = Math.floor(state._tooltipperYPos - tooltipper.options.scrollOffset);
        }
        else if (/^bottom|^left|^right/.test(position)) {
            scrollTop = Math.floor(targetTop - tooltipper.options.scrollOffset);
        }

        return scrollTop;
    },

    /**
     * Keydown event listener
     * @this Mixin
     * @param {Event} e - Keyboard event
     */
    keyboardNavigation: function (e) {
        var intKey = (window.Event) ? e.which : e.keyCode;

        if (this.state._tooltipperShowTooltip && intKey === 27) { // escape
            tooltipper.toggleTooltip(false, true);
        }
    },

    /**
     * Beacon click event listener
     * @this Mixin
     * @param {Event} e - Keyboard event
     */
    onClickBeacon: function (e) {
        e.preventDefault();
        tooltipper.toggleTooltip(true);
    },

    /**
     * Tooltip click event listener
     * @this Mixin
     * @param {Event} e - Keyboard event
     */
    onClickTooltip: function (e) {
        e.preventDefault();
        e.stopPropagation();

        var type = e.currentTarget.getAttribute('data-type');
        tooltipper.toggleTooltip(type !== 'close', type === 'close');
    },

    /**
     * Position absolute elements next to its target based on
     * the tooltip position and window size
     * @this this
     * @this Mixin
     */
    calcPlacement: function () {
        var mixin     = this.mixin || this,
            state     = mixin.state,
            tooltipData      = tooltipper.tooltipData,
            component = document.querySelector((state._tooltipperShowTooltip ? '.tooltipper-tooltip' : '.tooltipper-beacon')),
            position,
            body,
            target,
            placement = {
                x: -1000,
                y: -1000
            };

        if (Object.keys(tooltipData).length && component) {
            position = tooltipData.position;
            body = document.body.getBoundingClientRect();
            target = document.querySelector(tooltipData.selector).getBoundingClientRect();
            component = tooltipper.getElementDimensions((state._tooltipperShowTooltip ? '.tooltipper-tooltip' : '.tooltipper-beacon'));

            // Change the tooltip position if the tooltip won't fit in the window
            if (/^left/.test(position) && target.left - (component.width + tooltipper.options.tooltipOffset) < 0) {
                position = 'top';
            }
            else if (/^right/.test(position) && target.left + target.width + (component.width + tooltipper.options.tooltipOffset) > body.width) {
                position = 'bottom';
            }

            // Calculate x position
            if (/^left/.test(position)) {
                placement.x = target.left - (state._tooltipperShowTooltip ? component.width + tooltipper.options.tooltipOffset : component.width / 2);
            }
            else if (/^right/.test(position)) {
                placement.x = target.left + target.width - (state._tooltipperShowTooltip ? -tooltipper.options.tooltipOffset : component.width / 2);
            }
            else {
                placement.x = target.left + target.width / 2 - component.width / 2;
            }

            // Calculate y position
            if (/^top/.test(position)) {
                placement.y = (target.top - body.top) - (state._tooltipperShowTooltip ? component.height + tooltipper.options.tooltipOffset : component.height / 2);
            }
            else if (/^bottom/.test(position)) {
                placement.y = (target.top - body.top) + target.height - (state._tooltipperShowTooltip ? -tooltipper.options.tooltipOffset : component.height / 2);
            }
            else {
                placement.y = (target.top - body.top) + target.height / 2 - component.height / 2 + (state._tooltipperShowTooltip ? tooltipper.options.tooltipOffset : 0);
            }

            if (/^bottom|^top/.test(position)) {
                if (/left/.test(position)) {
                    placement.x = target.left - (state._tooltipperShowTooltip ? tooltipper.options.tooltipOffset : component.width / 2);
                }
                else if (/right/.test(position)) {
                    placement.x = target.left + target.width - (state._tooltipperShowTooltip ? component.width - tooltipper.options.tooltipOffset : component.width / 2);
                }
            }

            mixin.setState({
                _tooltipperXPos: tooltipper.preventWindowOverflow(Math.ceil(placement.x), 'x', component.width, component.height),
                _tooltipperYPos: tooltipper.preventWindowOverflow(Math.ceil(placement.y), 'y', component.width, component.height),
                tooltipperOverridePosition: tooltipData.position !== position ? position : false
            });
        }
    },

    /**
     * Prevent tooltip to render outside the window
     * @param {Number} value - The axis position
     * @param {String} axis - The Axis X or Y
     * @param {Number} elWidth - The target element width
     * @param {Number} elHeight - The target element height
     * @returns {Number}
     */
    preventWindowOverflow: function (value, axis, elWidth, elHeight) {
        var winWidth  = window.innerWidth,
            docHeight = document.body.offsetHeight,
            newValue  = value;

        if (axis === 'x') {
            if (value + elWidth >= winWidth) {
                newValue = winWidth - elWidth - 15;
            }
            else if (value < 0) {
                newValue = 15;
            }
        }
        else if (axis === 'y') {
            if (value + elHeight >= docHeight) {
                newValue = docHeight - elHeight - 15;
            }
            else if (value < 0) {
                newValue = 15;
            }
        }

        return newValue;
    },

    /**
     * Toggle Tooltip's visibility and deactivate Tooltipper if necessary
     * @param {Boolean} show Render the tooltip directly (true) or just the beacon (false)
     * @param {boolean} shouldDeactivate Deactivate Tooltipper if true (unmount and remove listeners)
     */
    toggleTooltip: function (show, shouldDeactivate) {
        this.mixin.setState({
            _tooltipperShouldBeActive: !shouldDeactivate,
            _tooltipperShowTooltip: show,
            _tooltipperXPos: -1000,
            _tooltipperYPos: -1000,
            _tooltipperTooltipSeenAndClosed: shouldDeactivate
        });
    }
};

/**
 * @constructor
 */
var Mixin = {
    getInitialState: function () {
        return {
            _tooltipperShouldBeActive: false,
            _tooltipperShowTooltip: false,
            _tooltipperXPos: -1000,
            _tooltipperYPos: -1000,
            _tooltipperTooltipSeenAndClosed: false
        };
    },

    componentDidMount: function () {
        var state = this.state;

        this._target = document.createElement('div');
        this._target.className = 'tooltipper';
        document.body.appendChild(this._target);

        if (Object.keys(tooltipper.tooltipData).length) {
            this._tooltipperRenderLayer();
        }

        tooltipper.mixin = this;

        if (!tooltipper.options.doDebounceResize) {
            tooltipper.listeners.resize = tooltipper.calcPlacement.bind(this);
        } else {
            tooltipper.listeners.resize = (function() {
                var timeoutId;
                return function() {
                    clearTimeout(timeoutId);
                    timeoutId = setTimeout(function() {
                        timeoutId = null;
                        tooltipper.calcPlacement.call(tooltipper.mixin);
                    }, 200);
                }
            }());
        }
        window.addEventListener('resize', tooltipper.listeners.resize);

        tooltipper.listeners.keyboard = tooltipper.keyboardNavigation.bind(this);
        document.body.addEventListener('keydown', tooltipper.listeners.keyboard);
    },

    componentWillUnmount: function () {
        this._tooltipperUnrenderLayer();
        document.body.removeChild(this._target);

        window.removeEventListener('resize', tooltipper.listeners.resize);
        if (tooltipper.options.keyboardNavigation) {
            document.body.removeEventListener('keydown', tooltipper.listeners.keyboard);
        }
    },

    componentDidUpdate: function (prevProps, prevState) {
        var state = this.state,
            opt   = {
                newX: state._tooltipperXPos !== prevState._tooltipperXPos,
                newY: state._tooltipperYPos !== prevState._tooltipperYPos,
                hasTooltipData: Object.keys(tooltipper.tooltipData).length,
                toggleTooltip: true, // FIXME
                tooltipSeenAndClosed: state._tooltipperTooltipSeenAndClosed
            };

        if (opt.tooltipSeenAndClosed && state._tooltipperTooltipSeenAndClosed !== prevState._tooltipperTooltipSeenAndClosed) {
            if (typeof tooltipper.options.completeCallback === 'function') {
                tooltipper.options.completeCallback();
            }
            this._tooltipperUnrenderLayer();
        } else if (state._tooltipperShouldBeActive && (opt.toggleTooltip || opt.newX || opt.newY) && opt.hasTooltipData) {
            this._tooltipperRenderLayer();
        }
    },

    /**
     * Triggers (displays) a tooltip or its beacon
     * @param {boolean} [autorun]- Starts with the tooltip opened if true
     */
    tooltipperTrigger: function (autorun) {
        autorun = autorun || false;

        this.setState({
            _tooltipperShowTooltip: autorun,
            _tooltipperShouldBeActive: true,
            _tooltipperTooltipSeenAndClosed: false
        });
    },

    /**
     * Set the tooltip data to be used for the tooltip.
     *
     * @param {object} tooltipData A flat object specifying title, text, selector, and position.
     */
    tooltipperSetTooltipData: function(tooltipData) {
        tooltipper.tooltipData = {}; // reset first

        // clone the data (shallowly) so that we don't accidentally mutate whatever was passed
        Object.keys(tooltipData).forEach(function(key) {
            tooltipper.tooltipData[key] = tooltipData[key];
        });
    },

    /**
     * Change the default options
     */
    tooltipperSetOptions: function (opts) {
        Object.keys(tooltipper.options).forEach(function (o) {
            if (opts[o] !== undefined) {
                tooltipper.options[o] = opts[o];
            }
        })
    },

    /**
     * Force the tooltipper to update its positioning. This should only be
     * necessary when an element has been removed from or added to the DOM and
     * the tooltip's position should be effected.
     */
    tooltipperForceCalcPlacement: function() {
        tooltipper.calcPlacement.call(this);
    },

    _tooltipperRenderLayer: function () {
        var component = this._tooltipperRenderTooltip();
        if (component) {
            if (!tooltipper.initialized) {
                tooltipper.initialized = true;
                ReactDOMServer.renderToString(component);
            }

            ReactDOM.render(component, this._target, function () {
                tooltipper.calcPlacement();
                if (tooltipper.options.scrollToTooltip) {
                    scroll.top(document.body, tooltipper.getScrollTop());
                }
            }.bind(this));
        }
    },

    _tooltipperUnrenderLayer: function () {
        ReactDOM.unmountComponentAtNode(this._target);
    },

    _tooltipperRenderTooltip: function () {
        var state       = this.state,
            component,
            tooltipData = !this.state._tooltipperTooltipSeenAndClosed && tooltipper.tooltipData,
            target      = tooltipData && tooltipData.selector ? document.querySelector(tooltipData.selector) : null,
            cssPosition = tooltipper.options.showOverlay ?
                (target ? target.style.position : null) :
                (tooltipper.options.closeOnAnyClick ? 'absolute' : 'fixed');

        if (target) {
            if (state._tooltipperShowTooltip) {
                component = React.createElement(Tooltip, {
                    animate: state._tooltipperXPos > -1,
                    browser: tooltipper.getBrowser(),
                    closeOnAnyClick: tooltipper.options.closeOnAnyClick,
                    cssPosition: cssPosition,
                    overridePosition: tooltipper.options.overridePosition,
                    showOverlay: tooltipper.options.showOverlay,
                    tooltipData: tooltipData,
                    xPos: state._tooltipperXPos,
                    yPos: state._tooltipperYPos,
                    onClick: tooltipper.onClickTooltip.bind(this)
                });
            }
            else {
                component = React.createElement(Beacon, {
                    cssPosition: cssPosition,
                    xPos: state._tooltipperXPos,
                    yPos: state._tooltipperYPos,
                    onClick: tooltipper.onClickBeacon.bind(this)
                });
            }
        }

        return component;
    }
};

module.exports = Mixin;
<|endoftext|>


<commit_before>function Slime(attributes = {}) {
  this.x = attributes.x || 275;
  this.y = attributes.y || 600;
  this.radius = 80;
  this.speed = attributes.speed || 3;
  this.color = attributes.color || 'red';
  this.context = attributes.context;
  this.keysDown = attributes.keysDown;
  this.canvas = attributes.canvas;
  this.ball = attributes.ball;
  this.player = attributes.player;
  this.jumping = false;
  this.ai = false;
}

Slime.prototype.render = function() {
  this.drawSlime();
  this.drawEyes();
  this.drawPupils();
};

Slime.prototype.drawSlime = function () {
  this.context.beginPath();
  this.context.arc(this.x, this.y, this.radius, Math.PI, false);
  this.context.fillStyle = this.color;
  this.context.fill();
  this.context.closePath();
};

Slime.prototype.drawEyes = function() {
  this.context.beginPath();
  this.shouldDrawPlayer1Eye();
  this.shouldDrawPlayer2Eye();
  this.fillEyes();
};

Slime.prototype.shouldDrawPlayer1Eye = function() {
  if (this.player === "player 1") {
    this.drawPlayer1Eye();
  }
};

Slime.prototype.shouldDrawPlayer2Eye = function() {
  if (this.player === "player 2") {
    this.drawPlayer2Eye();
  }
};

Slime.prototype.drawPlayer1Eye = function() {
  this.context.arc(this.x + 43, this.y -54, 10, Math.PI * 2, false);
};

Slime.prototype.drawPlayer2Eye = function() {
  this.context.arc(this.x - 43, this.y -54, 10, Math.PI * 2, false);
};

Slime.prototype.fillEyes = function() {
  this.context.fillStyle = "white";
  this.context.fill();
  this.context.closePath();
};

Slime.prototype.drawPupils = function() {
  this.context.beginPath();
  this.shouldDrawPlayer1Pupil();
  this.shouldDrawPlayer2Pupil();
  this.fillPupils();
};

Slime.prototype.shouldDrawPlayer1Pupil = function() {
  if (this.player === "player 1") {
    this.drawPlayer1Pupil();
  }
};

Slime.prototype.shouldDrawPlayer2Pupil = function() {
  if (this.player === "player 2") {
    this.drawPlayer2Pupil();
  }
};

Slime.prototype.drawPlayer1Pupil = function() {
  this.context.arc((this.x + 40 + (this.ball.x/120)), this.y - 53.5 - (this.ball.y / 175), 4, Math.PI * 2, false);
};

Slime.prototype.drawPlayer2Pupil = function() {
  this.context.arc((this.x - 47 + (this.ball.x/120)), this.y - 53.5 - (this.ball.y / 175), 4, Math.PI * 2, false);
};

Slime.prototype.fillPupils = function() {
  this.context.fillStyle = "black";
  this.context.fill();
  this.context.closePath();
};

Slime.prototype.move = function(x, y) {
  this.x += x;
  this.y += y;
};

Slime.prototype.gravity = function() {
  if (this.isJumping()) { this.y += 7.5; }
};

Slime.prototype.isJumping = function() {
  return (this.y < 600);
};

Slime.prototype.updatePosition = function(moveLeft, moveRight, jump) {
  for (var key in this.keysDown) {
    var value = Number(key);
    this.ifMoveRight(value, moveRight);
    this.ifMoveLeft(value, moveLeft);
    this.ifFinishJump(value, jump);
    this.ifJumping(value, jump);
    this.ifStayingStill();
  }
  this.ifAI(value, jump);
  return this;
};

Slime.prototype.ifAI = function(value, jump) {
  if (this.ai == true && this.ball.x > this.canvas.width/2){
    var x_difference = (this.x - this.ball.x)
    if (this.ball.x == 775){
      x_difference = .5
    }else if (x_difference == 0){
      x_difference = 5
    }else if (-80 > x_difference > .1){
      x_difference = 15
    } else if (.1 < x_difference < 80) {
      x_difference = -15
    }
    this.move(x_difference, 0);
    var y_difference = ((this.y - this.radius - 60) < this.ball.y && x_difference < 80)
    if (y_difference == true){
      value = jump
      this.ifFinishJump(value, jump);
      this.ifJumping(value, jump);
    }
    if(this.x < (this.canvas.width / 2) + this.radius){
      this.x = (this.canvas.width / 2) + this.radius;
    }else if (this.x + this.radius > this.canvas.width) {
      this.x = this.canvas.width - this.radius
    }
  }
}

Slime.prototype.ifMoveRight = function(value, moveRight) {
  if (value === moveRight && this.isNotTouchingRightWall() && this.isNotTouchingLeftSideOfNet()) {
    this.moveToRight();
  }
};

Slime.prototype.ifMoveLeft = function(value, moveLeft){
  if (value === moveLeft && this.isNotTouchingLeftWall() && this.isNotTouchingRightSideOfNet()) {
    this.moveToLeft();
  }
};

Slime.prototype.ifFinishJump = function(value, jump) {
  if (value === jump && this.isOnTheGround() && !this.jumping) {
    this.finishJump();
  }
};

Slime.prototype.ifJumping = function(value, jump) {
  if (value === jump && this.jumping) {
    this.jump();
  }
};

Slime.prototype.isNotTouchingRightWall = function() {
  return this.x < this.canvas.width - this.radius;
};

Slime.prototype.isNotTouchingLeftSideOfNet = function() {
  return this.x !== (this.canvas.width / 2 - this.radius - 5);
};

Slime.prototype.isNotTouchingLeftWall = function() {
  return this.x > (0 + this.radius);
};

Slime.prototype.isNotTouchingRightSideOfNet = function() {
  return this.x !== (this.canvas.width / 2 + this.radius + 5);
};

Slime.prototype.isOnTheGround = function() {
  return this.y === 600;
};

Slime.prototype.moveToRight = function() {
  return this.move(10, 0);
};

Slime.prototype.moveToLeft = function() {
  return this.move(-10, 0);
};

Slime.prototype.finishJump = function() {
  this.jump();
  this.jumping = true;
};

Slime.prototype.ifStayingStill = function() {
  this.move(0, 0);
};

Slime.prototype.jump = function() {
  if (this.y >= 520) { this.move(0, -15); }
  if (this.y <= 520) { this.jumping = false; }
};

Slime.prototype.resetPosition = function() {
  if (this.player === 'player 1') { this.x = 275; }
  if (this.player === 'player 2') { this.x = 775; }
  this.y = 600;
};

module.exports = Slime;
<commit_msg>jshint clean<commit_after>function Slime(attributes = {}) {
  this.x = attributes.x || 275;
  this.y = attributes.y || 600;
  this.radius = 80;
  this.speed = attributes.speed || 3;
  this.color = attributes.color || 'red';
  this.context = attributes.context;
  this.keysDown = attributes.keysDown;
  this.canvas = attributes.canvas;
  this.ball = attributes.ball;
  this.player = attributes.player;
  this.jumping = false;
  this.ai = false;
}

Slime.prototype.render = function() {
  this.drawSlime();
  this.drawEyes();
  this.drawPupils();
};

Slime.prototype.drawSlime = function () {
  this.context.beginPath();
  this.context.arc(this.x, this.y, this.radius, Math.PI, false);
  this.context.fillStyle = this.color;
  this.context.fill();
  this.context.closePath();
};

Slime.prototype.drawEyes = function() {
  this.context.beginPath();
  this.shouldDrawPlayer1Eye();
  this.shouldDrawPlayer2Eye();
  this.fillEyes();
};

Slime.prototype.shouldDrawPlayer1Eye = function() {
  if (this.player === "player 1") {
    this.drawPlayer1Eye();
  }
};

Slime.prototype.shouldDrawPlayer2Eye = function() {
  if (this.player === "player 2") {
    this.drawPlayer2Eye();
  }
};

Slime.prototype.drawPlayer1Eye = function() {
  this.context.arc(this.x + 43, this.y -54, 10, Math.PI * 2, false);
};

Slime.prototype.drawPlayer2Eye = function() {
  this.context.arc(this.x - 43, this.y -54, 10, Math.PI * 2, false);
};

Slime.prototype.fillEyes = function() {
  this.context.fillStyle = "white";
  this.context.fill();
  this.context.closePath();
};

Slime.prototype.drawPupils = function() {
  this.context.beginPath();
  this.shouldDrawPlayer1Pupil();
  this.shouldDrawPlayer2Pupil();
  this.fillPupils();
};

Slime.prototype.shouldDrawPlayer1Pupil = function() {
  if (this.player === "player 1") {
    this.drawPlayer1Pupil();
  }
};

Slime.prototype.shouldDrawPlayer2Pupil = function() {
  if (this.player === "player 2") {
    this.drawPlayer2Pupil();
  }
};

Slime.prototype.drawPlayer1Pupil = function() {
  this.context.arc((this.x + 40 + (this.ball.x/120)), this.y - 53.5 - (this.ball.y / 175), 4, Math.PI * 2, false);
};

Slime.prototype.drawPlayer2Pupil = function() {
  this.context.arc((this.x - 47 + (this.ball.x/120)), this.y - 53.5 - (this.ball.y / 175), 4, Math.PI * 2, false);
};

Slime.prototype.fillPupils = function() {
  this.context.fillStyle = "black";
  this.context.fill();
  this.context.closePath();
};

Slime.prototype.move = function(x, y) {
  this.x += x;
  this.y += y;
};

Slime.prototype.gravity = function() {
  if (this.isJumping()) { this.y += 7.5; }
};

Slime.prototype.isJumping = function() {
  return (this.y < 600);
};

Slime.prototype.updatePosition = function(moveLeft, moveRight, jump) {
  for (var key in this.keysDown) {
    var value = Number(key);
    this.ifMoveRight(value, moveRight);
    this.ifMoveLeft(value, moveLeft);
    this.ifFinishJump(value, jump);
    this.ifJumping(value, jump);
    this.ifStayingStill();
  }
  this.ifAI(jump);
  return this;
};

Slime.prototype.ifAI = function(jump) {
  if (this.ai === true && this.ball.x > this.canvas.width/2){
    var x_difference = (this.x - this.ball.x);
    if (this.ball.x === 775){
      x_difference = 0.5;
    }else if (x_difference === 0){
      x_difference = 5;
    }else if (-80 > x_difference > 0.1){
      x_difference = 15;
    } else if (0.1 < x_difference < 80) {
      x_difference = -15;
    }
    this.move(x_difference, 0);
    var y_difference = ((this.y - this.radius - 60) < this.ball.y && x_difference < 80);
    if (y_difference === true){
    var value = jump;
      this.ifFinishJump(value, jump);
      this.ifJumping(value, jump);
    }
    if(this.x < (this.canvas.width / 2) + this.radius){
      this.x = (this.canvas.width / 2) + this.radius;
    }else if (this.x + this.radius > this.canvas.width) {
      this.x = this.canvas.width - this.radius;
    }
  }
};

Slime.prototype.ifMoveRight = function(value, moveRight) {
  if (value === moveRight && this.isNotTouchingRightWall() && this.isNotTouchingLeftSideOfNet()) {
    this.moveToRight();
  }
};

Slime.prototype.ifMoveLeft = function(value, moveLeft){
  if (value === moveLeft && this.isNotTouchingLeftWall() && this.isNotTouchingRightSideOfNet()) {
    this.moveToLeft();
  }
};

Slime.prototype.ifFinishJump = function(value, jump) {
  if (value === jump && this.isOnTheGround() && !this.jumping) {
    this.finishJump();
  }
};

Slime.prototype.ifJumping = function(value, jump) {
  if (value === jump && this.jumping) {
    this.jump();
  }
};

Slime.prototype.isNotTouchingRightWall = function() {
  return this.x < this.canvas.width - this.radius;
};

Slime.prototype.isNotTouchingLeftSideOfNet = function() {
  return this.x !== (this.canvas.width / 2 - this.radius - 5);
};

Slime.prototype.isNotTouchingLeftWall = function() {
  return this.x > (0 + this.radius);
};

Slime.prototype.isNotTouchingRightSideOfNet = function() {
  return this.x !== (this.canvas.width / 2 + this.radius + 5);
};

Slime.prototype.isOnTheGround = function() {
  return this.y === 600;
};

Slime.prototype.moveToRight = function() {
  return this.move(10, 0);
};

Slime.prototype.moveToLeft = function() {
  return this.move(-10, 0);
};

Slime.prototype.finishJump = function() {
  this.jump();
  this.jumping = true;
};

Slime.prototype.ifStayingStill = function() {
  this.move(0, 0);
};

Slime.prototype.jump = function() {
  if (this.y >= 520) { this.move(0, -15); }
  if (this.y <= 520) { this.jumping = false; }
};

Slime.prototype.resetPosition = function() {
  if (this.player === 'player 1') { this.x = 275; }
  if (this.player === 'player 2') { this.x = 775; }
  this.y = 600;
};

module.exports = Slime;
<|endoftext|>


<commit_before>/*
 * Licensed to Cloudkick, Inc ('Cloudkick') under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * Cloudkick licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var config = require('util/config');
var crypto = require('crypto');
var misc = require('util/misc');

var signatureHeader = 'x-cast-signature';
var nonceHeader = 'x-cast-nonce';
var signatureEncoding = 'base64';

function hmacGenreate(input)
{
  var conf = config.get();
  var h = crypto.createHmac(conf['hmac_algorithm'], conf['secret']);

  input.forEach(function(item) {
    h.update(item);
  });

  return h.digest(signatureEncoding);
}

/* To prevent timing attacks, you must not just do a plain
 * string comparision of an HMAC.  The following function is constant time,
 * if the input HMAC is of the correct length, and assuming v8
 * doesn't optimize it all out.
 */
function hmacValidate(a, b)
{
  if (a.length !== b.length) {
    return false;
  }

  var rv = 0;

  for (var i = 0; i < a.length; i++) {
    rv = rv | (a[i] ^ b[i]);
  }

  if (rv === 0) {
    return true;
  }

  return false;
}

/**
 * Signs a request with the default HMAC algorithm and secret.
 *
 * @param {String} method  HTTP Method to sign.
 * @param {String} url  HTTP url to sign.
 * @param {Object} headers  Headers to set, modified by this function.
 */
exports.createHttpHmac = function(method, url, headers)
{
  var inputs = [method, url];

  if (!headers[nonceHeader]) {
    headers[nonceHeader] = misc.randstr(32);
  }

  inputs.push(headers[nonceHeader]);

  headers[signatureHeader] = hmacGenreate(inputs);
};

exports.validateHttpHmac = function(method, url, headers)
{
  var inputs = [method, url];

  if (!headers[nonceHeader]) {
    headers[nonceHeader] = misc.randstr(32);
  }

  if (!headers[signatureHeader]) {
    headers[signatureHeader] = '';
  }

  inputs.push(headers[nonceHeader]);

  var expected = hmacGenreate(inputs);

  return hmacValidate(expected, headers[signatureHeader]);
};

<commit_msg>rename hmacGenreate -> generateHmac<commit_after>/*
 * Licensed to Cloudkick, Inc ('Cloudkick') under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * Cloudkick licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var config = require('util/config');
var crypto = require('crypto');
var misc = require('util/misc');

var signatureHeader = 'x-cast-signature';
var nonceHeader = 'x-cast-nonce';
var signatureEncoding = 'base64';

function generateHmac(input)
{
  var conf = config.get();
  var h = crypto.createHmac(conf['hmac_algorithm'], conf['secret']);

  input.forEach(function(item) {
    h.update(item);
  });

  return h.digest(signatureEncoding);
}

/* To prevent timing attacks, you must not just do a plain
 * string comparision of an HMAC.  The following function is constant time,
 * if the input HMAC is of the correct length, and assuming v8
 * doesn't optimize it all out.
 */
function hmacValidate(a, b)
{
  if (a.length !== b.length) {
    return false;
  }

  var rv = 0;

  for (var i = 0; i < a.length; i++) {
    rv = rv | (a[i] ^ b[i]);
  }

  if (rv === 0) {
    return true;
  }

  return false;
}

/**
 * Signs a request with the default HMAC algorithm and secret.
 *
 * @param {String} method  HTTP Method to sign.
 * @param {String} url  HTTP url to sign.
 * @param {Object} headers  Headers to set, modified by this function.
 */
exports.createHttpHmac = function(method, url, headers)
{
  var inputs = [method, url];

  if (!headers[nonceHeader]) {
    headers[nonceHeader] = misc.randstr(32);
  }

  inputs.push(headers[nonceHeader]);

  headers[signatureHeader] = generateHmac(inputs);
};

exports.validateHttpHmac = function(method, url, headers)
{
  var inputs = [method, url];

  if (!headers[nonceHeader]) {
    headers[nonceHeader] = misc.randstr(32);
  }

  if (!headers[signatureHeader]) {
    headers[signatureHeader] = '';
  }

  inputs.push(headers[nonceHeader]);

  var expected = generateHmac(inputs);

  return hmacValidate(expected, headers[signatureHeader]);
};

<|endoftext|>


<commit_before>/* Модуль сжатия js-скриптов и css*/

/* функция сжимает js-скрипты
 * и сохраняет их с именем .min.js
 */

var fs = require('fs');

exports.jsScripts=function jsScripts(){
    'use strict';

    /* подключаем модуль uglify-js
     * если его нет - дальнейшая
     * работа функции не имеет смысла
     */
    try{
        var jsp = require("uglify-js").parser;
        var pro = require("uglify-js").uglify;
    }catch(error){
        console.log('can\'n load uglify-js\n'                  +
            'to use js-minification you need to install uglify-js\n'    +
                'npm install uglify-js\n'                               +
                'https://github.com/mishoo/UglifyJS');
        return false;
    }
    /* Константы */
    var CLIENT_JS='client.js';
    var CLOUDFUNC_JS='lib/cloudfunc.js';
    var CLIENT_KEYBINDING_JS='lib/client/keyBinding.js';

    var dataReaded_f=function(pFileName, pData){
            console.log('file ' + pFileName + ' readed');

            /*********************************/
            /* сжимаем код через uglify-js */
            var uglify_js=function(pDdata){
                var orig_code = pDdata.toString();
                var ast = jsp.parse(orig_code); // parse code and get the initial AST
                ast = pro.ast_mangle(ast); // get a new AST with mangled names
                ast = pro.ast_squeeze(ast); // get an AST with compression optimizations
                var result_code = pro.gen_code(ast); // compressed code here
                return result_code;
            };
            /*********************************/
            var final_code=uglify_js(pData);

            var minFileName=pFileName.replace('.js','.min.js');
            /* если мы сжимаем client.js -
             * меняем строку cloudfunc.js на
             * cloudfunc.min.js и выводим сообщение
             *
             * меняем строку keyBinding.js на
             * keyBinding.min.js
             * если другой файл - ничего не деалем
             */
            if(pFileName===CLIENT_JS)
                console.log('file name of ' +
                    CLOUDFUNC_JS            +
                    ' in '                  +
                    CLIENT_JS               +
                    ' changed. size:',
                    (final_code=final_code
                        .replace('cloudfunc.js','cloudfunc.min.js')
                            .replace('keyBinding.js','keyBinding.min.js')).length);

            /* записываем сжатый js-скрипт*/
            fs.writeFile(minFileName, final_code, fileWrited(minFileName));
        };
     console.log('reading file ' + CLIENT_JS+'...');
    fs.readFile(CLIENT_JS,fileReaded(CLIENT_JS,dataReaded_f));

    console.log('reading file ' + CLOUDFUNC_JS+'...');
    fs.readFile(CLOUDFUNC_JS,fileReaded(CLOUDFUNC_JS,dataReaded_f));

    console.log('reading file ' + CLIENT_KEYBINDING_JS+'...');
    fs.readFile(CLIENT_KEYBINDING_JS, fileReaded(CLIENT_KEYBINDING_JS,dataReaded_f));

    return true;
};

/* функция сжимает css-стили
 * и сохраняет их с именем .min.css
 * @pImgConvertToBase64_b - булевый признак,
 * который отвечает за то, что быконвертировать
 * картинки в base64 и поместить в выходной css файл
 */
exports.cssStyles=function cssStyles(pImgConvertToBase64_b){
    'use strict';

     /* connecting cleanCSS,
      * if we can't find it -
      * return false
      */
     var cleanCSS;
     try{
        cleanCSS = require('clean-css');
    }catch(error){
        console.log('can\'n load clean-css \n'                          +
            'to use css-minification you need to install clean-css \n'  +
                'npm install clean-css\n'                               +
                'https://github.com/GoalSmashers/clean-css');
        return false;
    }

    /* Константы */
    var STYLE_CSS='style.css';
    var RESET_CSS='reset.css';

    var lAllStyle='';
    var lResetCssDone=false;
    var lStyleCssDone=false;
    var dataReaded_f=function(pFileName, pData){
        console.log('file ' + pFileName + ' readed');
        /*********************************/
        /* сжимаем код через clean-css */
        var clean_css=function(pData){
            /* Сохраняем весь стиль в одну переменную*/
            return cleanCSS.process(pData);
        };
        /*********************************/
        var final_code=clean_css(pData);

        lAllStyle+=final_code;

        var minFileName=pFileName.replace('.css','.min.css');

        if(pFileName===STYLE_CSS)lStyleCssDone=true;
        if(pFileName===RESET_CSS)lResetCssDone=true;
        /* if all files writed we
         * save all minimized css
         * to one file all.min.css
         */
        /* если включена конвертация картинок в base64
         * вызываем её
         */
        if(lStyleCssDone && lResetCssDone)
            if(pImgConvertToBase64_b)
                base64_images(lAllStyle);
        else
            fs.writeFile('all.min.css', lAllStyle, fileWrited('all.min.css'));

         /* записываем сжатый css файл*/
        else fs.writeFile(minFileName, final_code, fileWrited(minFileName));
    };

    console.log('reading file ' + STYLE_CSS+'...');
    fs.readFile(STYLE_CSS,fileReaded(STYLE_CSS,dataReaded_f));

    console.log('reading file ' + RESET_CSS+'...');
    fs.readFile(RESET_CSS,fileReaded(RESET_CSS,dataReaded_f));

    return true;
};

/* функция сжимает css-стили
 * и сохраняет их с именем .min.css
 */
exports.html=function(){
    'use strict';

     /* connecting cleanCSS,
      * if we can't find it -
      * return false
      */
     var htmlMinifier;
     try{
        htmlMinifier = require('html-minifier');
    }catch(error){
        console.log('can\'n load html-minifier \n'                 +
            'to use html-minification you need to install html-minifier\n'  +
                'npm install html-minifier\n'                               +
                'https://github.com/kangax/html-minifier');
        return false;
    }

    /* Константы */
    var INDEX_HTML='index.html';

    var dataReaded_f=function(pFileName, pData){
        console.log('file ' + pFileName + ' readed');
        /*********************************/
        /* сжимаем код через clean-css */
        var html_minify=function(pData){
            /* Сохраняем весь стиль в одну переменную*/

            var lOptions={
                removeComments:                 true,
                removeCommentsFromCDATA:        true,
                removeCDATASectionsFromCDATA:   true,
                collapseWhitespace:             true,
                collapseBooleanAttributes:      true,
                removeAttributeQuotes:          true,
                removeRedundantAttributes:      true,
                useShortDoctype:                true,
                removeEmptyAttributes:          true,
                /* оставляем, поскольку у нас
                 * в элемент fm генерируеться
                 * таблица файлов
                 */
                removeEmptyElements:            false,
                removeOptionalTags:             true,
                removeScriptTypeAttributes:     true,
                removeStyleLinkTypeAttributes:  true
            };

            return htmlMinifier.minify(pData,lOptions);
        };
        /*********************************/
        var final_code=html_minify(pData);

        var minFileName=pFileName.replace('.html','.min.html');

         /* записываем сжатый html файл*/
        fs.writeFile(minFileName, final_code, fileWrited(minFileName));
    };

    console.log('reading file ' + INDEX_HTML+'...');
    fs.readFile(INDEX_HTML,fileReaded(INDEX_HTML,dataReaded_f));

    return true;
};

/* функция переводит картинки в base64 и записывает в css-файл*/
function base64_images(pFileContent_s){
    'use strict';
     var b64img;
     try{
        b64img = require('css-b64-images');
    }catch(error){
        console.log('can\'n load clean-css \n'                 +
            'to use images to base64 convertation you need to install css-base64-images \n'  +
                'npm install -g css-b64-images\n'                               +
                'https://github.com/Filirom1/css-base64-images');
        return false;
    }
    b64img.fromString(pFileContent_s, '.','', function(err, css){
        fs.writeFile('all.min.css', css, fileWrited('all.min.css'));
    });
}

/* Функция создаёт асинхроную версию
 * для чтения файла
 * @pFileName - имя считываемого файла
 */
function fileReaded(pFileName,pCompressFunc){
    "use strict";
    return function(pError,pData){
        /* функция в которую мы попадаем,
         * если данные считались
         *
         * если ошибка - показываем её
         * иначе если переданная функция -
         * функция запускаем её
         */
        if(!pError)
            if (pCompressFunc && typeof pCompressFunc==="function")
                    pCompressFunc(pFileName,pData.toString());
        else console.log(pError);
    };
}

/*
 * Функция вызываеться после записи файла
 * и выводит ошибку или сообщает,
 * что файл успешно записан
 */
function fileWrited(pFileName){
    "use strict";
    return function(error){
        console.log(error?error:('file '+pFileName+' writed...'));
    };
}<commit_msg>minor changes<commit_after>/* Модуль сжатия js-скриптов и css*/

/* функция сжимает js-скрипты
 * и сохраняет их с именем .min.js
 */

var fs = require('fs');

exports.jsScripts=function jsScripts(){
    'use strict';

    /* подключаем модуль uglify-js
     * если его нет - дальнейшая
     * работа функции не имеет смысла
     */
    try{
        var jsp = require("uglify-js").parser;
        var pro = require("uglify-js").uglify;
    }catch(error){
        console.log('can\'n load uglify-js\n'                  +
            'to use js-minification you need to install uglify-js\n'    +
                'npm install uglify-js\n'                               +
                'https://github.com/mishoo/UglifyJS');
        return false;
    }
    /* Константы */
    var CLIENT_JS='client.js';
    var CLOUDFUNC_JS='lib/cloudfunc.js';
    var CLIENT_KEYBINDING_JS='lib/client/keyBinding.js';

    var dataReaded_f=function(pFileName, pData){
            console.log('file ' + pFileName + ' readed');

            /*********************************/
            /* сжимаем код через uglify-js */
            var uglify_js=function(pDdata){
                var orig_code = pDdata.toString();
                var ast = jsp.parse(orig_code); // parse code and get the initial AST
                ast = pro.ast_mangle(ast); // get a new AST with mangled names
                ast = pro.ast_squeeze(ast); // get an AST with compression optimizations
                var result_code = pro.gen_code(ast); // compressed code here
                return result_code;
            };
            /*********************************/
            var final_code=uglify_js(pData);

            var minFileName=pFileName.replace('.js','.min.js');
            /* если мы сжимаем client.js -
             * меняем строку cloudfunc.js на
             * cloudfunc.min.js и выводим сообщение
             *
             * меняем строку keyBinding.js на
             * keyBinding.min.js
             * если другой файл - ничего не деалем
             */
            if(pFileName===CLIENT_JS)
                console.log('file name of ' +
                    CLOUDFUNC_JS            +
                    ' in '                  +
                    CLIENT_JS               +
                    ' changed. size:',
                    (final_code=final_code
                        .replace('cloudfunc.js','cloudfunc.min.js')
                            .replace('keyBinding.js','keyBinding.min.js')).length);

            /* записываем сжатый js-скрипт*/
            fs.writeFile(minFileName, final_code, fileWrited(minFileName));
        };
     console.log('reading file ' + CLIENT_JS+'...');
    fs.readFile(CLIENT_JS,fileReaded(CLIENT_JS,dataReaded_f));

    console.log('reading file ' + CLOUDFUNC_JS+'...');
    fs.readFile(CLOUDFUNC_JS,fileReaded(CLOUDFUNC_JS,dataReaded_f));

    console.log('reading file ' + CLIENT_KEYBINDING_JS+'...');
    fs.readFile(CLIENT_KEYBINDING_JS, fileReaded(CLIENT_KEYBINDING_JS,dataReaded_f));

    return true;
};

/* функция сжимает css-стили
 * и сохраняет их с именем .min.css
 * @pImgConvertToBase64_b - булевый признак,
 * который отвечает за то, что быконвертировать
 * картинки в base64 и поместить в выходной css файл
 */
exports.cssStyles=function cssStyles(pImgConvertToBase64_b){
    'use strict';

     /* connecting cleanCSS,
      * if we can't find it -
      * return false
      */
     var cleanCSS;
     try{
        cleanCSS = require('clean-css');
    }catch(error){
        console.log('can\'n load clean-css \n'                          +
            'to use css-minification you need to install clean-css \n'  +
                'npm install clean-css\n'                               +
                'https://github.com/GoalSmashers/clean-css');
        return false;
    }

    /* Константы */
    var STYLE_CSS='style.css';
    var RESET_CSS='reset.css';

    var lAllStyle='';
    var lResetCssDone=false;
    var lStyleCssDone=false;
    var dataReaded_f=function(pFileName, pData){
        console.log('file ' + pFileName + ' readed');
        /*********************************/
        /* сжимаем код через clean-css */
        var clean_css=function(pData){
            /* Сохраняем весь стиль в одну переменную*/
            return cleanCSS.process(pData);
        };
        /*********************************/
        var final_code=clean_css(pData);

        lAllStyle+=final_code;

        var minFileName=pFileName.replace('.css','.min.css');

        if(pFileName===STYLE_CSS)lStyleCssDone=true;
        if(pFileName===RESET_CSS)lResetCssDone=true;
        /* if all files writed we
         * save all minimized css
         * to one file all.min.css
         */
        /* если включена конвертация картинок в base64
         * вызываем её
         */
        if(lStyleCssDone && lResetCssDone)
            if(pImgConvertToBase64_b)
                base64_images(lAllStyle);
        else
            fs.writeFile('all.min.css', lAllStyle, fileWrited('all.min.css'));

         /* записываем сжатый css файл*/
        else fs.writeFile(minFileName, final_code, fileWrited(minFileName));
    };

    console.log('reading file ' + STYLE_CSS+'...');
    fs.readFile(STYLE_CSS,fileReaded(STYLE_CSS,dataReaded_f));

    console.log('reading file ' + RESET_CSS+'...');
    fs.readFile(RESET_CSS,fileReaded(RESET_CSS,dataReaded_f));

    return true;
};

/* функция сжимает css-стили
 * и сохраняет их с именем .min.css
 */
exports.html=function(){
    'use strict';

     /* connecting cleanCSS,
      * if we can't find it -
      * return false
      */
     var htmlMinifier;
     try{
        htmlMinifier = require('html-minifier');
    }catch(error){
        console.log('can\'n load html-minifier \n'                 +
            'to use html-minification you need to install html-minifier\n'  +
                'npm install html-minifier\n'                               +
                'https://github.com/kangax/html-minifier');
        return false;
    }

    /* Константы */
    var INDEX_HTML='index.html';

    var dataReaded_f=function(pFileName, pData){
        console.log('file ' + pFileName + ' readed');
        /*********************************/
        /* сжимаем код через clean-css */
        var html_minify=function(pData){
            /* Сохраняем весь стиль в одну переменную*/

            var lOptions={
                removeComments:                 true,
                removeCommentsFromCDATA:        true,
                removeCDATASectionsFromCDATA:   true,
                collapseWhitespace:             true,
                collapseBooleanAttributes:      true,
                removeAttributeQuotes:          true,
                removeRedundantAttributes:      true,
                useShortDoctype:                true,
                removeEmptyAttributes:          true,
                /* оставляем, поскольку у нас
                 * в элемент fm генерируеться
                 * таблица файлов
                 */
                removeEmptyElements:            false,
                removeOptionalTags:             true,
                removeScriptTypeAttributes:     true,
                removeStyleLinkTypeAttributes:  true
            };

            return htmlMinifier.minify(pData,lOptions);
        };
        /*********************************/
        var final_code=html_minify(pData);

        var minFileName=pFileName.replace('.html','.min.html');

         /* записываем сжатый html файл*/
        fs.writeFile(minFileName, final_code, fileWrited(minFileName));
    };

    console.log('reading file ' + INDEX_HTML+'...');
    fs.readFile(INDEX_HTML,fileReaded(INDEX_HTML,dataReaded_f));

    return true;
};

/* функция переводит картинки в base64 и записывает в css-файл*/
function base64_images(pFileContent_s){
    'use strict';
     var b64img;
     try{
        b64img = require('css-b64-images');
    }catch(error){
        console.log('can\'n load clean-css \n'                 +
            'to use images to base64 convertation you need to install css-base64-images \n'  +
                'npm install -g css-b64-images\n'                               +
                'https://github.com/Filirom1/css-base64-images');
        return false;
    }
    b64img.fromString(pFileContent_s, '.','', function(err, css){
        fs.writeFile('all.min.css', css, fileWrited('all.min.css'));
    });
}

/* Функция создаёт асинхроную версию
 * для чтения файла
 * @pFileName - имя считываемого файла
 */
function fileReaded(pFileName,pCompressFunc){
    "use strict";
    return function(pError,pData){
        /* функция в которую мы попадаем,
         * если данные считались
         *
         * если ошибка - показываем её
         * иначе если переданная функция -
         * функция запускаем её
         */
        if(!pError)
            if (pCompressFunc && typeof pCompressFunc==="function")
                    pCompressFunc(pFileName,pData.toString());
        else console.log(pError);
    };
}

/*
 * Функция вызываеться после записи файла
 * и выводит ошибку или сообщает,
 * что файл успешно записан
 */
function fileWrited(pFileName){
    "use strict";
    return function(error){
        console.log(error?error:('file '+pFileName+' writed...'));
    };
}<|endoftext|>


<commit_before>'use strict'

var miss = require('mississippi')
var config = require('../config')

var setupArchive = miss.through(function (chunck, encoding, callback) {
  var item = JSON.parse(chunck)
  var now = new Date()
  var archive = {}

  console.log(item._id + ': setup-archive')

  archive._id = item._id
  archive.title = item.documentType + ' - ' + item.documentCategory + ' - ' + item.studentName + ' - ' + item.studentMainGroupName + ' - ' + item.schoolName + ' - ' + now.getFullYear() + ' - ' + item.period.replace(/\s+/g, '')
  archive.documentType = item.documentType
  archive.documentCategory = item.documentCategory
  archive.CALLBACK_STATUS_URL = item.CALLBACK_STATUS_URL
  archive.date = datePadding(now.getDate()) + '.' + datePadding(now.getMonth() + 1) + '.' + now.getFullYear()
  archive.year = now.getFullYear()

  archive.student = {
    id: item.studentId,
    firstName: item.studentFirstName,
    middleName: item.studentMiddleName,
    lastName: item.studentLastName,
    email: item.studentMail,
    phone: item.studentPhone,
    fullName: item.studentName
  }

  archive.school = {
    name: item.schoolName,
    id: item.schoolId,
    orgId: item.schoolOrganizationNumber.replace(/\D+/, ''),
    mainGroup: item.studentMainGroupName
  }

  archive.documents = []

  item.archive = archive

  return callback(null, JSON.stringify(item))
})

function datePadding (date) {
  var padded = date.toString()
  if (padded.length === 1) {
    padded = '0' + date.toString()
  }
  return padded
}

module.exports = setupArchive
<commit_msg>Removes unused config<commit_after>'use strict'

var miss = require('mississippi')

var setupArchive = miss.through(function (chunck, encoding, callback) {
  var item = JSON.parse(chunck)
  var now = new Date()
  var archive = {}

  console.log(item._id + ': setup-archive')

  archive._id = item._id
  archive.title = item.documentType + ' - ' + item.documentCategory + ' - ' + item.studentName + ' - ' + item.studentMainGroupName + ' - ' + item.schoolName + ' - ' + now.getFullYear() + ' - ' + item.period.replace(/\s+/g, '')
  archive.documentType = item.documentType
  archive.documentCategory = item.documentCategory
  archive.CALLBACK_STATUS_URL = item.CALLBACK_STATUS_URL
  archive.date = datePadding(now.getDate()) + '.' + datePadding(now.getMonth() + 1) + '.' + now.getFullYear()
  archive.year = now.getFullYear()

  archive.student = {
    id: item.studentId,
    firstName: item.studentFirstName,
    middleName: item.studentMiddleName,
    lastName: item.studentLastName,
    email: item.studentMail,
    phone: item.studentPhone,
    fullName: item.studentName
  }

  archive.school = {
    name: item.schoolName,
    id: item.schoolId,
    orgId: item.schoolOrganizationNumber.replace(/\D+/, ''),
    mainGroup: item.studentMainGroupName
  }

  archive.documents = []

  item.archive = archive

  return callback(null, JSON.stringify(item))
})

function datePadding (date) {
  var padded = date.toString()
  if (padded.length === 1) {
    padded = '0' + date.toString()
  }
  return padded
}

module.exports = setupArchive
<|endoftext|>


<commit_before>/**
 * The MIT License (MIT)
 * Copyright (c) 2016 GochoMugo <mugo@forfuture.co.ke>
 * Copyright (c) 2016 Forfuture, LLC < we@forfuture.co.ke>
 *
 * Stage: update remote
 * Actions:
 * - push files
 * - delete files
 * - update filelist
 */

// built-in modules
const path = require('path');

// npm-installed modules
const async = require('async');
const debug = require('debug')('ftpush:stages:update');

// own modules
const filelist = require('../filelist');

exports = module.exports = function update(globals, state, next) {
    // push the changed files
    function push(actionDone) {
        async.each(state.diffs.changed, function(filename, done) {
            var filepath = path.join(state.remotedir.path, filename);
            globals.ftp.put(state.localdir.buffers[filename], filepath, done);
        }, actionDone);
    }

    // delete the deleted files
    function del(actionDone) {
        // if we are to skip deletion, do so
        if (globals.options.skipDeletion) {
            return actionDone(null);
        }

        async.each(state.diffs.deleted, function(filename, done) {
            var filepath = path.join(state.remotedir.path, filename);
            globals.ftp.raw.dele(filepath, done);
        }, actionDone);
    }

    async.parallel([
        push,
        del,
    ], function(actionErr) {
        if (actionErr) {
            debug('error in push/del action: %s', actionErr);
            return next(actionErr);
        }
        // push an updated filelist
        var buffer = filelist.toBuffer(state.diffs.filelist);
        globals.ftp.put(buffer, state.remotedir.filelistpath, function(putErr) {
            return next(putErr);
        });
    });
};
<commit_msg>[stage/update] Handle putting/deleting files in series<commit_after>/**
 * The MIT License (MIT)
 * Copyright (c) 2016 GochoMugo <mugo@forfuture.co.ke>
 * Copyright (c) 2016 Forfuture, LLC < we@forfuture.co.ke>
 *
 * Stage: update remote
 * Actions:
 * - push files
 * - delete files
 * - update filelist
 */

// built-in modules
const path = require('path');

// npm-installed modules
const async = require('async');
const debug = require('debug')('ftpush:stages:update');

// own modules
const filelist = require('../filelist');

exports = module.exports = function update(globals, state, next) {
    // push the changed files
    function push(actionDone) {
        async.eachSeries(state.diffs.changed, function(filename, done) {
            var filepath = path.join(state.remotedir.path, filename);
            globals.ftp.put(state.localdir.buffers[filename], filepath, done);
        }, actionDone);
    }

    // delete the deleted files
    function del(actionDone) {
        // if we are to skip deletion, do so
        if (globals.options.skipDeletion) {
            return actionDone(null);
        }

        async.eachSeries(state.diffs.deleted, function(filename, done) {
            var filepath = path.join(state.remotedir.path, filename);
            globals.ftp.raw.dele(filepath, done);
        }, actionDone);
    }

    async.series([
        push,
        del,
    ], function(actionErr) {
        if (actionErr) {
            debug('error in push/del action: %s', actionErr);
            return next(actionErr);
        }
        // push an updated filelist
        var buffer = filelist.toBuffer(state.diffs.filelist);
        globals.ftp.put(buffer, state.remotedir.filelistpath, function(putErr) {
            return next(putErr);
        });
    });
};
<|endoftext|>


<commit_before>"use strict";

var fs = require('fs');
var upath = require('upath');
var gulp = require('gulp');
var gulpWatch = require('gulp-watch');
var runSequence = require('run-sequence');
var postcss = require('gulp-postcss');
var sourcemaps = require('gulp-sourcemaps');
var extname = require('gulp-extname');
var livereload = require('gulp-livereload');
var errorHandler = require('../assemble/plugins/error').postcss;
var taskGenerator = require('../taskGenerator');

module.exports = function(name, config, serverConfig) {
    return taskGenerator(name, config, serverConfig, function(taskName, task) {
        gulp.task(taskName, function() {
            var pipe = gulp.src(task.files.src)
                .on('error', errorHandler);
            if (task.sourcemap) {
                pipe = pipe.pipe(sourcemaps.init());
            }
            pipe = pipe.on('error', errorHandler)
                .pipe(extname('css'))
                .on('error', errorHandler)
                .pipe(postcss(task.plugins.map(function(plugin) {
                    if (plugin.name === 'postcss-import') {
                        plugin.params.push({
                            resolve: function(id, options) {
                                var path;
                                if (task.packagePattern.find(function(value) {
                                        return RegExp(value).test(id);
                                    })) {
                                    if (RegExp(/\.pcss$/).test(id)) {
                                        path = id.replace(/\.\//, '').split('/');
                                        path[0] = upath.join(path[0], 'src/pcss');
                                        path = upath.resolve(process.cwd(), 'node_modules', path.join('/'));
                                        if (fs.statSync(path).isDirectory()) {
                                            // @import "package/element"; -> @import "package/element/index.pcss"
                                            return upath.join(path, 'index.pcss');
                                        } else {
                                            return path;
                                        }
                                    } else {
                                        return upath.resolve(process.cwd(), 'node_modules', id, 'index.pcss');
                                    }
                                } else {
                                    // @TODO added config for available file types for postcss-import
                                    path = upath.resolve(options.paths[0], id);
                                    try {
                                        if (!fs.statSync(path).isFile()) {
                                            path = id.replace(/\.\//, '').split('/');
                                            path[0] = upath.join(path[0], 'src/pcss');
                                            path = upath.resolve(process.cwd(), 'node_modules', path.join('/'));
                                        } else {
                                            return path;
                                        }
                                    } catch (e) {
                                        // Path not found, search in node_modules.
                                        path = id.replace(/\.\//, '').split('/');
                                        path = upath.resolve(process.cwd(), 'node_modules', path.join('/'));
                                        return path;
                                    }
                                }
                            }
                        });
                    }
                    return require(plugin.name).apply(null, plugin.params);
                })))
                .on('error', errorHandler);
            if (task.sourcemap) {
                pipe = pipe.pipe(sourcemaps.write('.', {
                        sourceMappingURLPrefix: '/' + upath.relative(serverConfig.root, task.files.dest)
                    }))
                    .on('error', errorHandler);
            }
            return pipe.pipe(gulp.dest(task.files.dest));
        });
        gulpWatch([task.files.dest + '/**/style.css'], function(file) {
            livereload.changed(file);
        });
    });
};
<commit_msg>clean<commit_after>"use strict";

var fs = require('fs');
var upath = require('upath');
var gulp = require('gulp');
var gulpWatch = require('gulp-watch');
var runSequence = require('run-sequence');
var postcss = require('gulp-postcss');
var sourcemaps = require('gulp-sourcemaps');
var extname = require('gulp-extname');
var livereload = require('gulp-livereload');
var errorHandler = require('../assemble/plugins/error').postcss;
var taskGenerator = require('../taskGenerator');

module.exports = function(name, config, serverConfig) {
    return taskGenerator(name, config, serverConfig, function(taskName, task) {
        gulp.task(taskName, function() {
            var pipe = gulp.src(task.files.src)
                .on('error', errorHandler);
            if (task.sourcemap) {
                pipe = pipe.pipe(sourcemaps.init());
            }
            pipe = pipe.on('error', errorHandler)
                .pipe(extname('css'))
                .on('error', errorHandler)
                .pipe(postcss(task.plugins.map(function(plugin) {
                    if (plugin.name === 'postcss-import') {
                        plugin.params.push({
                            resolve: function(id, options) {
                                var path;
                                if (task.packagePattern.find(function(value) {
                                        return RegExp(value).test(id);
                                    })) {
                                    if (RegExp(/\.pcss$/).test(id)) {
                                        path = id.replace(/\.\//, '').split('/');
                                        path[0] = upath.join(path[0], 'src/pcss');
                                        path = upath.resolve(process.cwd(), 'node_modules', path.join('/'));
                                        if (fs.statSync(path).isDirectory()) {
                                            // @import "package/element"; -> @import "package/element/index.pcss"
                                            return upath.join(path, 'index.pcss');
                                        } else {
                                            return path;
                                        }
                                    } else {
                                        return upath.resolve(process.cwd(), 'node_modules', id, 'index.pcss');
                                    }
                                } else {
                                    // @TODO added config for available file types for postcss-import
                                    path = upath.resolve(options.paths[0], id);
                                    if (!fs.statSync(path).isFile()) {
                                        path = id.replace(/\.\//, '').split('/');
                                        path[0] = upath.join(path[0], 'src/pcss');
                                        path = upath.resolve(process.cwd(), 'node_modules', path.join('/'));
                                    } else {
                                        return path;
                                    }
                                }
                            }
                        });
                    }
                    return require(plugin.name).apply(null, plugin.params);
                })))
                .on('error', errorHandler);
            if (task.sourcemap) {
                pipe = pipe.pipe(sourcemaps.write('.', {
                        sourceMappingURLPrefix: '/' + upath.relative(serverConfig.root, task.files.dest)
                    }))
                    .on('error', errorHandler);
            }
            return pipe.pipe(gulp.dest(task.files.dest));
        });
        gulpWatch([task.files.dest + '/**/style.css'], function(file) {
            livereload.changed(file);
        });
    });
};
<|endoftext|>


<commit_before>/** @babel */
/** @jsx etch.dom */

import { CompositeDisposable } from 'atom';
import ResizeObserver from 'resize-observer-polyfill';
import * as fit from 'xterm/lib/addons/fit/fit';
import etch from 'etch';
import ThemeMatcher from './theme-matcher';

const TERMINAL_PADDING = 5;

export default class TerminalView {

  constructor(session) {
    this.disposables = new CompositeDisposable();
    this.session = session;

    //
    // Observe the Session to know when it is destroyed so that we can
    // clean up our state (i.e. remove event observers).
    //
    this.session.onDidDestroy(this.destroy.bind(this));

    // TODO: Documentation says this should be set for Atom... Research!
    // etch.setScheduler(atom.views);
    etch.initialize(this);

    // this.openTerminal();
    this.observeResizeEvents();
  }

  render() {
    // TODO: Convert to

    return (
      <terminal-view attributes={{tabindex: -1}} on={{focus: this.didFocus}} />
    );
  }

  update() {
    return etch.update(this);
  }

  destroy() {
    this.resizeObserver.disconnect();
    this.disposables.dispose();
    etch.destroy(this);
  }

  //
  // Attach the Xterm instance from the session to this view's element, then
  // focus and resize it to fit its viewport.
  //
  openTerminal() {
    this.session.xterm.open(this.element);
    this.session.xterm.focus();

    this.observeAndApplyThemeStyles();
    this.observeAndApplyTypeSettings();
  }

  //
  // Observe for resize events so that we can instruct the Xterm instance
  // to recalculate rows and columns to fit into its viewport when it
  // changes.
  //
  observeResizeEvents() {
    this.resizeObserver = new ResizeObserver(this.didResize.bind(this));
    this.resizeObserver.observe(this.element);
  }

  //
  // Resizes the terminal instance to fit its parent container. Once the new
  // dimensions are established, the calculated columns and rows are passed to
  // the pseudoterminal (pty) to remain consistent.
  //
  didResize() {
    if (!this.session.xterm.element) {
      this.openTerminal();
    }

    // TODO: Move this to resizeTerminalToFitContainer()
    if (this.session && this.session.pty && this.session.xterm) {

      // Set padding and resize the terminal to fit its container (as best as possible)
      this.session.xterm.element.style.padding = `${TERMINAL_PADDING}px`;
      fit.fit(this.session.xterm);

      // Check the new size and add additional padding to the top of the
      // terminal so that it fills all available space.
      // TODO: Extract this into a new calculatePadding() or something...
      const elementStyles = getComputedStyle(this.element);
      const xtermElementStyles = getComputedStyle(this.session.xterm.element);
      const elementHeight = parseInt(elementStyles.height, 10);
      const xtermHeight = parseInt(xtermElementStyles.height, 10);
      const newHeight = elementHeight - xtermHeight + TERMINAL_PADDING;
      this.session.xterm.element.style.paddingTop = `${newHeight}px`;
      fit.fit(this.session.xterm);

      // Update Pseudoterminal Process w/New Dimensions
      this.session.pty.resize(this.session.xterm.cols, this.session.xterm.rows);

    }
  }

  //
  // Transfer focus to the Xterm instance when the element is focused. When
  // switching between tabs, Atom will send a focus event to the element,
  // which makes it easy for us to delegate focus to the Xterm instance, whose
  // element we are managing in our view.
  //
  didFocus(/* event */) {
    this.session.xterm.focus();
  }

  //
  // Observe for changes to the matchTheme configuration directive and apply
  // the styles when the value changes. This will also apply them when called
  // for the first time.
  //
  observeAndApplyThemeStyles() {
    if (this.isObservingThemeSettings) return;
    this.disposables.add(atom.config.onDidChange('terminal-tab.matchTheme', this.applyThemeStyles.bind(this)));
    this.disposables.add(atom.themes.onDidChangeActiveThemes(this.applyThemeStyles.bind(this)));
    this.isObservingThemeSettings = true;
    this.applyThemeStyles();
  }

  //
  // Observe for changes to the Editor configuration for Atom and apply
  // the type settings when the values we are interested in change. This
  // will also apply them when called for the first time.
  //
  observeAndApplyTypeSettings() {
    if (this.isObservingTypeSettings) return;
    this.disposables.add(atom.config.onDidChange('editor.fontFamily', this.applyTypeSettings.bind(this)));
    this.disposables.add(atom.config.onDidChange('editor.fontSize', this.applyTypeSettings.bind(this)));
    this.disposables.add(atom.config.onDidChange('editor.lineHeight', this.applyTypeSettings.bind(this)));
    this.isObservingTypeSettings = true;
    this.applyTypeSettings();
  }

  //
  // Attempts to match the Xterm instance with the current Atom theme colors.
  //
  // TODO: This should take advantage of update()
  // TODO: This doesn't undo the font settings when the theme is disabled...
  //
  applyThemeStyles() {

    // Bail out if the user has not requested to match the theme styles
    if (!atom.config.get('terminal-tab.matchTheme')) {
      this.session.xterm.setOption('theme', {});
      return;
    }

    // Parse the Atom theme styles and configure the Xterm to match.
    const themeStyles = ThemeMatcher.parseThemeStyles();
    this.session.xterm.setOption('theme', themeStyles);

  }

  //
  // Attempts to match the Atom type settings (font family, size and line height) with
  // Xterm.
  //
  applyTypeSettings() {

    //
    // Set the font family in Xterm to match Atom.
    //
    let fontFamily = atom.config.get('editor.fontFamily');
    if (fontFamily) {
      this.session.xterm.setOption('fontFamily', fontFamily);
    } else {
      this.session.xterm.setOption('fontFamily', 'Menlo, Consolas, "DejaVu Sans Mono", monospace');
    }

    //
    // Set the font size in Xterm to match Atom.
    //
    const fontSize = atom.config.get('editor.fontSize');
    this.session.xterm.setOption('fontSize', fontSize);

    //
    // Set the line height in Xterm to match Atom.
    //
    // TODO: This is disabled, because the line height as specified in
    //       Atom is not the same as what Xterm is using to render its
    //       lines (i.e. 1.5 in Atom is more like 2x in Xterm). Need to
    //       figure out the correct conversion or fix the bug, if there
    //       is one.
    //
    // const lineHeight = atom.config.get('editor.lineHeight');
    // this.session.xterm.setOption('lineHeight', lineHeight);

    //
    // Changing the font size and/or line height requires that we
    // recalcuate the size of the Xterm instance.
    //
    // TODO: Call the renamed method (i.e. resizeTerminalToFitContainer())
    //
    this.didResize();

  }

}
<commit_msg>Removed a defunct and commented-out call to openTerminal().<commit_after>/** @babel */
/** @jsx etch.dom */

import { CompositeDisposable } from 'atom';
import ResizeObserver from 'resize-observer-polyfill';
import * as fit from 'xterm/lib/addons/fit/fit';
import etch from 'etch';
import ThemeMatcher from './theme-matcher';

const TERMINAL_PADDING = 5;

export default class TerminalView {

  constructor(session) {
    this.disposables = new CompositeDisposable();
    this.session = session;

    //
    // Observe the Session to know when it is destroyed so that we can
    // clean up our state (i.e. remove event observers).
    //
    this.session.onDidDestroy(this.destroy.bind(this));

    // TODO: Documentation says this should be set for Atom... Research!
    // etch.setScheduler(atom.views);
    etch.initialize(this);

    this.observeResizeEvents();
  }

  render() {
    // TODO: Convert to

    return (
      <terminal-view attributes={{tabindex: -1}} on={{focus: this.didFocus}} />
    );
  }

  update() {
    return etch.update(this);
  }

  destroy() {
    this.resizeObserver.disconnect();
    this.disposables.dispose();
    etch.destroy(this);
  }

  //
  // Attach the Xterm instance from the session to this view's element, then
  // focus and resize it to fit its viewport.
  //
  openTerminal() {
    this.session.xterm.open(this.element);
    this.session.xterm.focus();

    this.observeAndApplyThemeStyles();
    this.observeAndApplyTypeSettings();
  }

  //
  // Observe for resize events so that we can instruct the Xterm instance
  // to recalculate rows and columns to fit into its viewport when it
  // changes.
  //
  observeResizeEvents() {
    this.resizeObserver = new ResizeObserver(this.didResize.bind(this));
    this.resizeObserver.observe(this.element);
  }

  //
  // Resizes the terminal instance to fit its parent container. Once the new
  // dimensions are established, the calculated columns and rows are passed to
  // the pseudoterminal (pty) to remain consistent.
  //
  didResize() {
    if (!this.session.xterm.element) {
      this.openTerminal();
    }

    // TODO: Move this to resizeTerminalToFitContainer()
    if (this.session && this.session.pty && this.session.xterm) {

      // Set padding and resize the terminal to fit its container (as best as possible)
      this.session.xterm.element.style.padding = `${TERMINAL_PADDING}px`;
      fit.fit(this.session.xterm);

      // Check the new size and add additional padding to the top of the
      // terminal so that it fills all available space.
      // TODO: Extract this into a new calculatePadding() or something...
      const elementStyles = getComputedStyle(this.element);
      const xtermElementStyles = getComputedStyle(this.session.xterm.element);
      const elementHeight = parseInt(elementStyles.height, 10);
      const xtermHeight = parseInt(xtermElementStyles.height, 10);
      const newHeight = elementHeight - xtermHeight + TERMINAL_PADDING;
      this.session.xterm.element.style.paddingTop = `${newHeight}px`;
      fit.fit(this.session.xterm);

      // Update Pseudoterminal Process w/New Dimensions
      this.session.pty.resize(this.session.xterm.cols, this.session.xterm.rows);

    }
  }

  //
  // Transfer focus to the Xterm instance when the element is focused. When
  // switching between tabs, Atom will send a focus event to the element,
  // which makes it easy for us to delegate focus to the Xterm instance, whose
  // element we are managing in our view.
  //
  didFocus(/* event */) {
    this.session.xterm.focus();
  }

  //
  // Observe for changes to the matchTheme configuration directive and apply
  // the styles when the value changes. This will also apply them when called
  // for the first time.
  //
  observeAndApplyThemeStyles() {
    if (this.isObservingThemeSettings) return;
    this.disposables.add(atom.config.onDidChange('terminal-tab.matchTheme', this.applyThemeStyles.bind(this)));
    this.disposables.add(atom.themes.onDidChangeActiveThemes(this.applyThemeStyles.bind(this)));
    this.isObservingThemeSettings = true;
    this.applyThemeStyles();
  }

  //
  // Observe for changes to the Editor configuration for Atom and apply
  // the type settings when the values we are interested in change. This
  // will also apply them when called for the first time.
  //
  observeAndApplyTypeSettings() {
    if (this.isObservingTypeSettings) return;
    this.disposables.add(atom.config.onDidChange('editor.fontFamily', this.applyTypeSettings.bind(this)));
    this.disposables.add(atom.config.onDidChange('editor.fontSize', this.applyTypeSettings.bind(this)));
    this.disposables.add(atom.config.onDidChange('editor.lineHeight', this.applyTypeSettings.bind(this)));
    this.isObservingTypeSettings = true;
    this.applyTypeSettings();
  }

  //
  // Attempts to match the Xterm instance with the current Atom theme colors.
  //
  // TODO: This should take advantage of update()
  // TODO: This doesn't undo the font settings when the theme is disabled...
  //
  applyThemeStyles() {

    // Bail out if the user has not requested to match the theme styles
    if (!atom.config.get('terminal-tab.matchTheme')) {
      this.session.xterm.setOption('theme', {});
      return;
    }

    // Parse the Atom theme styles and configure the Xterm to match.
    const themeStyles = ThemeMatcher.parseThemeStyles();
    this.session.xterm.setOption('theme', themeStyles);

  }

  //
  // Attempts to match the Atom type settings (font family, size and line height) with
  // Xterm.
  //
  applyTypeSettings() {

    //
    // Set the font family in Xterm to match Atom.
    //
    let fontFamily = atom.config.get('editor.fontFamily');
    if (fontFamily) {
      this.session.xterm.setOption('fontFamily', fontFamily);
    } else {
      this.session.xterm.setOption('fontFamily', 'Menlo, Consolas, "DejaVu Sans Mono", monospace');
    }

    //
    // Set the font size in Xterm to match Atom.
    //
    const fontSize = atom.config.get('editor.fontSize');
    this.session.xterm.setOption('fontSize', fontSize);

    //
    // Set the line height in Xterm to match Atom.
    //
    // TODO: This is disabled, because the line height as specified in
    //       Atom is not the same as what Xterm is using to render its
    //       lines (i.e. 1.5 in Atom is more like 2x in Xterm). Need to
    //       figure out the correct conversion or fix the bug, if there
    //       is one.
    //
    // const lineHeight = atom.config.get('editor.lineHeight');
    // this.session.xterm.setOption('lineHeight', lineHeight);

    //
    // Changing the font size and/or line height requires that we
    // recalcuate the size of the Xterm instance.
    //
    // TODO: Call the renamed method (i.e. resizeTerminalToFitContainer())
    //
    this.didResize();

  }

}
<|endoftext|>