<commit_before>/**
 * The Color Picker Component
 *
 * @module aui-color-picker
 * @submodule aui-hsva-palette
 */

var Lang = A.Lang,
    AColor = A.Color,
    AWidget = A.Widget,

    getClassName = A.getClassName,

    MIN_ALPHA = 0,

    MAX_ALPHA = 255,
    MAX_HUE = 360,
    MAX_SATURATION = 100,
    MAX_VALUE = 100,

    MAXLEN_HEX = 8,

    CSS_CONTAINER_ALPHA = getClassName('hsv-container-alpha'),

    CSS_ALPHA_CANVAS = getClassName('hsv-alpha-canvas'),
    CSS_ALPHA_SLIDER_CONTAINER = getClassName('hsv-alpha-slider-container'),
    CSS_ALPHA_SLIDER_WRAPPER = getClassName('hsv-alpha-slider-wrapper'),
    CSS_ALPHA_THUMB = getClassName('hsv-alpha-thumb'),
    CSS_ALPHA_THUMB_IMAGE = getClassName('hsv-alpha-image'),

    REGEX_HEX_COLOR_ALPHA = /^([a-f0-9]{6}|[a-f0-9]{8}|[a-f0-9]{3})$/i,

    /**
     * A base class for `HSVAPalette`.
     *
     * @class A.HSVAPalette
     * @extends A.HSVPalette
     * @param {Object} config Object literal specifying widget configuration
     *      properties.
     * @constructor
     */
    HSVAPalette = A.Base.create('hsva-palette', A.HSVPalette, [], {
        CSS_VALUE_RIGHT_SIDE_CONTAINER: 'col-sm-8 col-xs-8',

        TPL_ALPHA_CANVAS: '',

        TPL_ALPHA_SLIDER_WRAPPER: '

',

        TPL_ALPHA_THUMB: '',

        /**
         * Construction logic executed during `HSVAPalette` instantiation.
         * Lifecycle.
         *
         * @method initializer
         * @protected
         */
        initializer: function() {
            var instance = this;

            instance.set('fieldValidator.hex', REGEX_HEX_COLOR_ALPHA);

            instance.after('selectedChange', instance._updateAlphaValue, instance);

            instance.onceAfter('render', instance._updateAlphaValue, instance);
        },

        /**
         * Sets color after hex input `valueChange`.
         *
         * @method _afterHexInputChange
         * @param {EventFacade} event
         * @protected
         */
        _afterHexInputChange: function(event) {
            // YUI Code toHSVA from hex + alpha is broken, will remove the alpha
            // value
            var instance = this,
                hexColor = event.hexColor,
                alpha = hexColor.substr(6, 2),
                alphaDec = parseInt(alpha, 16);

            instance._alphaSlider.set(
                'value',
                MAX_ALPHA - alphaDec, {
                    src: AWidget.UI_SRC
                }
            );

            instance._alphaSliderContainer.setStyle('backgroundColor', hexColor);

            instance._resultView.setStyle('opacity', alphaDec / MAX_ALPHA);

            instance._setFieldValue(instance._aContainer, alphaDec);
        },

        /**
         * Calculates and returns RGB value from array of values.
         *
         * @method _calculateRGBArray
         * @param {Number} r
         * @param {Number} g
         * @param {Number} b
         * @return {String} RGB value
         * @protected
         */
        _calculateRGBArray: function(r, g, b) {
            var instance = this,
                alpha;

            alpha = 255 - instance._alphaSlider.get('value');

            return AColor.fromArray([r, g, b, alpha], 'RGBA');
        },

        /**
         * Calculates and returns RGB color value.
         *
         * @method _calculateRGBColor
         * @param {Number} hue
         * @param {Number} saturation
         * @param {Number} value
         * @return {String} RGB value
         * @protected
         */
        _calculateRGBColor: function(hue, saturation, value) {
            var instance = this,
                alpha = 255 - instance._alphaSlider.get('value');

            // TODO: Use feature checking instead
            if (A.UA.ie === 8) {
                return instance._calculateRGB(hue, saturation, value, alpha);
            }
            else {
                return instance._calculateRGBA(hue, saturation, value, alpha);
            }
        },

        /**
         * Calculates RGB color value from HSVA color value.
         *
         * @method _calculateRGBA
         * @param {Number} hue
         * @param {Number} saturation
         * @param {Number} value
         * @param {Number} alpha
         * @return {String} RGB value
         * @protected
         */
        _calculateRGBA: function(hue, saturation, value, alpha) {
            var rgbColor = 'rgb(255, 0, 0, 0)',
                hsvColor,
                tmp;

            if (Lang.toInt(hue) !== MAX_HUE || Lang.toInt(saturation) !== MAX_SATURATION || Lang.toInt(value) !== MAX_VALUE) {
                hsvColor = 'hsva(' + (hue === MAX_HUE ? MAX_HUE - 1 : hue) + ', ' + saturation + '%, ' + value +
                    '%, ' + alpha + ')';

                rgbColor = AColor.toRGBA(hsvColor);

                // fix YUI bug on getting alpha - if it is 0, they return 1
                if (Lang.toInt(alpha) === 0) {
                    tmp = AColor.toArray(rgbColor);

                    tmp[3] = '0';

                    rgbColor = AColor.fromArray(tmp, 'RGBA');
                }
            }

            return rgbColor;
        },

        /**
         * Converts one color value type to another.
         *
         * @method _convertColor
         * @param {String} color value
         * @param {String} from type
         * @param {String} to type
         * @return {String} converted color value
         * @protected
         */
        _convertColor: function(value, from, to) {
            var out,
                outputHex;

            outputHex = (to === 'hex');

            if (from === 'hex') {
                value = '#' + value;
            }

            if (outputHex) {
                to = 'Hex';
            }
            else {
                to = (to + 'a').toUpperCase();
            }

            out = AColor['to' + to](value);

            if (outputHex) {
                out = out.substr(1);
            }

            return out;
        },

        /**
         * Returns container class name.
         *
         * @method _getContainerClassName
         * @return {String} class name
         * @protected
         */
        _getContainerClassName: function() {
            var instance = this,
                className;

            className = A.HSVAPalette.superclass._getContainerClassName.call(instance);

            className += ' ' + CSS_CONTAINER_ALPHA;

            return className;
        },

        /**
         * Returns hexadecimal color value.
         *
         * @method _getHexValue
         * @param {String} hexColor
         * @param {Array} rgbColorArray
         * @return {String} hex value
         * @protected
         */
        _getHexValue: function(hexColor, rgbColorArray) {
            // YUI doesn't have toRGBA method, we have to add alpha explicitly
            var alpha,
                result;

            alpha = parseInt(rgbColorArray[3], 10).toString(16);

            if (alpha.length === 1) {
                alpha = '0' + alpha;
            }

            result = hexColor + alpha;

            return result.substring(1);
        },

        /**
         * Returns HSV color values.
         *
         * @method _getHSVArray
         * @param {Number} hsv
         * @return {Array} hsv color array
         * @protected
         */
        _getHSVArray: function(hsv) {
            return AColor.toArray(hsv, 'HSVA');
        },

        /**
         * Renders alpha slider and binds events.
         *
         * @method _createAlphaSlider
         * @protected
         */
        _createAlphaSlider: function() {
            var instance = this,
                alphaThumbHeight,
                contentBox,
                slider;

            contentBox = instance.get('contentBox'),

            slider = new A.Slider({
                axis: 'y',
                min: MIN_ALPHA,
                max: MAX_ALPHA
            });

            slider.RAIL_TEMPLATE = instance.TPL_ALPHA_CANVAS;
            slider.THUMB_TEMPLATE = instance.TPL_ALPHA_THUMB;

            slider.render(instance._alphaSliderContainer);

            alphaThumbHeight = contentBox.one('.' + CSS_ALPHA_THUMB_IMAGE).get('offsetHeight');

            slider.set(
                'length',
                instance._alphaSliderContainer.get('offsetHeight') + (alphaThumbHeight / 2)
            );

            slider.on(['slideStart', 'railMouseDown'], instance._setHSContainerXY, instance);

            slider.on('valueChange', instance._onAlphaChange, instance);

            instance._alphaSlider = slider;
        },

        /**
         * Creates sliders.
         *
         * @method _createSliders
         * @protected
         */
        _createSliders: function() {
            var instance = this;

            A.HSVAPalette.superclass._createSliders.call(instance);

            instance._createAlphaSlider();
        },

        /**
         * Returns hex container configuration.
         *
         * @method _getHexContainerConfig
         * @return {Object} hex container config object
         * @protected
         */
        _getHexContainerConfig: function() {
            var instance = this;

            return {
                label: instance.get('strings').hex,
                maxlength: MAXLEN_HEX,
                suffix: '-hex',
                type: 'hex',
                unit: '',
                value: 'ff0000ff'
            };
        },

        /**
         * Formats hexadecimal color values.
         *
         * @method _normalizeHexValue
         * @param {String} hex
         * @return {String} formatted hex color value
         * @protected
         */
        _normalizeHexValue: function(hex) {
            var padding = '';

            if (hex.length === 3) {
                padding = 'fffff';
            }
            else if (hex.length === 6) {
                padding = 'ff';
            }

            return (hex += padding);
        },

        /**
         * Syncs `HSVAPalette` UI on alpha `valueChange`.
         *
         * @method _onAlphaChange
         * @param {EventFacade} event
         * @protected
         */
        _onAlphaChange: function(event) {
            var instance = this,
                alpha,
                thumbXY,
                x,
                y,
                hue,
                saturation,
                value,
                rgbColor,
                rgbColorArray,
                hexValue;

            if (event.src !== AWidget.UI_SRC) {
                alpha = event.newVal;

                instance._resultView.setStyle('opacity', 1 - (alpha / MAX_ALPHA));

                thumbXY = instance._colorThumb.getXY();

                x = (thumbXY[0] - instance._hsContainerXY[0] + instance._colorThumbGutter);
                y = (thumbXY[1] - instance._hsContainerXY[1] + instance._colorThumbGutter);

                hue = instance._calculateHue(x);
                saturation = instance._calculateSaturation(y);
                value = MAX_VALUE - instance._valueSlider.get('value');

                rgbColor = instance._calculateRGBA(hue, saturation, value, MAX_ALPHA - alpha);
                rgbColorArray = AColor.toArray(rgbColor);
                hexValue = instance._getHexValue(AColor.toHex(rgbColor), rgbColorArray);

                instance._setFieldValue(instance._outputContainer, hexValue);
                instance._setFieldValue(instance._aContainer, MAX_ALPHA - alpha);
                instance._setFieldValue(instance._rContainer, rgbColorArray[0]);
                instance._setFieldValue(instance._gContainer, rgbColorArray[1]);
                instance._setFieldValue(instance._bContainer, rgbColorArray[2]);
            }
        },

        /**
         * Renders alpha slider container.
         *
         * @method _renderAlphaSliderContainer
         * @protected
         */
        _renderAlphaSliderContainer: function() {
            var instance = this,
                sliderWrapper;

            sliderWrapper = A.Node.create(this.TPL_ALPHA_SLIDER_WRAPPER);
            instance._valueSliderWrapper.insert(sliderWrapper, 'after');

            instance._alphaSliderContainer = sliderWrapper.one(
                '.' + CSS_ALPHA_SLIDER_CONTAINER
            );
        },

        /**
         * Renders alpha field.
         *
         * @method _renderFields
         * @protected
         */
        _renderFields: function() {
            var instance = this;

            A.HSVAPalette.superclass._renderFields.apply(instance, arguments);

            instance._aContainer = instance._renderField(
                instance._labelValueHSVContainer, {
                    label: instance.get('strings').a,
                    max: 255,
                    maxlength: 3,
                    min: 0,
                    suffix: '-a',
                    type: 'alpha',
                    unit: '',
                    value: MAX_ALPHA
                }
            );
        },

        /**
         * Renders view container content.
         *
         * @method _renderViewContainerContent
         * @protected
         */
        _renderViewContainerContent: function() {
            var instance = this;

            A.HSVAPalette.superclass._renderViewContainerContent.call(instance);

            instance._renderAlphaSliderContainer();
        },

        /**
         * Updates the alpha slider's value according to the currently selected
         * hex color.
         *
         * @method _updateAlphaValue
         * @protected
         */
        _updateAlphaValue: function() {
            var alpha = this._getFieldValue(this._aContainer),
                hex = '#' + this.get('selected').substr(0, 6);

            this._alphaSlider.set(
                'value',
                MAX_ALPHA - alpha, {
                    src: AWidget.UI_SRC
                }
            );

            this._alphaSliderContainer.setStyle('backgroundColor', hex);

            this._resultView.setStyle('opacity', alpha / MAX_ALPHA);
        }
    }, {

        /**
         * Static property provides a string to identify the class.
         *
         * @property NAME
         * @type {String}
         * @static
         */
        NAME: 'hsva-palette',

        /**
         * Static property provides a string to identify the namespace.
         *
         * @property NS
         * @type {String}
         * @static
         */
        NS: 'hsva-palette'
    });

