<commit_before>import React from "react";
import { scaleOrdinal } from "d3";
import ReactTooltip from "react-tooltip";

import * as d3 from "d3";

import { GraphManager } from "./index";
import columnAccessor from "../../utils/columnAccessor";
import crossfilter from "crossfilter2";

export default class AbstractGraph extends React.Component {

    constructor(props, properties = {}) {
        super(props);

        this.defaults = GraphManager.getDefaultProperties(properties);

        // Provide tooltips for subclasses.
        const { tooltip } = this.getConfiguredProperties();
        if(tooltip) {

            // Generate accessors that apply number and date formatters.
            const accessors = tooltip.map(columnAccessor);

            // This function is invoked to produce the content of a tooltip.
            this.getTooltipContent = () => {

                // The value of this.hoveredDatum should be set by subclasses
                // on mouseEnter and mouseMove of visual marks
                // to the data entry corresponding to the hovered mark.
                if(this.hoveredDatum) {
                    return (

                            {/* Display each tooltip column as "label : value". */}
                            {tooltip.map(({column, label}, i) => (

                                        {/* Use label if present, fall back to column name. */}
                                        {label || column}
                                     :
                                        {/* Apply number and date formatting to the value. */}
                                        {accessors[i](this.hoveredDatum)}

                            ))}

                    );
                } else {
                    return null;
                }
            }

            // Use a unique tooltip ID per visualization,
            // otherwise there are overlapping tooltips.
            const tooltipId = String(Math.random());

            // Expose tooltipId in case subclasses need it.
            this.tooltipId = tooltipId;

            // This JSX object can be used by subclasses to enable tooltips.
            this.tooltip = (
                <ReactTooltip
                    id={ tooltipId }
                    place="top"
                    type="dark"
                    effect="float"
                    getContent={[() => this.getTooltipContent(), 200]}
                />
            );

            // Subclasses can enable tooltips on their marks
            // by spreading over the return value from this function
            // when invoked with the mark's data element `d` like this:
            // data.map((d) => <rect { ...this.tooltipProps(d) } />
            this.tooltipProps = (d) => ({
                "data-tip": true,
                "data-for": tooltipId,
                "onMouseEnter": () => this.hoveredDatum = d,
                "onMouseMove": () => this.hoveredDatum = d
            });

        } else {
            this.getTooltipContent = () => null
            this.tooltipProps = () => null
        }
    }

    wrapD3Text (text, width) {
      text.each(function() {
        var text = d3.select(this),
          words = text.text().split(/\s+/).reverse(),
          word = words.pop(),
          line = [],
          y = text.attr("y"),
          dy = parseFloat(text.attr("dy")),
          tspan = text.text(null).append("tspan").attr("x", -2).attr("y", y).attr("dy", dy + "em");

        while (word && tspan.node().getComputedTextLength() < width) {
          line.push(word);
          tspan.text(line.join(" "));
          word = words.pop();
        }

        if(word) {
          tspan.text(line.join(" ") + '...');
        }
      });
    };

    getConfiguredProperties() {
        return Object.assign({}, this.defaults, this.props.configuration.data);
    }

    getMappedScaleColor(data, defaultColumn) {
        const {
            colors,
            mapColors,
            colorColumn,
        } = this.getConfiguredProperties();

        if (!colorColumn && !defaultColumn)
            return;

        let domainData = d3.map(data, (d) => d[colorColumn || defaultColumn]).keys().sort();

        let propColors = [];
        domainData.forEach((d) => {
            if(mapColors[d]) {
                propColors.push(mapColors[d]);
            }
        })
        propColors = propColors.concat(colors);

        const scale = scaleOrdinal(propColors);
        scale.domain(domainData);

        return scale;
    }

    updateYExtent(yExtent, zeroStart) {
      let padding = 0.10;

      if(zeroStart && yExtent[0] > 0) {
        yExtent[0] = 0;
      }

      let diff = Math.floor((yExtent[1] - yExtent[0]) * padding, 0);

      yExtent[0] = (yExtent[0] >= 0 && (yExtent[0] - diff) < 0) ? 0 : yExtent[0] - diff;
      yExtent[1] = (yExtent[1] <= 0 && (yExtent[1] + diff) > 0) ? 0 : yExtent[1] + diff;

      return yExtent;
    }

    scaleColor(data, defaultColumn) {
        const {
            colors,
            colorColumn,
        } = this.getConfiguredProperties();

        if (!colorColumn && !defaultColumn)
            return;

        const scale = scaleOrdinal(colors);
        scale.domain(data.map((d) => d[colorColumn || defaultColumn]));

        return scale;
    }

    longestLabelLength(data, label, formatter) {
        // Basic function if none provided
        if (!label)
            label = (d) => d;

        let format = (d) => d;

        if (formatter)
            format = d3.format(formatter);

        // Extract the longest legend according to the label function
        const lab = label(data.reduce((a, b) => {
            let labelA = label(a);
            let labelB = label(b);

            if (!labelA)
                return b;

            if (!labelB)
                return a;
            return format(labelA.toString()).length > format(labelB.toString()).length ? a : b;
        }));

        const longestLabel = lab ? lab.toString() : '';

        // and return its length + 1 to ensure we have enough space
        return format(longestLabel).length + 1;
    }

    renderLegend(data, legend, getColor, label) {

        if (!legend.show)
            return;

        const {
            width,
            height
        } = this.props;

        const {
          margin,
          fontColor,
          circleToPixel,
        } = this.getConfiguredProperties();

        const isVertical = legend.orientation === 'vertical';
        const lineHeight = legend.circleSize * circleToPixel;

        if (isVertical)
        {
            // Place the legends in the bottom left corner
            let left = margin.left;
            let top  = height - (margin.bottom + ((data.length - 1) * lineHeight));

            return (
                <g>
                    {data.map((d, i) => {
                        const x = left;
                        const y = top + (i *  lineHeight);
                        return (
                            <g
                                key={i}
                                transform={ `translate(${x}, ${y})` }>

                                <circle
                                  r={ legend.circleSize }
                                  fill={ getColor(d) }
                                />

                                <text
                                  fill={ fontColor }
                                  alignmentBaseline="central"
                                  x={ legend.circleSize + legend.labelOffset }
                                >
                                    { label(d) }
                                </text>
                            </g>
                        );
                    })}
                </g>
            );
        }

        // Place legends horizontally
        const availableWidth    = width - margin.left - margin.right;
        const legendWidth       = legend.width + legend.circleSize + 2 * legend.labelOffset;
        const nbElementsPerLine = parseInt(availableWidth / legendWidth, 10);
        const nbLines           = parseInt(data.length / nbElementsPerLine, 10);
        const left              = margin.left;
        const top               = height - (margin.bottom + (nbLines * lineHeight));

        return (
            <g>
                {data.map((d, i) => {
                    const x = left + ((i % nbElementsPerLine) * legendWidth);
                    const y = top + parseInt(i / nbElementsPerLine, 10) * lineHeight;
                    return (
                        <g
                            key={i}
                            transform={ `translate(${x}, ${y})` }>

                            <circle
                              r={ legend.circleSize }
                              fill={ getColor(d) }
                            />

                            <text
                              fill={ fontColor }
                              alignmentBaseline="central"
                              x={ legend.circleSize + legend.labelOffset }
                            >
                                { label(d) }
                            </text>
                        </g>
                    );
                })}
            </g>
        );
    }

    getGroupedData(data, settings) {

        let cfData = crossfilter(data);

        let metricDimension = cfData.dimension(function(d) { return d[settings.metric]; });
        let topData = [];

        if(settings.otherOptions && settings.otherOptions.limit && (data.length - 1) > settings.otherOptions.limit) {

            topData = metricDimension.top(settings.otherOptions.limit);
            const otherDatas = metricDimension.top(Infinity, settings.otherOptions.limit);

            const sum = otherDatas.reduce(function(total, d) {
              return +total + d[settings.metric];
            }, 0);

            topData = topData.concat({[settings.dimension]: settings.otherOptions.label, [settings.metric]: sum})
        } else {
            topData = metricDimension.top(Infinity);
        }

        metricDimension.remove();
        cfData.remove();

        return topData;
    }

    getOpacity(d) {
        const {
            configuration,
            context
        } = this.props;
        let vkey = `${configuration.id.replace(/-/g, '')}vkey`;
        return (!context[vkey] || !configuration.key || context[vkey]  === eval("(" + configuration.key + ")")(d)) ? "1" : "0.5"
    }

}
<commit_msg>Changed other options limit check<commit_after>import React from "react";
import { scaleOrdinal } from "d3";
import ReactTooltip from "react-tooltip";

import * as d3 from "d3";

import { GraphManager } from "./index";
import columnAccessor from "../../utils/columnAccessor";
import crossfilter from "crossfilter2";

export default class AbstractGraph extends React.Component {

    constructor(props, properties = {}) {
        super(props);

        this.defaults = GraphManager.getDefaultProperties(properties);

        // Provide tooltips for subclasses.
        const { tooltip } = this.getConfiguredProperties();
        if(tooltip) {

            // Generate accessors that apply number and date formatters.
            const accessors = tooltip.map(columnAccessor);

            // This function is invoked to produce the content of a tooltip.
            this.getTooltipContent = () => {

                // The value of this.hoveredDatum should be set by subclasses
                // on mouseEnter and mouseMove of visual marks
                // to the data entry corresponding to the hovered mark.
                if(this.hoveredDatum) {
                    return (

                            {/* Display each tooltip column as "label : value". */}
                            {tooltip.map(({column, label}, i) => (

                                        {/* Use label if present, fall back to column name. */}
                                        {label || column}
                                     :
                                        {/* Apply number and date formatting to the value. */}
                                        {accessors[i](this.hoveredDatum)}

                            ))}

                    );
                } else {
                    return null;
                }
            }

            // Use a unique tooltip ID per visualization,
            // otherwise there are overlapping tooltips.
            const tooltipId = String(Math.random());

            // Expose tooltipId in case subclasses need it.
            this.tooltipId = tooltipId;

            // This JSX object can be used by subclasses to enable tooltips.
            this.tooltip = (
                <ReactTooltip
                    id={ tooltipId }
                    place="top"
                    type="dark"
                    effect="float"
                    getContent={[() => this.getTooltipContent(), 200]}
                />
            );

            // Subclasses can enable tooltips on their marks
            // by spreading over the return value from this function
            // when invoked with the mark's data element `d` like this:
            // data.map((d) => <rect { ...this.tooltipProps(d) } />
            this.tooltipProps = (d) => ({
                "data-tip": true,
                "data-for": tooltipId,
                "onMouseEnter": () => this.hoveredDatum = d,
                "onMouseMove": () => this.hoveredDatum = d
            });

        } else {
            this.getTooltipContent = () => null
            this.tooltipProps = () => null
        }
    }

