<commit_before>define([
    'knockout-plus',
    'marked',
    'kb_common/html'
], function (
    ko,
    marked,
    html
) {
    'use strict';
    var t = html.tag,
        div = t('div'),
        a = t('a'),
        ul = t('ul'),
        li = t('li');

    function viewModel(params) {
        var helpDb = params.helpDb;

        var subscriptions = ko.kb.SubscriptionManager.make();

        var topicsIndex = {};
        helpDb.topics.forEach(function (topic) {
            topicsIndex[topic.id] = topic;
        });

        var currentTopicId = ko.observable();

        var currentTopic = ko.observable();

        subscriptions.add(currentTopicId.subscribe(function () {
            currentTopic(topicsIndex[currentTopicId()]);
        }));

        // ACTIONS
        function doSelectTopic(topic) {
            currentTopicId(topic.id);
        }

        currentTopicId(params.topic || 'overview');

        function dispose() {
            subscriptions.dispose();
        }

        return {
            topics: helpDb.topics,
            references: helpDb.references,
            currentTopicId: currentTopicId,
            doSelectTopic: doSelectTopic,
            currentTopic: currentTopic,

            dispose: dispose
        };
    }

    var styles = html.makeStyles({
        classes: {
            component: {
                css: {
                    paddingTop: '12px'
                }
            },
            index: {
                css: {
                    display: 'inline-block',
                    width: '30%',
                    border: '1px rgb(221,221,221) solid',
                    padding: '6px',
                    verticalAlign: 'top'
                }
            },
            indexList: {
                css: {
                    listStyle: 'none',
                    padding: '0'
                }
            },
            indexListItem: {
                css: {
                    display: 'block',
                    padding: '4px'
                }
            },
            indexListItemLink: {
                css: {
                    padding: '4px',
                    display: 'block'
                },
                pseudo: {
                    hover: {
                        backgroundColor: '#DDD'
                    }
                }
            },
            active: {
                css: {
                    backgroundColor: '#DDD'
                }
            },
            body: {
                css: {
                    display: 'inline-block',
                    width: '70%',
                    padding: '6px 6px 6px 12px',
                    verticalAlign: 'top'
                }
            },
            title: {
                css: {
                    fontWeight: 'bold'
                }
            },
            references: {
                css: {
                    marginTop: '12px'
                }
            },
            markdown: {
                css: {

                },
                inner: {
                    blockquote: {
                        fontSize: 'inherit',
                        marginLeft: '1em',
                        paddingLeft: '1em',
                        borderLeft: '3px silver solid'
                    },
                    p: {
                        maxWidth: '50em'
                    },
                    h1: {
                        marginTop: '0',
                        marginBottom: '0',
                        fontWeight: 'bold',
                        fontSize: '150%'
                    },
                    h2: {
                        marginTop: '1em',
                        marginBottom: '0',
                        fontWeight: 'bold',
                        fontSize: '133%'
                    },
                    h3: {
                        marginTop: '1em',
                        marginBottom: '0',
                        fontWeight: 'bold',
                        fontSize: '120%'
                    },
                    h4: {
                        marginTop: '1em',
                        marginBottom: '0',
                        fontWeight: 'bold',
                        textDecoration: 'underline',
                        fontSize: '100%'
                    },
                    h5: {
                        marginTop: '1em',
                        marginBottom: '0',
                        fontWeight: 'bold',
                        fontSize: '100%'
                    }
                }
            }
        }
    });

    ko.bindingHandlers.htmlMarkdown = {
        update: function (element, valueAccessor) {
            var markdown = marked(valueAccessor());
            element.innerHTML = markdown;
        }
    };

    function template() {
        return div({
            class: styles.classes.component
        }, [
            styles.sheet,
            div({
                class: styles.classes.index
            }, [
                div({
                    style: {
                        fontWeight: 'bold'
                    }
                }),
                ul({
                    dataBind: {
                        foreach: 'topics'
                    },
                    class: styles.classes.indexList
                }, [
                    '<!-- ko if: !$data.disabled -->',
                    li(a({
                        dataBind: {
                            text: 'title',
                            click: '$component.doSelectTopic',
                            css: 'id === $component.currentTopicId() ? "' + styles.classes.active + '": ""'
                        },
                        class: styles.classes.indexListItem
                    })),
                    '<!-- /ko -->'
                ])
            ]),
            div({
                dataBind: {
                    with: 'currentTopic'
                },
                class: styles.classes.body
            }, [
                div({
                    dataBind: {
                        text: 'title'
                    },
                    class: styles.classes.title
                }),
                div({
                    dataBind: {
                        htmlMarkdown: 'content'
                    },
                    class: styles.classes.markdown
                    // class: 'kb-help-markdown '
                })
                // div({
                //     dataBind: {
                //         foreach: 'content'
                //     },
                //     class: '-content'
                // }, p({
                //     dataBind: {
                //         text: '$data'
                //     }
                // }))
            ]),
            '<!-- ko if: $data.references && references.length > 0 -->',
            div({
                class: styles.classes.references
            }, [
                div({
                    class: styles.classes.title
                }, 'References'),
                ul({
                    dataBind: {
                        foreach: 'references'
                    }
                }, li(a({
                    dataBind: {
                        attr: {
                            href: 'url',
                            target: 'external ? "_blank" : ""'
                        },
                        text: 'title'
                    }
                })))
            ]),
            '<!-- /ko -->'
        ]);
    }

    function component() {
        return {
            viewModel: viewModel,
            template: template()
        };
    }
    return component;
});<commit_msg>remove extraneous binding handler [SCT-1556]<commit_after>define([
    'knockout-plus',
    'marked',
    'kb_common/html'
], function (
    ko,
    marked,
    html
) {
    'use strict';
    var t = html.tag,
        div = t('div'),
        a = t('a'),
        ul = t('ul'),
        li = t('li');

    function viewModel(params) {
        var helpDb = params.helpDb;

        var subscriptions = ko.kb.SubscriptionManager.make();

        var topicsIndex = {};
        helpDb.topics.forEach(function (topic) {
            topicsIndex[topic.id] = topic;
        });

        var currentTopicId = ko.observable();

        var currentTopic = ko.observable();

        subscriptions.add(currentTopicId.subscribe(function () {
            currentTopic(topicsIndex[currentTopicId()]);
        }));

        // ACTIONS
        function doSelectTopic(topic) {
            currentTopicId(topic.id);
        }

        currentTopicId(params.topic || 'overview');

        function dispose() {
            subscriptions.dispose();
        }

        return {
            topics: helpDb.topics,
            references: helpDb.references,
            currentTopicId: currentTopicId,
            doSelectTopic: doSelectTopic,
            currentTopic: currentTopic,

            dispose: dispose
        };
    }

    var styles = html.makeStyles({
        classes: {
            component: {
                css: {
                    paddingTop: '12px'
                }
            },
            index: {
                css: {
                    display: 'inline-block',
                    width: '30%',
                    border: '1px rgb(221,221,221) solid',
                    padding: '6px',
                    verticalAlign: 'top'
                }
            },
            indexList: {
                css: {
                    listStyle: 'none',
                    padding: '0'
                }
            },
            indexListItem: {
                css: {
                    display: 'block',
                    padding: '4px'
                }
            },
            indexListItemLink: {
                css: {
                    padding: '4px',
                    display: 'block'
                },
                pseudo: {
                    hover: {
                        backgroundColor: '#DDD'
                    }
                }
            },
            active: {
                css: {
                    backgroundColor: '#DDD'
                }
            },
            body: {
                css: {
                    display: 'inline-block',
                    width: '70%',
                    padding: '6px 6px 6px 12px',
                    verticalAlign: 'top'
                }
            },
            title: {
                css: {
                    fontWeight: 'bold'
                }
            },
            references: {
                css: {
                    marginTop: '12px'
                }
            },
            markdown: {
                css: {

                },
                inner: {
                    blockquote: {
                        fontSize: 'inherit',
                        marginLeft: '1em',
                        paddingLeft: '1em',
                        borderLeft: '3px silver solid'
                    },
                    p: {
                        maxWidth: '50em'
                    },
                    h1: {
                        marginTop: '0',
                        marginBottom: '0',
                        fontWeight: 'bold',
                        fontSize: '150%'
                    },
                    h2: {
                        marginTop: '1em',
                        marginBottom: '0',
                        fontWeight: 'bold',
                        fontSize: '133%'
                    },
                    h3: {
                        marginTop: '1em',
                        marginBottom: '0',
                        fontWeight: 'bold',
                        fontSize: '120%'
                    },
                    h4: {
                        marginTop: '1em',
                        marginBottom: '0',
                        fontWeight: 'bold',
                        textDecoration: 'underline',
                        fontSize: '100%'
                    },
                    h5: {
                        marginTop: '1em',
                        marginBottom: '0',
                        fontWeight: 'bold',
                        fontSize: '100%'
                    }
                }
            }
        }
    });

    function template() {
        return div({
            class: styles.classes.component
        }, [
            styles.sheet,
            div({
                class: styles.classes.index
            }, [
                div({
                    style: {
                        fontWeight: 'bold'
                    }
                }),
                ul({
                    dataBind: {
                        foreach: 'topics'
                    },
                    class: styles.classes.indexList
                }, [
                    '<!-- ko if: !$data.disabled -->',
                    li(a({
                        dataBind: {
                            text: 'title',
                            click: '$component.doSelectTopic',
                            css: 'id === $component.currentTopicId() ? "' + styles.classes.active + '": ""'
                        },
                        class: styles.classes.indexListItem
                    })),
                    '<!-- /ko -->'
                ])
            ]),
            div({
                dataBind: {
                    with: 'currentTopic'
                },
                class: styles.classes.body
            }, [
                div({
                    dataBind: {
                        text: 'title'
                    },
                    class: styles.classes.title
                }),
                div({
                    dataBind: {
                        htmlMarkdown: 'content'
                    },
                    class: styles.classes.markdown
                    // class: 'kb-help-markdown '
                })
                // div({
                //     dataBind: {
                //         foreach: 'content'
                //     },
                //     class: '-content'
                // }, p({
                //     dataBind: {
                //         text: '$data'
                //     }
                // }))
            ]),
            '<!-- ko if: $data.references && references.length > 0 -->',
            div({
                class: styles.classes.references
            }, [
                div({
                    class: styles.classes.title
                }, 'References'),
                ul({
                    dataBind: {
                        foreach: 'references'
                    }
                }, li(a({
                    dataBind: {
                        attr: {
                            href: 'url',
                            target: 'external ? "_blank" : ""'
                        },
                        text: 'title'
                    }
                })))
            ]),
            '<!-- /ko -->'
        ]);
    }

    function component() {
        return {
            viewModel: viewModel,
            template: template()
        };
    }
    return component;
});<|endoftext|>