A.HSVAPalette = HSVAPalette;
<commit_msg>AUI-3143 - Use Clay 2 markup in HSVA Palette Color Picker<commit_after>/**
 * The Color Picker Component
 *
 * @module aui-color-picker
 * @submodule aui-hsva-palette
 */

var Lang = A.Lang,
    AColor = A.Color,
    AWidget = A.Widget,

    getClassName = A.getClassName,

    MIN_ALPHA = 0,

    MAX_ALPHA = 255,
    MAX_HUE = 360,
    MAX_SATURATION = 100,
    MAX_VALUE = 100,

    MAXLEN_HEX = 8,

    CSS_CONTAINER_ALPHA = getClassName('hsv-container-alpha'),

    CSS_ALPHA_CANVAS = getClassName('hsv-alpha-canvas'),
    CSS_ALPHA_SLIDER_CONTAINER = getClassName('hsv-alpha-slider-container'),
    CSS_ALPHA_SLIDER_WRAPPER = getClassName('hsv-alpha-slider-wrapper'),
    CSS_ALPHA_THUMB = getClassName('hsv-alpha-thumb'),
    CSS_ALPHA_THUMB_IMAGE = getClassName('hsv-alpha-image'),

    REGEX_HEX_COLOR_ALPHA = /^([a-f0-9]{6}|[a-f0-9]{8}|[a-f0-9]{3})$/i,

    /**
     * A base class for `HSVAPalette`.
     *
     * @class A.HSVAPalette
     * @extends A.HSVPalette
     * @param {Object} config Object literal specifying widget configuration
     *      properties.
     * @constructor
     */
    HSVAPalette = A.Base.create('hsva-palette', A.HSVPalette, [], {
        TPL_ALPHA_CANVAS: '',

        TPL_ALPHA_SLIDER_WRAPPER: '

',

        TPL_ALPHA_THUMB: '',

        /**
         * Construction logic executed during `HSVAPalette` instantiation.
         * Lifecycle.
         *
         * @method initializer
         * @protected
         */
        initializer: function() {
            var instance = this;

            instance.set('fieldValidator.hex', REGEX_HEX_COLOR_ALPHA);

            instance.after('selectedChange', instance._updateAlphaValue, instance);

            instance.onceAfter('render', instance._updateAlphaValue, instance);
        },

        /**
         * Sets color after hex input `valueChange`.
         *
         * @method _afterHexInputChange
         * @param {EventFacade} event
         * @protected
         */
        _afterHexInputChange: function(event) {
            // YUI Code toHSVA from hex + alpha is broken, will remove the alpha
            // value
            var instance = this,
                hexColor = event.hexColor,
                alpha = hexColor.substr(6, 2),
                alphaDec = parseInt(alpha, 16);

            instance._alphaSlider.set(
                'value',
                MAX_ALPHA - alphaDec, {
                    src: AWidget.UI_SRC
                }
            );

            instance._alphaSliderContainer.setStyle('backgroundColor', hexColor);

            instance._resultView.setStyle('opacity', alphaDec / MAX_ALPHA);

            instance._setFieldValue(instance._aContainer, alphaDec);
        },

        /**
         * Calculates and returns RGB value from array of values.
         *
         * @method _calculateRGBArray
         * @param {Number} r
         * @param {Number} g
         * @param {Number} b
         * @return {String} RGB value
         * @protected
         */
        _calculateRGBArray: function(r, g, b) {
            var instance = this,
                alpha;

            alpha = 255 - instance._alphaSlider.get('value');

            return AColor.fromArray([r, g, b, alpha], 'RGBA');
        },

        /**
         * Calculates and returns RGB color value.
         *
         * @method _calculateRGBColor
         * @param {Number} hue
         * @param {Number} saturation
         * @param {Number} value
         * @return {String} RGB value
         * @protected
         */
        _calculateRGBColor: function(hue, saturation, value) {
            var instance = this,
                alpha = 255 - instance._alphaSlider.get('value');

            // TODO: Use feature checking instead
            if (A.UA.ie === 8) {
                return instance._calculateRGB(hue, saturation, value, alpha);
            }
            else {
                return instance._calculateRGBA(hue, saturation, value, alpha);
            }
        },

        /**
         * Calculates RGB color value from HSVA color value.
         *
         * @method _calculateRGBA
         * @param {Number} hue
         * @param {Number} saturation
         * @param {Number} value
         * @param {Number} alpha
         * @return {String} RGB value
         * @protected
         */
        _calculateRGBA: function(hue, saturation, value, alpha) {
            var rgbColor = 'rgb(255, 0, 0, 0)',
                hsvColor,
                tmp;

            if (Lang.toInt(hue) !== MAX_HUE || Lang.toInt(saturation) !== MAX_SATURATION || Lang.toInt(value) !== MAX_VALUE) {
                hsvColor = 'hsva(' + (hue === MAX_HUE ? MAX_HUE - 1 : hue) + ', ' + saturation + '%, ' + value +
                    '%, ' + alpha + ')';

                rgbColor = AColor.toRGBA(hsvColor);

                // fix YUI bug on getting alpha - if it is 0, they return 1
                if (Lang.toInt(alpha) === 0) {
                    tmp = AColor.toArray(rgbColor);

                    tmp[3] = '0';

                    rgbColor = AColor.fromArray(tmp, 'RGBA');
                }
            }

            return rgbColor;
        },

        /**
         * Converts one color value type to another.
         *
         * @method _convertColor
         * @param {String} color value
         * @param {String} from type
         * @param {String} to type
         * @return {String} converted color value
         * @protected
         */
        _convertColor: function(value, from, to) {
            var out,
                outputHex;

            outputHex = (to === 'hex');

            if (from === 'hex') {
                value = '#' + value;
            }

            if (outputHex) {
                to = 'Hex';
            }
            else {
                to = (to + 'a').toUpperCase();
            }

            out = AColor['to' + to](value);

            if (outputHex) {
                out = out.substr(1);
            }

            return out;
        },

        /**
         * Returns container class name.
         *
         * @method _getContainerClassName
         * @return {String} class name
         * @protected
         */
        _getContainerClassName: function() {
            var instance = this,
                className;

            className = A.HSVAPalette.superclass._getContainerClassName.call(instance);

            className += ' ' + CSS_CONTAINER_ALPHA;

            return className;
        },

        /**
         * Returns hexadecimal color value.
         *
         * @method _getHexValue
         * @param {String} hexColor
         * @param {Array} rgbColorArray
         * @return {String} hex value
         * @protected
         */
        _getHexValue: function(hexColor, rgbColorArray) {
            // YUI doesn't have toRGBA method, we have to add alpha explicitly
            var alpha,
                result;

            alpha = parseInt(rgbColorArray[3], 10).toString(16);

            if (alpha.length === 1) {
                alpha = '0' + alpha;
            }

            result = hexColor + alpha;

            return result.substring(1);
        },

        /**
         * Returns HSV color values.
         *
         * @method _getHSVArray
         * @param {Number} hsv
         * @return {Array} hsv color array
         * @protected
         */
        _getHSVArray: function(hsv) {
            return AColor.toArray(hsv, 'HSVA');
        },

        /**
         * Renders alpha slider and binds events.
         *
         * @method _createAlphaSlider
         * @protected
         */
        _createAlphaSlider: function() {
            var instance = this,
                alphaThumbHeight,
                contentBox,
                slider;

            contentBox = instance.get('contentBox'),

            slider = new A.Slider({
                axis: 'y',
                min: MIN_ALPHA,
                max: MAX_ALPHA
            });

            slider.RAIL_TEMPLATE = instance.TPL_ALPHA_CANVAS;
            slider.THUMB_TEMPLATE = instance.TPL_ALPHA_THUMB;

            slider.render(instance._alphaSliderContainer);

            alphaThumbHeight = contentBox.one('.' + CSS_ALPHA_THUMB_IMAGE).get('offsetHeight');

            slider.set(
                'length',
                instance._alphaSliderContainer.get('offsetHeight') + (alphaThumbHeight / 2)
            );

            slider.on(['slideStart', 'railMouseDown'], instance._setHSContainerXY, instance);

            slider.on('valueChange', instance._onAlphaChange, instance);

            instance._alphaSlider = slider;
        },

        /**
         * Creates sliders.
         *
         * @method _createSliders
         * @protected
         */
        _createSliders: function() {
            var instance = this;

            A.HSVAPalette.superclass._createSliders.call(instance);

            instance._createAlphaSlider();
        },

        /**
         * Returns hex container configuration.
         *
         * @method _getHexContainerConfig
         * @return {Object} hex container config object
         * @protected
         */
        _getHexContainerConfig: function() {
            var instance = this;

            return {
                label: instance.get('strings').hex,
                maxlength: MAXLEN_HEX,
                suffix: '-hex',
                type: 'hex',
                unit: '',
                value: 'ff0000ff'
            };
        },

        /**
         * Formats hexadecimal color values.
         *
         * @method _normalizeHexValue
         * @param {String} hex
         * @return {String} formatted hex color value
         * @protected
         */
        _normalizeHexValue: function(hex) {
            var padding = '';

            if (hex.length === 3) {
                padding = 'fffff';
            }
            else if (hex.length === 6) {
                padding = 'ff';
            }

            return (hex += padding);
        },

        /**
         * Syncs `HSVAPalette` UI on alpha `valueChange`.
         *
         * @method _onAlphaChange
         * @param {EventFacade} event
         * @protected
         */
        _onAlphaChange: function(event) {
            var instance = this,
                alpha,
                thumbXY,
                x,
                y,
                hue,
                saturation,
                value,
                rgbColor,
                rgbColorArray,
                hexValue;

            if (event.src !== AWidget.UI_SRC) {
                alpha = event.newVal;

                instance._resultView.setStyle('opacity', 1 - (alpha / MAX_ALPHA));

                thumbXY = instance._colorThumb.getXY();

                x = (thumbXY[0] - instance._hsContainerXY[0] + instance._colorThumbGutter);
                y = (thumbXY[1] - instance._hsContainerXY[1] + instance._colorThumbGutter);

                hue = instance._calculateHue(x);
                saturation = instance._calculateSaturation(y);
                value = MAX_VALUE - instance._valueSlider.get('value');

                rgbColor = instance._calculateRGBA(hue, saturation, value, MAX_ALPHA - alpha);
                rgbColorArray = AColor.toArray(rgbColor);
                hexValue = instance._getHexValue(AColor.toHex(rgbColor), rgbColorArray);

                instance._setFieldValue(instance._outputContainer, hexValue);
                instance._setFieldValue(instance._aContainer, MAX_ALPHA - alpha);
                instance._setFieldValue(instance._rContainer, rgbColorArray[0]);
                instance._setFieldValue(instance._gContainer, rgbColorArray[1]);
                instance._setFieldValue(instance._bContainer, rgbColorArray[2]);
            }
        },

        /**
         * Renders alpha slider container.
         *
         * @method _renderAlphaSliderContainer
         * @protected
         */
        _renderAlphaSliderContainer: function() {
            var instance = this,
                sliderWrapper;

            sliderWrapper = A.Node.create(this.TPL_ALPHA_SLIDER_WRAPPER);
            instance._valueSliderWrapper.insert(sliderWrapper, 'after');

            instance._alphaSliderContainer = sliderWrapper.one(
                '.' + CSS_ALPHA_SLIDER_CONTAINER
            );
        },

        /**
         * Renders alpha field.
         *
         * @method _renderFields
         * @protected
         */
        _renderFields: function() {
            var instance = this;

            A.HSVAPalette.superclass._renderFields.apply(instance, arguments);

            instance._aContainer = instance._renderField(
                instance._labelValueHSVContainer, {
                    label: instance.get('strings').a,
                    max: 255,
                    maxlength: 3,
                    min: 0,
                    suffix: '-a',
                    type: 'alpha',
                    unit: '',
                    value: MAX_ALPHA
                }
            );
        },

        /**
         * Renders view container content.
         *
         * @method _renderViewContainerContent
         * @protected
         */
        _renderViewContainerContent: function() {
            var instance = this;

            A.HSVAPalette.superclass._renderViewContainerContent.call(instance);

            instance._renderAlphaSliderContainer();
        },

        /**
         * Updates the alpha slider's value according to the currently selected
         * hex color.
         *
         * @method _updateAlphaValue
         * @protected
         */
        _updateAlphaValue: function() {
            var alpha = this._getFieldValue(this._aContainer),
                hex = '#' + this.get('selected').substr(0, 6);

            this._alphaSlider.set(
                'value',
                MAX_ALPHA - alpha, {
                    src: AWidget.UI_SRC
                }
            );

            this._alphaSliderContainer.setStyle('backgroundColor', hex);

            this._resultView.setStyle('opacity', alpha / MAX_ALPHA);
        }
    }, {

        /**
         * Static property provides a string to identify the class.
         *
         * @property NAME
         * @type {String}
         * @static
         */
        NAME: 'hsva-palette',

        /**
         * Static property provides a string to identify the namespace.
         *
         * @property NS
         * @type {String}
         * @static
         */
        NS: 'hsva-palette'
    });