    wrapD3Text (text, width) {
      text.each(function() {
        var text = d3.select(this),
          words = text.text().split(/\s+/).reverse(),
          word = words.pop(),
          line = [],
          y = text.attr("y"),
          dy = parseFloat(text.attr("dy")),
          tspan = text.text(null).append("tspan").attr("x", -2).attr("y", y).attr("dy", dy + "em");

        while (word && tspan.node().getComputedTextLength() < width) {
          line.push(word);
          tspan.text(line.join(" "));
          word = words.pop();
        }

        if(word) {
          tspan.text(line.join(" ") + '...');
        }
      });
    };

    getConfiguredProperties() {
        return Object.assign({}, this.defaults, this.props.configuration.data);
    }

    getMappedScaleColor(data, defaultColumn) {
        const {
            colors,
            mapColors,
            colorColumn,
        } = this.getConfiguredProperties();

        if (!colorColumn && !defaultColumn)
            return;

        let domainData = d3.map(data, (d) => d[colorColumn || defaultColumn]).keys().sort();

        let propColors = [];
        domainData.forEach((d) => {
            if(mapColors[d]) {
                propColors.push(mapColors[d]);
            }
        })
        propColors = propColors.concat(colors);

        const scale = scaleOrdinal(propColors);
        scale.domain(domainData);

        return scale;
    }

    updateYExtent(yExtent, zeroStart) {
      let padding = 0.10;

      if(zeroStart && yExtent[0] > 0) {
        yExtent[0] = 0;
      }

      let diff = Math.floor((yExtent[1] - yExtent[0]) * padding, 0);

      yExtent[0] = (yExtent[0] >= 0 && (yExtent[0] - diff) < 0) ? 0 : yExtent[0] - diff;
      yExtent[1] = (yExtent[1] <= 0 && (yExtent[1] + diff) > 0) ? 0 : yExtent[1] + diff;

      return yExtent;
    }

    scaleColor(data, defaultColumn) {
        const {
            colors,
            colorColumn,
        } = this.getConfiguredProperties();

        if (!colorColumn && !defaultColumn)
            return;

        const scale = scaleOrdinal(colors);
        scale.domain(data.map((d) => d[colorColumn || defaultColumn]));

        return scale;
    }

    longestLabelLength(data, label, formatter) {
        // Basic function if none provided
        if (!label)
            label = (d) => d;

        let format = (d) => d;

        if (formatter)
            format = d3.format(formatter);

        // Extract the longest legend according to the label function
        const lab = label(data.reduce((a, b) => {
            let labelA = label(a);
            let labelB = label(b);

            if (!labelA)
                return b;

            if (!labelB)
                return a;
            return format(labelA.toString()).length > format(labelB.toString()).length ? a : b;
        }));

        const longestLabel = lab ? lab.toString() : '';

        // and return its length + 1 to ensure we have enough space
        return format(longestLabel).length + 1;
    }

    renderLegend(data, legend, getColor, label) {

        if (!legend.show)
            return;

        const {
            width,
            height
        } = this.props;

        const {
          margin,
          fontColor,
          circleToPixel,
        } = this.getConfiguredProperties();

        const isVertical = legend.orientation === 'vertical';
        const lineHeight = legend.circleSize * circleToPixel;

        if (isVertical)
        {
            // Place the legends in the bottom left corner
            let left = margin.left;
            let top  = height - (margin.bottom + ((data.length - 1) * lineHeight));

            return (
                <g>
                    {data.map((d, i) => {
                        const x = left;
                        const y = top + (i *  lineHeight);
                        return (
                            <g
                                key={i}
                                transform={ `translate(${x}, ${y})` }>

                                <circle
                                  r={ legend.circleSize }
                                  fill={ getColor(d) }
                                />

                                <text
                                  fill={ fontColor }
                                  alignmentBaseline="central"
                                  x={ legend.circleSize + legend.labelOffset }
                                >
                                    { label(d) }
                                </text>
                            </g>
                        );
                    })}
                </g>
            );
        }

        // Place legends horizontally
        const availableWidth    = width - margin.left - margin.right;
        const legendWidth       = legend.width + legend.circleSize + 2 * legend.labelOffset;
        const nbElementsPerLine = parseInt(availableWidth / legendWidth, 10);
        const nbLines           = parseInt(data.length / nbElementsPerLine, 10);
        const left              = margin.left;
        const top               = height - (margin.bottom + (nbLines * lineHeight));

        return (
            <g>
                {data.map((d, i) => {
                    const x = left + ((i % nbElementsPerLine) * legendWidth);
                    const y = top + parseInt(i / nbElementsPerLine, 10) * lineHeight;
                    return (
                        <g
                            key={i}
                            transform={ `translate(${x}, ${y})` }>

                            <circle
                              r={ legend.circleSize }
                              fill={ getColor(d) }
                            />

                            <text
                              fill={ fontColor }
                              alignmentBaseline="central"
                              x={ legend.circleSize + legend.labelOffset }
                            >
                                { label(d) }
                            </text>
                        </g>
                    );
                })}
            </g>
        );
    }

    getGroupedData(data, settings) {

        let cfData = crossfilter(data);

        let metricDimension = cfData.dimension(function(d) { return d[settings.metric]; });
        let topData = [];

        if(settings.otherOptions && settings.otherOptions.limit && data.length > settings.otherOptions.limit) {

            topData = metricDimension.top(settings.otherOptions.limit);
            const otherDatas = metricDimension.top(Infinity, settings.otherOptions.limit);

            const sum = otherDatas.reduce(function(total, d) {
              return +total + d[settings.metric];
            }, 0);

            topData = topData.concat({[settings.dimension]: settings.otherOptions.label, [settings.metric]: sum})
        } else {
            topData = metricDimension.top(Infinity);
        }

        metricDimension.remove();
        cfData.remove();

        return topData;
    }

    getOpacity(d) {
        const {
            configuration,
            context
        } = this.props;
        let vkey = `${configuration.id.replace(/-/g, '')}vkey`;
        return (!context[vkey] || !configuration.key || context[vkey]  === eval("(" + configuration.key + ")")(d)) ? "1" : "0.5"
    }

}
<|endoftext|>


<commit_before>import React from 'react';
import settings from 'settings';
import TextCenter from 'components/TextCenter';
import Heading2 from 'components/Heading2';
import styles from './imprint-details.scss';

const ImprintDetails = () => (

    <TextCenter>
      <Heading2 size='large'>
        {settings.imprint.detail.title}
      </Heading2>
    </TextCenter>

{settings.imprint.detail.company.title}

        {Object.keys(settings.imprint.detail.company).slice(1).map(item =>

{settings.imprint.detail.company[item]}

        )}

{settings.imprint.detail.contact.title}

        {Object.keys(settings.imprint.detail.contact).slice(1).map(item =>

{settings.imprint.detail.contact[item]}

        )}

);

export default ImprintDetails;
<commit_msg>Add map key<commit_after>import React from 'react';
import settings from 'settings';
import TextCenter from 'components/TextCenter';
import Heading2 from 'components/Heading2';
import styles from './imprint-details.scss';

const ImprintDetails = () => (

    <TextCenter>
      <Heading2 size='large'>
        {settings.imprint.detail.title}
      </Heading2>
    </TextCenter>

{settings.imprint.detail.company.title}

        {Object.keys(settings.imprint.detail.company).slice(1).map(item =>

{settings.imprint.detail.company[item]}

        )}

{settings.imprint.detail.contact.title}

        {Object.keys(settings.imprint.detail.contact).slice(1).map(item =>

{settings.imprint.detail.contact[item]}

        )}

);

export default ImprintDetails;
<|endoftext|>


<commit_before>import Multivalue from '../multivalue/Multivalue';
import { delay } from '../../../utils/utils';
import Widgets from '../../../widgets';
import _ from 'lodash';

export default class Input extends Multivalue {
  constructor(component, options, data) {
    super(component, options, data);
    this.triggerUpdateValueAt = _.debounce(this.updateValueAt.bind(this), 100);
  }

  static schema(...extend) {
    return Multivalue.schema({
      widget: {
        type: 'input'
      }
    }, ...extend);
  }

  get inputInfo() {
    const attr = {
      name: this.options.name,
      type: this.component.inputType || 'text',
      class: 'form-control',
      lang: this.options.language
    };

    if (this.component.placeholder) {
      attr.placeholder = this.t(this.component.placeholder);
    }

    if (this.component.tabindex) {
      attr.tabindex = this.component.tabindex;
    }

    if (this.disabled) {
      attr.disabled = 'disabled';
    }

    _.defaults(attr, this.component.attributes);

    return {
      id: this.key,
      type: 'input',
      changeEvent: 'input',
      content: '',
      attr
    };
  }

  get maskOptions() {
    return _.map(this.component.inputMasks, mask => {
      return {
        label: mask.label,
        value: mask.label
      };
    });
  }

  get isMultipleMasksField() {
    return this.component.allowMultipleMasks && !!this.component.inputMasks && !!this.component.inputMasks.length;
  }

  getMaskByName(maskName) {
    const inputMask = _.find(this.component.inputMasks, (inputMask) => {
      return inputMask.label === maskName;
    });
    return inputMask ? inputMask.mask : undefined;
  }