<commit_before>
// @require core/widget/widget.js
// @require widgets/draggable/draggable.js

(function ( $, _, Svelto, Factory ) {

  /* CONFIG */

  let config = {
    name: 'layoutResizable',
    plugin: true,
    selector: '.layout.resizable',
    templates: {
      sash: _.template ( '

' )
    },
    options: {
      classes: {
        nosash: 'no-sash',
        vertical: 'vertical'
      }
    }
  };

  /* LAYOUT RESIZER */

  class LayoutResizer extends Svelto.Widget {

    /* SPECIAL */

    _variables () {

      this.$layout = this.$element;
      this.$panes = this.$layout.children ();
      this.$sashes = $.$empty;
      this.isHorizontal = !this.$layout.hasClass ( this.options.classes.vertical );
      this.mapping = {}; // id => [$pane, $sash, hasSash, isResizable, minDimension, maxDimension, dimension]

    }

    _init () {

      this._initMapping ();

      this.$layout.prepend ( this.$sashes );

      this._updateDimension ();
      this._updateMapping ();
      this._updatePanes ();
      this._updateSashes ();

    }

    _events () {

      this.___drag ();
      this.___resize ();

    }

    _destroy () {

      this.$sashes.remove ();

    }

    /* HELPERS */

    _calcProp ( $ele, prop, fallback = 0 ) {

      return parseFloat ( $ele.css ( prop ) ) || fallback;

    }

    _initMapping () {

      this.$panes.get ().forEach ( ( pane, id ) => {

        const $pane = $(pane),
              isLast = id === ( this.$panes.length - 1 ),
              hasSash = !isLast && !$pane.hasClass ( this.options.classes.nosash ),
              isResizable = hasSash || ( id && this.mapping[id - 1][2] ),
              $sash = hasSash ? $(this._template ( 'sash' )) : undefined,
              minDimensionRaw = this._calcProp ( $pane, this.isHorizontal ? 'min-width' : 'min-height' ),
              minDimension = minDimensionRaw || ( this._calcProp ( $pane, this.isHorizontal ? 'padding-left' : 'padding-top' ) + this._calcProp ( $pane, this.isHorizontal ? 'padding-right' : 'padding-bottom' ) + this._calcProp ( $pane, this.isHorizontal ? 'border-left-width' : 'border-top-width' ) + this._calcProp ( $pane, this.isHorizontal ? 'border-right-width' : 'border-bottom-width' ) ) || 0,
              maxDimensionRaw = parseFloat ( $pane.css ( this.isHorizontal ? 'max-width' : 'max-height' ) ),
              maxDimension = maxDimensionRaw || Infinity,
              dimension = 0;

        this.mapping[id] = [$pane, $sash, hasSash, isResizable, minDimension, maxDimension, dimension];

        if ( !hasSash ) return;

        $sash[0]._resid = id;

        this.$sashes = this.$sashes.add ( $sash );

      });

    }

    _updateDimension () {

      this.dimension = this.isHorizontal ? this.$layout.outerWidth () : this.$layout.outerHeight ();

    }

    _updateMapping () {

      for ( let id in this.mapping ) {
        const mapping = this.mapping[id];
        const dimension = this.isHorizontal ? mapping[0].outerWidth () : mapping[0].outerHeight ();
        mapping[6] = dimension;
      }

    }

    _updatePanes () {

      for ( let id in this.mapping ) {
        const mapping = this.mapping[id];
        const $pane = mapping[0];
        const dimension = mapping[6];
        this.isHorizontal ? $pane.css ( 'width', dimension ) : $pane.css ( 'height', dimension );
        if ( !mapping[3] ) continue;
        $pane.css ( 'flex-basis', dimension ); // So that panes scale properly on resize
      }

    }

    _updateSashes () {

      let offset = 0;

      for ( let id in this.mapping ) {
        const mapping = this.mapping[id];
        const $sash = mapping[1];
        const dimension = mapping[6];
        offset += dimension;
        if ( !$sash ) continue;
        this.isHorizontal ? $sash.translateX ( offset ) : $sash.translateY ( offset );
      }

    }

    /* DRAGGING */

    ___drag () {

      this.$sashes.draggable ({
        axis: this.isHorizontal ? 'x' : 'y',
        classes: {
          layout: {
            priorityZIndex: 'layout-priority-z-index sash-dragging'
          }
        },
        callbacks: {
          start: this.__dragStart.bind ( this ),
          move: this.__dragMove.bind ( this ),
          end: this.__dragEnd.bind ( this )
        }
      });

    }

    __dragStart ( event, data ) {

      this._prevMoveXY = data.startXY;

    }

    __dragMove ( event, data ) {

      const {draggable, moveXY} = data,
            deltaDimension = this.isHorizontal ? moveXY.x - this._prevMoveXY.x : moveXY.y - this._prevMoveXY.y;

      if ( !deltaDimension ) return;

      const id = draggable._resid,
            mapping = this.mapping[id];

      // We are starting by decrementing because we can actually determine when we've reached the limit

      let decSign = Math.sign ( deltaDimension ), // Direction of the decrement
          decId = decSign > 0 ? id + 1 : id, // Next id to target
          incSign = - decSign, // Direction of the increment
          incId = incSign > 0 ? id + 1 : id, // Id to increment
          extraId = incId, // Just a copy of incId, so that we can mutate it
          remDimension = Math.abs ( deltaDimension ), // Amount of remaining dimension left to distribute
          extraDimension = remDimension, // Dimension that goes over max-dimension and therefore can't be assigned
          accDimension = 0; // Amount of accumulated dimension that has been redistributed

      while ( true ) { // Checking how much extra dimension there is

        const mapping = this.mapping[extraId];

        if ( !mapping ) break;

        if ( mapping[3] ) {

          extraDimension -= Math.min ( extraDimension, ( mapping[5] - mapping[6] ) )

          if ( !extraDimension ) break;

        }

        extraId += incSign;

      }

      remDimension -= extraDimension;

      while ( true ) { // Decreasing dimension

        const mapping = this.mapping[decId];

        if ( !mapping ) break;

        if ( mapping[3] ) {

          const dimensionNext = Math.max ( mapping[4], mapping[6] - remDimension ),
                distributedDimension = mapping[6] - dimensionNext;

          accDimension += distributedDimension;
          remDimension -= distributedDimension;

          mapping[6] = dimensionNext;

          if ( !remDimension ) break;

        }

        decId += decSign;

      }

      if ( !accDimension ) return;

      this.isHorizontal ? data.moveXY.x -= remDimension + extraDimension : data.moveXY.y -= remDimension + extraDimension; // Removing remaining dimension in order to improve the alignment between the cursor and the sash
      this._prevMoveXY = data.moveXY; // If this event didn't cause any change, we don't consider it at all

      while ( true ) { // Increasing dimension

        const mapping = this.mapping[incId];

        if ( !mapping ) break;

        if ( mapping[3] ) {

          const partial = Math.min ( accDimension, ( mapping[5] - mapping[6] ) );

          mapping[6] += partial;
          accDimension -= partial;

          if ( !accDimension ) break;

        }

        incId += incSign;

      }

      this._updatePanes ();

    }

    __dragEnd () {

      this._updateMapping ();
      this._updateSashes ();

    }

    /* RESIZE */

    ___resize () {

      this._on ( true, $.$window, 'resize', this._throttle ( this.__resize.bind ( this ), 500 ) );

    }

    __resize () {

      this._updateDimension ();
      this._updateMapping ();
      this._updateSashes ();

    }

    /* API */

    getDimensions () {

      const dimensions = {};

      for ( let id in this.mapping ) {
        dimensions[id] = this.mapping[id][6];
      }

      return dimensions;

    }

    setDimensions ( dimensions ) {

      for ( let id in dimensions ) {
        if ( !this.mapping[id] ) break;
        this.mapping[id][6] = dimensions[id];
      }

      this._updatePanes ();
      this._updateDimension ();
      this._updateMapping ();
      this._updateSashes ();

    }

  }

  /* FACTORY */

  Factory.make ( LayoutResizer, config );

}( Svelto.$, Svelto._, Svelto, Svelto.Factory ));
<commit_msg>Layout Resizable: added a `resize` event<commit_after>
// @require core/widget/widget.js
// @require widgets/draggable/draggable.js

(function ( $, _, Svelto, Factory ) {

  /* CONFIG */

  let config = {
    name: 'layoutResizable',
    plugin: true,
    selector: '.layout.resizable',
    templates: {
      sash: _.template ( '

' )
    },
    options: {
      classes: {
        nosash: 'no-sash',
        vertical: 'vertical'
      },
      callbacks: {
        resize: _.noop
      }
    }
  };

  /* LAYOUT RESIZER */

  class LayoutResizer extends Svelto.Widget {

    /* SPECIAL */

    _variables () {

      this.$layout = this.$element;
      this.$panes = this.$layout.children ();
      this.$sashes = $.$empty;
      this.isHorizontal = !this.$layout.hasClass ( this.options.classes.vertical );
      this.mapping = {}; // id => [$pane, $sash, hasSash, isResizable, minDimension, maxDimension, dimension]

    }

    _init () {

      this._initMapping ();

      this.$layout.prepend ( this.$sashes );

      this._updateDimension ();
      this._updateMapping ();
      this._updatePanes ();
      this._updateSashes ();

    }

    _events () {

      this.___drag ();
      this.___resize ();

    }

    _destroy () {

      this.$sashes.remove ();

    }

    /* HELPERS */

    _calcProp ( $ele, prop, fallback = 0 ) {

      return parseFloat ( $ele.css ( prop ) ) || fallback;

    }

    _initMapping () {

      this.$panes.get ().forEach ( ( pane, id ) => {

        const $pane = $(pane),
              isLast = id === ( this.$panes.length - 1 ),
              hasSash = !isLast && !$pane.hasClass ( this.options.classes.nosash ),
              isResizable = hasSash || ( id && this.mapping[id - 1][2] ),
              $sash = hasSash ? $(this._template ( 'sash' )) : undefined,
              minDimensionRaw = this._calcProp ( $pane, this.isHorizontal ? 'min-width' : 'min-height' ),
              minDimension = minDimensionRaw || ( this._calcProp ( $pane, this.isHorizontal ? 'padding-left' : 'padding-top' ) + this._calcProp ( $pane, this.isHorizontal ? 'padding-right' : 'padding-bottom' ) + this._calcProp ( $pane, this.isHorizontal ? 'border-left-width' : 'border-top-width' ) + this._calcProp ( $pane, this.isHorizontal ? 'border-right-width' : 'border-bottom-width' ) ) || 0,
              maxDimensionRaw = parseFloat ( $pane.css ( this.isHorizontal ? 'max-width' : 'max-height' ) ),
              maxDimension = maxDimensionRaw || Infinity,
              dimension = 0;

        this.mapping[id] = [$pane, $sash, hasSash, isResizable, minDimension, maxDimension, dimension];

        if ( !hasSash ) return;

        $sash[0]._resid = id;

        this.$sashes = this.$sashes.add ( $sash );

      });

    }

    _updateDimension () {

      this.dimension = this.isHorizontal ? this.$layout.outerWidth () : this.$layout.outerHeight ();

    }

    _updateMapping () {

      for ( let id in this.mapping ) {
        const mapping = this.mapping[id];
        const dimension = this.isHorizontal ? mapping[0].outerWidth () : mapping[0].outerHeight ();
        mapping[6] = dimension;
      }

    }

    _updatePanes () {

      for ( let id in this.mapping ) {
        const mapping = this.mapping[id];
        const $pane = mapping[0];
        const dimension = mapping[6];
        this.isHorizontal ? $pane.css ( 'width', dimension ) : $pane.css ( 'height', dimension );
        if ( !mapping[3] ) continue;
        $pane.css ( 'flex-basis', dimension ); // So that panes scale properly on resize
      }

      this._trigger ( 'resize' );

    }

    _updateSashes () {

      let offset = 0;

      for ( let id in this.mapping ) {
        const mapping = this.mapping[id];
        const $sash = mapping[1];
        const dimension = mapping[6];
        offset += dimension;
        if ( !$sash ) continue;
        this.isHorizontal ? $sash.translateX ( offset ) : $sash.translateY ( offset );
      }

    }

    /* DRAGGING */

    ___drag () {

      this.$sashes.draggable ({
        axis: this.isHorizontal ? 'x' : 'y',
        classes: {
          layout: {
            priorityZIndex: 'layout-priority-z-index sash-dragging'
          }
        },
        callbacks: {
          start: this.__dragStart.bind ( this ),
          move: this.__dragMove.bind ( this ),
          end: this.__dragEnd.bind ( this )
        }
      });

    }

    __dragStart ( event, data ) {

      this._prevMoveXY = data.startXY;

    }

    __dragMove ( event, data ) {

      const {draggable, moveXY} = data,
            deltaDimension = this.isHorizontal ? moveXY.x - this._prevMoveXY.x : moveXY.y - this._prevMoveXY.y;

      if ( !deltaDimension ) return;

      const id = draggable._resid,
            mapping = this.mapping[id];

      // We are starting by decrementing because we can actually determine when we've reached the limit

      let decSign = Math.sign ( deltaDimension ), // Direction of the decrement
          decId = decSign > 0 ? id + 1 : id, // Next id to target
          incSign = - decSign, // Direction of the increment
          incId = incSign > 0 ? id + 1 : id, // Id to increment
          extraId = incId, // Just a copy of incId, so that we can mutate it
          remDimension = Math.abs ( deltaDimension ), // Amount of remaining dimension left to distribute
          extraDimension = remDimension, // Dimension that goes over max-dimension and therefore can't be assigned
          accDimension = 0; // Amount of accumulated dimension that has been redistributed

      while ( true ) { // Checking how much extra dimension there is

        const mapping = this.mapping[extraId];

        if ( !mapping ) break;

        if ( mapping[3] ) {

          extraDimension -= Math.min ( extraDimension, ( mapping[5] - mapping[6] ) )

          if ( !extraDimension ) break;

        }

        extraId += incSign;

      }

      remDimension -= extraDimension;

      while ( true ) { // Decreasing dimension

        const mapping = this.mapping[decId];

        if ( !mapping ) break;

        if ( mapping[3] ) {

          const dimensionNext = Math.max ( mapping[4], mapping[6] - remDimension ),
                distributedDimension = mapping[6] - dimensionNext;

          accDimension += distributedDimension;
          remDimension -= distributedDimension;

          mapping[6] = dimensionNext;

          if ( !remDimension ) break;

        }

        decId += decSign;

      }

      if ( !accDimension ) return;

      this.isHorizontal ? data.moveXY.x -= remDimension + extraDimension : data.moveXY.y -= remDimension + extraDimension; // Removing remaining dimension in order to improve the alignment between the cursor and the sash
      this._prevMoveXY = data.moveXY; // If this event didn't cause any change, we don't consider it at all

      while ( true ) { // Increasing dimension

        const mapping = this.mapping[incId];

        if ( !mapping ) break;

        if ( mapping[3] ) {

          const partial = Math.min ( accDimension, ( mapping[5] - mapping[6] ) );

          mapping[6] += partial;
          accDimension -= partial;

          if ( !accDimension ) break;

        }

        incId += incSign;

      }

      this._updatePanes ();

    }

    __dragEnd () {

      this._updateMapping ();
      this._updateSashes ();

    }

    /* RESIZE */

    ___resize () {

      this._on ( true, $.$window, 'resize', this._throttle ( this.__resize.bind ( this ), 500 ) );

    }

    __resize () {

      this._updateDimension ();
      this._updateMapping ();
      this._updateSashes ();

    }

    /* API */

    getDimensions () {

      const dimensions = {};

      for ( let id in this.mapping ) {
        dimensions[id] = this.mapping[id][6];
      }

      return dimensions;

    }

    setDimensions ( dimensions ) {

      for ( let id in dimensions ) {
        if ( !this.mapping[id] ) break;
        this.mapping[id][6] = dimensions[id];
      }

      this._updatePanes ();
      this._updateDimension ();
      this._updateMapping ();
      this._updateSashes ();

    }

  }

  /* FACTORY */

  Factory.make ( LayoutResizer, config );

}( Svelto.$, Svelto._, Svelto, Svelto.Factory ));
<|endoftext|>


<commit_before>angular
    .module('ngSharepointLists')
    .factory('$spList', ['$sp', 'SelectQuery', 'UpdateQuery', 'InsertIntoQuery', 'DeleteQuery', function ($sp, SelectQuery, UpdateQuery, InsertIntoQuery, DeleteQuery) {
        return ({
            getList: function(title) {
              return null;
            },
            select: function(fields) {
                return new SelectQuery(fields);
            },
            update: function(list) {
                return new UpdateQuery(list);
            },
            insertInto: function(list) {
                return new InsertIntoQuery(list);
            },
            delete: function() {
                return new DeleteQuery();
            }
        });
    }])
    .factory('SPList', ['$q', '$http', '$sp', function($q, $http, $sp) {
      var SPList = function(title) {
        this.title = title;
        if ($sp.getConnectionMode() == "JSOM") {
          this.__list = new JsomSPList(title);
        }else {
          this.__list = new RestSPList(title);
        }
      };
      SPList.prototype.insert = function(data) {
        return this.__list.insert(data);
      };
      SPList.prototype.select = function(query) {
        return this.__list.select(query);
      };
      SPList.prototype.delete = function(query) {
        return this.__list.delete(query);
      };
      var RestSPList = function(title) {
        this.title = title;
      };
      RestSPList.prototype.select = function(query) {
        var endpoint = "_api/web/Lists/GetByTitle('" + this.title + "')/GetItems";
        var body = {
          query: {
            __metadata: {
              type: "SP.CamlQuery"
            },
            ViewXml: query.build()
          }
        };
        var headers = {
          "X-RequestDigest": $("#__REQUESTDIGEST").val(),
          "Accept": "application/json; odata=verbose",
          "Content-Type": "application/json; odata=verbose"
        };
        return $q(function(resolve, reject) {
          $http({
            method: 'POST',
            url: $sp.getSiteUrl() + endpoint,
            data: body,
            headers: headers
          }).then(function(data) {
            //TODO: Parse Result
          }, reject);
        });
      };
      var JsomSPList = function(title) {
        this.title = title;
      };
      JsomSPList.prototype.select = function(query) {
        return $q(function(resolve, reject) {
          var context = $sp.getContext();
          var list = context.get_web().get_lists().getByTitle(this.title);
          var items = list.getItems(query.build());
          context.load(items);
          context.executeQueryAsync(function() {
            var result = [];
            var itemIterator = items.getEnumerator();
            while (itemIterator.moveNext()) {
              var item = itemIterator.get_current();
              result.push(query.unpackItem(item));
            }
            resolve(result);
          }, function(sender, args) {
            reject(args);
          });
        });
      };
      JsomSPList.prototype.insert = function(data) {
        var list = this;
        return $q(function(resolve, reject) {
          var clientContext = $sp.getContext();
          var list = clientContext.get_web().get_lists().getByTitle(list.title);
          var itemInfo = new SP.ListItemCreationInformation();
          var item = list.addItem(itemInfo);
          Object.getOwnPropertyNames(data).forEach(function(key) {
            var value = data[key];
            if (value !== null && value !== undefined && typeof value == 'string') {
              value = value.trim();
            }
            item.set_item(key, value);
          });
          item.update();
          clientContext.load(item);
          clientContext.executeQueryAsync(function(sender, args) {
              resolve(query.unpackItem(item));
          }, function(sender, args) {
              reject(args);
          });
        });
      };
      JsomSPList.prototype.delete = function(query) {
        var list = this;
        return $q(function(resolve, reject) {
          var clientContext = $sp.getContext();
          var list = clientContext.get_web().get_lists().getByTitle(list.title);
          var items = list.getItems(query);
          clientContext.load(items);
          clientContext.executeQueryAsync(
              function(sender, args) {
                  var itemIterator = items.getEnumerator();
                  var a = [];
                  while (itemIterator.moveNext()) {
                     var item = itemIterator.get_current();
                     a.push(item);
                  }
                  a.forEach(function(item) {
                      item.deleteObject();
                  });
                  clientContext.executeQueryAsync(function(sender, args) {
                      resolve(args);
                  }, function(sender, args) {
                      reject(args);
                  });
              },
              function(sender, args) {
                  reject(args);
              }
          );
        });
      };
      return (SPList);
    }]);
<commit_msg>Added Insert method to RestSPList<commit_after>angular
    .module('ngSharepointLists')
    .factory('$spList', ['$sp', 'SelectQuery', 'UpdateQuery', 'InsertIntoQuery', 'DeleteQuery', function ($sp, SelectQuery, UpdateQuery, InsertIntoQuery, DeleteQuery) {
        return ({
            getList: function(title) {
              return null;
            },
            select: function(fields) {
                return new SelectQuery(fields);
            },
            update: function(list) {
                return new UpdateQuery(list);
            },
            insertInto: function(list) {
                return new InsertIntoQuery(list);
            },
            delete: function() {
                return new DeleteQuery();
            }
        });
    }])
    .factory('SPList', ['$q', '$http', '$sp', function($q, $http, $sp) {
      var SPList = function(title) {
        this.title = title;
        if ($sp.getConnectionMode() == "JSOM") {
          this.__list = new JsomSPList(title);
        }else {
          this.__list = new RestSPList(title);
        }
      };
      SPList.prototype.insert = function(data) {
        return this.__list.insert(data);
      };
      SPList.prototype.select = function(query) {
        return this.__list.select(query);
      };
      SPList.prototype.delete = function(query) {
        return this.__list.delete(query);
      };
      var RestSPList = function(title) {
        this.title = title;
      };
      RestSPList.prototype.select = function(query) {
        var endpoint = "_api/web/Lists/GetByTitle('" + this.title + "')/GetItems";
        var body = {
          query: {
            __metadata: {
              type: "SP.CamlQuery"
            },
            ViewXml: query.build()
          }
        };
        var headers = {
          "X-RequestDigest": $("#__REQUESTDIGEST").val(),
          "Accept": "application/json; odata=verbose",
          "Content-Type": "application/json; odata=verbose"
        };
        return $q(function(resolve, reject) {
          $http({
            method: 'POST',
            url: $sp.getSiteUrl() + endpoint,
            data: body,
            headers: headers
          }).then(function(data) {
            //TODO: Parse Result
          }, reject);
        });
      };
      RestSPList.prototype.insert = function(data) {
        var endpoint = "_api/web/Lists/GetByTitle('" + this.title + "')/items";
        var body = {
          __metadata: {
            type: 'SP.Data.TestListItem'
          }
        };
        var headers = {
          "X-RequestDigest": $("#__REQUESTDIGEST").val(),
          "Accept": "application/json; odata=verbose",
          "Content-Type": "application/json; odata=verbose"
        };
        Object.getOwnPropertyNames(data).forEach(function(key) {
          var value = data[key];
          if (value !== null && value !== undefined && typeof value == 'string') {
            value = value.trim();
          }
          body[key] = value;
        });
        return $q(function(resolve, reject) {
          $http({
            method: 'POST',
            url: $sp.getSiteUrl() + endpoint,
            data: body,
            headers: headers
          }).then(function(data) {
            //TODO: Parse Result
          }, reject);
        });
      };
      //JsomSPList
      var JsomSPList = function(title) {
        this.title = title;
      };
      JsomSPList.prototype.select = function(query) {
        return $q(function(resolve, reject) {
          var context = $sp.getContext();
          var list = context.get_web().get_lists().getByTitle(this.title);
          var items = list.getItems(query.build());
          context.load(items);
          context.executeQueryAsync(function() {
            var result = [];
            var itemIterator = items.getEnumerator();
            while (itemIterator.moveNext()) {
              var item = itemIterator.get_current();
              result.push(query.unpackItem(item));
            }
            resolve(result);
          }, function(sender, args) {
            reject(args);
          });
        });
      };
      JsomSPList.prototype.insert = function(data) {
        var list = this;
        return $q(function(resolve, reject) {
          var clientContext = $sp.getContext();
          var list = clientContext.get_web().get_lists().getByTitle(list.title);
          var itemInfo = new SP.ListItemCreationInformation();
          var item = list.addItem(itemInfo);
          Object.getOwnPropertyNames(data).forEach(function(key) {
            var value = data[key];
            if (value !== null && value !== undefined && typeof value == 'string') {
              value = value.trim();
            }
            item.set_item(key, value);
          });
          item.update();
          clientContext.load(item);
          clientContext.executeQueryAsync(function(sender, args) {
              resolve(query.unpackItem(item));
          }, function(sender, args) {
              reject(args);
          });
        });
      };
      JsomSPList.prototype.delete = function(query) {
        var list = this;
        return $q(function(resolve, reject) {
          var clientContext = $sp.getContext();
          var list = clientContext.get_web().get_lists().getByTitle(list.title);
          var items = list.getItems(query);
          clientContext.load(items);
          clientContext.executeQueryAsync(
              function(sender, args) {
                  var itemIterator = items.getEnumerator();
                  var a = [];
                  while (itemIterator.moveNext()) {
                     var item = itemIterator.get_current();
                     a.push(item);
                  }
                  a.forEach(function(item) {
                      item.deleteObject();
                  });
                  clientContext.executeQueryAsync(function(sender, args) {
                      resolve(args);
                  }, function(sender, args) {
                      reject(args);
                  });
              },
              function(sender, args) {
                  reject(args);
              }
          );
        });
      };
      return (SPList);
    }]);
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015-2016 by Arena of Titans Contributors.
 *
 * This file is part of Arena of Titans.
 *
 * Arena of Titans is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Arena of Titans is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with Arena of Titans. If not, see <http://www.gnu.org/licenses/>.
 */

import * as LogManager from "aurelia-logging";
import { inject } from "aurelia-framework";
import { EventAggregator } from "aurelia-event-aggregator";
import { I18N } from "aurelia-i18n";

@inject(I18N, EventAggregator)
export class Popup {
    constructor(i18n, ea) {
        this._i18n = i18n;
        this._ea = ea;

        this._logger = LogManager.getLogger("AoTPopup");
        this._popups = [];
        this._displayedPopupData = null;
        // We initialize the displayed popup deferred to a resolved deferred.
        this._displayedPopupDeferred = this._createDeferred();
        this._displayedPopupDeferred.resolve();

        this._popupReadyDeferred = this._createDeferred();
        this._ea.subscribe("aot:popup:ready", () => {
            this._popupReadyDeferred.resolve();
        });
        this._ea.subscribe("i18n:locale:changed", () => {
            this._translatePopup();
        });
    }

    _createDeferred() {
        const deferred = {};
        deferred.promise = new Promise((resolve, reject) => {
            deferred.resolve = () => {
                resolve();
                deferred.pending = false;
            };
            deferred.reject = () => {
                reject();
                deferred.pending = false;
            };
            deferred.pending = true;
        });
        return deferred;
    }

    display(type, data, { timeout = 0 } = {}) {
        if (type === "transition") {
            this._closeAllWithoutTimeout();
        }

        let popupDeferred = this._createDeferred();

        let startCounter = () => {
            this._ea.publish("aot:game:counter_start");
        };
        popupDeferred.promise.then(startCounter, startCounter);

        this._popups.push({
            type: type,
            data: data,
            deferred: popupDeferred,
            timeout: timeout,
        });
        // Since services are instantiated first, we need to wait for aot-popup to be attached
        // before trying to display a popup.
        this._popupReadyDeferred.promise.then(() => {
            return this._displayedPopupDeferred.promise.then(
                () => this._displayNext(),
                () => this._displayNext(),
            );
        });

        return popupDeferred.promise;
    }

    _closeAllWithoutTimeout() {
        this._logger.debug("Closing all popups that require user interaction");
        this._popups = this._popups.filter(popup => popup.timeout);
        if (this._displayedPopupData !== null && !this._displayedPopupData.meta.timeout) {
            this._displayedPopupDeferred.reject(new Error("Closed automatically"));
            this._displayedPopupData = null;
        }
    }

    _displayNext() {
        // We check that we have popup to display: if we are in the recursion, this._popups
        // may be an empty array.
        if (this._popups.length === 0 || this._displayedPopupDeferred.pending) {
            return;
        }

        let popup = this._popups.shift();
        this._displayedPopupData = popup.data;
        this._logger.debug("Displaying next popup", JSON.stringify(this._displayedPopupData));
        this._displayedPopupData.meta = {
            timeout: popup.timeout,
        };
        this._translatePopup();

        if (popup.timeout) {
            let closeTimeout = setTimeout(() => {
                this._logger.debug("Hit popup time out, closing", popup.timeout);
                popup.deferred.resolve();
            }, popup.timeout);
            let cancelCloseTimeout = () => {
                clearTimeout(closeTimeout);
            };
            popup.deferred.promise.then(cancelCloseTimeout, cancelCloseTimeout);
        }
        this._logger.debug("Displayed popup is now of type", popup.type);
        this._displayedPopupDeferred = popup.deferred;

        this._ea.publish("aot:popup:display", {
            type: popup.type,
            data: popup.data,
            deferred: popup.deferred,
        });

        // As soon as the current popup is closed, we display the next one.
        this._displayedPopupDeferred.promise.then(
            () => {
                this._displayedPopupData = null;
                this._displayNext();
            },
            () => {
                this._displayedPopupData = null;
                this._displayNext();
            },
        );
    }

    _translatePopup() {
        if (!this._canTranslatePopup()) {
            return;
        }

        let translateData = this._displayedPopupData.translate;
        let params = translateData.params || {};
        this._translateObj(params, translateData.paramsToTranslate);
        this._translateObj(this._displayedPopupData, translateData.messages, params);
        this._translateChoices();
    }

    _canTranslatePopup() {
        return (
            this._displayedPopupData !== null &&
            this._displayedPopupData.translate &&
            this._displayedPopupData.translate.messages
        );
    }

    _translateObj(dest, source, params = {}) {
        for (let key in source) {
            dest[key] = this._i18n.tr(source[key], params);
        }
    }

    _translateChoices() {
        if (!this._displayedPopupData.choices) {
            return;
        }

        this._displayedPopupData.choices = this._displayedPopupData.choices.map(choice => ({
            ...choice,
            name: this._i18n.tr(choice.name),
        }));

        this._translateObj(this._displayedPopupData, this._displayedPopupData.choices);
    }
}
<commit_msg>feat(popup): only start the counter with the transition popup<commit_after>/*
 * Copyright (C) 2015-2016 by Arena of Titans Contributors.
 *
 * This file is part of Arena of Titans.
 *
 * Arena of Titans is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Arena of Titans is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with Arena of Titans. If not, see <http://www.gnu.org/licenses/>.
 */

import * as LogManager from "aurelia-logging";
import { inject } from "aurelia-framework";
import { EventAggregator } from "aurelia-event-aggregator";
import { I18N } from "aurelia-i18n";

@inject(I18N, EventAggregator)
export class Popup {
    constructor(i18n, ea) {
        this._i18n = i18n;
        this._ea = ea;

        this._logger = LogManager.getLogger("AoTPopup");
        this._popups = [];
        this._displayedPopupData = null;
        // We initialize the displayed popup deferred to a resolved deferred.
        this._displayedPopupDeferred = this._createDeferred();
        this._displayedPopupDeferred.resolve();

        this._popupReadyDeferred = this._createDeferred();
        this._ea.subscribe("aot:popup:ready", () => {
            this._popupReadyDeferred.resolve();
        });
        this._ea.subscribe("i18n:locale:changed", () => {
            this._translatePopup();
        });
    }

    _createDeferred() {
        const deferred = {};
        deferred.promise = new Promise((resolve, reject) => {
            deferred.resolve = () => {
                resolve();
                deferred.pending = false;
            };
            deferred.reject = () => {
                reject();
                deferred.pending = false;
            };
            deferred.pending = true;
        });
        return deferred;
    }

    display(type, data, { timeout = 0 } = {}) {
        if (type === "transition") {
            this._closeAllWithoutTimeout();
        }

        let popupDeferred = this._createDeferred();

        let startCounter = () => {
            if (type === "transition") {
                this._ea.publish("aot:game:counter_start");
            }
        };
        popupDeferred.promise.then(startCounter, startCounter);

        this._popups.push({
            type: type,
            data: data,
            deferred: popupDeferred,
            timeout: timeout,
        });
        // Since services are instantiated first, we need to wait for aot-popup to be attached
        // before trying to display a popup.
        this._popupReadyDeferred.promise.then(() => {
            return this._displayedPopupDeferred.promise.then(
                () => this._displayNext(),
                () => this._displayNext(),
            );
        });

        return popupDeferred.promise;
    }

    _closeAllWithoutTimeout() {
        this._logger.debug("Closing all popups that require user interaction");
        this._popups = this._popups.filter(popup => popup.timeout);
        if (this._displayedPopupData !== null && !this._displayedPopupData.meta.timeout) {
            this._displayedPopupDeferred.reject(new Error("Closed automatically"));
            this._displayedPopupData = null;
        }
    }

    _displayNext() {
        // We check that we have popup to display: if we are in the recursion, this._popups
        // may be an empty array.
        if (this._popups.length === 0 || this._displayedPopupDeferred.pending) {
            return;
        }

        let popup = this._popups.shift();
        this._displayedPopupData = popup.data;
        this._logger.debug("Displaying next popup", JSON.stringify(this._displayedPopupData));
        this._displayedPopupData.meta = {
            timeout: popup.timeout,
        };
        this._translatePopup();

        if (popup.timeout) {
            let closeTimeout = setTimeout(() => {
                this._logger.debug("Hit popup time out, closing", popup.timeout);
                popup.deferred.resolve();
            }, popup.timeout);
            let cancelCloseTimeout = () => {
                clearTimeout(closeTimeout);
            };
            popup.deferred.promise.then(cancelCloseTimeout, cancelCloseTimeout);
        }
        this._logger.debug("Displayed popup is now of type", popup.type);
        this._displayedPopupDeferred = popup.deferred;

        this._ea.publish("aot:popup:display", {
            type: popup.type,
            data: popup.data,
            deferred: popup.deferred,
        });

        // As soon as the current popup is closed, we display the next one.
        this._displayedPopupDeferred.promise.then(
            () => {
                this._displayedPopupData = null;
                this._displayNext();
            },
            () => {
                this._displayedPopupData = null;
                this._displayNext();
            },
        );
    }

    _translatePopup() {
        if (!this._canTranslatePopup()) {
            return;
        }

        let translateData = this._displayedPopupData.translate;
        let params = translateData.params || {};
        this._translateObj(params, translateData.paramsToTranslate);
        this._translateObj(this._displayedPopupData, translateData.messages, params);
        this._translateChoices();
    }

    _canTranslatePopup() {
        return (
            this._displayedPopupData !== null &&
            this._displayedPopupData.translate &&
            this._displayedPopupData.translate.messages
        );
    }

    _translateObj(dest, source, params = {}) {
        for (let key in source) {
            dest[key] = this._i18n.tr(source[key], params);
        }
    }

    _translateChoices() {
        if (!this._displayedPopupData.choices) {
            return;
        }

        this._displayedPopupData.choices = this._displayedPopupData.choices.map(choice => ({
            ...choice,
            name: this._i18n.tr(choice.name),
        }));

        this._translateObj(this._displayedPopupData, this._displayedPopupData.choices);
    }
}
<|endoftext|>


<commit_before>import * as _ from 'lodash'

// We should build lists of valid inputs!
// Like, 'these are the valid departments' and 'these are the valid professors'

var semesters = {
	'fall': 1,
	'interim': 2,
	'j-term': 2,
	'jterm': 2,
	'j': 2,
	'spring': 3,
	'summer 1': 4,
	'summer1': 4,
	'early': 4,
	'early summer': 4,
	'summer session 1': 4,
	'summer session 2': 5,
	'late': 5,
	'late summer': 5,
	'summer2': 5,
	'summer 2': 5,
	'summers': ['$OR', 4, 5],
}

var department_mapping = {
	'american conversation': 'AMCON',
	'american conversations': 'AMCON',
	'asian studies': 'ASIAN',
	'computer science': 'CSCI',
	'cs': 'CSCI',
	'physics': 'PHYS',
	'religion': 'REL',
	'ar': 'ART',
	'as': 'ASIAN',
	'bi': 'BIO',
	'ch': 'CHEM',
	'ec': 'ECON',
	'es': 'ENVST',
	'hi': 'HIST',
	'mu': 'MUSIC',
	'ph': 'PHIL',
	'ps': 'PSCI',
	're': 'REL',
	'sa': 'SOAN',
	'compsci': 'CSCI',
}

var keyword_mappings = {
	'departments': 'depts',
	'department': 'depts',
	'dept': 'depts',
	'semester': 'sem',
	'number': 'num',
	'name': 'title',
	'ge': 'gereqs',
	'gereq': 'gereqs',
	'ges': 'gereqs',
	'gened': 'gereqs',
	'geneds': 'gereqs',
}

var gereq_mapping = {
	'history of western culture': 'HWC',
	'historical studies in western culture': 'HWC',
	'artistic studies': 'ALS-A',
	'artistic and literary studies - art': 'ALS-A',
	'literary studies': 'ALS-L',
	'artistic and literary studies - literature': 'ALS-L',
	'multicultural domestic studies': 'MCD',
	'multicultural global studies': 'MCG',
	'integrated scientific topics': 'IST',
	'scientific exploration and discovery': 'SED',
	'first-year writing': 'FYW',
	'writing in context': 'WRI',
	'writing': 'WRI',
	'bible': 'BTS-B',
	'biblical': 'BTS-B',
	'bible studies': 'BTS-T',
	'biblical studies': 'BTS-T',
	'theology': 'BTS-T',
	'theological': 'BTS-T',
	'theology studies': 'BTS-T',
	'theological studies': 'BTS-T',
	'biblical and theological studies - bible': 'BTS-B',
	'biblical and theological studies - biblical': 'BTS-B',
	'biblical and theological studies - theology': 'BTS-T',
	'biblical and theological studies - theological': 'BTS-T',
	'foreign language': 'FOL',
	'oral communication': 'ORC',
	'abstract and quantitative reasoning': 'AQR',
	'studies in physical movement': 'SPM',
	'gym': 'SPM',
	'studies in human behavior and society': 'HBS',
	'ethical issues and normative perspectives': 'EIN',
	'ethical issues': 'EIN',
	'ethics': 'EIN',
}

var evenIndex = (value, index) => index % 2 === 0;
var oddIndex  = (value, index) => index % 2 === 1;
var notEmptyString = (value) => value.length > 0;

var zipToObjectWithArrays = (keys, vals) => {
	let arr = _.zip(keys, vals)
	return _(arr).reduce(function(obj, propKey) {
		if (_.has(obj, propKey[0]))
			obj[propKey[0]].push(propKey[1]);
		else
			obj[propKey[0]] = [propKey[1]];

		return obj;
	}, {});
}

function buildQueryFromString(queryString) {
	var rex = /(\b\w*?\b):/g

	// The {index} object is there to emulate the one property I expect from a RegExp.
	// If the regex fails, we grab the string through the end and build the object from what we assume to be the title.
	let rexTested = rex.exec(queryString) || {index: queryString.length}
	let stringThing = queryString.substr(0, rexTested.index)
	queryString = queryString.substring(rexTested.index)

	// Split apart the string into an array
	var matches = queryString.split(rex)

	// Remove extra whitespace and remove empty strings
	var cleaned = _(matches)
		.map((m) => m.trim())
		.filter(notEmptyString)
		.value()

	// Grab the keys and values from the lists
	var keys = _.filter(cleaned, evenIndex)
	var values = _.filter(cleaned, oddIndex)

	if (stringThing) {
		keys.push('title')
		values.push(stringThing.trim())
	}

	// Process the keys, to clean them up somewhat
	keys = _.map(keys, (key) => {
		key = key.toLowerCase()
		if (key.indexOf('_') !== 0)
			key = keyword_mappings[key] || key;
		return key;
	})

	// Group the [keys, vals] into an object, with arrays for each value
	var zipped = zipToObjectWithArrays(keys, values)

	// Perform initial cleaning of the values, dependent on the keys
	var grouped = _.mapValues(zipped, (vals, key) => {
		let organized = _.map(vals, (val) => {
			if (val.indexOf('$') === 0)
				return val.toUpperCase()

			if (key === 'depts') {
				val = val.toLowerCase()
				val = department_mapping[val] || val.toUpperCase();
			}

			else if (key === 'gereqs') {
				val = val.toLowerCase()
				val = gereq_mapping[val] || val.toUpperCase()
			}

			else if (key === 'deptnum') {
				val = val.toUpperCase()
			}

			else if (key === 'sem') {
				val = val.toLowerCase()
				val = semesters[val] || parseInt(val, 10);
			}

			else if (key === 'year')
				val = parseInt(val, 10)
			else if (key === 'term')
				val = parseInt(val, 10)
			else if (key === 'level')
				val = parseInt(val, 10)
			else if (key === 'num')
				val = parseInt(val, 10)

			return val;
		})

		if (organized.length > 1 && organized[0].indexOf('$') !== 0) {
			organized.unshift('$AND')
		}

		return organized
	})

	return grouped
}

function test() {
	var s1 = 'dept: Computer Science  dept: Asian Studies  name: Parallel  level: 300  year: $OR year:2013 year: 2014'

	var s2 = 'dept: ASIAN  Dept: Religion  title: "Japan*"  LEVEL: 200  year: 2014  semester: $OR  semester: 3  semester: 1'

	var s3 = 'department: American Conversations  name: Independence  year: 2014  time: Tuesdays after 12'

	var s4 = 'ges: $AND  geneds: history of western culture gened: HBS  semester: Spring  year: 2014'

	var s5 = 'History of Asia'

	// console.log(buildQueryFromString(s1))
	// console.log(buildQueryFromString(s2))
	// console.log(buildQueryFromString(s3))
	console.log(buildQueryFromString(s4))
	console.log(buildQueryFromString(s5))
}

test()

// window.buildQueryFromString = buildQueryFromString;

export default buildQueryFromString
<commit_msg>Don't run test automatically on load<commit_after>import * as _ from 'lodash'

// We should build lists of valid inputs!
// Like, 'these are the valid departments' and 'these are the valid professors'

var semesters = {
	'fall': 1,
	'interim': 2,
	'j-term': 2,
	'jterm': 2,
	'j': 2,
	'spring': 3,
	'summer 1': 4,
	'summer1': 4,
	'early': 4,
	'early summer': 4,
	'summer session 1': 4,
	'summer session 2': 5,
	'late': 5,
	'late summer': 5,
	'summer2': 5,
	'summer 2': 5,
	'summers': ['$OR', 4, 5],
}

var department_mapping = {
	'american conversation': 'AMCON',
	'american conversations': 'AMCON',
	'asian studies': 'ASIAN',
	'computer science': 'CSCI',
	'cs': 'CSCI',
	'physics': 'PHYS',
	'religion': 'REL',
	'ar': 'ART',
	'as': 'ASIAN',
	'bi': 'BIO',
	'ch': 'CHEM',
	'ec': 'ECON',
	'es': 'ENVST',
	'hi': 'HIST',
	'mu': 'MUSIC',
	'ph': 'PHIL',
	'ps': 'PSCI',
	're': 'REL',
	'sa': 'SOAN',
	'compsci': 'CSCI',
}

var keyword_mappings = {
	'departments': 'depts',
	'department': 'depts',
	'dept': 'depts',
	'semester': 'sem',
	'number': 'num',
	'name': 'title',
	'ge': 'gereqs',
	'gereq': 'gereqs',
	'ges': 'gereqs',
	'gened': 'gereqs',
	'geneds': 'gereqs',
}

var gereq_mapping = {
	'history of western culture': 'HWC',
	'historical studies in western culture': 'HWC',
	'artistic studies': 'ALS-A',
	'artistic and literary studies - art': 'ALS-A',
	'literary studies': 'ALS-L',
	'artistic and literary studies - literature': 'ALS-L',
	'multicultural domestic studies': 'MCD',
	'multicultural global studies': 'MCG',
	'integrated scientific topics': 'IST',
	'scientific exploration and discovery': 'SED',
	'first-year writing': 'FYW',
	'writing in context': 'WRI',
	'writing': 'WRI',
	'bible': 'BTS-B',
	'biblical': 'BTS-B',
	'bible studies': 'BTS-T',
	'biblical studies': 'BTS-T',
	'theology': 'BTS-T',
	'theological': 'BTS-T',
	'theology studies': 'BTS-T',
	'theological studies': 'BTS-T',
	'biblical and theological studies - bible': 'BTS-B',
	'biblical and theological studies - biblical': 'BTS-B',
	'biblical and theological studies - theology': 'BTS-T',
	'biblical and theological studies - theological': 'BTS-T',
	'foreign language': 'FOL',
	'oral communication': 'ORC',
	'abstract and quantitative reasoning': 'AQR',
	'studies in physical movement': 'SPM',
	'gym': 'SPM',
	'studies in human behavior and society': 'HBS',
	'ethical issues and normative perspectives': 'EIN',
	'ethical issues': 'EIN',
	'ethics': 'EIN',
}

var evenIndex = (value, index) => index % 2 === 0;
var oddIndex  = (value, index) => index % 2 === 1;
var notEmptyString = (value) => value.length > 0;

var zipToObjectWithArrays = (keys, vals) => {
	let arr = _.zip(keys, vals)
	return _(arr).reduce(function(obj, propKey) {
		if (_.has(obj, propKey[0]))
			obj[propKey[0]].push(propKey[1]);
		else
			obj[propKey[0]] = [propKey[1]];

		return obj;
	}, {});
}

function buildQueryFromString(queryString) {
	var rex = /(\b\w*?\b):/g

	// The {index} object is there to emulate the one property I expect from a RegExp.
	// If the regex fails, we grab the string through the end and build the object from what we assume to be the title.
	let rexTested = rex.exec(queryString) || {index: queryString.length}
	let stringThing = queryString.substr(0, rexTested.index)
	queryString = queryString.substring(rexTested.index)

	// Split apart the string into an array
	var matches = queryString.split(rex)

	// Remove extra whitespace and remove empty strings
	var cleaned = _(matches)
		.map((m) => m.trim())
		.filter(notEmptyString)
		.value()

	// Grab the keys and values from the lists
	var keys = _.filter(cleaned, evenIndex)
	var values = _.filter(cleaned, oddIndex)

	if (stringThing) {
		keys.push('title')
		values.push(stringThing.trim())
	}

	// Process the keys, to clean them up somewhat
	keys = _.map(keys, (key) => {
		key = key.toLowerCase()
		if (key.indexOf('_') !== 0)
			key = keyword_mappings[key] || key;
		return key;
	})

	// Group the [keys, vals] into an object, with arrays for each value
	var zipped = zipToObjectWithArrays(keys, values)

	// Perform initial cleaning of the values, dependent on the keys
	var grouped = _.mapValues(zipped, (vals, key) => {
		let organized = _.map(vals, (val) => {
			if (val.indexOf('$') === 0)
				return val.toUpperCase()

			if (key === 'depts') {
				val = val.toLowerCase()
				val = department_mapping[val] || val.toUpperCase();
			}

			else if (key === 'gereqs') {
				val = val.toLowerCase()
				val = gereq_mapping[val] || val.toUpperCase()
			}

			else if (key === 'deptnum') {
				val = val.toUpperCase()
			}

			else if (key === 'sem') {
				val = val.toLowerCase()
				val = semesters[val] || parseInt(val, 10);
			}

			else if (key === 'year')
				val = parseInt(val, 10)
			else if (key === 'term')
				val = parseInt(val, 10)
			else if (key === 'level')
				val = parseInt(val, 10)
			else if (key === 'num')
				val = parseInt(val, 10)

			return val;
		})

		if (organized.length > 1 && organized[0].indexOf('$') !== 0) {
			organized.unshift('$AND')
		}

		return organized
	})

	return grouped
}

function test() {
	var s1 = 'dept: Computer Science  dept: Asian Studies  name: Parallel  level: 300  year: $OR year:2013 year: 2014'

	var s2 = 'dept: ASIAN  Dept: Religion  title: "Japan*"  LEVEL: 200  year: 2014  semester: $OR  semester: 3  semester: 1'

	var s3 = 'department: American Conversations  name: Independence  year: 2014  time: Tuesdays after 12'

	var s4 = 'ges: $AND  geneds: history of western culture gened: HBS  semester: Spring  year: 2014'

	var s5 = 'History of Asia'

	// console.log(buildQueryFromString(s1))
	// console.log(buildQueryFromString(s2))
	// console.log(buildQueryFromString(s3))
	console.log(buildQueryFromString(s4))
	console.log(buildQueryFromString(s5))
}

// test()

// window.buildQueryFromString = buildQueryFromString;

export default buildQueryFromString
<|endoftext|>


<commit_before>import {
  isPlainObject,
  hasOwn
} from 'uni-shared'
import navigateTo from 'uni-helpers/navigate-to'
import redirectTo from '../../../mp-weixin/helpers/redirect-to'
import getSystemInfo from '../../helpers/system-info'

// 不支持的 API 列表
const todos = [
  'preloadPage',
  'unPreloadPage',
  'loadSubPackage'
  // 'getRecorderManager',
  // 'getBackgroundAudioManager',
  // 'createInnerAudioContext',
  // 'createCameraContext',
  // 'createLivePlayerContext',
  // 'startAccelerometer',
  // 'startCompass',
  // 'authorize',
  // 'chooseInvoiceTitle',
  // 'addTemplate',
  // 'deleteTemplate',
  // 'getTemplateLibraryById',
  // 'getTemplateLibraryList',
  // 'getTemplateList',
  // 'sendTemplateMessage',
  // 'setEnableDebug',
  // 'getExtConfig',
  // 'getExtConfigSync',
  // 'onWindowResize',
  // 'offWindowResize'
]

// 存在兼容性的 API 列表
const canIUses = [
  'startPullDownRefresh',
  'setTabBarItem',
  'setTabBarStyle',
  'hideTabBar',
  'showTabBar',
  'setTabBarBadge',
  'removeTabBarBadge',
  'showTabBarRedDot',
  'hideTabBarRedDot',
  'openSetting',
  'getSetting',
  'createIntersectionObserver',
  'getUpdateManager',
  'setBackgroundColor',
  'setBackgroundTextStyle',
  'checkIsSupportSoterAuthentication',
  'startSoterAuthentication',
  'checkIsSoterEnrolledInDevice',
  'openDocument',
  'createVideoContext',
  'onMemoryWarning',
  'addPhoneContact'
]

function _handleNetworkInfo (result) {
  switch (result.networkType) {
    case 'NOTREACHABLE':
      result.networkType = 'none'
      break
    case 'WWAN':
      // TODO ?
      result.networkType = '3g'
      break
    default:
      result.networkType = result.networkType.toLowerCase()
      break
  }
  return {}
}

const protocols = { // 需要做转换的 API 列表
  navigateTo,
  redirectTo,
  returnValue (methodName, res = {}) { // 通用 returnValue 解析
    if (res.error || res.errorMessage) {
      res.errMsg = `${methodName}:fail ${res.errorMessage || res.error}`
      delete res.error
      delete res.errorMessage
    } else {
      res.errMsg = `${methodName}:ok`
    }
    return res
  },
  request: {
    name: my.canIUse('request') ? 'request' : 'httpRequest',
    args (fromArgs) {
      const method = fromArgs.method || 'GET'
      if (!fromArgs.header) { // 默认增加 header 参数，方便格式化 content-type
        fromArgs.header = {}
      }
      const headers = {
        'content-type': 'application/json'
      }
      Object.keys(fromArgs.header).forEach(key => {
        headers[key.toLocaleLowerCase()] = fromArgs.header[key]
      })
      return {
        header (header = {}, toArgs) {
          return {
            name: 'headers',
            value: headers
          }
        },
        data (data) {
          // 钉钉小程序在content-type为application/json时需上传字符串形式data，使用my.dd在真机运行钉钉小程序时不能正确判断
          if (my.canIUse('saveFileToDingTalk') && method.toUpperCase() === 'POST' && headers['content-type'].indexOf(
            'application/json') === 0 && isPlainObject(data)) {
            return {
              name: 'data',
              value: JSON.stringify(data)
            }
          }
          return {
            name: 'data',
            value: data
          }
        },
        method: 'method', // TODO 支付宝小程序仅支持 get,post
        responseType: false
      }
    },
    returnValue: {
      status: 'statusCode',
      headers: 'header'
    }
  },
  setNavigationBarColor: {
    name: 'setNavigationBar',
    args: {
      frontColor: false,
      animation: false
    }
  },
  setNavigationBarTitle: {
    name: 'setNavigationBar'
  },
  showModal ({
    showCancel = true
  } = {}) {
    if (showCancel) {
      return {
        name: 'confirm',
        args: {
          cancelColor: false,
          confirmColor: false,
          cancelText: 'cancelButtonText',
          confirmText: 'confirmButtonText'
        },
        returnValue (fromRes, toRes) {
          toRes.confirm = fromRes.confirm
          toRes.cancel = !fromRes.confirm
        }
      }
    }
    return {
      name: 'alert',
      args: {
        confirmColor: false,
        confirmText: 'buttonText'
      },
      returnValue (fromRes, toRes) {
        toRes.confirm = true
        toRes.cancel = false
      }
    }
  },
  showToast ({
    icon = 'success'
  } = {}) {
    const args = {
      title: 'content',
      icon: 'type',
      duration: false,
      image: false,
      mask: false
    }
    if (icon === 'loading') {
      return {
        name: 'showLoading',
        args
      }
    }
    return {
      name: 'showToast',
      args
    }
  },
  showActionSheet: {
    name: 'showActionSheet',
    args: {
      itemList: 'items',
      itemColor: false
    },
    returnValue: {
      index: 'tapIndex'
    }
  },
  showLoading: {
    args: {
      title: 'content',
      mask: false
    }
  },
  uploadFile: {
    args: {
      name: 'fileName'
    }
    // 从测试结果看，是有返回对象的，文档上没有说明。
  },
  downloadFile: {
    returnValue: {
      apFilePath: 'tempFilePath'
    }
  },
  getFileInfo: {
    args: {
      filePath: 'apFilePath'
    }
  },
  compressImage: {
    args (fromArgs) {
      fromArgs.compressLevel = 4
      if (fromArgs && fromArgs.quality) {
        fromArgs.compressLevel = Math.floor(fromArgs.quality / 26)
      }
      fromArgs.apFilePaths = [fromArgs.src]
    },
    returnValue (result) {
      if (result.apFilePaths && result.apFilePaths.length) {
        result.tempFilePath = result.apFilePaths[0]
      }
    }
  },
  chooseVideo: {
    // 支付宝小程序文档中未找到（仅在getSetting处提及），但实际可用
    returnValue: {
      apFilePath: 'tempFilePath'
    }
  },
  connectSocket: {
    args: {
      method: false,
      protocols: false
    }
    // TODO 有没有返回值还需要测试下
  },
  chooseImage: {
    returnValue (result) {
      const hasTempFilePaths = hasOwn(result, 'tempFilePaths') && result.tempFilePaths
      if (hasOwn(result, 'apFilePaths') && !hasTempFilePaths) {
        result.tempFilePaths = result.apFilePaths
        delete result.apFilePaths
      }
      if (!hasOwn(result, 'tempFiles') && hasTempFilePaths) {
        result.tempFiles = []
        result.tempFilePaths.forEach(tempFilePath => result.tempFiles.push({ path: tempFilePath }))
      }
      return {}
    }
  },
  previewImage: {
    args (fromArgs) {
      // 支付宝小程序的 current 是索引值，而非图片地址。
      const currentIndex = Number(fromArgs.current)
      if (isNaN(currentIndex)) {
        if (fromArgs.current && Array.isArray(fromArgs.urls)) {
          const index = fromArgs.urls.indexOf(fromArgs.current)
          fromArgs.current = ~index ? index : 0
        }
      } else {
        fromArgs.current = currentIndex
      }
      return {
        indicator: false,
        loop: false
      }
    }
  },
  saveFile: {
    args: {
      tempFilePath: 'apFilePath'
    },
    returnValue: {
      apFilePath: 'savedFilePath'
    }
  },
  getSavedFileInfo: {
    args: {
      filePath: 'apFilePath'
    }
  },
  getSavedFileList: {
    returnValue (result) {
      if (result.fileList && result.fileList.length) {
        result.fileList.forEach(file => {
          file.filePath = file.apFilePath
          delete file.apFilePath
        })
      }
      return {}
    }
  },
  removeSavedFile: {
    args: {
      filePath: 'apFilePath'
    }
  },
  getLocation: {
    args: {
      type: false,
      altitude: false
    }
  },
  openLocation: {
    args: {
      // TODO address 参数在阿里上是必传的
    }
  },
  getNetworkType: {
    returnValue: _handleNetworkInfo
  },
  onNetworkStatusChange: {
    returnValue: _handleNetworkInfo
  },
  stopAccelerometer: {
    name: 'offAccelerometerChange'
  },
  stopCompass: {
    name: 'offCompassChange'
  },
  scanCode: {
    name: 'scan',
    args (fromArgs) {
      if (fromArgs.scanType) {
        switch (fromArgs.scanType[0]) {
          case 'qrCode':
            fromArgs.type = 'qr'
            break
          case 'barCode':
            fromArgs.type = 'bar'
            break
        }
      }
      return {
        onlyFromCamera: 'hideAlbum'
      }
    },
    returnValue: {
      code: 'result'
    }
  },
  setClipboardData: {
    name: 'setClipboard',
    args: {
      data: 'text'
    }
  },
  getClipboardData: {
    name: 'getClipboard',
    returnValue: {
      text: 'data'
    }
  },
  login: {
    name: 'getAuthCode',
    returnValue (result) {
      result.code = result.authCode
    }
  },
  getUserInfo: {
    name: my.canIUse('getOpenUserInfo') ? 'getOpenUserInfo' : 'getAuthUserInfo',
    returnValue (result) {
      if (my.canIUse('getOpenUserInfo')) {
        let response = {}
        try {
          response = JSON.parse(result.response).response
        } catch (e) {}
        result.nickName = response.nickName
        result.avatar = response.avatar
      }
      result.userInfo = {
        nickName: result.nickName,
        avatarUrl: result.avatar
      }
    }
  },
  getUserProfile: {
    name: my.canIUse('getOpenUserInfo') ? 'getOpenUserInfo' : 'getAuthUserInfo',
    returnValue (result) {
      if (my.canIUse('getOpenUserInfo')) {
        let response = {}
        try {
          response = JSON.parse(result.response).response
        } catch (e) {}
        result.nickName = response.nickName
        result.avatar = response.avatar
      }
      result.userInfo = {
        nickName: result.nickName,
        avatarUrl: result.avatar
      }
    }
  },
  requestPayment: {
    name: 'tradePay',
    args: {
      orderInfo: 'tradeNO'
    }
  },
  getBLEDeviceServices: {
    returnValue (result) {
      result.services.forEach((item) => {
        item.uuid = item.serviceId
      })
    }
  },
  createBLEConnection: {
    name: 'connectBLEDevice',
    args: {
      timeout: false
    }
  },
  closeBLEConnection: {
    name: 'disconnectBLEDevice'
  },
  onBLEConnectionStateChange: {
    name: 'onBLEConnectionStateChanged'
  },
  makePhoneCall: {
    args: {
      phoneNumber: 'number'
    }
  },
  stopGyroscope: {
    name: 'offGyroscopeChange'
  },
  getSystemInfo: getSystemInfo,
  getSystemInfoSync: getSystemInfo,
  // 文档没提到，但是实测可用。
  canvasToTempFilePath: {
    returnValue (result) {
      // 真机的情况下会有 tempFilePath 这个值，因此需要主动修改。
      result.tempFilePath = result.apFilePath
    }
  },
  setScreenBrightness: {
    args: {
      value: 'brightness'
    }
  },
  getScreenBrightness: {
    returnValue: {
      brightness: 'value'
    }
  },
  showShareMenu: {
    name: 'showSharePanel'
  },
  hideHomeButton: {
    name: 'hideBackHome'
  },
  saveVideoToPhotosAlbum: {
    args: {
      filePath: 'src'
    }
  },
  chooseAddress: {
    name: 'getAddress',
    returnValue (result) {
      const info = result.result || {}
      result.userName = info.fullname
      result.provinceName = info.prov
      result.cityName = info.city
      result.countyName = info.area
      result.detailInfo = info.address
      result.telNumber = info.mobilePhone
      result.errMsg = result.resultStatus
    }
  }
}

export {
  protocols,
  todos,
  canIUses
}
<commit_msg>fix(mp-alipay): showToast duration question/147279<commit_after>import {
  isPlainObject,
  hasOwn
} from 'uni-shared'
import navigateTo from 'uni-helpers/navigate-to'
import redirectTo from '../../../mp-weixin/helpers/redirect-to'
import getSystemInfo from '../../helpers/system-info'

// 不支持的 API 列表
const todos = [
  'preloadPage',
  'unPreloadPage',
  'loadSubPackage'
  // 'getRecorderManager',
  // 'getBackgroundAudioManager',
  // 'createInnerAudioContext',
  // 'createCameraContext',
  // 'createLivePlayerContext',
  // 'startAccelerometer',
  // 'startCompass',
  // 'authorize',
  // 'chooseInvoiceTitle',
  // 'addTemplate',
  // 'deleteTemplate',
  // 'getTemplateLibraryById',
  // 'getTemplateLibraryList',
  // 'getTemplateList',
  // 'sendTemplateMessage',
  // 'setEnableDebug',
  // 'getExtConfig',
  // 'getExtConfigSync',
  // 'onWindowResize',
  // 'offWindowResize'
]

// 存在兼容性的 API 列表
const canIUses = [
  'startPullDownRefresh',
  'setTabBarItem',
  'setTabBarStyle',
  'hideTabBar',
  'showTabBar',
  'setTabBarBadge',
  'removeTabBarBadge',
  'showTabBarRedDot',
  'hideTabBarRedDot',
  'openSetting',
  'getSetting',
  'createIntersectionObserver',
  'getUpdateManager',
  'setBackgroundColor',
  'setBackgroundTextStyle',
  'checkIsSupportSoterAuthentication',
  'startSoterAuthentication',
  'checkIsSoterEnrolledInDevice',
  'openDocument',
  'createVideoContext',
  'onMemoryWarning',
  'addPhoneContact'
]

function _handleNetworkInfo (result) {
  switch (result.networkType) {
    case 'NOTREACHABLE':
      result.networkType = 'none'
      break
    case 'WWAN':
      // TODO ?
      result.networkType = '3g'
      break
    default:
      result.networkType = result.networkType.toLowerCase()
      break
  }
  return {}
}

const protocols = { // 需要做转换的 API 列表
  navigateTo,
  redirectTo,
  returnValue (methodName, res = {}) { // 通用 returnValue 解析
    if (res.error || res.errorMessage) {
      res.errMsg = `${methodName}:fail ${res.errorMessage || res.error}`
      delete res.error
      delete res.errorMessage
    } else {
      res.errMsg = `${methodName}:ok`
    }
    return res
  },
  request: {
    name: my.canIUse('request') ? 'request' : 'httpRequest',
    args (fromArgs) {
      const method = fromArgs.method || 'GET'
      if (!fromArgs.header) { // 默认增加 header 参数，方便格式化 content-type
        fromArgs.header = {}
      }
      const headers = {
        'content-type': 'application/json'
      }
      Object.keys(fromArgs.header).forEach(key => {
        headers[key.toLocaleLowerCase()] = fromArgs.header[key]
      })
      return {
        header (header = {}, toArgs) {
          return {
            name: 'headers',
            value: headers
          }
        },
        data (data) {
          // 钉钉小程序在content-type为application/json时需上传字符串形式data，使用my.dd在真机运行钉钉小程序时不能正确判断
          if (my.canIUse('saveFileToDingTalk') && method.toUpperCase() === 'POST' && headers['content-type'].indexOf(
            'application/json') === 0 && isPlainObject(data)) {
            return {
              name: 'data',
              value: JSON.stringify(data)
            }
          }
          return {
            name: 'data',
            value: data
          }
        },
        method: 'method', // TODO 支付宝小程序仅支持 get,post
        responseType: false
      }
    },
    returnValue: {
      status: 'statusCode',
      headers: 'header'
    }
  },
  setNavigationBarColor: {
    name: 'setNavigationBar',
    args: {
      frontColor: false,
      animation: false
    }
  },
  setNavigationBarTitle: {
    name: 'setNavigationBar'
  },
  showModal ({
    showCancel = true
  } = {}) {
    if (showCancel) {
      return {
        name: 'confirm',
        args: {
          cancelColor: false,
          confirmColor: false,
          cancelText: 'cancelButtonText',
          confirmText: 'confirmButtonText'
        },
        returnValue (fromRes, toRes) {
          toRes.confirm = fromRes.confirm
          toRes.cancel = !fromRes.confirm
        }
      }
    }
    return {
      name: 'alert',
      args: {
        confirmColor: false,
        confirmText: 'buttonText'
      },
      returnValue (fromRes, toRes) {
        toRes.confirm = true
        toRes.cancel = false
      }
    }
  },
  showToast ({
    icon = 'success'
  } = {}) {
    const args = {
      title: 'content',
      icon: 'type',
      image: false,
      mask: false
    }
    if (icon === 'loading') {
      return {
        name: 'showLoading',
        args
      }
    }
    return {
      name: 'showToast',
      args
    }
  },
  showActionSheet: {
    name: 'showActionSheet',
    args: {
      itemList: 'items',
      itemColor: false
    },
    returnValue: {
      index: 'tapIndex'
    }
  },
  showLoading: {
    args: {
      title: 'content',
      mask: false
    }
  },
  uploadFile: {
    args: {
      name: 'fileName'
    }
    // 从测试结果看，是有返回对象的，文档上没有说明。
  },
  downloadFile: {
    returnValue: {
      apFilePath: 'tempFilePath'
    }
  },
  getFileInfo: {
    args: {
      filePath: 'apFilePath'
    }
  },
  compressImage: {
    args (fromArgs) {
      fromArgs.compressLevel = 4
      if (fromArgs && fromArgs.quality) {
        fromArgs.compressLevel = Math.floor(fromArgs.quality / 26)
      }
      fromArgs.apFilePaths = [fromArgs.src]
    },
    returnValue (result) {
      if (result.apFilePaths && result.apFilePaths.length) {
        result.tempFilePath = result.apFilePaths[0]
      }
    }
  },
  chooseVideo: {
    // 支付宝小程序文档中未找到（仅在getSetting处提及），但实际可用
    returnValue: {
      apFilePath: 'tempFilePath'
    }
  },
  connectSocket: {
    args: {
      method: false,
      protocols: false
    }
    // TODO 有没有返回值还需要测试下
  },
  chooseImage: {
    returnValue (result) {
      const hasTempFilePaths = hasOwn(result, 'tempFilePaths') && result.tempFilePaths
      if (hasOwn(result, 'apFilePaths') && !hasTempFilePaths) {
        result.tempFilePaths = result.apFilePaths
        delete result.apFilePaths
      }
      if (!hasOwn(result, 'tempFiles') && hasTempFilePaths) {
        result.tempFiles = []
        result.tempFilePaths.forEach(tempFilePath => result.tempFiles.push({ path: tempFilePath }))
      }
      return {}
    }
  },
  previewImage: {
    args (fromArgs) {
      // 支付宝小程序的 current 是索引值，而非图片地址。
      const currentIndex = Number(fromArgs.current)
      if (isNaN(currentIndex)) {
        if (fromArgs.current && Array.isArray(fromArgs.urls)) {
          const index = fromArgs.urls.indexOf(fromArgs.current)
          fromArgs.current = ~index ? index : 0
        }
      } else {
        fromArgs.current = currentIndex
      }
      return {
        indicator: false,
        loop: false
      }
    }
  },
  saveFile: {
    args: {
      tempFilePath: 'apFilePath'
    },
    returnValue: {
      apFilePath: 'savedFilePath'
    }
  },
  getSavedFileInfo: {
    args: {
      filePath: 'apFilePath'
    }
  },
  getSavedFileList: {
    returnValue (result) {
      if (result.fileList && result.fileList.length) {
        result.fileList.forEach(file => {
          file.filePath = file.apFilePath
          delete file.apFilePath
        })
      }
      return {}
    }
  },
  removeSavedFile: {
    args: {
      filePath: 'apFilePath'
    }
  },
  getLocation: {
    args: {
      type: false,
      altitude: false
    }
  },
  openLocation: {
    args: {
      // TODO address 参数在阿里上是必传的
    }
  },
  getNetworkType: {
    returnValue: _handleNetworkInfo
  },
  onNetworkStatusChange: {
    returnValue: _handleNetworkInfo
  },
  stopAccelerometer: {
    name: 'offAccelerometerChange'
  },
  stopCompass: {
    name: 'offCompassChange'
  },
  scanCode: {
    name: 'scan',
    args (fromArgs) {
      if (fromArgs.scanType) {
        switch (fromArgs.scanType[0]) {
          case 'qrCode':
            fromArgs.type = 'qr'
            break
          case 'barCode':
            fromArgs.type = 'bar'
            break
        }
      }
      return {
        onlyFromCamera: 'hideAlbum'
      }
    },
    returnValue: {
      code: 'result'
    }
  },
  setClipboardData: {
    name: 'setClipboard',
    args: {
      data: 'text'
    }
  },
  getClipboardData: {
    name: 'getClipboard',
    returnValue: {
      text: 'data'
    }
  },
  login: {
    name: 'getAuthCode',
    returnValue (result) {
      result.code = result.authCode
    }
  },
  getUserInfo: {
    name: my.canIUse('getOpenUserInfo') ? 'getOpenUserInfo' : 'getAuthUserInfo',
    returnValue (result) {
      if (my.canIUse('getOpenUserInfo')) {
        let response = {}
        try {
          response = JSON.parse(result.response).response
        } catch (e) {}
        result.nickName = response.nickName
        result.avatar = response.avatar
      }
      result.userInfo = {
        nickName: result.nickName,
        avatarUrl: result.avatar
      }
    }
  },
  getUserProfile: {
    name: my.canIUse('getOpenUserInfo') ? 'getOpenUserInfo' : 'getAuthUserInfo',
    returnValue (result) {
      if (my.canIUse('getOpenUserInfo')) {
        let response = {}
        try {
          response = JSON.parse(result.response).response
        } catch (e) {}
        result.nickName = response.nickName
        result.avatar = response.avatar
      }
      result.userInfo = {
        nickName: result.nickName,
        avatarUrl: result.avatar
      }
    }
  },
  requestPayment: {
    name: 'tradePay',
    args: {
      orderInfo: 'tradeNO'
    }
  },
  getBLEDeviceServices: {
    returnValue (result) {
      result.services.forEach((item) => {
        item.uuid = item.serviceId
      })
    }
  },
  createBLEConnection: {
    name: 'connectBLEDevice',
    args: {
      timeout: false
    }
  },
  closeBLEConnection: {
    name: 'disconnectBLEDevice'
  },
  onBLEConnectionStateChange: {
    name: 'onBLEConnectionStateChanged'
  },
  makePhoneCall: {
    args: {
      phoneNumber: 'number'
    }
  },
  stopGyroscope: {
    name: 'offGyroscopeChange'
  },
  getSystemInfo: getSystemInfo,
  getSystemInfoSync: getSystemInfo,
  // 文档没提到，但是实测可用。
  canvasToTempFilePath: {
    returnValue (result) {
      // 真机的情况下会有 tempFilePath 这个值，因此需要主动修改。
      result.tempFilePath = result.apFilePath
    }
  },
  setScreenBrightness: {
    args: {
      value: 'brightness'
    }
  },
  getScreenBrightness: {
    returnValue: {
      brightness: 'value'
    }
  },
  showShareMenu: {
    name: 'showSharePanel'
  },
  hideHomeButton: {
    name: 'hideBackHome'
  },
  saveVideoToPhotosAlbum: {
    args: {
      filePath: 'src'
    }
  },
  chooseAddress: {
    name: 'getAddress',
    returnValue (result) {
      const info = result.result || {}
      result.userName = info.fullname
      result.provinceName = info.prov
      result.cityName = info.city
      result.countyName = info.area
      result.detailInfo = info.address
      result.telNumber = info.mobilePhone
      result.errMsg = result.resultStatus
    }
  }
}

export {
  protocols,
  todos,
  canIUses
}
<|endoftext|>