A.HSVAPalette = HSVAPalette;
<|endoftext|>


<commit_before>/*
Copyright 2017 New Vector Ltd

Licensed 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.
*/
import {Store} from 'flux/utils';
import dis from '../dispatcher';
import GroupStore from './GroupStore';
import Analytics from '../Analytics';
import * as RoomNotifs from "../RoomNotifs";
import MatrixClientPeg from '../MatrixClientPeg';

const INITIAL_STATE = {
    orderedTags: null,
    orderedTagsAccountData: null,
    hasSynced: false,
    joinedGroupIds: null,

    selectedTags: [],
    // Last selected tag when shift was not being pressed
    anchorTag: null,
};

/**
 * A class for storing application state for ordering tags in the TagPanel.
 */
class TagOrderStore extends Store {
    constructor() {
        super(dis);

        // Initialise state
        this._state = Object.assign({}, INITIAL_STATE);
    }

    _setState(newState) {
        this._state = Object.assign(this._state, newState);
        this.__emitChange();
    }

    __onDispatch(payload) {
        switch (payload.action) {
            // Initialise state after initial sync
            case 'view_room': {
                const relatedGroupIds = GroupStore.getGroupIdsForRoomId(payload.room_id);
                this._updateBadges(relatedGroupIds);
                break;
            }
            case 'MatrixActions.sync': {
                if (payload.state === 'SYNCING' || payload.state === 'PREPARED') {
                    this._updateBadges();
                }
                if (!(payload.prevState !== 'PREPARED' && payload.state === 'PREPARED')) {
                    break;
                }
                const tagOrderingEvent = payload.matrixClient.getAccountData('im.vector.web.tag_ordering');
                const tagOrderingEventContent = tagOrderingEvent ? tagOrderingEvent.getContent() : {};
                this._setState({
                    orderedTagsAccountData: tagOrderingEventContent.tags || null,
                    removedTagsAccountData: tagOrderingEventContent.removedTags || null,
                    hasSynced: true,
                });
                this._updateOrderedTags();
                break;
            }
            // Get ordering from account data
            case 'MatrixActions.accountData': {
                if (payload.event_type !== 'im.vector.web.tag_ordering') break;

                // Ignore remote echos caused by this store so as to avoid setting
                // state back to old state.
                if (payload.event_content._storeId === this.getStoreId()) break;

                this._setState({
                    orderedTagsAccountData: payload.event_content ? payload.event_content.tags : null,
                    removedTagsAccountData: payload.event_content ? payload.event_content.removedTags : null,
                });
                this._updateOrderedTags();
                break;
            }
            // Initialise the state such that if account data is unset, default to joined groups
            case 'GroupActions.fetchJoinedGroups.success': {
                this._setState({
                    joinedGroupIds: payload.result.groups.sort(), // Sort lexically
                    hasFetchedJoinedGroups: true,
                });
                this._updateOrderedTags();
                break;
            }
            case 'TagOrderActions.moveTag.pending': {
                // Optimistic update of a moved tag
                this._setState({
                    orderedTags: payload.request.tags,
                    removedTagsAccountData: payload.request.removedTags,
                });
                break;
            }
            case 'TagOrderActions.removeTag.pending': {
                // Optimistic update of a removed tag
                this._setState({
                    removedTagsAccountData: payload.request.removedTags,
                });
                this._updateOrderedTags();
                break;
            }
            case 'select_tag': {
                let newTags = [];
                // Shift-click semantics
                if (payload.shiftKey) {
                    // Select range of tags
                    let start = this._state.orderedTags.indexOf(this._state.anchorTag);
                    let end = this._state.orderedTags.indexOf(payload.tag);

                    if (start === -1) {
                        start = end;
                    }
                    if (start > end) {
                        const temp = start;
                        start = end;
                        end = temp;
                    }
                    newTags = payload.ctrlOrCmdKey ? this._state.selectedTags : [];
                    newTags = [...new Set(
                        this._state.orderedTags.slice(start, end + 1).concat(newTags),
                    )];
                } else {
                    if (payload.ctrlOrCmdKey) {
                        // Toggle individual tag
                        if (this._state.selectedTags.includes(payload.tag)) {
                            newTags = this._state.selectedTags.filter((t) => t !== payload.tag);
                        } else {
                            newTags = [...this._state.selectedTags, payload.tag];
                        }
                    } else {
                        // Select individual tag
                        newTags = [payload.tag];
                    }
                    // Only set the anchor tag if the tag was previously unselected, otherwise
                    // the next range starts with an unselected tag.
                    if (!this._state.selectedTags.includes(payload.tag)) {
                        this._setState({
                            anchorTag: payload.tag,
                        });
                    }
                }

                this._setState({
                    selectedTags: newTags,
                });

                Analytics.trackEvent('FilterStore', 'select_tag');
            }
            break;
            case 'deselect_tags':
                this._setState({
                    selectedTags: [],
                });
                Analytics.trackEvent('FilterStore', 'deselect_tags');
            break;
            case 'on_logged_out': {
                // Reset state without pushing an update to the view, which generally assumes that
                // the matrix client isn't `null` and so causing a re-render will cause NPEs.
                this._state = Object.assign({}, INITIAL_STATE);
                break;
            }
        }
    }

    _updateBadges(groupIds = this._state.joinedGroupIds) {
        if (groupIds && groupIds.length) {
            const client = MatrixClientPeg.get();
            const changedBadges = {};
            groupIds.forEach(groupId => {
                const rooms =
                    GroupStore.getGroupRooms(groupId)
                    .map(r => client.getRoom(r.roomId)) // to Room objects
                    .filter(r => !!r);   // filter out rooms we haven't joined from the group
                const badge = rooms && RoomNotifs.aggregateNotificationCount(rooms);
                changedBadges[groupId] = (badge && badge.count !== 0) ? badge : undefined;
            });
            const newBadges = Object.assign({}, this._state.badges, changedBadges);
            this._setState({badges: newBadges});
        }
    }

    _updateOrderedTags() {
        this._setState({
            orderedTags:
                this._state.hasSynced &&
                this._state.hasFetchedJoinedGroups ?
                    this._mergeGroupsAndTags() : null,
        });
    }

    _mergeGroupsAndTags() {
        const groupIds = this._state.joinedGroupIds || [];
        const tags = this._state.orderedTagsAccountData || [];
        const removedTags = new Set(this._state.removedTagsAccountData || []);

        const tagsToKeep = tags.filter(
            (t) => (t[0] !== '+' || groupIds.includes(t)) && !removedTags.has(t),
        );

        const groupIdsToAdd = groupIds.filter(
            (groupId) => !tags.includes(groupId) && !removedTags.has(groupId),
        );

        return tagsToKeep.concat(groupIdsToAdd);
    }

    getGroupBadge(groupId) {
        const badges = this._state.badges;
        return badges && badges[groupId];
    }