  setInputMask(input, inputMask) {
    return super.setInputMask(input, (inputMask || this.component.inputMask), !this.component.placeholder);
  }

  getMaskOptions() {
    return this.component.inputMasks
      .map(mask => ({
        label: mask.label,
        value: mask.label,
      }));
  }

  get remainingWords() {
    const maxWords = _.parseInt(_.get(this.component, 'validate.maxWords'), 10);
    const wordCount = _.words(this.dataValue).length;
    return maxWords - wordCount;
  }

  renderElement(value, index) {
    const info = this.inputInfo;
    info.attr = info.attr || {};
    info.attr.value = this.getValueAsString(this.formatValue(this.parseValue(value)));
    if (this.isMultipleMasksField) {
      info.attr.class += ' formio-multiple-mask-input';
    }
    // This should be in the calendar widget but it doesn't have access to renderTemplate.
    if (this.component.widget && this.component.widget.type === 'calendar') {
      const calendarIcon = this.renderTemplate('icon', {
        ref: 'icon',
        className: this.iconClass(this.component.enableDate || this.component.widget.enableDate ? 'calendar' : 'time'),
        styles: '',
        content: ''
      }).trim();
      if (this.component.prefix !== calendarIcon) {
        this.component.suffix = calendarIcon;
      }
    }

    return this.isMultipleMasksField
      ? this.renderTemplate('multipleMasksInput', {
        input: info,
        value,
        index,
        selectOptions: this.getMaskOptions() || [],
      })
      : this.renderTemplate('input', {
        input: info,
        value: this.formatValue(this.parseValue(value)),
        index
      });
  }

  setCounter(type, element, count, max) {
    if (max) {
      const remaining = max - count;
      if (remaining > 0) {
        this.removeClass(element, 'text-danger');
      }
      else {
        this.addClass(element, 'text-danger');
      }
      this.setContent(element, this.t(`{{ remaining }} ${type} remaining.`, {
        remaining: remaining
      }));
    }
    else {
      this.setContent(element, this.t(`{{ count }} ${type}`, {
        count: count
      }));
    }
  }

  updateValueAt(value, flags, index) {
    if (_.get(this.component, 'showWordCount', false)) {
      if (this.refs.wordcount && this.refs.wordcount[index]) {
        const maxWords = _.parseInt(_.get(this.component, 'validate.maxWords', 0), 10);
        this.setCounter('words', this.refs.wordcount[index], _.words(value).length, maxWords);
      }
    }
    if (_.get(this.component, 'showCharCount', false)) {
      if (this.refs.charcount && this.refs.charcount[index]) {
        const maxChars = _.parseInt(_.get(this.component, 'validate.maxLength', 0), 10);
        this.setCounter('characters', this.refs.charcount[index], value.length, maxChars);
      }
    }
  }

  getValueAt(index) {
    const input = this.performInputMapping(this.refs.input[index]);
    if (input && input.widget) {
      return input.widget.getValue();
    }
    return input ? input.value : undefined;
  }

  updateValue(value, flags, index) {
    const changed = super.updateValue(value, flags);
    this.triggerUpdateValueAt(this.dataValue, flags, index);
    return changed;
  }

  parseValue(value) {
    return value;
  }

  formatValue(value) {
    return value;
  }

  attach(element) {
    this.loadRefs(element, {
      charcount: 'multiple',
      wordcount: 'multiple',
      prefix: 'multiple',
      suffix: 'multiple'
    });
    return super.attach(element);
  }

  getWidget(index) {
    index = index || 0;
    if (this.refs.input && this.refs.input[index]) {
      return this.refs.input[index].widget;
    }
    return null;
  }

  getValueAsString(value) {
    return super.getValueAsString(this.getWidgetValueAsString(value));
  }

  attachElement(element, index) {
    super.attachElement(element, index);
    if (element.widget) {
      element.widget.destroy();
    }
    // Attach the widget.
    element.widget = this.createWidget(index);
    if (element.widget) {
      element.widget.attach(element);
      if (this.refs.prefix && this.refs.prefix[index]) {
        element.widget.addPrefix(this.refs.prefix[index]);
      }
      if (this.refs.suffix && this.refs.suffix[index]) {
        element.widget.addSuffix(this.refs.suffix[index]);
      }
    }

    // Add focus and blur events.
    this.addFocusBlurEvents(element);

    if (this.options.submitOnEnter) {
      this.addEventListener(element, 'keypress', (event) => {
        const key = event.keyCode || event.which;
        if (key === 13) {
          event.preventDefault();
          event.stopPropagation();
          this.emit('submitButton');
        }
      });
    }
  }

  /**
   * Creates an instance of a widget for this component.
   *
   * @return {null}
   */
  createWidget(index) {
    // Return null if no widget is found.
    if (!this.component.widget) {
      return null;
    }

    // Get the widget settings.
    const settings = (typeof this.component.widget === 'string') ? {
      type: this.component.widget
    } : this.component.widget;

    // Make sure we have a widget.
    if (!Widgets.hasOwnProperty(settings.type)) {
      return null;
    }

    // Create the widget.
    const widget = new Widgets[settings.type](settings, this.component);
    widget.on('update', () => this.updateValue(widget.getValue(), {
      modified: true
    }, index), true);
    widget.on('redraw', () => this.redraw(), true);
    return widget;
  }

  detach() {
    super.detach();
    if (this.refs && this.refs.input) {
      for (let i = 0; i <= this.refs.input.length; i++) {
        const widget = this.getWidget(i);
        if (widget) {
          widget.destroy();
        }
      }
    }
  }

  addFocusBlurEvents(element) {
    this.addEventListener(element, 'focus', () => {
      if (this.root.focusedComponent !== this) {
        if (this.root.pendingBlur) {
          this.root.pendingBlur();
        }

        this.root.focusedComponent = this;

        this.emit('focus', this);
      }
      else if (this.root.focusedComponent === this && this.root.pendingBlur) {
        this.root.pendingBlur.cancel();
        this.root.pendingBlur = null;
      }
    });
    this.addEventListener(element, 'blur', () => {
      this.root.pendingBlur = delay(() => {
        this.emit('blur', this);
        if (this.component.validateOn === 'blur') {
          this.root.triggerChange({}, {
            instance: this,
            component: this.component,
            value: this.dataValue,
            flags: {}
          });
        }
        this.root.focusedComponent = null;
        this.root.pendingBlur = null;
      });
    });
  }
}
<commit_msg>Fix quotes in input components.<commit_after>import Multivalue from '../multivalue/Multivalue';
import { delay } from '../../../utils/utils';
import Widgets from '../../../widgets';
import _ from 'lodash';

export default class Input extends Multivalue {
  constructor(component, options, data) {
    super(component, options, data);
    this.triggerUpdateValueAt = _.debounce(this.updateValueAt.bind(this), 100);
  }

  static schema(...extend) {
    return Multivalue.schema({
      widget: {
        type: 'input'
      }
    }, ...extend);
  }

  get inputInfo() {
    const attr = {
      name: this.options.name,
      type: this.component.inputType || 'text',
      class: 'form-control',
      lang: this.options.language
    };

    if (this.component.placeholder) {
      attr.placeholder = this.t(this.component.placeholder);
    }

    if (this.component.tabindex) {
      attr.tabindex = this.component.tabindex;
    }

    if (this.disabled) {
      attr.disabled = 'disabled';
    }

    _.defaults(attr, this.component.attributes);

    return {
      id: this.key,
      type: 'input',
      changeEvent: 'input',
      content: '',
      attr
    };
  }

  get maskOptions() {
    return _.map(this.component.inputMasks, mask => {
      return {
        label: mask.label,
        value: mask.label
      };
    });
  }

  get isMultipleMasksField() {
    return this.component.allowMultipleMasks && !!this.component.inputMasks && !!this.component.inputMasks.length;
  }

  getMaskByName(maskName) {
    const inputMask = _.find(this.component.inputMasks, (inputMask) => {
      return inputMask.label === maskName;
    });
    return inputMask ? inputMask.mask : undefined;
  }

  setInputMask(input, inputMask) {
    return super.setInputMask(input, (inputMask || this.component.inputMask), !this.component.placeholder);
  }

  getMaskOptions() {
    return this.component.inputMasks
      .map(mask => ({
        label: mask.label,
        value: mask.label,
      }));
  }

  get remainingWords() {
    const maxWords = _.parseInt(_.get(this.component, 'validate.maxWords'), 10);
    const wordCount = _.words(this.dataValue).length;
    return maxWords - wordCount;
  }