    getOrderedTags() {
        return this._state.orderedTags;
    }

    getRemovedTagsAccountData() {
        return this._state.removedTagsAccountData;
    }

    getStoreId() {
        // Generate a random ID to prevent this store from clobbering its
        // state with redundant remote echos.
        if (!this._id) this._id = Math.random().toString(16).slice(2, 10);
        return this._id;
    }

    getSelectedTags() {
        return this._state.selectedTags;
    }
}

if (global.singletonTagOrderStore === undefined) {
    global.singletonTagOrderStore = new TagOrderStore();
}
export default global.singletonTagOrderStore;
<commit_msg>do more explicit check<commit_after>/*
Copyright 2017 New Vector Ltd

Licensed 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.
*/
import {Store} from 'flux/utils';
import dis from '../dispatcher';
import GroupStore from './GroupStore';
import Analytics from '../Analytics';
import * as RoomNotifs from "../RoomNotifs";
import MatrixClientPeg from '../MatrixClientPeg';

const INITIAL_STATE = {
    orderedTags: null,
    orderedTagsAccountData: null,
    hasSynced: false,
    joinedGroupIds: null,

    selectedTags: [],
    // Last selected tag when shift was not being pressed
    anchorTag: null,
};

/**
 * A class for storing application state for ordering tags in the TagPanel.
 */
class TagOrderStore extends Store {
    constructor() {
        super(dis);

        // Initialise state
        this._state = Object.assign({}, INITIAL_STATE);
    }

    _setState(newState) {
        this._state = Object.assign(this._state, newState);
        this.__emitChange();
    }

    __onDispatch(payload) {
        switch (payload.action) {
            // Initialise state after initial sync
            case 'view_room': {
                const relatedGroupIds = GroupStore.getGroupIdsForRoomId(payload.room_id);
                this._updateBadges(relatedGroupIds);
                break;
            }
            case 'MatrixActions.sync': {
                if (payload.state === 'SYNCING' || payload.state === 'PREPARED') {
                    this._updateBadges();
                }
                if (!(payload.prevState !== 'PREPARED' && payload.state === 'PREPARED')) {
                    break;
                }
                const tagOrderingEvent = payload.matrixClient.getAccountData('im.vector.web.tag_ordering');
                const tagOrderingEventContent = tagOrderingEvent ? tagOrderingEvent.getContent() : {};
                this._setState({
                    orderedTagsAccountData: tagOrderingEventContent.tags || null,
                    removedTagsAccountData: tagOrderingEventContent.removedTags || null,
                    hasSynced: true,
                });
                this._updateOrderedTags();
                break;
            }
            // Get ordering from account data
            case 'MatrixActions.accountData': {
                if (payload.event_type !== 'im.vector.web.tag_ordering') break;

                // Ignore remote echos caused by this store so as to avoid setting
                // state back to old state.
                if (payload.event_content._storeId === this.getStoreId()) break;

                this._setState({
                    orderedTagsAccountData: payload.event_content ? payload.event_content.tags : null,
                    removedTagsAccountData: payload.event_content ? payload.event_content.removedTags : null,
                });
                this._updateOrderedTags();
                break;
            }
            // Initialise the state such that if account data is unset, default to joined groups
            case 'GroupActions.fetchJoinedGroups.success': {
                this._setState({
                    joinedGroupIds: payload.result.groups.sort(), // Sort lexically
                    hasFetchedJoinedGroups: true,
                });
                this._updateOrderedTags();
                break;
            }
            case 'TagOrderActions.moveTag.pending': {
                // Optimistic update of a moved tag
                this._setState({
                    orderedTags: payload.request.tags,
                    removedTagsAccountData: payload.request.removedTags,
                });
                break;
            }
            case 'TagOrderActions.removeTag.pending': {
                // Optimistic update of a removed tag
                this._setState({
                    removedTagsAccountData: payload.request.removedTags,
                });
                this._updateOrderedTags();
                break;
            }
            case 'select_tag': {
                let newTags = [];
                // Shift-click semantics
                if (payload.shiftKey) {
                    // Select range of tags
                    let start = this._state.orderedTags.indexOf(this._state.anchorTag);
                    let end = this._state.orderedTags.indexOf(payload.tag);

                    if (start === -1) {
                        start = end;
                    }
                    if (start > end) {
                        const temp = start;
                        start = end;
                        end = temp;
                    }
                    newTags = payload.ctrlOrCmdKey ? this._state.selectedTags : [];
                    newTags = [...new Set(
                        this._state.orderedTags.slice(start, end + 1).concat(newTags),
                    )];
                } else {
                    if (payload.ctrlOrCmdKey) {
                        // Toggle individual tag
                        if (this._state.selectedTags.includes(payload.tag)) {
                            newTags = this._state.selectedTags.filter((t) => t !== payload.tag);
                        } else {
                            newTags = [...this._state.selectedTags, payload.tag];
                        }
                    } else {
                        // Select individual tag
                        newTags = [payload.tag];
                    }
                    // Only set the anchor tag if the tag was previously unselected, otherwise
                    // the next range starts with an unselected tag.
                    if (!this._state.selectedTags.includes(payload.tag)) {
                        this._setState({
                            anchorTag: payload.tag,
                        });
                    }
                }

                this._setState({
                    selectedTags: newTags,
                });

                Analytics.trackEvent('FilterStore', 'select_tag');
            }
            break;
            case 'deselect_tags':
                this._setState({
                    selectedTags: [],
                });
                Analytics.trackEvent('FilterStore', 'deselect_tags');
            break;
            case 'on_logged_out': {
                // Reset state without pushing an update to the view, which generally assumes that
                // the matrix client isn't `null` and so causing a re-render will cause NPEs.
                this._state = Object.assign({}, INITIAL_STATE);
                break;
            }
        }
    }

    _updateBadges(groupIds = this._state.joinedGroupIds) {
        if (groupIds && groupIds.length) {
            const client = MatrixClientPeg.get();
            const changedBadges = {};
            groupIds.forEach(groupId => {
                const rooms =
                    GroupStore.getGroupRooms(groupId)
                    .map(r => client.getRoom(r.roomId)) // to Room objects
                    .filter(r => r !== null && r !== undefined);   // filter out rooms we haven't joined from the group
                const badge = rooms && RoomNotifs.aggregateNotificationCount(rooms);
                changedBadges[groupId] = (badge && badge.count !== 0) ? badge : undefined;
            });
            const newBadges = Object.assign({}, this._state.badges, changedBadges);
            this._setState({badges: newBadges});
        }
    }

    _updateOrderedTags() {
        this._setState({
            orderedTags:
                this._state.hasSynced &&
                this._state.hasFetchedJoinedGroups ?
                    this._mergeGroupsAndTags() : null,
        });
    }

    _mergeGroupsAndTags() {
        const groupIds = this._state.joinedGroupIds || [];
        const tags = this._state.orderedTagsAccountData || [];
        const removedTags = new Set(this._state.removedTagsAccountData || []);

        const tagsToKeep = tags.filter(
            (t) => (t[0] !== '+' || groupIds.includes(t)) && !removedTags.has(t),
        );

        const groupIdsToAdd = groupIds.filter(
            (groupId) => !tags.includes(groupId) && !removedTags.has(groupId),
        );

        return tagsToKeep.concat(groupIdsToAdd);
    }

    getGroupBadge(groupId) {
        const badges = this._state.badges;
        return badges && badges[groupId];
    }

    getOrderedTags() {
        return this._state.orderedTags;
    }

    getRemovedTagsAccountData() {
        return this._state.removedTagsAccountData;
    }

    getStoreId() {
        // Generate a random ID to prevent this store from clobbering its
        // state with redundant remote echos.
        if (!this._id) this._id = Math.random().toString(16).slice(2, 10);
        return this._id;
    }

    getSelectedTags() {
        return this._state.selectedTags;
    }
}

if (global.singletonTagOrderStore === undefined) {
    global.singletonTagOrderStore = new TagOrderStore();
}
export default global.singletonTagOrderStore;
<|endoftext|>


<commit_before>YUI.add('aui-form-builder-tests', function(Y) {

    var suite = new Y.Test.Suite('aui-form-builder');

    var radioField = new Y.FormBuilderRadioField({
        label: 'Colours',
        type: 'radio',
        options: [
            {
                label: 'Red',
                value: 'red'
                },
            {
                label: 'Green',
                value: 'green'
                },
            {
                label: 'Blue',
                value: 'blue'
                }
            ]
    });

    var textAreaField = new Y.FormBuilderTextAreaField({
        label: 'Custom Textarea Label',
        type: 'textarea',
        hiddenAttributes: ['label', 'type']
    });

    var formBuilder = new Y.FormBuilder({
        boundingBox: '#formBuilder',
        availableFields: [
            {
                iconClass: 'form-builder-field-icon form-builder-field-icon-radio',
                label: 'Radio Buttons',
                type: 'radio'
            }
        ],
        fields: [radioField, textAreaField]
    }).render();

    suite.add(new Y.Test.Case({
        name: 'FormBuilderRadioField',
        'predefined value should default to first option value': function() {
            var options = radioField.get('options'),
                predefinedValue = radioField.get('predefinedValue');

            Y.Assert.areEqual(options[0].value, predefinedValue);
        }
    }));

    suite.add(new Y.Test.Case({
        name: 'Form Builder clone field tests',

        'cloned field should inherit hiddenAttributes': function() {
            formBuilder.duplicateField(textAreaField);

            var textAreaFields = [];

            formBuilder.get('fields').each(
                function(item, index) {
                    if (item.name === 'form-builder-textarea-field') {
                        textAreaFields.push(item);
                    }
                }
            );

            var firstTextArea = textAreaFields[0];
            var clonedTextArea = textAreaFields[1];

            // Fields should have the same attributes hidden in the settings panel
            Y.Assert.areSame(firstTextArea.get('hiddenAttributes'), clonedTextArea.get('hiddenAttributes'));

            // Values from those hidden fields should be cloned anyways
            Y.Assert.areSame(firstTextArea.get('label'), clonedTextArea.get('label'));
            Y.Assert.areSame(firstTextArea.get('type'), clonedTextArea.get('type'));
        }
    }));

    Y.Test.Runner.add(suite);

}, '', {
    requires: ['test', 'aui-form-builder']
});
<commit_msg>AUI-1346 Linting<commit_after>YUI.add('aui-form-builder-tests', function(Y) {

    var suite = new Y.Test.Suite('aui-form-builder');

    var radioField = new Y.FormBuilderRadioField({
        label: 'Colours',
        type: 'radio',
        options: [
            {
                label: 'Red',
                value: 'red'
                },
            {
                label: 'Green',
                value: 'green'
                },
            {
                label: 'Blue',
                value: 'blue'
                }
            ]
    });

    var textAreaField = new Y.FormBuilderTextAreaField({
        label: 'Custom Textarea Label',
        type: 'textarea',
        hiddenAttributes: ['label', 'type']
    });

    var formBuilder = new Y.FormBuilder({
        boundingBox: '#formBuilder',
        availableFields: [
            {
                iconClass: 'form-builder-field-icon form-builder-field-icon-radio',
                label: 'Radio Buttons',
                type: 'radio'
            }
        ],
        fields: [radioField, textAreaField]
    }).render();

    suite.add(new Y.Test.Case({
        name: 'FormBuilderRadioField',
        'predefined value should default to first option value': function() {
            var options = radioField.get('options'),
                predefinedValue = radioField.get('predefinedValue');

            Y.Assert.areEqual(options[0].value, predefinedValue);
        }
    }));

    suite.add(new Y.Test.Case({
        name: 'Form Builder clone field tests',

        'cloned field should inherit hiddenAttributes': function() {
            formBuilder.duplicateField(textAreaField);

            var textAreaFields = [];

            formBuilder.get('fields').each(
                function(item) {
                    if (item.name === 'form-builder-textarea-field') {
                        textAreaFields.push(item);
                    }
                }
            );

            var firstTextArea = textAreaFields[0];
            var clonedTextArea = textAreaFields[1];

            // Fields should have the same attributes hidden in the settings panel
            Y.Assert.areSame(firstTextArea.get('hiddenAttributes'), clonedTextArea.get('hiddenAttributes'));

            // Values from those hidden fields should be cloned anyways
            Y.Assert.areSame(firstTextArea.get('label'), clonedTextArea.get('label'));
            Y.Assert.areSame(firstTextArea.get('type'), clonedTextArea.get('type'));
        }
    }));

    Y.Test.Runner.add(suite);

}, '', {
    requires: ['test', 'aui-form-builder']
});
<|endoftext|>


<commit_before>define([
	"dojo/_base/declare", // declare
	"dojo/dom-class", // domClass.add
	"dojo/sniff", // has("ie") has("opera")
	"./TextBox"
], function(declare, domClass, has, TextBox){

	// module:
	//		dijit/form/SimpleTextarea

	return declare("dijit.form.SimpleTextarea", TextBox, {
		// summary:
		//		A simple textarea that degrades, and responds to
		//		minimal LayoutContainer usage, and works with dijit/form/Form.
		//		Doesn't automatically size according to input, like Textarea.
		//
		// example:
		//	|	<textarea data-dojo-type="dijit/form/SimpleTextarea" name="foo" value="bar" rows=30 cols=40></textarea>
		//
		// example:
		//	|	new SimpleTextarea({ rows:20, cols:30 }, "foo");

		baseClass: "dijitTextBox dijitTextArea",

		// rows: Number
		//		The number of rows of text.
		rows: "3",

		// rows: Number
		//		The number of characters per line.
		cols: "20",

		templateString: "<textarea ${!nameAttrSetting} data-dojo-attach-point='focusNode,containerNode,textbox' autocomplete='off'></textarea>",

		postMixInProperties: function(){
			// Copy value from srcNodeRef, unless user specified a value explicitly (or there is no srcNodeRef)
			// TODO: parser will handle this in 2.0
			if(!this.value && this.srcNodeRef){
				this.value = this.srcNodeRef.value;
			}
			this.inherited(arguments);
		},

		buildRendering: function(){
			this.inherited(arguments);
			if(has("ie") && this.cols){ // attribute selectors is not supported in IE6
				domClass.add(this.textbox, "dijitTextAreaCols");
			}
		},

		filter: function(/*String*/ value){
			// Override TextBox.filter to deal with newlines... specifically (IIRC) this is for IE which writes newlines
			// as \r\n instead of just \n
			if(value){
				value = value.replace(/\r/g, "");
			}
			return this.inherited(arguments);
		},

		_onInput: function(/*Event?*/ e){
			// Override TextBox._onInput() to enforce maxLength restriction
			if(this.maxLength){
				var maxLength = parseInt(this.maxLength);
				var value = this.textbox.value.replace(/\r/g, '');
				var overflow = value.length - maxLength;
				if(overflow > 0){
					var textarea = this.textbox;
					if(textarea.selectionStart){
						var pos = textarea.selectionStart;
						var cr = 0;
						if(has("opera")){
							cr = (this.textbox.value.substring(0, pos).match(/\r/g) || []).length;
						}
						this.textbox.value = value.substring(0, pos - overflow - cr) + value.substring(pos - cr);
						textarea.setSelectionRange(pos - overflow, pos - overflow);
					}else if(this.ownerDocument.selection){ //IE
						textarea.focus();
						var range = this.ownerDocument.selection.createRange();
						// delete overflow characters
						range.moveStart("character", -overflow);
						range.text = '';
						// show cursor
						range.select();
					}
				}
			}
			this.inherited(arguments);
		}
	});
});
<commit_msg>SimpleTextArea: remove nameAttrSetting variable; it was only needed for IE6/IE7<commit_after>define([
	"dojo/_base/declare", // declare
	"dojo/dom-class", // domClass.add
	"dojo/sniff", // has("ie") has("opera")
	"./TextBox"
], function(declare, domClass, has, TextBox){

	// module:
	//		dijit/form/SimpleTextarea

	return declare("dijit.form.SimpleTextarea", TextBox, {
		// summary:
		//		A simple textarea that degrades, and responds to
		//		minimal LayoutContainer usage, and works with dijit/form/Form.
		//		Doesn't automatically size according to input, like Textarea.
		//
		// example:
		//	|	<textarea data-dojo-type="dijit/form/SimpleTextarea" name="foo" value="bar" rows=30 cols=40></textarea>
		//
		// example:
		//	|	new SimpleTextarea({ rows:20, cols:30 }, "foo");

		baseClass: "dijitTextBox dijitTextArea",

		// rows: Number
		//		The number of rows of text.
		rows: "3",

		// rows: Number
		//		The number of characters per line.
		cols: "20",

		templateString: "<textarea data-dojo-attach-point='focusNode,containerNode,textbox' autocomplete='off'></textarea>",

		postMixInProperties: function(){
			// Copy value from srcNodeRef, unless user specified a value explicitly (or there is no srcNodeRef)
			// TODO: parser will handle this in 2.0
			if(!this.value && this.srcNodeRef){
				this.value = this.srcNodeRef.value;
			}
			this.inherited(arguments);
		},

		buildRendering: function(){
			this.inherited(arguments);
			if(has("ie") && this.cols){ // attribute selectors is not supported in IE6
				domClass.add(this.textbox, "dijitTextAreaCols");
			}
		},

		filter: function(/*String*/ value){
			// Override TextBox.filter to deal with newlines... specifically (IIRC) this is for IE which writes newlines
			// as \r\n instead of just \n
			if(value){
				value = value.replace(/\r/g, "");
			}
			return this.inherited(arguments);
		},

		_onInput: function(/*Event?*/ e){
			// Override TextBox._onInput() to enforce maxLength restriction
			if(this.maxLength){
				var maxLength = parseInt(this.maxLength);
				var value = this.textbox.value.replace(/\r/g, '');
				var overflow = value.length - maxLength;
				if(overflow > 0){
					var textarea = this.textbox;
					if(textarea.selectionStart){
						var pos = textarea.selectionStart;
						var cr = 0;
						if(has("opera")){
							cr = (this.textbox.value.substring(0, pos).match(/\r/g) || []).length;
						}
						this.textbox.value = value.substring(0, pos - overflow - cr) + value.substring(pos - cr);
						textarea.setSelectionRange(pos - overflow, pos - overflow);
					}else if(this.ownerDocument.selection){ //IE
						textarea.focus();
						var range = this.ownerDocument.selection.createRange();
						// delete overflow characters
						range.moveStart("character", -overflow);
						range.text = '';
						// show cursor
						range.select();
					}
				}
			}
			this.inherited(arguments);
		}
	});
});
<|endoftext|>


<commit_before>'use babel';

import { Disposable, CompositeDisposable } from 'atom'; // eslint-disable-line
import { loadFileInfoAsync } from 'filesize-calculator';
import filesizeView from './filesize-view';

export default {
  config: {
    UseDecimal: {
      type: 'boolean',
      default: false,
    },
    EnablePopupAppearance: {
      type: 'boolean',
      default: true,
    },
    DisplayFullDayTimeOnPopup: {
      type: 'boolean',
      default: true,
    },
    DisplayGzippedSizeOnPopup: {
      type: 'boolean',
      default: true,
    },
  },

  workspace: null,
  filesizeView: null,
  disposables: null,
  view: null,
  tile: null,
  status: 'activating',

  activate() {
    // Requiring atom components
    this.workspace = atom.views.getView(atom.workspace);
    this.disposables = new CompositeDisposable();
    // Getting the filesize view
    this.view = filesizeView(this.workspace);
    // Trigger initial UI refresh
    return this.scheduleSizeUpdate();
  },

  registerActionEvents() {
    if (this.status !== 'active') {
      // Register action events
      const boundScheduleUpdate = this.scheduleSizeUpdate.bind(this);
      this.disposables.add(atom.workspace.onDidChangeActivePaneItem(boundScheduleUpdate));
      this.workspace.addEventListener('core:save', boundScheduleUpdate, false);
      this.disposables.add(new Disposable(() => {
        this.workspace.removeEventListener('core:save', boundScheduleUpdate);
      }));
      this.status = 'active';
    }
    return this.status;
  },

  scheduleSizeUpdate() {
    this.registerActionEvents();
    const editor = atom.workspace.getActivePaneItem() || {};
    // Atom TextEditor keep the file string in the buffer object
    const editorBuffer = editor.buffer;
    // Check if we're dealing with a text editor, an image editor or neither
    const file = (editorBuffer) ? editorBuffer.file : editor.file;
    if (file) {
      return loadFileInfoAsync(file.path)
        .then(this.view.refresh)
        .catch(this.view.clean);
    }
    return Promise.resolve(this.view.clean());
  },

  consumeStatusBar(statusBar) {
    const element = this.view.container;
    if (element) {
      this.tile = statusBar.addLeftTile({ item: element, priority: 25 });
    }
    return this;
  },

  deactivate() {
    if (this.view) this.view.destroy();
    if (this.tile) this.tile.destroy();
    if (this.disposables) this.disposables.dispose();
    this.view = null;
    this.tile = null;
    this.status = 'disabled';
    return this;
  },
};
<commit_msg>Fix change in Atom core:save by defering the update to next cycle<commit_after>'use babel';

import { Disposable, CompositeDisposable } from 'atom'; // eslint-disable-line
import { loadFileInfoAsync } from 'filesize-calculator';
import filesizeView from './filesize-view';