  renderElement(value, index) {
    // Double quotes cause the input value to close so replace them with html quote char.
    if (value && typeof value === 'string') {
      value = value.replace(/"/g, '"');
    }
    const info = this.inputInfo;
    info.attr = info.attr || {};
    info.attr.value = this.getValueAsString(this.formatValue(this.parseValue(value)));
    if (this.isMultipleMasksField) {
      info.attr.class += ' formio-multiple-mask-input';
    }
    // This should be in the calendar widget but it doesn't have access to renderTemplate.
    if (this.component.widget && this.component.widget.type === 'calendar') {
      const calendarIcon = this.renderTemplate('icon', {
        ref: 'icon',
        className: this.iconClass(this.component.enableDate || this.component.widget.enableDate ? 'calendar' : 'time'),
        styles: '',
        content: ''
      }).trim();
      if (this.component.prefix !== calendarIcon) {
        this.component.suffix = calendarIcon;
      }
    }

    return this.isMultipleMasksField
      ? this.renderTemplate('multipleMasksInput', {
        input: info,
        value,
        index,
        selectOptions: this.getMaskOptions() || [],
      })
      : this.renderTemplate('input', {
        input: info,
        value: this.formatValue(this.parseValue(value)),
        index
      });
  }

  setCounter(type, element, count, max) {
    if (max) {
      const remaining = max - count;
      if (remaining > 0) {
        this.removeClass(element, 'text-danger');
      }
      else {
        this.addClass(element, 'text-danger');
      }
      this.setContent(element, this.t(`{{ remaining }} ${type} remaining.`, {
        remaining: remaining
      }));
    }
    else {
      this.setContent(element, this.t(`{{ count }} ${type}`, {
        count: count
      }));
    }
  }

  updateValueAt(value, flags, index) {
    if (_.get(this.component, 'showWordCount', false)) {
      if (this.refs.wordcount && this.refs.wordcount[index]) {
        const maxWords = _.parseInt(_.get(this.component, 'validate.maxWords', 0), 10);
        this.setCounter('words', this.refs.wordcount[index], _.words(value).length, maxWords);
      }
    }
    if (_.get(this.component, 'showCharCount', false)) {
      if (this.refs.charcount && this.refs.charcount[index]) {
        const maxChars = _.parseInt(_.get(this.component, 'validate.maxLength', 0), 10);
        this.setCounter('characters', this.refs.charcount[index], value.length, maxChars);
      }
    }
  }

  getValueAt(index) {
    const input = this.performInputMapping(this.refs.input[index]);
    if (input && input.widget) {
      return input.widget.getValue();
    }
    return input ? input.value : undefined;
  }

  updateValue(value, flags, index) {
    const changed = super.updateValue(value, flags);
    this.triggerUpdateValueAt(this.dataValue, flags, index);
    return changed;
  }

  parseValue(value) {
    return value;
  }

  formatValue(value) {
    return value;
  }

  attach(element) {
    this.loadRefs(element, {
      charcount: 'multiple',
      wordcount: 'multiple',
      prefix: 'multiple',
      suffix: 'multiple'
    });
    return super.attach(element);
  }

  getWidget(index) {
    index = index || 0;
    if (this.refs.input && this.refs.input[index]) {
      return this.refs.input[index].widget;
    }
    return null;
  }

  getValueAsString(value) {
    return super.getValueAsString(this.getWidgetValueAsString(value));
  }

  attachElement(element, index) {
    super.attachElement(element, index);
    if (element.widget) {
      element.widget.destroy();
    }
    // Attach the widget.
    element.widget = this.createWidget(index);
    if (element.widget) {
      element.widget.attach(element);
      if (this.refs.prefix && this.refs.prefix[index]) {
        element.widget.addPrefix(this.refs.prefix[index]);
      }
      if (this.refs.suffix && this.refs.suffix[index]) {
        element.widget.addSuffix(this.refs.suffix[index]);
      }
    }

    // Add focus and blur events.
    this.addFocusBlurEvents(element);

    if (this.options.submitOnEnter) {
      this.addEventListener(element, 'keypress', (event) => {
        const key = event.keyCode || event.which;
        if (key === 13) {
          event.preventDefault();
          event.stopPropagation();
          this.emit('submitButton');
        }
      });
    }
  }

  /**
   * Creates an instance of a widget for this component.
   *
   * @return {null}
   */
  createWidget(index) {
    // Return null if no widget is found.
    if (!this.component.widget) {
      return null;
    }

    // Get the widget settings.
    const settings = (typeof this.component.widget === 'string') ? {
      type: this.component.widget
    } : this.component.widget;

    // Make sure we have a widget.
    if (!Widgets.hasOwnProperty(settings.type)) {
      return null;
    }

    // Create the widget.
    const widget = new Widgets[settings.type](settings, this.component);
    widget.on('update', () => this.updateValue(widget.getValue(), {
      modified: true
    }, index), true);
    widget.on('redraw', () => this.redraw(), true);
    return widget;
  }

  detach() {
    super.detach();
    if (this.refs && this.refs.input) {
      for (let i = 0; i <= this.refs.input.length; i++) {
        const widget = this.getWidget(i);
        if (widget) {
          widget.destroy();
        }
      }
    }
  }

  addFocusBlurEvents(element) {
    this.addEventListener(element, 'focus', () => {
      if (this.root.focusedComponent !== this) {
        if (this.root.pendingBlur) {
          this.root.pendingBlur();
        }

        this.root.focusedComponent = this;

        this.emit('focus', this);
      }
      else if (this.root.focusedComponent === this && this.root.pendingBlur) {
        this.root.pendingBlur.cancel();
        this.root.pendingBlur = null;
      }
    });
    this.addEventListener(element, 'blur', () => {
      this.root.pendingBlur = delay(() => {
        this.emit('blur', this);
        if (this.component.validateOn === 'blur') {
          this.root.triggerChange({}, {
            instance: this,
            component: this.component,
            value: this.dataValue,
            flags: {}
          });
        }
        this.root.focusedComponent = null;
        this.root.pendingBlur = null;
      });
    });
  }
}
<|endoftext|>


<commit_before>/*
Copyright 2015, 2016 OpenMarket 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 React from 'react';
import MatrixClientPeg from "../../../MatrixClientPeg";
import sdk from '../../../index';
import dis from "../../../dispatcher";
import ObjectUtils from '../../../ObjectUtils';
import AppsDrawer from './AppsDrawer';
import  { _t, _tJsx} from '../../../languageHandler';

module.exports = React.createClass({
    displayName: 'AuxPanel',

    propTypes: {
        // js-sdk room object
        room: React.PropTypes.object.isRequired,
        userId: React.PropTypes.string.isRequired,
        showApps: React.PropTypes.bool,

        // Conference Handler implementation
        conferenceHandler: React.PropTypes.object,

        // set to true to show the file drop target
        draggingFile: React.PropTypes.bool,

        // set to true to show the 'active conf call' banner
        displayConfCallNotification: React.PropTypes.bool,

        // maxHeight attribute for the aux panel and the video
        // therein
        maxHeight: React.PropTypes.number,

        // a callback which is called when the content of the aux panel changes
        // content in a way that is likely to make it change size.
        onResize: React.PropTypes.func,
    },

    shouldComponentUpdate: function(nextProps, nextState) {
        return (!ObjectUtils.shallowEqual(this.props, nextProps) ||
                !ObjectUtils.shallowEqual(this.state, nextState));
    },

    componentDidUpdate: function(prevProps, prevState) {
        // most changes are likely to cause a resize
        if (this.props.onResize) {
            this.props.onResize();
        }
    },

    onConferenceNotificationClick: function(ev, type) {
        dis.dispatch({
            action: 'place_call',
            type: type,
            room_id: this.props.room.roomId,
        });
        ev.stopPropagation();
        ev.preventDefault();
    },

    render: function() {
        const CallView = sdk.getComponent("voip.CallView");
        const TintableSvg = sdk.getComponent("elements.TintableSvg");

        let fileDropTarget = null;
        if (this.props.draggingFile) {
            fileDropTarget = (

                        <TintableSvg src="img/upload-big.svg" width="45" height="59"/>

                        {_t("Drop file here to upload")}

            );
        }

        let conferenceCallNotification = null;
        if (this.props.displayConfCallNotification) {
            let supportedText = '';
            let joinNode;
            if (!MatrixClientPeg.get().supportsVoip()) {
                supportedText = _t(" (unsupported)");
            } else {
                joinNode = (
                    {_tJsx(
                        "Join as <voiceText>voice</voiceText> or <videoText>video</videoText>.",
                        [/<voiceText>(.*?)<\/voiceText>/, /<videoText>(.*?)<\/videoText>/],
                        [
                            (sub) => { this.onConferenceNotificationClick(event, 'voice');}} href="#">{sub},
                            (sub) => { this.onConferenceNotificationClick(event, 'video');}} href="#">{sub},
                        ]
                    )}
                );
            }
            // XXX: the translation here isn't great: appending ' (unsupported)' is likely to not make sense in many languages,
            // but there are translations for this in the languages we do have so I'm leaving it for now.
            conferenceCallNotification = (

                    {_t("Ongoing conference call%(supportedText)s.", {supportedText: supportedText})}

                    {joinNode}

            );
        }

        const callView = (
            <CallView ref="callView" room={this.props.room}
                ConferenceHandler={this.props.conferenceHandler}
                onResize={this.props.onResize}
                maxVideoHeight={this.props.maxHeight}
            />
        );

        let appsDrawer = null;
        if(this.props.showApps) {
            appsDrawer = <AppsDrawer ref="appsDrawer"
                room={this.props.room}
                userId={this.props.userId}
                maxHeight={this.props.maxHeight}/>;
        }

        return (

                { appsDrawer }
                { fileDropTarget }
                { callView }
                { conferenceCallNotification }
                { this.props.children }

        );
    },
});
<commit_msg>Only show apps drawer if matrix apps labs setting ids enabled<commit_after>/*
Copyright 2015, 2016 OpenMarket 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 React from 'react';
import MatrixClientPeg from "../../../MatrixClientPeg";
import sdk from '../../../index';
import dis from "../../../dispatcher";
import ObjectUtils from '../../../ObjectUtils';
import AppsDrawer from './AppsDrawer';
import { _t, _tJsx} from '../../../languageHandler';
import UserSettingsStore from '../../../UserSettingsStore';

module.exports = React.createClass({
    displayName: 'AuxPanel',

    propTypes: {
        // js-sdk room object
        room: React.PropTypes.object.isRequired,
        userId: React.PropTypes.string.isRequired,
        showApps: React.PropTypes.bool,

        // Conference Handler implementation
        conferenceHandler: React.PropTypes.object,

        // set to true to show the file drop target
        draggingFile: React.PropTypes.bool,

        // set to true to show the 'active conf call' banner
        displayConfCallNotification: React.PropTypes.bool,

        // maxHeight attribute for the aux panel and the video
        // therein
        maxHeight: React.PropTypes.number,

        // a callback which is called when the content of the aux panel changes
        // content in a way that is likely to make it change size.
        onResize: React.PropTypes.func,
    },

    shouldComponentUpdate: function(nextProps, nextState) {
        return (!ObjectUtils.shallowEqual(this.props, nextProps) ||
                !ObjectUtils.shallowEqual(this.state, nextState));
    },

    componentDidUpdate: function(prevProps, prevState) {
        // most changes are likely to cause a resize
        if (this.props.onResize) {
            this.props.onResize();
        }
    },

    onConferenceNotificationClick: function(ev, type) {
        dis.dispatch({
            action: 'place_call',
            type: type,
            room_id: this.props.room.roomId,
        });
        ev.stopPropagation();
        ev.preventDefault();
    },

    render: function() {
        const CallView = sdk.getComponent("voip.CallView");
        const TintableSvg = sdk.getComponent("elements.TintableSvg");

        let fileDropTarget = null;
        if (this.props.draggingFile) {
            fileDropTarget = (

                        <TintableSvg src="img/upload-big.svg" width="45" height="59"/>

                        {_t("Drop file here to upload")}

            );
        }

        let conferenceCallNotification = null;
        if (this.props.displayConfCallNotification) {
            let supportedText = '';
            let joinNode;
            if (!MatrixClientPeg.get().supportsVoip()) {
                supportedText = _t(" (unsupported)");
            } else {
                joinNode = (
                    {_tJsx(
                        "Join as <voiceText>voice</voiceText> or <videoText>video</videoText>.",
                        [/<voiceText>(.*?)<\/voiceText>/, /<videoText>(.*?)<\/videoText>/],
                        [
                            (sub) => { this.onConferenceNotificationClick(event, 'voice');}} href="#">{sub},
                            (sub) => { this.onConferenceNotificationClick(event, 'video');}} href="#">{sub},
                        ]
                    )}
                );
            }
            // XXX: the translation here isn't great: appending ' (unsupported)' is likely to not make sense in many languages,
            // but there are translations for this in the languages we do have so I'm leaving it for now.
            conferenceCallNotification = (

                    {_t("Ongoing conference call%(supportedText)s.", {supportedText: supportedText})}

                    {joinNode}

            );
        }

        const callView = (
            <CallView ref="callView" room={this.props.room}
                ConferenceHandler={this.props.conferenceHandler}
                onResize={this.props.onResize}
                maxVideoHeight={this.props.maxHeight}
            />
        );

        let appsDrawer = null;
        if(UserSettingsStore.isFeatureEnabled('matrix_apps') && this.props.showApps) {
            appsDrawer = <AppsDrawer ref="appsDrawer"
                room={this.props.room}
                userId={this.props.userId}
                maxHeight={this.props.maxHeight}/>;
        }

        return (

                { appsDrawer }
                { fileDropTarget }
                { callView }
                { conferenceCallNotification }
                { this.props.children }

        );
    },
});
<|endoftext|>


<commit_before>define( [
	"./core",
	"./var/indexOf",
	"./traversing/var/dir",
	"./traversing/var/siblings",
	"./traversing/var/rneedsContext",
	"./core/nodeName",

	"./core/init",
	"./traversing/findFilter",
	"./selector"
], function( jQuery, indexOf, dir, siblings, rneedsContext, nodeName ) {

"use strict";

var rparentsprev = /^(?:parents|prev(?:Until|All))/,

	// Methods guaranteed to produce a unique set when starting from a unique set
	guaranteedUnique = {
		children: true,
		contents: true,
		next: true,
		prev: true
	};

jQuery.fn.extend( {
	has: function( target ) {
		var targets = jQuery( target, this ),
			l = targets.length;

		return this.filter( function() {
			var i = 0;
			for ( ; i < l; i++ ) {
				if ( jQuery.contains( this, targets[ i ] ) ) {
					return true;
				}
			}
		} );
	},

	closest: function( selectors, context ) {
		var cur,
			i = 0,
			l = this.length,
			matched = [],
			targets = typeof selectors !== "string" && jQuery( selectors );

		// Positional selectors never match, since there's no _selection_ context
		if ( !rneedsContext.test( selectors ) ) {
			for ( ; i < l; i++ ) {
				for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) {

					// Always skip document fragments
					if ( cur.nodeType < 11 && ( targets ?
						targets.index( cur ) > -1 :

						// Don't pass non-elements to Sizzle
						cur.nodeType === 1 &&
							jQuery.find.matchesSelector( cur, selectors ) ) ) {

						matched.push( cur );
						break;
					}
				}
			}
		}

		return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched );
	},

	// Determine the position of an element within the set
	index: function( elem ) {

		// No argument, return index in parent
		if ( !elem ) {
			return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1;
		}

		// Index in selector
		if ( typeof elem === "string" ) {
			return indexOf.call( jQuery( elem ), this[ 0 ] );
		}

		// Locate the position of the desired element
		return indexOf.call( this,

			// If it receives a jQuery object, the first element is used
			elem.jquery ? elem[ 0 ] : elem
		);
	},

	add: function( selector, context ) {
		return this.pushStack(
			jQuery.uniqueSort(
				jQuery.merge( this.get(), jQuery( selector, context ) )
			)
		);
	},

	addBack: function( selector ) {
		return this.add( selector == null ?
			this.prevObject : this.prevObject.filter( selector )
		);
	}
} );

function sibling( cur, dir ) {
	while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {}
	return cur;
}

jQuery.each( {
	parent: function( elem ) {
		var parent = elem.parentNode;
		return parent && parent.nodeType !== 11 ? parent : null;
	},
	parents: function( elem ) {
		return dir( elem, "parentNode" );
	},
	parentsUntil: function( elem, i, until ) {
		return dir( elem, "parentNode", until );
	},
	next: function( elem ) {
		return sibling( elem, "nextSibling" );
	},
	prev: function( elem ) {
		return sibling( elem, "previousSibling" );
	},
	nextAll: function( elem ) {
		return dir( elem, "nextSibling" );
	},
	prevAll: function( elem ) {
		return dir( elem, "previousSibling" );
	},
	nextUntil: function( elem, i, until ) {
		return dir( elem, "nextSibling", until );
	},
	prevUntil: function( elem, i, until ) {
		return dir( elem, "previousSibling", until );
	},
	siblings: function( elem ) {
		return siblings( ( elem.parentNode || {} ).firstChild, elem );
	},
	children: function( elem ) {
		return siblings( elem.firstChild );
	},
	contents: function( elem ) {
		if ( elem.contentDocument != null ) {
			return elem.contentDocument;
		}

		// Support: IE 9 - 11+
		// Treat the template element as a regular one in browsers that
		// don't support it.
		if ( nodeName( elem, "template" ) ) {
			elem = elem.content || elem;
		}

		return jQuery.merge( [], elem.childNodes );
	}
}, function( name, fn ) {
	jQuery.fn[ name ] = function( until, selector ) {
		var matched = jQuery.map( this, fn, until );

		if ( name.slice( -5 ) !== "Until" ) {
			selector = until;
		}

		if ( selector && typeof selector === "string" ) {
			matched = jQuery.filter( selector, matched );
		}

		if ( this.length > 1 ) {

			// Remove duplicates
			if ( !guaranteedUnique[ name ] ) {
				jQuery.uniqueSort( matched );
			}

			// Reverse order for parents* and prev-derivatives
			if ( rparentsprev.test( name ) ) {
				matched.reverse();
			}
		}

		return this.pushStack( matched );
	};
} );

return jQuery;
} );
<commit_msg>Traversing: Fix `contents()` on `<object>`s with children in IE<commit_after>define( [
	"./core",
	"./var/getProto",
	"./var/indexOf",
	"./traversing/var/dir",
	"./traversing/var/siblings",
	"./traversing/var/rneedsContext",
	"./core/nodeName",

	"./core/init",
	"./traversing/findFilter",
	"./selector"
], function( jQuery, getProto, indexOf, dir, siblings, rneedsContext, nodeName ) {

"use strict";

var rparentsprev = /^(?:parents|prev(?:Until|All))/,

	// Methods guaranteed to produce a unique set when starting from a unique set
	guaranteedUnique = {
		children: true,
		contents: true,
		next: true,
		prev: true
	};

jQuery.fn.extend( {
	has: function( target ) {
		var targets = jQuery( target, this ),
			l = targets.length;

		return this.filter( function() {
			var i = 0;
			for ( ; i < l; i++ ) {
				if ( jQuery.contains( this, targets[ i ] ) ) {
					return true;
				}
			}
		} );
	},

	closest: function( selectors, context ) {
		var cur,
			i = 0,
			l = this.length,
			matched = [],
			targets = typeof selectors !== "string" && jQuery( selectors );

		// Positional selectors never match, since there's no _selection_ context
		if ( !rneedsContext.test( selectors ) ) {
			for ( ; i < l; i++ ) {
				for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) {

					// Always skip document fragments
					if ( cur.nodeType < 11 && ( targets ?
						targets.index( cur ) > -1 :

						// Don't pass non-elements to Sizzle
						cur.nodeType === 1 &&
							jQuery.find.matchesSelector( cur, selectors ) ) ) {

						matched.push( cur );
						break;
					}
				}
			}
		}

		return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched );
	},

	// Determine the position of an element within the set
	index: function( elem ) {

		// No argument, return index in parent
		if ( !elem ) {
			return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1;
		}

		// Index in selector
		if ( typeof elem === "string" ) {
			return indexOf.call( jQuery( elem ), this[ 0 ] );
		}

		// Locate the position of the desired element
		return indexOf.call( this,

			// If it receives a jQuery object, the first element is used
			elem.jquery ? elem[ 0 ] : elem
		);
	},

	add: function( selector, context ) {
		return this.pushStack(
			jQuery.uniqueSort(
				jQuery.merge( this.get(), jQuery( selector, context ) )
			)
		);
	},

	addBack: function( selector ) {
		return this.add( selector == null ?
			this.prevObject : this.prevObject.filter( selector )
		);
	}
} );

function sibling( cur, dir ) {
	while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {}
	return cur;
}

jQuery.each( {
	parent: function( elem ) {
		var parent = elem.parentNode;
		return parent && parent.nodeType !== 11 ? parent : null;
	},
	parents: function( elem ) {
		return dir( elem, "parentNode" );
	},
	parentsUntil: function( elem, i, until ) {
		return dir( elem, "parentNode", until );
	},
	next: function( elem ) {
		return sibling( elem, "nextSibling" );
	},
	prev: function( elem ) {
		return sibling( elem, "previousSibling" );
	},
	nextAll: function( elem ) {
		return dir( elem, "nextSibling" );
	},
	prevAll: function( elem ) {
		return dir( elem, "previousSibling" );
	},
	nextUntil: function( elem, i, until ) {
		return dir( elem, "nextSibling", until );
	},
	prevUntil: function( elem, i, until ) {
		return dir( elem, "previousSibling", until );
	},
	siblings: function( elem ) {
		return siblings( ( elem.parentNode || {} ).firstChild, elem );
	},
	children: function( elem ) {
		return siblings( elem.firstChild );
	},
	contents: function( elem ) {
		if ( elem.contentDocument != null &&

			// Support: IE 11+
			// <object> elements with no `data` attribute has an object
			// `contentDocument` with a `null` prototype.
			getProto( elem.contentDocument ) ) {

			return elem.contentDocument;
		}

		// Support: IE 9 - 11+
		// Treat the template element as a regular one in browsers that
		// don't support it.
		if ( nodeName( elem, "template" ) ) {
			elem = elem.content || elem;
		}

		return jQuery.merge( [], elem.childNodes );
	}
}, function( name, fn ) {
	jQuery.fn[ name ] = function( until, selector ) {
		var matched = jQuery.map( this, fn, until );

		if ( name.slice( -5 ) !== "Until" ) {
			selector = until;
		}

		if ( selector && typeof selector === "string" ) {
			matched = jQuery.filter( selector, matched );
		}

		if ( this.length > 1 ) {

			// Remove duplicates
			if ( !guaranteedUnique[ name ] ) {
				jQuery.uniqueSort( matched );
			}

			// Reverse order for parents* and prev-derivatives
			if ( rparentsprev.test( name ) ) {
				matched.reverse();
			}
		}

		return this.pushStack( matched );
	};
} );

return jQuery;
} );
<|endoftext|>


<commit_before>on.get(function (middleware, step) {
    middleware(function () {
        var error = new Error
        error.statusCode = 403
        throw error
    }, step())
})
<commit_msg>Forward Sencha Connect HTTP errors using callback.<commit_after>on.get(function (middleware, step) {
    middleware(function (request, response, next) {
        var error = new Error
        error.statusCode = 403
        next(error)
    }, step())
})
<|endoftext|>


<commit_before>var Graphics = require('../display/Graphics'),
    Pool = require('../extra/Pool'),
    math = require('../../lib/pixi/src/core/math/index');

function Path(){
    this.curveAccuracy = 30;
    this.polygon = new math.Polygon();
    this.polygon.closed = false;

    this.tmpPoint = new math.Point();
    this.tmpPoint2 = new math.Point();
    this.tmpDistance = [];
}

Path.prototype.constructor = Path;

Path.prototype.setCurveAccuracy = function(value){
    this.curveAccuracy = value || 30;
    return this;
};

Path.prototype.clear = function(){
    this.polygon.points.length = 0;
    return this;
};

Path.prototype.moveTo = function(x,y){
    this.clear();
    this.polygon.points.push(x,y);
    return this;
};

Path.prototype.lineTo = function(x,y){
    if(this.polygon.points.length === 0){
        this.moveTo(0,0);
    }

    this.polygon.points.push(x,y);
    return this;
};

Path.prototype.getPoint = function(num){
    var len = num*2;
    this.tmpPoint.set(this.polygon.points[len],this.polygon.points[len + 1]);
    return this.tmpPoint;
};

Path.prototype.close = function(){
    var start = this.getPoint(0);
    this.lineTo(start.x, start.y);
    return this;
};

Path.prototype.generateGraphics = function(){
    //TODO: generateGraphics
};

Path.prototype.distanceBetween = function(num1, num2){
    var p1 = this.getPoint(num1),
        p1X = p1.x,
        p1Y = p1.y;

    var p2 = this.getPoint(num2),
        p2X = p2.x,
        p2Y = p2.y;

    var dx = p2X-p1X;
    var dy = p2Y-p1Y;

    return Math.sqrt(dx*dx+dy*dy);
};

Path.prototype.totalDistance = function() {
    this.tmpDistance.length = 0;
    this.tmpDistance.push(0);

    var len = this.length;
    var distance = 0;
    for (var i = 0; i < len - 1; i++) {
        distance += this.distanceBetween(i, i + 1);
        this.tmpDistance.push(distance);
    }

    return distance;
};

Path.prototype.getPointAt = function(num){
    if(num > this.length){
        return this.getPoint(this.length-1);
    }

    if(num%1 === 0){
        return this.getPoint(num);
    }else{
        this.tmpPoint2.set(0,0);

        var diff = num%1;

        var ceil = this.getPoint(Math.ceil(num)),
            ceilX = ceil.x,
            ceilY = ceil.y;

        var floor = this.getPoint(Math.floor(num)),
            floorX = floor.x,
            floorY = floor.y;

        var xx = -((floorX - ceilX)*diff);
        var yy = -((floorY - ceilY)*diff);
        this.tmpPoint2.set(floorX + xx, floorY + yy);

        return this.tmpPoint2;
    }
};

Path.prototype.getPointAtDistance = function(distance){
    if(!this.tmpDistance)this.totalDistance();
    var len = this.tmpDistance.length;
    var n = 0;

    var totalDistance = this.tmpDistance[this.tmpDistance.length-1];
    if(distance < 0){
        distance = totalDistance+distance;
    }else if(distance > totalDistance){
        distance = distance-totalDistance;
    }

    for(var i = 0; i < len; i++){
        if(distance >= this.tmpDistance[i]){
            n = i;
        }

        if(distance < this.tmpDistance[i]){
            break;
        }
    }

    if(n === this.length-1){
        return this.getPointAt(n);
    }

    var diff1 = distance-this.tmpDistance[n];
    var diff2 = this.tmpDistance[n+1] - this.tmpDistance[n];

    return this.getPointAt(n+diff1/diff2);
};

module.exports = Path;

Object.defineProperties(Path.prototype, {
    length : {
        get : function(){
            return (this.polygon.points.length === 0) ? 0 : this.polygon.points.length/2;
        }
    },

    isClosed : {
        get : function(){
            var p1 = this.getPoint(0),
                p1X = p1.x,
                p1Y = p1.y;

            var p2 = this.getPoint(this.length-1),
                p2X = p2.x,
                p2Y = p2.y;

            return (p1X === p2X && p1Y === p2Y);
        }
    }
});<commit_msg>added new methods to draw path more complex<commit_after>var Graphics = require('../display/Graphics'),
    Pool = require('../extra/Pool'),
    math = require('../../lib/pixi/src/core/math/index');

function Path(){
    this.curveAccuracy = 30;
    this.polygon = new math.Polygon();
    this.polygon.closed = false;

    this.tmpPoint = new math.Point();
    this.tmpPoint2 = new math.Point();
    this.tmpDistance = [];
}

Path.prototype.constructor = Path;

Path.prototype.setCurveAccuracy = function(value){
    this.curveAccuracy = value || 30;
    return this;
};

Path.prototype.clear = function(){
    this.polygon.points.length = 0;
    return this;
};

Path.prototype.moveTo = function(x,y){
    this.clear();
    this.polygon.points.push(x,y);
    return this;
};

Path.prototype.lineTo = function(x,y){
    if(this.polygon.points.length === 0){
        this.moveTo(0,0);
    }

    this.polygon.points.push(x,y);
    return this;
};

Path.prototype.getPoint = function(num){
    var len = num*2;
    this.tmpPoint.set(this.polygon.points[len],this.polygon.points[len + 1]);
    return this.tmpPoint;
};

Path.prototype.close = function(){
    var start = this.getPoint(0);
    this.lineTo(start.x, start.y);
    return this;
};

Path.prototype.generateGraphics = function(){
    //TODO: generateGraphics
};

Path.prototype.distanceBetween = function(num1, num2){
    var p1 = this.getPoint(num1),
        p1X = p1.x,
        p1Y = p1.y;

    var p2 = this.getPoint(num2),
        p2X = p2.x,
        p2Y = p2.y;

    var dx = p2X-p1X;
    var dy = p2Y-p1Y;

    return Math.sqrt(dx*dx+dy*dy);
};

Path.prototype.totalDistance = function() {
    this.tmpDistance.length = 0;
    this.tmpDistance.push(0);

    var len = this.length;
    var distance = 0;
    for (var i = 0; i < len - 1; i++) {
        distance += this.distanceBetween(i, i + 1);
        this.tmpDistance.push(distance);
    }

    return distance;
};

Path.prototype.getPointAt = function(num){
    if(num > this.length){
        return this.getPoint(this.length-1);
    }

    if(num%1 === 0){
        return this.getPoint(num);
    }else{
        this.tmpPoint2.set(0,0);

        var diff = num%1;

        var ceil = this.getPoint(Math.ceil(num)),
            ceilX = ceil.x,
            ceilY = ceil.y;

        var floor = this.getPoint(Math.floor(num)),
            floorX = floor.x,
            floorY = floor.y;

        var xx = -((floorX - ceilX)*diff);
        var yy = -((floorY - ceilY)*diff);
        this.tmpPoint2.set(floorX + xx, floorY + yy);

        return this.tmpPoint2;
    }
};

Path.prototype.getPointAtDistance = function(distance){
    if(!this.tmpDistance)this.totalDistance();
    var len = this.tmpDistance.length;
    var n = 0;

    var totalDistance = this.tmpDistance[this.tmpDistance.length-1];
    if(distance < 0){
        distance = totalDistance+distance;
    }else if(distance > totalDistance){
        distance = distance-totalDistance;
    }

    for(var i = 0; i < len; i++){
        if(distance >= this.tmpDistance[i]){
            n = i;
        }

        if(distance < this.tmpDistance[i]){
            break;
        }
    }

    if(n === this.length-1){
        return this.getPointAt(n);
    }

    var diff1 = distance-this.tmpDistance[n];
    var diff2 = this.tmpDistance[n+1] - this.tmpDistance[n];

    return this.getPointAt(n+diff1/diff2);
};

/**
 * Calculate the points for a bezier curve and then draws it.
 *
 * @param cpX {number} Control point x
 * @param cpY {number} Control point y
 * @param cpX2 {number} Second Control point x
 * @param cpY2 {number} Second Control point y
 * @param toX {number} Destination point x
 * @param toY {number} Destination point y
 * @return {Path}
 */
Path.prototype.bezierCurveTo = function (cpX, cpY, cpX2, cpY2, toX, toY) {
    if(this.polygon.points.length === 0){
        this.moveTo(0,0);
    }

    var n = this.curveAccuracy,
        dt,
        dt2,
        dt3,
        t2,
        t3,
        points = this.polygon.points;

    var fromX = points[points.length-2];
    var fromY = points[points.length-1];

    var j = 0;

    for (var i = 1; i <= n; ++i)
    {
        j = i / n;

        dt = (1 - j);
        dt2 = dt * dt;
        dt3 = dt2 * dt;

        t2 = j * j;
        t3 = t2 * j;

        points.push( dt3 * fromX + 3 * dt2 * j * cpX + 3 * dt * t2 * cpX2 + t3 * toX,
            dt3 * fromY + 3 * dt2 * j * cpY + 3 * dt * t2 * cpY2 + t3 * toY);
    }

    return this;
};

/**
 * Calculate the points for a quadratic bezier curve and then draws it.
 * Based on: https://stackoverflow.com/questions/785097/how-do-i-implement-a-bezier-curve-in-c
 *
 * @param cpX {number} Control point x
 * @param cpY {number} Control point y
 * @param toX {number} Destination point x
 * @param toY {number} Destination point y
 * @return {Path}
 */
Path.prototype.quadraticCurveTo = function (cpX, cpY, toX, toY) {
    if(this.polygon.points.length === 0){
        this.moveTo(0,0);
    }

    var xa,
        ya,
        n = this.curveAccuracy,
        points = this.polygon.points;

    var fromX = points[points.length-2];
    var fromY = points[points.length-1];

    var j = 0;
    for (var i = 1; i <= n; ++i)
    {
        j = i / n;

        xa = fromX + ( (cpX - fromX) * j );
        ya = fromY + ( (cpY - fromY) * j );

        points.push( xa + ( ((cpX + ( (toX - cpX) * j )) - xa) * j ),
            ya + ( ((cpY + ( (toY - cpY) * j )) - ya) * j ) );
    }

    return this;
};

/**
 * The arcTo() method creates an arc/curve between two tangents on the canvas.
 *
 * "borrowed" from https://code.google.com/p/fxcanvas/ - thanks google!
 *
 * @param x1 {number} The x-coordinate of the beginning of the arc
 * @param y1 {number} The y-coordinate of the beginning of the arc
 * @param x2 {number} The x-coordinate of the end of the arc
 * @param y2 {number} The y-coordinate of the end of the arc
 * @param radius {number} The radius of the arc
 * @return {Path}
 */
Path.prototype.arcTo = function (x1, y1, x2, y2, radius) {
    if(this.polygon.points.length === 0){
        this.moveTo(x1,y1);
    }

    var points = this.polygon.points,
        fromX = points[points.length-2],
        fromY = points[points.length-1],
        a1 = fromY - y1,
        b1 = fromX - x1,
        a2 = y2   - y1,
        b2 = x2   - x1,
        mm = Math.abs(a1 * b2 - b1 * a2);

    if (mm < 1.0e-8 || radius === 0)
    {
        if (points[points.length-2] !== x1 || points[points.length-1] !== y1)
        {
            points.push(x1, y1);
        }
    }
    else
    {
        var dd = a1 * a1 + b1 * b1,
            cc = a2 * a2 + b2 * b2,
            tt = a1 * a2 + b1 * b2,
            k1 = radius * Math.sqrt(dd) / mm,
            k2 = radius * Math.sqrt(cc) / mm,
            j1 = k1 * tt / dd,
            j2 = k2 * tt / cc,
            cx = k1 * b2 + k2 * b1,
            cy = k1 * a2 + k2 * a1,
            px = b1 * (k2 + j1),
            py = a1 * (k2 + j1),
            qx = b2 * (k1 + j2),
            qy = a2 * (k1 + j2),
            startAngle = Math.atan2(py - cy, px - cx),
            endAngle   = Math.atan2(qy - cy, qx - cx);

        this.arc(cx + x1, cy + y1, radius, startAngle, endAngle, b1 * a2 > b2 * a1);
    }

    return this;
};

/**
 * The arc method creates an arc/curve (used to create circles, or parts of circles).
 *
 * @param cx {number} The x-coordinate of the center of the circle
 * @param cy {number} The y-coordinate of the center of the circle
 * @param radius {number} The radius of the circle
 * @param startAngle {number} The starting angle, in radians (0 is at the 3 o'clock position of the arc's circle)
 * @param endAngle {number} The ending angle, in radians
 * @param anticlockwise {boolean} Optional. Specifies whether the drawing should be counterclockwise or clockwise. False is default, and indicates clockwise, while true indicates counter-clockwise.
 * @return {Path}
 */
Path.prototype.arc = function(cx, cy, radius, startAngle, endAngle, anticlockwise) {
    var startX = cx + Math.cos(startAngle) * radius;
    var startY = cy + Math.sin(startAngle) * radius;

    var points = this.polygon.points;

    if(points.length === 0)
    {
        this.moveTo(startX, startY);
        points = this.polygon.points;
    }
    else if( points[points.length-2] !== startX || points[points.length-1] !== startY)
    {
        points.push(startX, startY);
    }

    if (startAngle === endAngle)return this;

    if( !anticlockwise && endAngle <= startAngle )
    {
        endAngle += Math.PI * 2;
    }
    else if( anticlockwise && startAngle <= endAngle )
    {
        startAngle += Math.PI * 2;
    }

    var sweep = anticlockwise ? (startAngle - endAngle) *-1 : (endAngle - startAngle);
    var segs =  ( Math.abs(sweep)/ (Math.PI * 2) ) * 40;

    if( sweep === 0 ) return this;

    var theta = sweep/(segs*2);
    var theta2 = theta*2;

    var cTheta = Math.cos(theta);
    var sTheta = Math.sin(theta);

    var segMinus = segs - 1;

    var remainder = ( segMinus % 1 ) / segMinus;

    for(var i=0; i<=segMinus; i++)
    {
        var real =  i + remainder * i;

        var angle = ((theta) + startAngle + (theta2 * real));

        var c = Math.cos(angle);
        var s = -Math.sin(angle);

        points.push(( (cTheta *  c) + (sTheta * s) ) * radius + cx,
            ( (cTheta * -s) + (sTheta * c) ) * radius + cy);
    }
    return this;
};

module.exports = Path;

Object.defineProperties(Path.prototype, {
    length : {
        get : function(){
            return (this.polygon.points.length === 0) ? 0 : this.polygon.points.length/2;
        }
    },

    isClosed : {
        get : function(){
            var p1 = this.getPoint(0),
                p1X = p1.x,
                p1Y = p1.y;

            var p2 = this.getPoint(this.length-1),
                p2X = p2.x,
                p2Y = p2.y;

            return (p1X === p2X && p1Y === p2Y);
        }
    }
});<|endoftext|>


<commit_before>
export default () => {
    return {
        require: '^moList',
        restrict: 'E',
        templateUrl: '../monad/directives/list/table/template.html',
        scope: {rows: '=', total: '@', list: '=', templates: '='},
        controller: ['$scope', '$route', '$transclude', '$element', function($scope, $route, $transclude, $element) {
            this.refresh = promise => promise.then(() => {
                $route.reset();
                this.list.page = this.list.page;
            });
            this.columns = [];
            let hdrs = $transclude().find('th');
            hdrs.each((i, hdr) => {
                this.columns.push(angular.element(hdr).attr('property'));
            });
            $element.find('thead tr').append(hdrs);
        }],
        controllerAs: 'tbody',
        link: (scope, elem, attrs, ctrl) => {
            scope.tbody.module = ctrl.module;
        },
        bindToController: true,
        transclude: true
    };
};

<commit_msg>cleanup and set default columns if none supplied<commit_after>
export default () => {
    return {
        require: '^moList',
        restrict: 'E',
        templateUrl: '../monad/directives/list/table/template.html',
        scope: {rows: '=', templates: '='},
        controller: ['$scope', '$route', '$transclude', '$element', function($scope, $route, $transclude, $element) {
            this.refresh = promise => promise.then(() => {
                $route.reset();
                this.list.page = this.list.page;
            });
            this.columns = [];
            if (this.items && this.items.length) {
                for (let key in this.items[0]) {
                    this.columns.push(key);
                }
            }
            let hdrs = $transclude().find('th');
            if (hdrs.length) {
                this.columns = [];
                hdrs.each((i, hdr) => {
                    this.columns.push(angular.element(hdr).attr('property'));
                });
                $element.find('thead tr').append(hdrs);
            } else {
                let h = $element.find('thead tr');
                this.columns.map(col => {
                    h.append('<th>' + col + '</th>');
                });
            }
        }],
        controllerAs: 'tbody',
        link: (scope, elem, attrs, ctrl) => {
            scope.tbody.component = ctrl.component;
        },
        bindToController: true,
        transclude: true
    };
};

<|endoftext|>


<commit_before>import { isString, isArray, keys } from 'underscore';
import { shallowDiff } from 'utils/mixins';
import ParserHtml from 'parser/model/ParserHtml';

const parseStyle = ParserHtml().parseStyle;
export default {
  parseStyle,

  /**
   * To trigger the style change event on models I have to
   * pass a new object instance
   * @param {Object} prop
   * @return {Object}
   */
  extendStyle(prop) {
    return { ...this.getStyle(), ...prop };
  },

  /**
   * Get style object
   * @return {Object}
   */
  getStyle() {
    return { ...this.get('style') };
  },

  /**
   * Set new style object
   * @param {Object|string} prop
   * @param {Object} opts
   * @return {Object} Applied properties
   */
  setStyle(prop = {}, opts = {}) {
    if (isString(prop)) {
      prop = parseStyle(prop);
    }

    const propOrig = this.getStyle();
    const propNew = { ...prop };
    this.set('style', propNew, opts);
    const diff = shallowDiff(propOrig, propNew);
    keys(diff).forEach(pr => {
      const em = this.em;
      this.trigger(`change:style:${pr}`);
      if (em) {
        em.trigger(`styleable:change`, this, pr);
        em.trigger(`styleable:change:${pr}`, this, pr);
      }
    });

    return propNew;
  },

  /**
   * Add style property
   * @param {Object|string} prop
   * @param {string} value
   * @example
   * this.addStyle({color: 'red'});
   * this.addStyle('color', 'blue');
   */
  addStyle(prop, value = '', opts = {}) {
    if (typeof prop == 'string') {
      prop = {
        prop: value
      };
    } else {
      opts = value || {};
    }

    prop = this.extendStyle(prop);
    this.setStyle(prop, opts);
  },

  /**
   * Remove style property
   * @param {string} prop
   */
  removeStyle(prop) {
    let style = this.getStyle();
    delete style[prop];
    this.setStyle(style);
  },

  /**
   * Returns string of style properties
   * @param {Object} [opts={}] Options
   * @return {String}
   */
  styleToString(opts = {}) {
    const result = [];
    const style = this.getStyle();

    for (let prop in style) {
      const imp = opts.important;
      const important = isArray(imp) ? imp.indexOf(prop) >= 0 : imp;
      const value = `${style[prop]}${important ? ' !important' : ''}`;
      result.push(`${prop}:${value};`);
    }

    return result.join('');
  }
};
<commit_msg>Don't output style without values<commit_after>import { isString, isArray, keys } from 'underscore';
import { shallowDiff } from 'utils/mixins';
import ParserHtml from 'parser/model/ParserHtml';

const parseStyle = ParserHtml().parseStyle;
export default {
  parseStyle,

  /**
   * To trigger the style change event on models I have to
   * pass a new object instance
   * @param {Object} prop
   * @return {Object}
   */
  extendStyle(prop) {
    return { ...this.getStyle(), ...prop };
  },

  /**
   * Get style object
   * @return {Object}
   */
  getStyle() {
    return { ...this.get('style') };
  },

  /**
   * Set new style object
   * @param {Object|string} prop
   * @param {Object} opts
   * @return {Object} Applied properties
   */
  setStyle(prop = {}, opts = {}) {
    if (isString(prop)) {
      prop = parseStyle(prop);
    }

    const propOrig = this.getStyle();
    const propNew = { ...prop };
    this.set('style', propNew, opts);
    const diff = shallowDiff(propOrig, propNew);
    keys(diff).forEach(pr => {
      const em = this.em;
      this.trigger(`change:style:${pr}`);
      if (em) {
        em.trigger(`styleable:change`, this, pr);
        em.trigger(`styleable:change:${pr}`, this, pr);
      }
    });

    return propNew;
  },

  /**
   * Add style property
   * @param {Object|string} prop
   * @param {string} value
   * @example
   * this.addStyle({color: 'red'});
   * this.addStyle('color', 'blue');
   */
  addStyle(prop, value = '', opts = {}) {
    if (typeof prop == 'string') {
      prop = {
        prop: value
      };
    } else {
      opts = value || {};
    }

    prop = this.extendStyle(prop);
    this.setStyle(prop, opts);
  },

  /**
   * Remove style property
   * @param {string} prop
   */
  removeStyle(prop) {
    let style = this.getStyle();
    delete style[prop];
    this.setStyle(style);
  },

  /**
   * Returns string of style properties
   * @param {Object} [opts={}] Options
   * @return {String}
   */
  styleToString(opts = {}) {
    const result = [];
    const style = this.getStyle();

    for (let prop in style) {
      const imp = opts.important;
      const important = isArray(imp) ? imp.indexOf(prop) >= 0 : imp;
      const value = `${style[prop]}${important ? ' !important' : ''}`;
      value && result.push(`${prop}:${value};`);
    }

    return result.join('');
  }
};
<|endoftext|>


<commit_before>import {
    invariant,
    isPropSchema,
    isAliasedPropSchema,
    parallel,
    processAdditionalPropArgs
} from "../utils/utils"
import { onAfterDeserialize, onBeforeDeserialize } from "../core/deserialize"
import { _defaultPrimitiveProp } from "../constants"

/**
 * List indicates that this property contains a list of things.
 * Accepts a sub model schema to serialize the contents
 *
 * @example
 * class SubTask {}
 * class Task {}
 * class Todo {}
 *
 * createModelSchema(SubTask, {
 *     title: true,
 * });
 * createModelSchema(Todo, {
 *     title: true,
 *     subTask: list(object(SubTask)),
 * });
 *
 * const todo = deserialize(Todo, {
 *     title: 'Task',
 *     subTask: [
 *         {
 *             title: 'Sub task 1',
 *         },
 *     ],
 * });
 *
 * @param {PropSchema} propSchema to be used to (de)serialize the contents of the array
 * @param {AdditionalPropArgs} additionalArgs optional object that contains beforeDeserialize and/or afterDeserialize handlers
 * @returns {PropSchema}
 */
export default function list(propSchema, additionalArgs) {
    propSchema = propSchema || _defaultPrimitiveProp
    invariant(isPropSchema(propSchema), "expected prop schema as first argument")
    invariant(!isAliasedPropSchema(propSchema),
        "provided prop is aliased, please put aliases first")
    var result = {
        serializer: function (ar) {
            invariant(ar && "length" in ar && "map" in ar, "expected array (like) object")
            return ar.map(propSchema.serializer)
        },
        deserializer: function (jsonArray, done, context) {
            if (!Array.isArray(jsonArray))
                return void done("[serializr] expected JSON array")

            function processItem(jsonValue, onItemDone, itemIndex) {
                function callbackBefore(err, value) {
                    if (!err) {
                        propSchema.deserializer(value, deserializeDone, context)
                    } else {
                        onItemDone(err)
                    }
                }

                function deserializeDone(err, value) {
                    if (typeof propSchema.afterDeserialize === "function") {
                        onAfterDeserialize(callbackAfter, err, value, jsonValue, itemIndex, context,
                            propSchema)
                    } else {
                        onItemDone(err, value)
                    }
                }

                function callbackAfter(errPreliminary, finalOrRetryValue) {
                    if (errPreliminary && finalOrRetryValue !== undefined &&
                        typeof propSchema.afterDeserialize === "function") {

                        propSchema.deserializer(
                            finalOrRetryValue,
                            deserializeDone,
                            context
                        )
                    } else {
                        onItemDone(errPreliminary, finalOrRetryValue)
                    }
                }

                onBeforeDeserialize(callbackBefore, jsonValue, jsonArray, itemIndex, context,
                    propSchema)
            }

            parallel(
                jsonArray,
                processItem,
                done
            )
        }
    }
    result = processAdditionalPropArgs(result, additionalArgs)
    return result
}
<commit_msg>added skipping undefined arrays on serialization<commit_after>import { SKIP } from "../constants"
import {
    invariant,
    isPropSchema,
    isAliasedPropSchema,
    parallel,
    processAdditionalPropArgs
} from "../utils/utils"
import { onAfterDeserialize, onBeforeDeserialize } from "../core/deserialize"
import { _defaultPrimitiveProp } from "../constants"

/**
 * List indicates that this property contains a list of things.
 * Accepts a sub model schema to serialize the contents
 *
 * @example
 * class SubTask {}
 * class Task {}
 * class Todo {}
 *
 * createModelSchema(SubTask, {
 *     title: true,
 * });
 * createModelSchema(Todo, {
 *     title: true,
 *     subTask: list(object(SubTask)),
 * });
 *
 * const todo = deserialize(Todo, {
 *     title: 'Task',
 *     subTask: [
 *         {
 *             title: 'Sub task 1',
 *         },
 *     ],
 * });
 *
 * @param {PropSchema} propSchema to be used to (de)serialize the contents of the array
 * @param {AdditionalPropArgs} additionalArgs optional object that contains beforeDeserialize and/or afterDeserialize handlers
 * @returns {PropSchema}
 */
export default function list(propSchema, additionalArgs) {
    propSchema = propSchema || _defaultPrimitiveProp
    invariant(isPropSchema(propSchema), "expected prop schema as first argument")
    invariant(!isAliasedPropSchema(propSchema),
        "provided prop is aliased, please put aliases first")
    var result = {
        serializer: function (ar) {
            if (ar === undefined) {
                return SKIP
            }
            invariant(ar && "length" in ar && "map" in ar, "expected array (like) object")
            return ar.map(propSchema.serializer)
        },
        deserializer: function (jsonArray, done, context) {
            if (!Array.isArray(jsonArray))
                return void done("[serializr] expected JSON array")

            function processItem(jsonValue, onItemDone, itemIndex) {
                function callbackBefore(err, value) {
                    if (!err) {
                        propSchema.deserializer(value, deserializeDone, context)
                    } else {
                        onItemDone(err)
                    }
                }

                function deserializeDone(err, value) {
                    if (typeof propSchema.afterDeserialize === "function") {
                        onAfterDeserialize(callbackAfter, err, value, jsonValue, itemIndex, context,
                            propSchema)
                    } else {
                        onItemDone(err, value)
                    }
                }

                function callbackAfter(errPreliminary, finalOrRetryValue) {
                    if (errPreliminary && finalOrRetryValue !== undefined &&
                        typeof propSchema.afterDeserialize === "function") {

                        propSchema.deserializer(
                            finalOrRetryValue,
                            deserializeDone,
                            context
                        )
                    } else {
                        onItemDone(errPreliminary, finalOrRetryValue)
                    }
                }

                onBeforeDeserialize(callbackBefore, jsonValue, jsonArray, itemIndex, context,
                    propSchema)
            }

            parallel(
                jsonArray,
                processItem,
                done
            )
        }
    }
    result = processAdditionalPropArgs(result, additionalArgs)
    return result
}
<|endoftext|>