export default {
  config: {
    UseDecimal: {
      type: 'boolean',
      default: false,
    },
    EnablePopupAppearance: {
      type: 'boolean',
      default: true,
    },
    DisplayFullDayTimeOnPopup: {
      type: 'boolean',
      default: true,
    },
    DisplayGzippedSizeOnPopup: {
      type: 'boolean',
      default: true,
    },
  },

  workspace: null,
  filesizeView: null,
  disposables: null,
  view: null,
  tile: null,
  status: 'activating',

  activate() {
    // Requiring atom components
    this.workspace = atom.views.getView(atom.workspace);
    this.disposables = new CompositeDisposable();
    // Getting the filesize view
    this.view = filesizeView(this.workspace);
    // Trigger initial UI refresh
    return this.scheduleSizeUpdate();
  },

  registerActionEvents() {
    if (this.status !== 'active') {
      // Register action events
      const boundScheduleUpdate = this.scheduleSizeUpdate.bind(this);
      this.disposables.add(atom.workspace.onDidChangeActivePaneItem(boundScheduleUpdate));
      this.workspace.addEventListener('core:save', boundScheduleUpdate, false);
      this.disposables.add(new Disposable(() => {
        this.workspace.removeEventListener('core:save', boundScheduleUpdate);
      }));
      this.status = 'active';
    }
    return this.status;
  },

  scheduleSizeUpdate() {
    setTimeout(() => {
      this.registerActionEvents();
      const editor = atom.workspace.getActivePaneItem() || {};
      // Atom TextEditor keep the file string in the buffer object
      const editorBuffer = editor.buffer;
      // Check if we're dealing with a text editor, an image editor or neither
      const file = (editorBuffer) ? editorBuffer.file : editor.file;
      if (file) {
        return loadFileInfoAsync(file.path)
          .then(this.view.refresh)
          .catch(this.view.clean);
      }
      return Promise.resolve(this.view.clean());
    }, 0)
  },

  consumeStatusBar(statusBar) {
    const element = this.view.container;
    if (element) {
      this.tile = statusBar.addLeftTile({ item: element, priority: 25 });
    }
    return this;
  },

  deactivate() {
    if (this.view) this.view.destroy();
    if (this.tile) this.tile.destroy();
    if (this.disposables) this.disposables.dispose();
    this.view = null;
    this.tile = null;
    this.status = 'disabled';
    return this;
  },
};
<|endoftext|>


<commit_before>/*
 * flatiron.js: An elegant blend of convention and configuration for building apps in Node.js and the browser.
 *
 * Copyright(c) 2011 Nodejitsu Inc. <info@nodejitsu.com>
 * MIT LICENCE
 *
 */

var fs = require('fs'),
    broadway = require('broadway');

var flatiron = exports,
    _app;

//
// ### Export core `flatiron` modules
//
flatiron.common    = broadway.common.mixin({}, broadway.common, require('./flatiron/common'));
flatiron.constants = require('./flatiron/constants');
flatiron.App       = require('./flatiron/app').App;

//
// ### Expose core `flatiron` plugins
// Hoist those up from `broadway` and define each of
// the `flatiron` plugins as a lazy loaded `require` statement
//
flatiron.plugins = broadway.plugins;
fs.readdirSync(__dirname + '/flatiron/plugins').forEach(function (plugin) {
  plugin = plugin.replace('.js', '');
  flatiron.plugins.__defineGetter__(plugin, function () {
    return require('./flatiron/plugins/' + plugin);
  });
});

//
// ### getter @app {flatiron.App}
// Gets the default top-level Application for `flatiron`
//
flatiron.__defineGetter__('app', function () {
  if (!_app) {
    _app = new flatiron.App();
  }

  return _app;
});

//
// ### setter @app {flatiron.App}
// Sets the default top-level Application for `flatiron`
//
flatiron.__defineSetter__('app', function (value) {
  _app = value;
});

//
// ### function createApp (options)
// #### @options {Object} Options for the application to create
// Creates a new instance of `flatiron.App` with the
// specified `options`.
//
flatiron.createApp = function (options) {
  return new flatiron.App(options);
};<commit_msg>[fix] `.mixin` on top of `broadway.common` to ensure getters are copied<commit_after>/*
 * flatiron.js: An elegant blend of convention and configuration for building apps in Node.js and the browser.
 *
 * Copyright(c) 2011 Nodejitsu Inc. <info@nodejitsu.com>
 * MIT LICENCE
 *
 */

var fs = require('fs'),
    broadway = require('broadway');

var flatiron = exports,
    _app;

//
// ### Export core `flatiron` modules
//
flatiron.common    = broadway.common.mixin(broadway.common, require('./flatiron/common'));
flatiron.constants = require('./flatiron/constants');
flatiron.App       = require('./flatiron/app').App;

//
// ### Expose core `flatiron` plugins
// Hoist those up from `broadway` and define each of
// the `flatiron` plugins as a lazy loaded `require` statement
//
flatiron.plugins = broadway.plugins;
fs.readdirSync(__dirname + '/flatiron/plugins').forEach(function (plugin) {
  plugin = plugin.replace('.js', '');
  flatiron.plugins.__defineGetter__(plugin, function () {
    return require('./flatiron/plugins/' + plugin);
  });
});

//
// ### getter @app {flatiron.App}
// Gets the default top-level Application for `flatiron`
//
flatiron.__defineGetter__('app', function () {
  if (!_app) {
    _app = new flatiron.App();
  }

  return _app;
});

//
// ### setter @app {flatiron.App}
// Sets the default top-level Application for `flatiron`
//
flatiron.__defineSetter__('app', function (value) {
  _app = value;
});

//
// ### function createApp (options)
// #### @options {Object} Options for the application to create
// Creates a new instance of `flatiron.App` with the
// specified `options`.
//
flatiron.createApp = function (options) {
  return new flatiron.App(options);
};<|endoftext|>


<commit_before>// Agave.JS

define(function () {
  // object.getKeys() returns an array of keys
  var getKeys = function(){
    return Object.keys(this);
  };

  // object.getSize() return the numberof properties in the obj
  var getSize = function() {
    return Object.keys(this).length;
  };

  // object.getPath - get the value of the nested keys provided in the object.
  // If any are missing, return undefined. Used for checking JSON results.
  var getPath = function(pathItems) {
    var obj = this;
    var result;
    var still_checking = true;
    pathItems.forEach( function(pathItem) {
      if ( still_checking ) {
        if ( ! obj.hasOwnProperty(pathItem) ) {
          result = undefined;
          still_checking = false;
        } else {
          result = obj[pathItem];
        }
        obj = obj[pathItem];
      }
    })
    return result;
  };

  // array.findItem(test_function) returns the first item that matches the test_function
  var findItem = function(test_function){
    var arr = this;
    var last_index;
    var found = arr.some(function(item, index) {
      last_index = index;
      return test_function(item);
    });
    if ( found ) {
      return arr[last_index];
    } else {
      return null;
    }
  };

  // array.hasItem() returns true if an array has an item
  // string.hasSubstring() returns true if a string contains a substring
  var contains = function(item){
    return ( this.indexOf(item) !== -1);
  };

  Object.defineProperty( Array.prototype, "findItem", {value: findItem, enumerable: false});
  Object.defineProperty( Object.prototype, "getKeys", {value: getKeys, enumerable: false});
  Object.defineProperty( Object.prototype, "getSize", {value: getSize, enumerable: false});
  Object.defineProperty( Object.prototype, "getPath", {value: getPath, enumerable: false});
  Object.defineProperty( Array.prototype, "hasItem", {value: contains, enumerable: false});
  Object.defineProperty( String.prototype, "hasSubstring", {value: contains, enumerable: false});

  // NodeLists don't have .forEach() standard but the one from Array works fine
  NodeList.prototype.forEach = Array.prototype.forEach
});<commit_msg>Add String.prototype.forEach<commit_after>// Agave.JS

define(function () {
  // object.getKeys() returns an array of keys
  var getKeys = function(){
    return Object.keys(this);
  };

  // object.getSize() return the numberof properties in the obj
  var getSize = function() {
    return Object.keys(this).length;
  };

  // object.getPath - get the value of the nested keys provided in the object.
  // If any are missing, return undefined. Used for checking JSON results.
  var getPath = function(pathItems) {
    var obj = this;
    var result;
    var still_checking = true;
    pathItems.forEach( function(pathItem) {
      if ( still_checking ) {
        if ( ! obj.hasOwnProperty(pathItem) ) {
          result = undefined;
          still_checking = false;
        } else {
          result = obj[pathItem];
        }
        obj = obj[pathItem];
      }
    })
    return result;
  };

  // array.findItem(test_function) returns the first item that matches the test_function
  var findItem = function(test_function){
    var arr = this;
    var last_index;
    var found = arr.some(function(item, index) {
      last_index = index;
      return test_function(item);
    });
    if ( found ) {
      return arr[last_index];
    } else {
      return null;
    }
  };

  // array.hasItem() returns true if an array has an item
  // string.hasSubstring() returns true if a string contains a substring
  var contains = function(item){
    return ( this.indexOf(item) !== -1);
  };

  Object.defineProperty( Array.prototype, "findItem", {value: findItem, enumerable: false});
  Object.defineProperty( Object.prototype, "getKeys", {value: getKeys, enumerable: false});
  Object.defineProperty( Object.prototype, "getSize", {value: getSize, enumerable: false});
  Object.defineProperty( Object.prototype, "getPath", {value: getPath, enumerable: false});
  Object.defineProperty( Array.prototype, "hasItem", {value: contains, enumerable: false});
  Object.defineProperty( String.prototype, "hasSubstring", {value: contains, enumerable: false});

  // nodelists and strings don't have .forEach() standard but the one from Array works fine
  NodeList.prototype.forEach = Array.prototype.forEach
  String.prototype.forEach = Array.prototype.forEach
});<|endoftext|>


<commit_before>import PropTypes from 'prop-types'
import React, { Component, Fragment } from 'react'
import Context from './Context'
import withContext from 'components/Wrappers/withContext'
import ContentLink from 'components/ContentLink'
import { timeSince } from 'helpers/timeStampHelpers'
import LikeButton from 'components/Buttons/LikeButton'
import { Color } from 'constants/css'
import ProfilePic from 'components/ProfilePic'
import Button from 'components/Button'
import QuestionModal from './QuestionModal'
import StarMark from 'components/StarMark'
import UsernameText from 'components/Texts/UsernameText'
import Icon from 'components/Icon'
import { uploadComment } from 'helpers/requestHelpers'
import { css } from 'emotion'
import { connect } from 'react-redux'

class Heading extends Component {
  static propTypes = {
    action: PropTypes.string,
    onCommentSubmit: PropTypes.func.isRequired,
    onLikeContent: PropTypes.func.isRequired,
    attachedVideoShown: PropTypes.bool,
    contentObj: PropTypes.shape({
      id: PropTypes.number,
      commentId: PropTypes.number,
      replyId: PropTypes.number,
      rootObj: PropTypes.object,
      rootId: PropTypes.number,
      rootType: PropTypes.string,
      targetObj: PropTypes.object,
      timeStamp: PropTypes.oneOfType([PropTypes.number, PropTypes.string])
        .isRequired,
      type: PropTypes.string,
      uploader: PropTypes.object
    }).isRequired,
    myId: PropTypes.number,
    onPlayVideoClick: PropTypes.func
  }

  state = {
    questionModalShown: false
  }

  render() {
    const {
      contentObj: {
        uploader = {},
        rootObj = {},
        rootType,
        rootId,
        timeStamp,
        type
      }
    } = this.props
    const { questionModalShown } = this.state
    return (

        <ProfilePic
          style={{ width: '6rem', height: '6rem' }}
          userId={uploader.id}
          profilePicId={uploader.profilePicId}
        />

            {this.renderHeading()}

              {timeStamp ? `(${timeSince(timeStamp)})` : ''}

          {type === 'comment' &&
            rootType !== 'url' &&
            rootType !== 'user' && (

                {this.renderCornerButton()}

            )}

        {questionModalShown && (
          <QuestionModal
            onHide={() => this.setState({ questionModalShown: false })}
            question={rootObj.content}
            uploadAnswer={this.onAnswerUpload}
            parent={{
              id: rootObj.id,
              type: rootType,
              rootId,
              rootType
            }}
          />
        )}

    )
  }

  renderHeading = () => {
    const {
      contentObj,
      contentObj: { id, rootObj = {}, type, uploader = {}, rootType },
      action
    } = this.props
    const contentLabel =
      rootType === 'url'
        ? 'link'
        : rootType === 'question'
          ? 'a question'
          : rootType
    switch (type) {
      case 'video':
        return (
          <Fragment>
            <UsernameText user={uploader} color={Color.blue()} /> uploaded a
            video: <ContentLink content={contentObj} type={type} />{' '}
          </Fragment>
        )
      case 'comment':
        return (
          <Fragment>
            <UsernameText user={uploader} color={Color.blue()} />{' '}
            <ContentLink
              content={{ id, title: action }}
              type={type}
              style={{ color: Color.green() }}
            />
            {this.renderTargetAction()} {contentLabel}:{' '}
            <ContentLink content={rootObj} type={rootType} />{' '}
          </Fragment>
        )
      case 'url':
        return (
          <Fragment>
            <UsernameText user={uploader} color={Color.blue()} /> shared a
            link:
            <ContentLink content={contentObj} type={type} />{' '}
          </Fragment>
        )
      case 'question':
        return (
          <Fragment>
            <UsernameText user={uploader} color={Color.blue()} /> asked a{' '}
            <ContentLink
              content={{ id, title: 'question' }}
              type={type}
              style={{ color: Color.green() }}
            />{' '}
          </Fragment>
        )
      case 'discussion':
        return (
          <Fragment>
            <UsernameText user={uploader} color={Color.blue()} /> started a{' '}
            <ContentLink
              content={{ id, title: 'discussion' }}
              type={type}
              style={{ color: Color.green() }}
            />
             on {contentLabel}:{' '}
            <ContentLink content={rootObj} type={rootType} />
          </Fragment>
        )
      default:
        return Error
    }
  }

  onAnswerUpload = async({ content, parent }) => {
    const { dispatch, onCommentSubmit } = this.props
    const data = await uploadComment({
      content,
      parent,
      dispatch
    })
    if (data) onCommentSubmit(data)
  }

  renderCornerButton = () => {
    const {
      contentObj: {
        rootId,
        rootObj: { content, likes = [], isStarred } = {},
        rootType
      },
      attachedVideoShown,
      myId,
      onPlayVideoClick
    } = this.props
    const userLikedVideo = likes.map(like => like.userId).indexOf(myId) !== -1
    if (!content) return null
    if (rootType === 'video') {
      return (
        <Fragment>
          {attachedVideoShown ? (
            <LikeButton
              contentType="video"
              contentId={rootId}
              small
              liked={userLikedVideo}
              onClick={this.onLikeClick}
            />
          ) : (
            content && (

                  {!!isStarred && (
                    <StarMark style={{ top: 1, left: 1 }} size={2.5} />
                  )}

            )
          )}
        </Fragment>
      )
    } else if (rootType === 'question') {
      return (
        <Button
          success
          onClick={() => this.setState({ questionModalShown: true })}
        >
          <Icon icon="comment" />
          Answer
        </Button>
      )
    }
  }

  renderTargetAction = () => {
    const {
      contentObj: { commentId, replyId, targetObj = {}, rootType }
    } = this.props
    if (targetObj.comment && !targetObj.comment.notFound) {
      return (

          {' '}
          <UsernameText
            user={targetObj.comment.uploader}
            color={Color.blue()}
          />
          {"'s "}
          <ContentLink
            content={{
              id: replyId || commentId,
              title: replyId
                ? 'reply '
                : rootType === 'user'
                  ? 'message '
                  : 'comment '
            }}
            type="comment"
            style={{ color: Color.green() }}
          />
          {!replyId && rootType === 'user' ? 'to' : 'on'}

      )
    }
    return null
  }

  onLikeClick = likes => {
    const {
      contentObj: { rootId, rootType },
      onLikeContent
    } = this.props
    onLikeContent({ likes, contentId: rootId, type: rootType })
  }
}

export default connect(
  null,
  dispatch => ({ dispatch })
)(withContext({ Component: Heading, Context }))
<commit_msg>minor change<commit_after>import PropTypes from 'prop-types'
import React, { Component, Fragment } from 'react'
import Context from './Context'
import withContext from 'components/Wrappers/withContext'
import ContentLink from 'components/ContentLink'
import { timeSince } from 'helpers/timeStampHelpers'
import LikeButton from 'components/Buttons/LikeButton'
import { Color } from 'constants/css'
import ProfilePic from 'components/ProfilePic'
import Button from 'components/Button'
import QuestionModal from './QuestionModal'
import StarMark from 'components/StarMark'
import UsernameText from 'components/Texts/UsernameText'
import Icon from 'components/Icon'
import { uploadComment } from 'helpers/requestHelpers'
import { css } from 'emotion'
import { connect } from 'react-redux'

class Heading extends Component {
  static propTypes = {
    action: PropTypes.string,
    onCommentSubmit: PropTypes.func.isRequired,
    onLikeContent: PropTypes.func.isRequired,
    attachedVideoShown: PropTypes.bool,
    contentObj: PropTypes.shape({
      id: PropTypes.number,
      commentId: PropTypes.number,
      replyId: PropTypes.number,
      rootObj: PropTypes.object,
      rootId: PropTypes.number,
      rootType: PropTypes.string,
      targetObj: PropTypes.object,
      timeStamp: PropTypes.oneOfType([PropTypes.number, PropTypes.string])
        .isRequired,
      type: PropTypes.string,
      uploader: PropTypes.object
    }).isRequired,
    myId: PropTypes.number,
    onPlayVideoClick: PropTypes.func
  }

  state = {
    questionModalShown: false
  }

  render() {
    const {
      contentObj: {
        uploader = {},
        rootObj = {},
        rootType,
        rootId,
        timeStamp,
        type
      }
    } = this.props
    const { questionModalShown } = this.state
    return (

        <ProfilePic
          style={{ width: '6rem', height: '6rem' }}
          userId={uploader.id}
          profilePicId={uploader.profilePicId}
        />

            {this.renderHeading()}

              {timeStamp ? `(${timeSince(timeStamp)})` : ''}

          {type === 'comment' &&
            rootType !== 'url' &&
            rootType !== 'user' && (

                {this.renderCornerButton()}

            )}

        {questionModalShown && (
          <QuestionModal
            onHide={() => this.setState({ questionModalShown: false })}
            question={rootObj.content}
            uploadAnswer={this.onAnswerUpload}
            parent={{
              id: rootObj.id,
              type: rootType,
              rootId,
              rootType
            }}
          />
        )}

    )
  }

  renderHeading = () => {
    const {
      contentObj,
      contentObj: { id, rootObj = {}, type, uploader = {}, rootType },
      action
    } = this.props
    const contentLabel =
      rootType === 'url'
        ? 'link'
        : rootType === 'question'
          ? 'a question'
          : rootType
    switch (type) {
      case 'video':
        return (
          <Fragment>
            <UsernameText user={uploader} color={Color.blue()} /> uploaded a
            video: <ContentLink content={contentObj} type={type} />{' '}
          </Fragment>
        )
      case 'comment':
        return (
          <Fragment>
            <UsernameText user={uploader} color={Color.blue()} />{' '}
            <ContentLink
              content={{ id, title: action }}
              type={type}
              style={{ color: Color.green() }}
            />
            {this.renderTargetAction()} {contentLabel}:{' '}
            <ContentLink content={rootObj} type={rootType} />{' '}
          </Fragment>
        )
      case 'url':
        return (
          <Fragment>
            <UsernameText user={uploader} color={Color.blue()} /> shared a
            link:
            <ContentLink content={contentObj} type={type} />{' '}
          </Fragment>
        )
      case 'question':
        return (
          <Fragment>
            <UsernameText user={uploader} color={Color.blue()} /> asked a{' '}
            <ContentLink
              content={{ id, title: 'question' }}
              type={type}
              style={{ color: Color.green() }}
            />{' '}
          </Fragment>
        )
      case 'discussion':
        return (
          <Fragment>
            <UsernameText user={uploader} color={Color.blue()} /> started a{' '}
            <ContentLink
              content={{ id, title: 'discussion' }}
              type={type}
              style={{ color: Color.green() }}
            />
             on {contentLabel}:{' '}
            <ContentLink content={rootObj} type={rootType} />
          </Fragment>
        )
      default:
        return Error
    }
  }

  onAnswerUpload = async({ content, parent }) => {
    const { dispatch, onCommentSubmit } = this.props
    const data = await uploadComment({
      content,
      parent,
      dispatch
    })
    if (data) onCommentSubmit(data)
  }

  renderCornerButton = () => {
    const {
      contentObj: {
        rootId,
        rootObj: { content, likes = [], isStarred } = {},
        rootType
      },
      attachedVideoShown,
      myId,
      onPlayVideoClick
    } = this.props
    const userLikedVideo = likes.map(like => like.userId).indexOf(myId) !== -1
    if (!content) return null
    if (rootType === 'video') {
      return (
        <Fragment>
          {attachedVideoShown ? (
            <LikeButton
              contentType="video"
              contentId={rootId}
              small
              liked={userLikedVideo}
              onClick={this.onLikeClick}
            />
          ) : (
            content && (

                  {!!isStarred && (
                    <StarMark style={{ top: 1, left: 1 }} size={2.5} />
                  )}

            )
          )}
        </Fragment>
      )
    } else if (rootType === 'question') {
      return (
        <Button
          success
          onClick={() => this.setState({ questionModalShown: true })}
        >
          <Icon icon="comment" />
          Answer
        </Button>
      )
    }
  }

  renderTargetAction = () => {
    const {
      contentObj: { commentId, replyId, targetObj = {}, rootType }
    } = this.props
    if (targetObj.comment && !targetObj.comment.notFound) {
      return (

          {' '}
          <UsernameText
            user={targetObj.comment.uploader}
            color={Color.blue()}
          />
          {"'s "}
          <ContentLink
            content={{
              id: replyId || commentId,
              title: replyId
                ? 'reply '
                : rootType === 'user'
                  ? 'message '
                  : 'comment '
            }}
            type="comment"
            style={{ color: Color.green() }}
          />
          {!replyId && rootType === 'user' ? 'to' : 'on'}

      )
    }
    return null
  }

  onLikeClick = likes => {
    const {
      contentObj: { rootId, rootType },
      onLikeContent
    } = this.props
    onLikeContent({ likes, contentId: rootId, type: rootType })
  }
}

export default connect(
  null,
  dispatch => ({ dispatch })
)(withContext({ Component: Heading, Context }))
<|endoftext|>


<commit_before>/* eslint-disable quote-props */

export const INTERNAL_TO_EXTERNAL = 0;
export const EXTERNAL_TO_INTERNAL = 1;

class SyncTranslator {
  constructor(internalToExternal) {
    this.internalToExternal = internalToExternal;
    this.externalToInternal = {};
    for (const [key, value] of Object.entries(internalToExternal)) {
      this.externalToInternal[value] = key;
    }
  }

  translate(key, direction) {
    switch (direction) {
      case INTERNAL_TO_EXTERNAL:
      default:
        return this.internalToExternal[key];
      case EXTERNAL_TO_INTERNAL:
        return this.externalToInternal[key];
    }
  }
}

// Map of internal database object types to external record types
export const RECORD_TYPES = new SyncTranslator({
  'Item': 'item',
  'ItemStoreJoin': 'item_store_join',
  'ItemBatch': 'item_line',
  'ItemDepartment': 'item_department',
  'ItemCategory': 'item_category',
  'LocalListItem': 'list_local_line',
  'MasterList': 'list_master',
  'MasterListItem': 'list_master_line',
  'MasterListNameJoin': 'list_master_name_join',
  'Name': 'name',
  'NameStoreJoin': 'name_store_join',
  'NumberSequence': 'number',
  'NumberToReuse': 'number_reuse',
  'Requisition': 'requisition',
  'RequisitionItem': 'requisition_line',
  'Stocktake': 'Stock_take',
  'StocktakeBatch': 'Stock_take_lines',
  'Transaction': 'transact',
  'TransactionCategory': 'transaction_category',
  'TransactionBatch': 'trans_line',
  'User': 'user',
});

export const REQUISITION_TYPES = new SyncTranslator({
  'imprest': 'im',
  'forecast': 'sh',
  'request': 'request',
});

// Map of internal database change types to external sync types
export const SYNC_TYPES = new SyncTranslator({
  'create': 'I', // For 'insert'
  'update': 'U',
  'delete': 'D',
});

export const TRANSACTION_TYPES = new SyncTranslator({
  'customer_invoice': 'ci',
  'customer_credit': 'cc',
  'supplier_invoice': 'si',
  'supplier_credit': 'sc',
  'inventory_adjustment': 'in',
});

export const TRANSACTION_BATCH_TYPES = new SyncTranslator({
  'customer_invoice': 'stock_out',
  'customer_credit': 'stock_in',
  'supplier_invoice': 'stock_in',
  'supplier_credit': 'stock_out',
});

export const NAME_TYPES = new SyncTranslator({
  'inventory_adjustment': 'invad',
  'facility': 'facility',
  'patient': 'patient',
  'build': 'build',
  'store': 'store',
  'repack': 'repack',
});

/**
 * Translates requisition statuses, which will be changed by the supplying store
 * once finalised in the mobile store. Despite the supplying store's requisition
 * going through sg, cn, and fn statuses, it should remain finalised in the mobile
 * store
 */
class RequisitionStatusTranslator extends SyncTranslator {
  translate(status, direction) {
    if (['sg', 'cn', 'fn'].includes(status)) return 'finalised';
    return super.translate(status, direction);
  }
}
export const REQUISITION_STATUSES = new RequisitionStatusTranslator({
  'new': 'wp',
  'finalised': 'wf',
});

/**
 * Translates statuses of transactions, stocktakes, etc., which include 'wp' and 'wf'
 * on legacy systems.
 */
class StatusTranslator extends SyncTranslator {
  translate(status, direction) {
    switch (status) {
      case 'wp':
        return 'new';
      case 'wf':
        return 'finalised';
      default:
        return super.translate(status, direction);
    }
  }
}
// Map of internal statuses to external statuses (of transactions, stocktakes, etc.)
export const STATUSES = new StatusTranslator({
  'confirmed': 'cn',
  'finalised': 'fn',
  'suggested': 'sg',
  'new': 'nw',
});

/**
 * Translates number sequence keys from internal to external formats, and vice versa.
 * If translating external to internal, will return null if it is a sequence key
 * not used by mobile, or if it relates to a different store.
 */
class SequenceKeyTranslator extends SyncTranslator {
  translate(sequenceKey, direction, thisStoreId) {
    let key = sequenceKey;
    if (direction === EXTERNAL_TO_INTERNAL) {
      if (key.length < thisStoreId.length) return null; // Definitely not a key we use
      // The mSupply sequence keys we care about end with the store id they relate to
      const storeId = key.substring(key.length - thisStoreId.length);
      // If the sequence doesn't relate to this store, we will ignore it
      if (storeId !== thisStoreId) return null;
      key = key.substring(0, key.length - thisStoreId.length);
    }
    let translatedKey = super.translate(key, direction);
    if (direction === INTERNAL_TO_EXTERNAL) translatedKey += thisStoreId;
    return translatedKey;
  }
}
export const SEQUENCE_KEYS = new SequenceKeyTranslator({
  customer_invoice_serial_number: 'customer_invoice_number_for_store_',
  inventory_adjustment_serial_number: 'inventory_adjustment_serial_number_for_store_',
  requisition_serial_number: 'requisition_serial_number_for_store_',
  requisition_requester_reference: 'requisition_requester_reference_for_store_',
  stocktake_serial_number: 'stock_take_number_for_store_',
});
<commit_msg>Need this to sync supplier invoice in<->out<commit_after>/* eslint-disable quote-props */

export const INTERNAL_TO_EXTERNAL = 0;
export const EXTERNAL_TO_INTERNAL = 1;

class SyncTranslator {
  constructor(internalToExternal) {
    this.internalToExternal = internalToExternal;
    this.externalToInternal = {};
    for (const [key, value] of Object.entries(internalToExternal)) {
      this.externalToInternal[value] = key;
    }
  }

  translate(key, direction) {
    switch (direction) {
      case INTERNAL_TO_EXTERNAL:
      default:
        return this.internalToExternal[key];
      case EXTERNAL_TO_INTERNAL:
        return this.externalToInternal[key];
    }
  }
}

// Map of internal database object types to external record types
export const RECORD_TYPES = new SyncTranslator({
  'Item': 'item',
  'ItemStoreJoin': 'item_store_join',
  'ItemBatch': 'item_line',
  'ItemDepartment': 'item_department',
  'ItemCategory': 'item_category',
  'LocalListItem': 'list_local_line',
  'MasterList': 'list_master',
  'MasterListItem': 'list_master_line',
  'MasterListNameJoin': 'list_master_name_join',
  'Name': 'name',
  'NameStoreJoin': 'name_store_join',
  'NumberSequence': 'number',
  'NumberToReuse': 'number_reuse',
  'Requisition': 'requisition',
  'RequisitionItem': 'requisition_line',
  'Stocktake': 'Stock_take',
  'StocktakeBatch': 'Stock_take_lines',
  'Transaction': 'transact',
  'TransactionCategory': 'transaction_category',
  'TransactionBatch': 'trans_line',
  'User': 'user',
});

export const REQUISITION_TYPES = new SyncTranslator({
  'imprest': 'im',
  'forecast': 'sh',
  'request': 'request',
});

// Map of internal database change types to external sync types
export const SYNC_TYPES = new SyncTranslator({
  'create': 'I', // For 'insert'
  'update': 'U',
  'delete': 'D',
});

export const TRANSACTION_TYPES = new SyncTranslator({
  'customer_invoice': 'ci',
  'customer_credit': 'cc',
  'supplier_invoice': 'si',
  'supplier_credit': 'sc',
  'inventory_adjustment': 'in',
});

export const TRANSACTION_BATCH_TYPES = new SyncTranslator({
  'customer_invoice': 'stock_out',
  'customer_credit': 'stock_in',
  'supplier_invoice': 'stock_in',
  'supplier_credit': 'stock_out',
});

export const NAME_TYPES = new SyncTranslator({
  'inventory_adjustment': 'invad',
  'facility': 'facility',
  'patient': 'patient',
  'build': 'build',
  'store': 'store',
  'repack': 'repack',
});

/**
 * Translates requisition statuses, which will be changed by the supplying store
 * once finalised in the mobile store. Despite the supplying store's requisition
 * going through sg, cn, and fn statuses, it should remain finalised in the mobile
 * store
 */
class RequisitionStatusTranslator extends SyncTranslator {
  translate(status, direction) {
    if (['sg', 'cn', 'fn'].includes(status)) return 'finalised';
    return super.translate(status, direction);
  }
}
export const REQUISITION_STATUSES = new RequisitionStatusTranslator({
  'new': 'wp',
  'finalised': 'wf',
});

/**
 * Translates statuses of transactions, stocktakes, etc., which include 'wp' and 'wf'
 * on legacy systems.
 */
class StatusTranslator extends SyncTranslator {
  translate(status, direction) {
    switch (status) {
      case 'wp':
        return 'new';
      case 'wf':
        return 'finalised';
      default:
        return super.translate(status, direction);
    }
  }
}
// Map of internal statuses to external statuses (of transactions, stocktakes, etc.)
export const STATUSES = new StatusTranslator({
  'confirmed': 'cn',
  'finalised': 'fn',
  'suggested': 'sg',
  'new': 'nw',
});

/**
 * Translates number sequence keys from internal to external formats, and vice versa.
 * If translating external to internal, will return null if it is a sequence key
 * not used by mobile, or if it relates to a different store.
 */
class SequenceKeyTranslator extends SyncTranslator {
  translate(sequenceKey, direction, thisStoreId) {
    let key = sequenceKey;
    if (direction === EXTERNAL_TO_INTERNAL) {
      if (key.length < thisStoreId.length) return null; // Definitely not a key we use
      // The mSupply sequence keys we care about end with the store id they relate to
      const storeId = key.substring(key.length - thisStoreId.length);
      // If the sequence doesn't relate to this store, we will ignore it
      if (storeId !== thisStoreId) return null;
      key = key.substring(0, key.length - thisStoreId.length);
    }
    let translatedKey = super.translate(key, direction);
    if (direction === INTERNAL_TO_EXTERNAL) translatedKey += thisStoreId;
    return translatedKey;
  }
}
export const SEQUENCE_KEYS = new SequenceKeyTranslator({
  customer_invoice_serial_number: 'customer_invoice_number_for_store_',
  inventory_adjustment_serial_number: 'inventory_adjustment_serial_number_for_store_',
  requisition_serial_number: 'requisition_serial_number_for_store_',
  requisition_requester_reference: 'requisition_requester_reference_for_store_',
  stocktake_serial_number: 'stock_take_number_for_store_',
  supplier_invoice_serial_number: 'supplier_invoice_number_for_store_'
});
<|endoftext|>