<commit_before>/**
 * @file 一个树形选择控件。
 * @author Han Bing Feng (hanbingfeng@baidu.com)
 */

define(function (require) {
    let _ = require('underscore');
    let React = require('react');

    /**
     * 一个树节点的定义
     */
    let treeNodeType = React.PropTypes.shape({
        /**
         * 树节点id
         */
        id: React.PropTypes.string,
        /**
         * 树节点文本内容
         */
        name: React.PropTypes.string,
        /**
         * 树节点的 载入中 状态
         */
        isChildrenLoading: React.PropTypes.bool,
        /**
         * 树节点的 已载入 状态
         */
        isChildrenLoaded: React.PropTypes.bool,
        /**
         * 所有孩子
         */
        children: React.PropTypes.array
    });

    let TreeNode = React.createClass({
        propTypes: {
            /**
             * 树节点
             */
            treeNode: treeNodeType,
            /**
             * 树名字过滤器，不包含过滤器中字符串的节点将加上class`fcui2-tree-node-filtered`。
             */
            nameFilter: React.PropTypes.string,
            /**
             * 树节点是否被展开。
             */
            isExpanded: React.PropTypes.bool,
            /**
             * 树节点展开按钮被点击时的回调。
             * @param {SyntheticEvent} onTreeNodeExpandClicked.e 点击事件对象
             * @param {treeNodeType} onTreeNodeExpandClicked.treeNode 被展开的树节点数据
             */
            onTreeNodeExpandClicked: React.PropTypes.func,
            /**
             * 树节点“操作”按钮被点击时的回调。
             * @param {SyntheticEvent} onTreeNodeOperationClicked.e 点击事件对象
             * @param {treeNodeType} onTreeNodeOperationClicked.treeNode 被操作的树节点数据
             */
            onTreeNodeOperationClicked: React.PropTypes.func,
            /**
             * 树节点其他位置被点击时的回调。
             * @param {SyntheticEvent} onTreeNodeClicked.e 点击事件对象
             * @param {treeNodeType} onTreeNodeClicked.treeNode 被操作的树节点数据
             */
            onTreeNodeClicked: React.PropTypes.func,
            /**
             * 树节点载入中时的话术提示。
             * @type {[type]}
             */
            textLoading: React.PropTypes.string,
            /**
             * 树节点根节点附加的class。
             */
            className: React.PropTypes.string
        },

        getDefaultProps() {
            return {
                onTreeNodeExpandClicked: _.noop,
                onTreeNodeOperationClicked: _.noop,
                onTreeNodeClicked: _.noop
            };
        },

        onTreeNodeExpandClicked(e) {
            this.props.onTreeNodeExpandClicked.call(null, e, this.props.treeNode);
        },

        onTreeNodeOperationClicked(e) {
            this.props.onTreeNodeOperationClicked.call(null, e, this.props.treeNode);
        },

        onTreeNodeClicked(e) {
            this.props.onTreeNodeClicked.call(null, e, this.props.treeNode);
        },

        render() {
            let {
                treeNode,
                nameFilter,
                isExpanded,
                textLoading,
                className,
                ...other
            } = this.props;

            className = [
                'fcui2-tree-node',
                className
            ];

            if (treeNode.isChildrenLoaded && (treeNode.children == null || treeNode.children.length === 0)) {
                className.push('fcui2-tree-node-leaf');
            }

            if (nameFilter != null && treeNode.name.indexOf(nameFilter) === -1) {
                className.push('fcui2-tree-node-filtered');
            }

            return (

                    {treeNode.name}
                    {
                        treeNode.isChildrenLoading
                            ? {textLoading}
                            : ''
                    }

            );
        }
    });

    let Tree = React.createClass({
        propTypes: {
            /**
             * 树所展现的所有节点集合
             */
            treeNodes: React.PropTypes.arrayOf(treeNodeType),
            /**
             * 本层次树的上层节点
             */
            parentTreeNodes: React.PropTypes.arrayOf(treeNodeType),
            /**
             * 根据节点的层数，计算当前层的wrapper的style
             * @param {number} level 树的层数
             * @return {Object} 当前层的style object
             */
            getTreeLevelStyle: React.PropTypes.func,
            /**
             * 树节点展开按钮被点击时的回调。
             * @param {SyntheticEvent} onTreeNodeExpandClicked.e 点击事件对象
             * @param {treeNodeType} onTreeNodeExpandClicked.treeNode 被展开的树节点数据
             * @param {Array<treeNodeType>} onTreeNodeExpandClicked.parentTreeNodes 当前节点的父节点列表
             */
            onTreeNodeExpandClicked: React.PropTypes.func,
            /**
             * 树节点“操作”按钮被点击时的回调。
             * @param {SyntheticEvent} onTreeNodeOperationClicked.e 点击事件对象
             * @param {treeNodeType} onTreeNodeOperationClicked.treeNode 被操作的树节点数据
             * @param {Array<treeNodeType>} onTreeNodeOperationClicked.parentTreeNodes 当前节点的父节点列表
             */
            onTreeNodeOperationClicked: React.PropTypes.func,
            /**
             * 树节点其他位置被点击时的回调。
             * @param {SyntheticEvent} onTreeNodeClicked.e 点击事件对象
             * @param {treeNodeType} onTreeNodeClicked.treeNode 被操作的树节点数据
             * @param {Array<treeNodeType>} onTreeNodeClicked.parentTreeNodes 当前节点的父节点列表
             */
            onTreeNodeClicked: React.PropTypes.func,
            /**
             * 节点加载中时的话术
             */
            textLoading: React.PropTypes.string,
            /**
             * 需要高亮显示的节点
             */
            focusedTreeNodeId: React.PropTypes.string,
            /**
             * filter string，不显示name中含有nameFilterString的node
             */
            nameFilter: React.PropTypes.string,
            /**
             * 被展开的节点集合
             */
            expandedTreeNodeId: React.PropTypes.objectOf(React.PropTypes.bool),
            /**
             * 被标记的节点集合
             */
            markedTreeNodeId: React.PropTypes.objectOf(React.PropTypes.bool)
        },

        statics: {
            treeNodeType
        },

        getDefaultProps() {
            return {
                treeLevel: 0,
                onTreeNodeExpandClicked: _.noop,
                onTreeNodeOperationClicked: _.noop,
                onTreeNodeClicked: _.noop,
                markedTreeNodeId: {},
                textLoading: '加载中...',
                nameFilter: null,
                parentTreeNodes: [],
                treeNodes: [],
                getTreeLevelStyle(level) {
                    return {
                        paddingLeft: (level * 0.5) + 'em'
                    };
                }
            };
        },

        getInitialState() {
            return {
                focusedTreeNodeId: this.props.focusedTreeNodeId || '',
                expandedTreeNodeId: this.props.expandedTreeNodeId || {},
                markedTreeNodeId: this.props.markedTreeNodeId || {}
            };
        },

        componentWillReceiveProps(nextProps) {
            this.setState(_.pick(nextProps, 'focusedTreeNodeId', 'expandedTreeNodeId', 'markedTreeNodeId'));
        },

        onTreeNodeExpandClicked(e, treeNode, parentTreeNodes) {
            this.props.onTreeNodeExpandClicked(
                e, treeNode, parentTreeNodes || this.props.parentTreeNodes
            );

            if (this.props.parentTreeNodes.length) {
                return;
            }

            if (e.isDefaultPrevented()) {
                return;
            }

            e.stopPropagation();

            let expandedTreeNodeId;

            if (this.state.expandedTreeNodeId[treeNode.id]) {
                expandedTreeNodeId = _.omit(this.state.expandedTreeNodeId, treeNode.id);
            }
            else {
                expandedTreeNodeId = _.extend({}, this.state.expandedTreeNodeId);
                expandedTreeNodeId[treeNode.id] = true;
            }
            this.setState({
                expandedTreeNodeId
            });
        },

        onTreeNodeClicked(e, treeNode, parentTreeNodes) {
            this.props.onTreeNodeClicked(
                e, treeNode, parentTreeNodes || this.props.parentTreeNodes
            );

            if (this.props.parentTreeNodes.length) {
                return;
            }

            if (e.isDefaultPrevented()) {
                return;
            }

            this.setState({
                focusedTreeNodeId: treeNode.id
            });
        },

        onTreeNodeOperationClicked(e, treeNode, parentTreeNodes) {
            this.props.onTreeNodeOperationClicked(
                e, treeNode, parentTreeNodes || this.props.parentTreeNodes
            );

            e.stopPropagation();
        },

        getHandlers() {
            return {
                onTreeNodeExpandClicked: this.onTreeNodeExpandClicked,
                onTreeNodeClicked: this.onTreeNodeClicked,
                onTreeNodeOperationClicked: this.onTreeNodeOperationClicked
            };
        },

        getTreeNodeProps() {
            return _.extend(
                _.pick(this.props, 'textLoading', 'treeLevel', 'nameFilter'),
                this.getHandlers()
            );
        },

        getInnerTreeProps() {
            return _.extend(
                _.omit(this.props, 'className', 'style'),
                _.pick(this.state, 'focusedTreeNodeId', 'expandedTreeNodeId'),
                this.getHandlers()
            );
        },

        getTreeNode(treeNode) {
            let isExpanded = !!this.state.expandedTreeNodeId[treeNode.id];
            let isMarked = !!this.state.markedTreeNodeId[treeNode.id];
            return (

                    <TreeNode
                        {...this.getTreeNodeProps()}
                        treeNode={treeNode}
                        isExpanded={isExpanded}
                        className={[
                            this.state.focusedTreeNodeId === treeNode.id ? 'fcui2-tree-node-focused' : '',
                            isMarked ? 'fcui2-tree-node-marked' : ''
                        ].join(' ')}
                    />
                    {
                        isExpanded && treeNode.children && treeNode.children.length
                            ? (
                                <Tree
                                    {...this.getInnerTreeProps()}
                                    parentTreeNodes={this.props.parentTreeNodes.concat(treeNode)}
                                    treeNodes={treeNode.children}
                                 />
                            )
                            : ''
                    }

            );
        },

        render() {
            let {
                getTreeLevelStyle,
                style,
                className = '',
                ...other
            } = this.props;

            let treeLevel = this.props.parentTreeNodes.length;

            return (

 0 ? 'fcui2-tree fcui2-tree-inner' : 'fcui2-tree'
                    ].join(' ')}
                >
                    {this.props.treeNodes.map(this.getTreeNode)}

            );
        }
    });

    return Tree;
});
<commit_msg>turn TreeNode into stateless function fixed fecs<commit_after>/**
 * @file 一个树形选择控件。
 * @author Han Bing Feng (hanbingfeng@baidu.com)
 */

define(function (require) {
    let _ = require('underscore');
    let React = require('react');

    /**
     * 一个树节点的定义
     */
    let treeNodeType = React.PropTypes.shape({
        /**
         * 树节点id
         */
        id: React.PropTypes.string,
        /**
         * 树节点文本内容
         */
        name: React.PropTypes.string,
        /**
         * 树节点的 载入中 状态
         */
        isChildrenLoading: React.PropTypes.bool,
        /**
         * 树节点的 已载入 状态
         */
        isChildrenLoaded: React.PropTypes.bool,
        /**
         * 所有孩子
         */
        children: React.PropTypes.array
    });

    /**
     * 默认的树单个节点的Renderer。
     *
     * @param {Object} props the props
     * @param {treeNodeType} props.treeNode 节点数据
     * @param {Array<treeNodeType>} props.parentTreeNode 当前节点的父节点表
     * @param {boolean} props.isExpanded 节点是否展开
     * @param {string} props.className 树根节点的class name
     *
     * @param {Function} props.afterTreeNodeRenderer 生成可插入节点正文后的元素
     * @param {Function} props.afterTreeNodeRendererProps 属性
     *
     * @param {Function} props.onTreeNodeExpandClicked 树节点展开按钮被点击时的回调
     * @param {SyntheticEvent} props.onTreeNodeExpandClicked.e 点击事件对象
     * @param {treeNodeType} props.onTreeNodeExpandClicked.treeNode 被展开的树节点数据
     *
     * @param {Function} props.onTreeNodeOperationClicked 树节点“操作”按钮被点击时的回调。
     * @param {SyntheticEvent} props.onTreeNodeOperationClicked.e 点击事件对象
     * @param {treeNodeType} props.onTreeNodeOperationClicked.treeNode 被操作的树节点数据
     *
     * @param {Function} props.onTreeNodeClicked 树节点其他位置被点击时的回调。
     * @param {SyntheticEvent} props.onTreeNodeClicked.e 点击事件对象
     * @param {treeNodeType} props.onTreeNodeClicked.treeNode 被点击的树节点数据
     *
     * @return {ReactElement} rendered react element
     */
    function baseTreeNodeRenderer(props) {
        let {
            treeNode,
            isExpanded,
            className = '',
            onTreeNodeExpandClicked = _.noop,
            onTreeNodeOperationClicked = _.noop,
            onTreeNodeClicked = _.noop,
            ...others
        } = props;

        className = [
            'fcui2-tree-node',
            className
        ];

        if (treeNode.isChildrenLoaded && (treeNode.children == null || treeNode.children.length === 0)) {
            className.push('fcui2-tree-node-leaf');
        }

        let AfterTreeNode = props.afterTreeNodeRenderer;

        return (

                {treeNode.name}
                {
                    typeof AfterTreeNode === 'function'
                        ? <AfterTreeNode
                            {...props.afterTreeNodeRendererProps}
                            {...props}
                          />
                        : ''
                }

        );
    }

    /**
     * 扩展TreeNodeRenderer，支持绘制树节点的loading状态
     *
     * @param  {Function} TreeNodeRenderer 被扩展的Renderer
     * @return {Function} 支持绘制树节点的loading状态的Renderer
     */
    function supportLoadingText(TreeNodeRenderer) {

        /**
         * 支持绘制树节点的loading状态的Renderer。
         *
         * @param {Object} props the props
         * @param {string} props.textLoading 树节点载入中时的话术提示。
         * @return {ReactElement} rendered element
         */
        return function (props) {
            let AfterTreeNode = this.props.afterTreeNodeRenderer;
            this.props.afterTreeNodeRenderer = function () {
                return (

                        {
                            typeof AfterTreeNode === 'function'
                                ? <AfterTreeNode
                                    {...props.afterTreeNodeRendererProps}
                                    {...props}
                                  />
                                : ''
                        }
                        {props.textLoading}

                );
            };
            return <TreeNodeRenderer {...props} />;
        };
    }

    /**
     * 扩展TreeNodeRenderer，支持根据nameFilter加入filter class。
     *
     * @param  {Function} TreeNodeRenderer 被扩展的Renderer。
     * @return {Function} 支持根据nameFilter加入filter class的Renderer。
     */
    function supportNameFilter(TreeNodeRenderer) {

        /**
         * 支持根据nameFilter加入filter class。
         *
         * @param {Object} props the props
         * @param {string} props.nameFilter 树名字过滤器，不包含过滤器中字符串的节点将加上class`fcui2-tree-node-filtered`。
         * @return {ReactElement} rendered element
         */
        return function (props) {
            let nameFilter = props.nameFilter;

            if (nameFilter != null && props.treeNode.name.indexOf(nameFilter) === -1) {
                props.className = props.className + ' fcui2-tree-node-filtered';
            }

            return <TreeNodeRenderer {...props} />;
        };
    }

    let Tree = React.createClass({
        propTypes: {
            /**
             * 树所展现的所有节点集合
             */
            treeNodes: React.PropTypes.arrayOf(treeNodeType),
            /**
             * 本层次树的上层节点
             */
            parentTreeNodes: React.PropTypes.arrayOf(treeNodeType),
            /**
             * 根据节点的层数，计算当前层的wrapper的style
             * @param {number} level 树的层数
             * @return {Object} 当前层的style object
             */
            getTreeLevelStyle: React.PropTypes.func,
            /**
             * 树节点展开按钮被点击时的回调。
             * @param {SyntheticEvent} onTreeNodeExpandClicked.e 点击事件对象
             * @param {treeNodeType} onTreeNodeExpandClicked.treeNode 被展开的树节点数据
             * @param {Array<treeNodeType>} onTreeNodeExpandClicked.parentTreeNodes 当前节点的父节点列表
             */
            onTreeNodeExpandClicked: React.PropTypes.func,
            /**
             * 树节点“操作”按钮被点击时的回调。
             * @param {SyntheticEvent} onTreeNodeOperationClicked.e 点击事件对象
             * @param {treeNodeType} onTreeNodeOperationClicked.treeNode 被操作的树节点数据
             * @param {Array<treeNodeType>} onTreeNodeOperationClicked.parentTreeNodes 当前节点的父节点列表
             */
            onTreeNodeOperationClicked: React.PropTypes.func,
            /**
             * 树节点其他位置被点击时的回调。
             * @param {SyntheticEvent} onTreeNodeClicked.e 点击事件对象
             * @param {treeNodeType} onTreeNodeClicked.treeNode 被操作的树节点数据
             * @param {Array<treeNodeType>} onTreeNodeClicked.parentTreeNodes 当前节点的父节点列表
             */
            onTreeNodeClicked: React.PropTypes.func,
            /**
             * 节点加载中时的话术
             */
            textLoading: React.PropTypes.string,
            /**
             * 需要高亮显示的节点
             */
            focusedTreeNodeId: React.PropTypes.string,
            /**
             * filter string，不显示name中含有nameFilterString的node
             */
            nameFilter: React.PropTypes.string,
            /**
             * 被展开的节点集合
             */
            expandedTreeNodeId: React.PropTypes.objectOf(React.PropTypes.bool),
            /**
             * 被标记的节点集合
             */
            markedTreeNodeId: React.PropTypes.objectOf(React.PropTypes.bool)
        },

        statics: {
            treeNodeType,
            baseTreeNodeRenderer,
            supportLoadingText,
            supportNameFilter
        },

        getDefaultProps() {
            return {
                treeLevel: 0,
                onTreeNodeExpandClicked: _.noop,
                onTreeNodeOperationClicked: _.noop,
                onTreeNodeClicked: _.noop,
                markedTreeNodeId: {},
                textLoading: '加载中...',
                nameFilter: null,
                parentTreeNodes: [],
                treeNodes: [],
                treeNodeRenderer: supportLoadingText(supportNameFilter(baseTreeNodeRenderer)),
                getTreeLevelStyle(level) {
                    return {
                        paddingLeft: (level * 0.5) + 'em'
                    };
                }
            };
        },

        getInitialState() {
            return {
                focusedTreeNodeId: this.props.focusedTreeNodeId || '',
                expandedTreeNodeId: this.props.expandedTreeNodeId || {},
                markedTreeNodeId: this.props.markedTreeNodeId || {}
            };
        },

        componentWillReceiveProps(nextProps) {
            this.setState(_.pick(nextProps, 'focusedTreeNodeId', 'expandedTreeNodeId', 'markedTreeNodeId'));
        },

        onTreeNodeExpandClicked(e, treeNode, parentTreeNodes) {
            this.props.onTreeNodeExpandClicked(
                e, treeNode, parentTreeNodes || this.props.parentTreeNodes
            );

            if (this.props.parentTreeNodes.length) {
                return;
            }

            if (e.isDefaultPrevented()) {
                return;
            }

            e.stopPropagation();

            let expandedTreeNodeId;

            if (this.state.expandedTreeNodeId[treeNode.id]) {
                expandedTreeNodeId = _.omit(this.state.expandedTreeNodeId, treeNode.id);
            }
            else {
                expandedTreeNodeId = _.extend({}, this.state.expandedTreeNodeId);
                expandedTreeNodeId[treeNode.id] = true;
            }
            this.setState({
                expandedTreeNodeId
            });
        },

        onTreeNodeClicked(e, treeNode, parentTreeNodes) {
            this.props.onTreeNodeClicked(
                e, treeNode, parentTreeNodes || this.props.parentTreeNodes
            );

            if (this.props.parentTreeNodes.length) {
                return;
            }

            if (e.isDefaultPrevented()) {
                return;
            }

            this.setState({
                focusedTreeNodeId: treeNode.id
            });
        },

        onTreeNodeOperationClicked(e, treeNode, parentTreeNodes) {
            this.props.onTreeNodeOperationClicked(
                e, treeNode, parentTreeNodes || this.props.parentTreeNodes
            );

            e.stopPropagation();
        },

        getHandlers() {
            return {
                onTreeNodeExpandClicked: this.onTreeNodeExpandClicked,
                onTreeNodeClicked: this.onTreeNodeClicked,
                onTreeNodeOperationClicked: this.onTreeNodeOperationClicked
            };
        },

        getTreeNodeProps() {
            return _.extend(
                _.pick(this.props, 'textLoading', 'treeLevel', 'nameFilter'),
                this.getHandlers()
            );
        },

        getInnerTreeProps() {
            return _.extend(
                _.omit(this.props, 'className', 'style'),
                _.pick(this.state, 'focusedTreeNodeId', 'expandedTreeNodeId'),
                this.getHandlers()
            );
        },

        getTreeNode(treeNode) {
            let isExpanded = !!this.state.expandedTreeNodeId[treeNode.id];
            let isMarked = !!this.state.markedTreeNodeId[treeNode.id];
            let TreeNode = this.props.treeNodeRenderer;
            return (

                    <TreeNode
                        {...this.getTreeNodeProps()}
                        treeNode={treeNode}
                        isExpanded={isExpanded}
                        className={[
                            this.state.focusedTreeNodeId === treeNode.id ? 'fcui2-tree-node-focused' : '',
                            isMarked ? 'fcui2-tree-node-marked' : ''
                        ].join(' ')}
                    />
                    {
                        isExpanded && treeNode.children && treeNode.children.length
                            ? (
                                <Tree
                                    {...this.getInnerTreeProps()}
                                    parentTreeNodes={this.props.parentTreeNodes.concat(treeNode)}
                                    treeNodes={treeNode.children}
                                 />
                            )
                            : ''
                    }

            );
        },

        render() {
            let {
                getTreeLevelStyle,
                style,
                className = '',
                ...other
            } = this.props;

            let treeLevel = this.props.parentTreeNodes.length;

            return (

 0 ? 'fcui2-tree fcui2-tree-inner' : 'fcui2-tree'
                    ].join(' ')}
                >
                    {this.props.treeNodes.map(this.getTreeNode)}

            );
        }
    });

    return Tree;
});
<|endoftext|>


<commit_before>/**
 * @flow
 */

import _ from 'lodash';
import semver from 'semver';

import Api from './Api';
import ApiV2Client from './ApiV2';
import ErrorCode from './ErrorCode';
import XDLError from './XDLError';
import UserManager from './User';

export async function versionsAsync() {
  const api = new ApiV2Client();
  return await api.getAsync('versions');
}

export async function setVersionsAsync(json: any) {
  const user = await UserManager.getCurrentUserAsync();
  const api = ApiV2Client.clientForUser(user);
  return await api.postAsync('versions/update', json);
}

export function gteSdkVersion(expJson: any, sdkVersion: string): boolean {
  if (!expJson.sdkVersion) {
    return false;
  }

  if (expJson.sdkVersion === 'UNVERSIONED') {
    return true;
  }

  try {
    return semver.gte(expJson.sdkVersion, sdkVersion);
  } catch (e) {
    throw new XDLError(ErrorCode.INVALID_VERSION, `${expJson.sdkVersion} is not a valid version. Must be in the form of x.y.z`);
  }
}

export function parseSdkVersionFromTag(tag: string) {
  if (tag.startsWith('sdk-')) {
    return tag.substring(4);
  }

  return tag;
}

export async function newestSdkVersionAsync() {
  let sdkVersions = await Api.sdkVersionsAsync();
  let result = {};
  let highestMajorVersion = '0.0.0';
  _.forEach(sdkVersions, (value, key) => {
    if (semver.major(key) > semver.major(highestMajorVersion)) {
      highestMajorVersion = key;
      result = value;
    }
  });
  result.version = highestMajorVersion;
  return result;
}

export async function facebookReactNativeVersionsAsync(): Promise<Array<string>> {
  let sdkVersions = await Api.sdkVersionsAsync();
  let facebookReactNativeVersions = new Set();

  _.forEach(sdkVersions, (value) => {
    if (value.facebookReactNativeVersion) {
      facebookReactNativeVersions.add(value.facebookReactNativeVersion);
    }
  });

  return Array.from(facebookReactNativeVersions);
}

export async function facebookReactNativeVersionToExponentVersionAsync(facebookReactNativeVersion: string): Promise<? string> {
  if (!semver.valid(facebookReactNativeVersion)) {
    throw new XDLError(ErrorCode.INVALID_VERSION, `${facebookReactNativeVersion} is not a valid version. Must be in the form of x.y.z`);
  }

  let sdkVersions = await Api.sdkVersionsAsync();
  let currentSdkVersion = null;

  _.forEach(sdkVersions, (value, key) => {
    if (semver.major(value.facebookReactNativeVersion) === semver.major(facebookReactNativeVersion) &&
        semver.minor(value.facebookReactNativeVersion) === semver.minor(facebookReactNativeVersion) &&
        (!currentSdkVersion || semver.gt(key, currentSdkVersion))) {
      currentSdkVersion = key;
    }
  });

  return currentSdkVersion;
}
<commit_msg>Temporarily use a secret for versions endpoint<commit_after>/**
 * @flow
 */

import _ from 'lodash';
import semver from 'semver';

import Api from './Api';
import ApiV2Client from './ApiV2';
import ErrorCode from './ErrorCode';
import XDLError from './XDLError';
import UserManager from './User';

export async function versionsAsync() {
  const api = new ApiV2Client();
  return await api.getAsync('versions');
}

export async function setVersionsAsync(value: any) {
  const user = await UserManager.getCurrentUserAsync();
  const api = ApiV2Client.clientForUser(user);
  return await api.postAsync('versions/update', {
    value,
    secret: process.env.EXPONENT_VERSIONS_SECRET,
  });
}

export function gteSdkVersion(expJson: any, sdkVersion: string): boolean {
  if (!expJson.sdkVersion) {
    return false;
  }

  if (expJson.sdkVersion === 'UNVERSIONED') {
    return true;
  }

  try {
    return semver.gte(expJson.sdkVersion, sdkVersion);
  } catch (e) {
    throw new XDLError(ErrorCode.INVALID_VERSION, `${expJson.sdkVersion} is not a valid version. Must be in the form of x.y.z`);
  }
}

export function parseSdkVersionFromTag(tag: string) {
  if (tag.startsWith('sdk-')) {
    return tag.substring(4);
  }

  return tag;
}

export async function newestSdkVersionAsync() {
  let sdkVersions = await Api.sdkVersionsAsync();
  let result = {};
  let highestMajorVersion = '0.0.0';
  _.forEach(sdkVersions, (value, key) => {
    if (semver.major(key) > semver.major(highestMajorVersion)) {
      highestMajorVersion = key;
      result = value;
    }
  });
  result.version = highestMajorVersion;
  return result;
}

export async function facebookReactNativeVersionsAsync(): Promise<Array<string>> {
  let sdkVersions = await Api.sdkVersionsAsync();
  let facebookReactNativeVersions = new Set();

  _.forEach(sdkVersions, (value) => {
    if (value.facebookReactNativeVersion) {
      facebookReactNativeVersions.add(value.facebookReactNativeVersion);
    }
  });

  return Array.from(facebookReactNativeVersions);
}

export async function facebookReactNativeVersionToExponentVersionAsync(facebookReactNativeVersion: string): Promise<? string> {
  if (!semver.valid(facebookReactNativeVersion)) {
    throw new XDLError(ErrorCode.INVALID_VERSION, `${facebookReactNativeVersion} is not a valid version. Must be in the form of x.y.z`);
  }

  let sdkVersions = await Api.sdkVersionsAsync();
  let currentSdkVersion = null;

  _.forEach(sdkVersions, (value, key) => {
    if (semver.major(value.facebookReactNativeVersion) === semver.major(facebookReactNativeVersion) &&
        semver.minor(value.facebookReactNativeVersion) === semver.minor(facebookReactNativeVersion) &&
        (!currentSdkVersion || semver.gt(key, currentSdkVersion))) {
      currentSdkVersion = key;
    }
  });

  return currentSdkVersion;
}
<|endoftext|>


<commit_before>/* @flow */

import { getLogger, getPayPalDomain, getIntent, getCurrency } from 'paypal-braintree-web-client/src';
import { ZalgoPromise } from 'zalgo-promise/src';
import { on, send } from 'post-robot/src';
import { getAncestor, isSameDomain } from 'cross-domain-utils/src';
import { memoize, request, base64encode } from 'belter/src';
import { FPTI_KEY, SDK_QUERY_KEYS } from 'paypal-sdk-constants/src';

import { getOrderAPIUrl, getAuthAPIUrl } from '../config';
import { FPTI_STATE, FPTI_CONTEXT_TYPE, FPTI_TRANSITION } from '../constants';
import { isPayPalDomain } from '../lib';

type ProxyRest = {
    [string] : (...args : Array<mixed>) => ZalgoPromise<*>
};

let proxyRest : ProxyRest = {};

export type OrderCreateRequest = {
    intent? : 'CAPTURE' | 'AUTHORIZE',
    purchase_units : Array<{
        amount : {
            currency_code : string,
            value : string
        }
    }>
};

export type OrderCaptureResponse = {};
export type OrderGetResponse = {};
export type OrderAuthorizeResponse = {};

export let createAccessToken = memoize((clientID : string) : ZalgoPromise<string> => {
    getLogger().info(`rest_api_create_access_token`);

    if (proxyRest.createAccessToken && !proxyRest.createAccessToken.source.closed) {
        return proxyRest.createAccessToken(clientID);
    }

    let basicAuth : string = base64encode(`${ clientID }:`);

    return request({

        method:  `post`,
        url:     getAuthAPIUrl(),
        headers: {
            Authorization: `Basic ${ basicAuth }`
        },
        data: {
            grant_type: `client_credentials`
        }

    }).then(({ body }) => {

        if (body && body.error === 'invalid_client') {
            throw new Error(`Auth Api invalid client id: ${ clientID }:\n\n${ JSON.stringify(body, null, 4) }`);
        }

        if (!body || !body.access_token) {
            throw new Error(`Auth Api response error:\n\n${ JSON.stringify(body, null, 4) }`);
        }

        return body.access_token;
    });

}, { time: 10 * 60 * 1000 });

function logOrderResponse(orderID) {
    getLogger().track({
        [ FPTI_KEY.STATE ]:        FPTI_STATE.BUTTON,
        [ FPTI_KEY.TRANSITION ]:   FPTI_TRANSITION.CREATE_ORDER,
        [ FPTI_KEY.CONTEXT_TYPE ]: FPTI_CONTEXT_TYPE.ORDER_ID,
        [ FPTI_KEY.TOKEN ]:        orderID,
        [ FPTI_KEY.CONTEXT_ID ]:   orderID
    });
}

export function createOrder(clientID : string, order : OrderCreateRequest) : ZalgoPromise<string> {
    getLogger().info(`rest_api_create_order_token`);

    if (!clientID) {
        throw new Error(`Client ID not passed`);
    }

    if (proxyRest.createOrder && !proxyRest.createOrder.source.closed) {
        return proxyRest.createOrder(clientID, order);
    }

    if (!order) {
        throw new Error(`Expected order details to be passed`);
    }

    let currency = window.xprops ? window.xprops.currency : getCurrency();
    let intent = window.xprops ? window.xprops.intent : getIntent();

    order = { ...order };

    if (order.intent && order.intent !== intent) {
        throw new Error(`Unexpected intent: ${ order.intent } passed to order.create. Please ensure you are passing /sdk/js?${ SDK_QUERY_KEYS.ORDER_INTENT }=${ order.intent } in the paypal script tag.`);
    }

    // $FlowFixMe
    order = { ...order, intent: intent.toUpperCase() };

    order.purchase_units = order.purchase_units.map(unit => {
        if (unit.amount.currency_code && unit.amount.currency_code !== currency) {
            throw new Error(`Unexpected currency: ${ unit.amount.currency_code } passed to order.create. Please ensure you are passing /sdk/js?${ SDK_QUERY_KEYS.ORDER_CURRENCY }=${ unit.amount.currency_code } in the paypal script tag.`);
        }

        return { ...unit, amount: { ...unit.amount, currency_code: currency } };
    });

    return createAccessToken(clientID).then((accessToken) : ZalgoPromise<Object> => {

        let headers : Object = {
            Authorization: `Bearer ${ accessToken }`
        };

        return request({
            method: `post`,
            url:    getOrderAPIUrl(),
            headers,
            json:   order
        });

    }).then(({ body }) : string => {

        logOrderResponse(body.id);

        if (body && body.id) {
            return body.id;
        }

        throw new Error(`Order Api response error:\n\n${ JSON.stringify(body, null, 4) }`);
    });
}

const PROXY_REST = `proxy_rest`;
let parentWin = getAncestor();

on(PROXY_REST, { domain: getPayPalDomain() }, ({ data }) => {
    proxyRest = data;
});

if (parentWin && isPayPalDomain() && !isSameDomain(parentWin)) {
    send(parentWin, PROXY_REST, { createAccessToken, createOrder })
        .catch(() => {
            // pass
        });
}
<commit_msg>Pass partner attribution id to orders api<commit_after>/* @flow */

import { getLogger, getPayPalDomain, getIntent, getCurrency, getPartnerAttributionID } from 'paypal-braintree-web-client/src';
import { ZalgoPromise } from 'zalgo-promise/src';
import { on, send } from 'post-robot/src';
import { getAncestor, isSameDomain } from 'cross-domain-utils/src';
import { memoize, request, base64encode } from 'belter/src';
import { FPTI_KEY, SDK_QUERY_KEYS } from 'paypal-sdk-constants/src';

import { getOrderAPIUrl, getAuthAPIUrl } from '../config';
import { FPTI_STATE, FPTI_CONTEXT_TYPE, FPTI_TRANSITION } from '../constants';
import { isPayPalDomain } from '../lib';

type ProxyRest = {
    [string] : (...args : Array<mixed>) => ZalgoPromise<*>
};

let proxyRest : ProxyRest = {};

export type OrderCreateRequest = {
    intent? : 'CAPTURE' | 'AUTHORIZE',
    purchase_units : Array<{
        amount : {
            currency_code : string,
            value : string
        }
    }>
};

export type OrderCaptureResponse = {};
export type OrderGetResponse = {};
export type OrderAuthorizeResponse = {};

export let createAccessToken = memoize((clientID : string) : ZalgoPromise<string> => {
    getLogger().info(`rest_api_create_access_token`);

    if (proxyRest.createAccessToken && !proxyRest.createAccessToken.source.closed) {
        return proxyRest.createAccessToken(clientID);
    }

    let basicAuth : string = base64encode(`${ clientID }:`);

    return request({

        method:  `post`,
        url:     getAuthAPIUrl(),
        headers: {
            Authorization: `Basic ${ basicAuth }`
        },
        data: {
            grant_type: `client_credentials`
        }

    }).then(({ body }) => {

        if (body && body.error === 'invalid_client') {
            throw new Error(`Auth Api invalid client id: ${ clientID }:\n\n${ JSON.stringify(body, null, 4) }`);
        }

        if (!body || !body.access_token) {
            throw new Error(`Auth Api response error:\n\n${ JSON.stringify(body, null, 4) }`);
        }

        return body.access_token;
    });

}, { time: 10 * 60 * 1000 });

function logOrderResponse(orderID) {
    getLogger().track({
        [ FPTI_KEY.STATE ]:        FPTI_STATE.BUTTON,
        [ FPTI_KEY.TRANSITION ]:   FPTI_TRANSITION.CREATE_ORDER,
        [ FPTI_KEY.CONTEXT_TYPE ]: FPTI_CONTEXT_TYPE.ORDER_ID,
        [ FPTI_KEY.TOKEN ]:        orderID,
        [ FPTI_KEY.CONTEXT_ID ]:   orderID
    });
}

export function createOrder(clientID : string, order : OrderCreateRequest) : ZalgoPromise<string> {
    getLogger().info(`rest_api_create_order_token`);

    if (!clientID) {
        throw new Error(`Client ID not passed`);
    }

    if (proxyRest.createOrder && !proxyRest.createOrder.source.closed) {
        return proxyRest.createOrder(clientID, order);
    }

    if (!order) {
        throw new Error(`Expected order details to be passed`);
    }

    let currency = window.xprops ? window.xprops.currency : getCurrency();
    let intent = window.xprops ? window.xprops.intent : getIntent();

    order = { ...order };

    if (order.intent && order.intent !== intent) {
        throw new Error(`Unexpected intent: ${ order.intent } passed to order.create. Please ensure you are passing /sdk/js?${ SDK_QUERY_KEYS.ORDER_INTENT }=${ order.intent } in the paypal script tag.`);
    }

    // $FlowFixMe
    order = { ...order, intent: intent.toUpperCase() };

    order.purchase_units = order.purchase_units.map(unit => {
        if (unit.amount.currency_code && unit.amount.currency_code !== currency) {
            throw new Error(`Unexpected currency: ${ unit.amount.currency_code } passed to order.create. Please ensure you are passing /sdk/js?${ SDK_QUERY_KEYS.ORDER_CURRENCY }=${ unit.amount.currency_code } in the paypal script tag.`);
        }

        return { ...unit, amount: { ...unit.amount, currency_code: currency } };
    });

    return createAccessToken(clientID).then((accessToken) : ZalgoPromise<Object> => {

        let headers : Object = {
            'Authorization':                 `Bearer ${ accessToken }`,
            'PayPal-Partner-Attribution-Id': getPartnerAttributionID()
        };

        return request({
            method: `post`,
            url:    getOrderAPIUrl(),
            headers,
            json:   order
        });

    }).then(({ body }) : string => {

        logOrderResponse(body.id);

        if (body && body.id) {
            return body.id;
        }

        throw new Error(`Order Api response error:\n\n${ JSON.stringify(body, null, 4) }`);
    });
}

const PROXY_REST = `proxy_rest`;
let parentWin = getAncestor();

on(PROXY_REST, { domain: getPayPalDomain() }, ({ data }) => {
    proxyRest = data;
});

if (parentWin && isPayPalDomain() && !isSameDomain(parentWin)) {
    send(parentWin, PROXY_REST, { createAccessToken, createOrder })
        .catch(() => {
            // pass
        });
}
<|endoftext|>


<commit_before>import {
  applyProp,
  attr,
  attributes,
  elementClose,
  elementOpen,
  elementOpenEnd,
  elementOpenStart,
  elementVoid,
  skip,
  symbols,
  text,
} from 'incremental-dom';
import { $name, $ref } from '../util/symbols';
import { shadowDomV0, shadowDomV1 } from '../util/support';

const applyDefault = attributes[symbols.default];
const fallbackToV0 = !shadowDomV1 && shadowDomV0;
const stackChren = [];
const stackProps = [];

// Attributes that are not handled by Incremental DOM.
attributes.key = attributes.skip = attributes.statics = function () {};

// Attributes that *must* be set via a property on all elements.
attributes.checked = attributes.className = attributes.disabled = attributes.value = applyProp;

// Default attribute applicator.
attributes[symbols.default] = function (elem, name, value) {
  // If the skip attribute was specified, skip
  if (name === 'skip' && value) {
    return skip();
  }

  // Add the ref to the element so it can be called when it's closed.
  if (name === 'ref') {
    elem[$ref] = value;
    return;
  }

  // Custom element properties should be set as properties.
  const props = elem.constructor.props;
  if (props && name in props) {
    return applyProp(elem, name, value);
  }

  // Boolean false values should not set attributes at all.
  if (value === false) {
    return;
  }

  // Handle built-in and custom events.
  if (name.indexOf('on') === 0) {
    return name in elem ? applyProp(elem, name, value) : applyEvent(elem, name.substring(2), name, value);
  }

  // Set the select attribute instead of name if it was a <slot> translated to
  // a <content> for v0.
  if (name === 'name' && elem.tagName === 'CONTENT') {
    name = 'select';
    value = `[slot="${value}"]`;
  }

  // Fallback to default IncrementalDOM behaviour.
  applyDefault(elem, name, value);
};

// Adds or removes an event listener for an element.
function applyEvent (elem, ename, name, value) {
  let events = elem.__events;

  if (!events) {
    events = elem.__events = {};
  }

  const eFunc = events[ename];

  // Remove old listener so they don't double up.
  if (eFunc) {
    elem.removeEventListener(ename, eFunc);
  }

  // Bind new listener.
  if (value) {
    elem.addEventListener(ename, events[ename] = value);
  }
}

function resolveTagName (tname) {
  // If the tag name is a function, a Skate constructor or a standard function
  // is supported.
  //
  // - If a Skate constructor, the tag name is extracted from that.
  // - If a standard function, it is used as a helper.
  if (typeof tname === 'function') {
    return tname[$name] || tname;
  }

  // Skate allows the consumer to use <slot /> and it will translate it to
  // <content /> if Shadow DOM V0 is preferred.
  if (tname === 'slot' && fallbackToV0) {
    return 'content';
  }

  // All other tag names are just passed through.
  return tname;
}

function wrapIdomFunc (func, tnameFuncHandler = () => {}) {
  return function wrap (...args) {
    const tname = args[0] = resolveTagName(args[0]);
    if (typeof tname === 'function') {
      // If we've encountered a function, handle it according to the type of
      // function that is being wrapped.
      tnameFuncHandler(tname);
    } else if (stackChren.length) {
      // We pass the wrap() function in here so that when it's called as
      // children, it will queue up for the next stack, if there is one.
      stackChren[stackChren.length - 1].push([wrap, args]);
    } else {
      // If there is no stack left, we call Incremental DOM directly.
      const elem = func(...args);

      // If we're in elementClose, try calling the ref.
      if (func.name === 'elementClose') {
        const eref = elem[$ref];
        if (typeof eref === 'function') {
          eref(elem);
        }
      }

      return elem;
    }
  };
}

function newAttr (key, val) {
  if (stackProps.length) {
    stackProps[stackProps.length - 1][key] = val;
  } else {
    return attr(key, val);
  }
}

function stackOpen () {
  stackChren.push([]);
  stackProps.push({});
}

function stackClose (tname) {
  const chren = stackChren.pop();
  const props = stackProps.pop();
  tname(props, () => chren.forEach(args => args[0](...args[1])));
}

// Convenience function for declaring an Incremental DOM element using
// hyperscript-style syntax.
export function element (tname, attrs, chren) {
  const atype = typeof attrs;

  // If attributes are a function, then they should be treated as children.
  if (atype === 'function' || atype === 'string') {
    chren = attrs;
  }

  // Ensure the attributes are an object.
  if (atype !== 'object') {
    attrs = {};
  }

  // We open the element so we can set attrs after.
  newElementOpenStart(tname, attrs.key, attrs.statics);

  // Delete so special attrs don't actually get set.
  delete attrs.key;
  delete attrs.statics;

  // Set attributes.
  Object.keys(attrs).forEach(name => newAttr(name, attrs[name]));

  // Close before we render the descendant tree.
  newElementOpenEnd(tname);

  const ctype = typeof chren;
  if (ctype === 'function') {
    chren();
  } else if (ctype === 'string' || ctype === 'number') {
    newText(chren);
  }

  return newElementClose(tname);
}

// Patch element factories.
const newElementClose = wrapIdomFunc(elementClose, stackClose);
const newElementOpen = wrapIdomFunc(elementOpen, stackOpen);
const newElementOpenEnd = wrapIdomFunc(elementOpenEnd);
const newElementOpenStart = wrapIdomFunc(elementOpenStart, stackOpen);
const newElementVoid = wrapIdomFunc(elementVoid);
const newText = wrapIdomFunc(text);

// We don't have to do anything special for the text function; it's just a
// straight export from Incremental DOM.
export {
  newAttr as attr,
  newElementClose as elementClose,
  newElementOpen as elementOpen,
  newElementOpenEnd as elementOpenEnd,
  newElementOpenStart as elementOpenStart,
  newElementVoid as elementVoid,
  newText as text,
};
<commit_msg>fix(vdom): Fix refs in IE11. Function.name isn't reliable in IE11.<commit_after>import {
  applyProp,
  attr,
  attributes,
  elementClose,
  elementOpen,
  elementOpenEnd,
  elementOpenStart,
  elementVoid,
  skip,
  symbols,
  text,
} from 'incremental-dom';
import { $name, $ref } from '../util/symbols';
import { shadowDomV0, shadowDomV1 } from '../util/support';

const applyDefault = attributes[symbols.default];
const fallbackToV0 = !shadowDomV1 && shadowDomV0;
const stackChren = [];
const stackProps = [];

// Attributes that are not handled by Incremental DOM.
attributes.key = attributes.skip = attributes.statics = function () {};

// Attributes that *must* be set via a property on all elements.
attributes.checked = attributes.className = attributes.disabled = attributes.value = applyProp;

// Default attribute applicator.
attributes[symbols.default] = function (elem, name, value) {
  // If the skip attribute was specified, skip
  if (name === 'skip' && value) {
    return skip();
  }

  // Add the ref to the element so it can be called when it's closed.
  if (name === 'ref') {
    elem[$ref] = value;
    return;
  }

  // Custom element properties should be set as properties.
  const props = elem.constructor.props;
  if (props && name in props) {
    return applyProp(elem, name, value);
  }

  // Boolean false values should not set attributes at all.
  if (value === false) {
    return;
  }

  // Handle built-in and custom events.
  if (name.indexOf('on') === 0) {
    return name in elem ? applyProp(elem, name, value) : applyEvent(elem, name.substring(2), name, value);
  }

  // Set the select attribute instead of name if it was a <slot> translated to
  // a <content> for v0.
  if (name === 'name' && elem.tagName === 'CONTENT') {
    name = 'select';
    value = `[slot="${value}"]`;
  }

  // Fallback to default IncrementalDOM behaviour.
  applyDefault(elem, name, value);
};

// Adds or removes an event listener for an element.
function applyEvent (elem, ename, name, value) {
  let events = elem.__events;

  if (!events) {
    events = elem.__events = {};
  }

  const eFunc = events[ename];

  // Remove old listener so they don't double up.
  if (eFunc) {
    elem.removeEventListener(ename, eFunc);
  }

  // Bind new listener.
  if (value) {
    elem.addEventListener(ename, events[ename] = value);
  }
}

function resolveTagName (tname) {
  // If the tag name is a function, a Skate constructor or a standard function
  // is supported.
  //
  // - If a Skate constructor, the tag name is extracted from that.
  // - If a standard function, it is used as a helper.
  if (typeof tname === 'function') {
    return tname[$name] || tname;
  }

  // Skate allows the consumer to use <slot /> and it will translate it to
  // <content /> if Shadow DOM V0 is preferred.
  if (tname === 'slot' && fallbackToV0) {
    return 'content';
  }

  // All other tag names are just passed through.
  return tname;
}

function wrapIdomFunc (func, tnameFuncHandler = () => {}) {
  return function wrap (...args) {
    const tname = args[0] = resolveTagName(args[0]);
    if (typeof tname === 'function') {
      // If we've encountered a function, handle it according to the type of
      // function that is being wrapped.
      tnameFuncHandler(tname);
    } else if (stackChren.length) {
      // We pass the wrap() function in here so that when it's called as
      // children, it will queue up for the next stack, if there is one.
      stackChren[stackChren.length - 1].push([wrap, args]);
    } else {
      // If there is no stack left, we call Incremental DOM directly.
      const elem = func(...args);

      // If we're in elementClose, try calling the ref.
      if (func === elementClose) {
        const eref = elem[$ref];
        if (typeof eref === 'function') {
          eref(elem);
        }
      }

      return elem;
    }
  };
}

function newAttr (key, val) {
  if (stackProps.length) {
    stackProps[stackProps.length - 1][key] = val;
  } else {
    return attr(key, val);
  }
}

function stackOpen () {
  stackChren.push([]);
  stackProps.push({});
}

function stackClose (tname) {
  const chren = stackChren.pop();
  const props = stackProps.pop();
  tname(props, () => chren.forEach(args => args[0](...args[1])));
}

// Convenience function for declaring an Incremental DOM element using
// hyperscript-style syntax.
export function element (tname, attrs, chren) {
  const atype = typeof attrs;

  // If attributes are a function, then they should be treated as children.
  if (atype === 'function' || atype === 'string') {
    chren = attrs;
  }

  // Ensure the attributes are an object.
  if (atype !== 'object') {
    attrs = {};
  }

  // We open the element so we can set attrs after.
  newElementOpenStart(tname, attrs.key, attrs.statics);

  // Delete so special attrs don't actually get set.
  delete attrs.key;
  delete attrs.statics;

  // Set attributes.
  Object.keys(attrs).forEach(name => newAttr(name, attrs[name]));

  // Close before we render the descendant tree.
  newElementOpenEnd(tname);

  const ctype = typeof chren;
  if (ctype === 'function') {
    chren();
  } else if (ctype === 'string' || ctype === 'number') {
    newText(chren);
  }

  return newElementClose(tname);
}

// Patch element factories.
const newElementClose = wrapIdomFunc(elementClose, stackClose);
const newElementOpen = wrapIdomFunc(elementOpen, stackOpen);
const newElementOpenEnd = wrapIdomFunc(elementOpenEnd);
const newElementOpenStart = wrapIdomFunc(elementOpenStart, stackOpen);
const newElementVoid = wrapIdomFunc(elementVoid);
const newText = wrapIdomFunc(text);

// We don't have to do anything special for the text function; it's just a
// straight export from Incremental DOM.
export {
  newAttr as attr,
  newElementClose as elementClose,
  newElementOpen as elementOpen,
  newElementOpenEnd as elementOpenEnd,
  newElementOpenStart as elementOpenStart,
  newElementVoid as elementVoid,
  newText as text,
};
<|endoftext|>


<commit_before>var fs = require('fs');

(function (exports) {
    var DC = exports.DC = {};
    var dataSet = null;
    var featureSources = null;
    var classSource = null;

    /**
     * parse the given JSON data and create all features which are specified in the config for all data entries.
     * @param configPath path to the feature configuration file
     * @param json_data_raw the raw JSON data received from the API
     * @return {Array} array which contains all the configured features for the provided data
     */
    DC.createDataSet = function(configPath, json_data_raw) {
        var json_data = removeIncompleteData(json_data_raw);
        var config = fileToJson(configPath);
        featureSources = [];
        classSource = null;

        // extract the configured features and load the required modules
        config.feature_sources.forEach(function (source) {
            var features = [];
            var isClassKeySource = false;
            source.features.forEach(function (feature) {
                if (feature.enabled === true && feature.key !== config.classKey) {
                    features.push(feature);
                }
                else if (feature.key === config.classKey) {
                    isClassKeySource = true;
                }
            });

            if (features.length > 0 || isClassKeySource === true) {
                var module = require(source.path).module;

                if (features.length > 0) {
                    featureSources.push({
                        "module": module,
                        "name": source.name,
                        "features": features
                    });
                }

                if (isClassKeySource === true) {
                    classSource = {
                        "module": module,
                        "name": source.name,
                        "classKey": config.classKey
                    }
                }
            }
        });

        dataSet = [];

        json_data.forEach(function (pokeEntry) {
            var dataRow = {};
            addCoordinatesToPokeEntry(pokeEntry);

            // add features for the configured feature sources
            featureSources.forEach(function (source) {
                source.features.forEach(function (feature) {
                    var value = source.module.getFeature(feature.key, pokeEntry);
                    dataRow[feature.key] = value;
                });
            });

            // add the class label to the data row
            var classLabel = classSource.module.getFeature(classSource.classKey, pokeEntry);
            dataRow[classSource.classKey] = classLabel;

            dataSet.push(dataRow);
        });

        return dataSet;
    };

    /**
     * parse the given JSON data and create an .arff file with all features
     * which are specified in the config for all data entries.
     * @param configPath path to the feature configuration file
     * @param json_data_raw the raw JSON data received from the API
     * @param fileNamePath the path with filename where the .arff file should be stored
    */
    DC.storeArffFile = function(configPath, json_data_raw, fileNamePath) {
        DC.createDataSet(configPath, json_data_raw);
        storeArff(dataSet, featureSources, classSource, fileNamePath);
    };

    /**
     * convert the data set to an .arff file and store it in the arff/ directory with the given filename
     * @param dataSet the data to be stored
     * @param featureSources the configured feature sources from the config
     * @param classSource the source for the class label
     * @param fileNamePath the path with filename where the .arff file should be stored
     */
    function storeArff(dataSet, featureSources, classSource, fileNamePath) {
        var arff = '@RELATION ' + fileNamePath + '\n\n';

        // add attributes for the configured features
        featureSources.forEach(function (source) {
            source.features.forEach(function (feature) {
                if (feature.type === 'nominal') {
                    var nominalValues = allValuesForKeyInData(feature.key, dataSet);
                    arff += '@ATTRIBUTE ' + feature.key + ' {' + nominalValues.join(', ') + '}\n';
                } else {
                    arff += '@ATTRIBUTE ' + feature.key + ' ' + feature.type + '\n';
                }
            });
        });

        // add the class label
        var classLabels = allValuesForKeyInData(classSource.classKey, dataSet);
        arff += '@ATTRIBUTE class {' + classLabels.join(', ') + '}\n\n';

        // add the data
        arff += '@DATA\n';
        dataSet.forEach(function (dataRow) {
            var values = [];
            for (var key in dataRow) {
                values.push(dataRow[key])
            }
            arff += values.join(',') + '\n';
        });

        fs.writeFileSync(fileNamePath, arff, 'utf8');
    }

    /**
     * get all distinct values for the specified key in the provided data array
     * @param key the key for which the values should be returned
     * @param json_data array of data
     * @returns {Array} all distinct values in the data for the specified key
     */
    function allValuesForKeyInData(key, json_data) {
        var valueSet = new Set();

        json_data.forEach(function (row) {
            valueSet.add(row[key]);
        });

        return Array.from(valueSet);
    }

    /**
     * add latitude and longitude to the Pokemon sighting object for easy access
     * @param pokeEntry the JSON object which is received from the API for a Pokemon sighting
     */
    function addCoordinatesToPokeEntry(pokeEntry) {
        pokeEntry.latitude = pokeEntry["location"]["coordinates"][0];
        pokeEntry.longitude = pokeEntry["location"]["coordinates"][1];
    }

    /**
     * remove data entries which do not provided all required features
     * @param json_data data to filter
     * @returns {Array} array which contains only entries which provide all necessary features
     */
    function removeIncompleteData(json_data) {
        var complete_data = [];

        json_data.forEach(function (element) {
            if (element["location"] !== null) {
                complete_data.push(element);
            }
        });

        return complete_data;
    }

    /**
     * convert a file into a JSON object
     * @param file relative file path
     * @returns JSON object of the file
     */
    function fileToJson(file) {
        var data = fs.readFileSync(file, 'utf8');
        return JSON.parse(data);
    }
})('undefined' !== typeof module ? module.exports : window);<commit_msg>added local time during the preconditioning process<commit_after>var fs = require('fs');
var tzwhere = require('tzwhere');

(function (exports) {
    var DC = exports.DC = {};
    var dataSet = null;
    var featureSources = null;
    var classSource = null;

    /**
     * parse the given JSON data and create all features which are specified in the config for all data entries.
     * @param configPath path to the feature configuration file
     * @param json_data_raw the raw JSON data received from the API
     * @return {Array} array which contains all the configured features for the provided data
     */
    DC.createDataSet = function(configPath, json_data_raw) {
        var json_data = removeIncompleteData(json_data_raw);
        var config = fileToJson(configPath);
        featureSources = [];
        classSource = null;

        // extract the configured features and load the required modules
        config.feature_sources.forEach(function (source) {
            var features = [];
            var isClassKeySource = false;
            source.features.forEach(function (feature) {
                if (feature.enabled === true && feature.key !== config.classKey) {
                    features.push(feature);
                }
                else if (feature.key === config.classKey) {
                    isClassKeySource = true;
                }
            });

            if (features.length > 0 || isClassKeySource === true) {
                var module = require(source.path).module;

                if (features.length > 0) {
                    featureSources.push({
                        "module": module,
                        "name": source.name,
                        "features": features
                    });
                }

                if (isClassKeySource === true) {
                    classSource = {
                        "module": module,
                        "name": source.name,
                        "classKey": config.classKey
                    }
                }
            }
        });

        dataSet = [];

        //Initialize the script to convert UTC to local time
        tzwhere.init();

        json_data.forEach(function (pokeEntry) {
            var dataRow = {};
            addCoordinatesToPokeEntry(pokeEntry);
            addLocalTime(pokeEntry);

            // add features for the configured feature sources
            featureSources.forEach(function (source) {
                source.features.forEach(function (feature) {
                    var value = source.module.getFeature(feature.key, pokeEntry);
                    dataRow[feature.key] = value;
                });
            });

            // add the class label to the data row
            var classLabel = classSource.module.getFeature(classSource.classKey, pokeEntry);
            dataRow[classSource.classKey] = classLabel;

            dataSet.push(dataRow);
        });

        return dataSet;
    };

    /**
     * parse the given JSON data and create an .arff file with all features
     * which are specified in the config for all data entries.
     * @param configPath path to the feature configuration file
     * @param json_data_raw the raw JSON data received from the API
     * @param fileNamePath the path with filename where the .arff file should be stored
    */
    DC.storeArffFile = function(configPath, json_data_raw, fileNamePath) {
        DC.createDataSet(configPath, json_data_raw);
        storeArff(dataSet, featureSources, classSource, fileNamePath);
    };

    /**
     * convert the data set to an .arff file and store it in the arff/ directory with the given filename
     * @param dataSet the data to be stored
     * @param featureSources the configured feature sources from the config
     * @param classSource the source for the class label
     * @param fileNamePath the path with filename where the .arff file should be stored
     */
    function storeArff(dataSet, featureSources, classSource, fileNamePath) {
        var arff = '@RELATION ' + fileNamePath + '\n\n';

        // add attributes for the configured features
        featureSources.forEach(function (source) {
            source.features.forEach(function (feature) {
                if (feature.type === 'nominal') {
                    var nominalValues = allValuesForKeyInData(feature.key, dataSet);
                    arff += '@ATTRIBUTE ' + feature.key + ' {' + nominalValues.join(', ') + '}\n';
                } else {
                    arff += '@ATTRIBUTE ' + feature.key + ' ' + feature.type + '\n';
                }
            });
        });

        // add the class label
        var classLabels = allValuesForKeyInData(classSource.classKey, dataSet);
        arff += '@ATTRIBUTE class {' + classLabels.join(', ') + '}\n\n';

        // add the data
        arff += '@DATA\n';
        dataSet.forEach(function (dataRow) {
            var values = [];
            for (var key in dataRow) {
                values.push(dataRow[key])
            }
            arff += values.join(',') + '\n';
        });

        fs.writeFileSync(fileNamePath, arff, 'utf8');
    }

    /**
     * get all distinct values for the specified key in the provided data array
     * @param key the key for which the values should be returned
     * @param json_data array of data
     * @returns {Array} all distinct values in the data for the specified key
     */
    function allValuesForKeyInData(key, json_data) {
        var valueSet = new Set();

        json_data.forEach(function (row) {
            valueSet.add(row[key]);
        });

        return Array.from(valueSet);
    }

    /**
     * add latitude and longitude to the Pokemon sighting object for easy access
     * @param pokeEntry the JSON object which is received from the API for a Pokemon sighting
     */
    function addCoordinatesToPokeEntry(pokeEntry) {
        pokeEntry.latitude = pokeEntry["location"]["coordinates"][0];
        pokeEntry.longitude = pokeEntry["location"]["coordinates"][1];
    }

    /**
     * add Local Time. Offset is based on coordinates
     * @param _pokeEntry the JSON object which is received from the API for a Pokemon sighting
     */
    function addLocalTime(_pokeEntry) {
        //If at some point a error tracks back to here, it might be that the data team changes Lat/Lon.
        //In this case just exchange the ...[1] and ...[0] in the next line
        var offset = tzwhere.tzOffsetAt(_pokeEntry["location"]["coordinates"][1],_pokeEntry["location"]["coordinates"][0]);
        //add the offset (milliseconds) to date
        var newDate = new Date(_pokeEntry.appearedOn);
        newDate = new Date(newDate.getTime() + offset);
        _pokeEntry.appearedLocalTime = newDate.toJSON();
    }

    /**
     * remove data entries which do not provided all required features
     * @param json_data data to filter
     * @returns {Array} array which contains only entries which provide all necessary features
     */
    function removeIncompleteData(json_data) {
        var complete_data = [];

        json_data.forEach(function (element) {
            if (element["location"] !== null) {
                complete_data.push(element);
            }
        });

        return complete_data;
    }

    /**
     * convert a file into a JSON object
     * @param file relative file path
     * @returns JSON object of the file
     */
    function fileToJson(file) {
        var data = fs.readFileSync(file, 'utf8');
        return JSON.parse(data);
    }
})('undefined' !== typeof module ? module.exports : window);<|endoftext|>


<commit_before>var gulp = require('gulp'),
	rename = require('gulp-rename'),
	uglify = require('gulp-uglify');

gulp.task('build', function() {
	return gulp.src('jquery.time-to.js')
		.pipe(uglify({
			preserveComments: 'license'
		}))
		.pipe(rename({
			suffix: '.min'
		}))
		.pipe(gulp.dest('.'))
});
<commit_msg>Delete Gulpfile.js<commit_after><|endoftext|>


<commit_before>(function($){
    $.fn.tagCloud = function(){
        var length = $(this).length;
        for(var i=0; i<length;i++){
            action($(this).get(i));
        }
        return $(this);
        function action(oDiv){
            var radius = 120;
            var dtr = Math.PI/180;
            var d=300;

            var mcList = [];
            var active = true;
            var lasta = 1;
            var lastb = 1;
            var distr = true;
            var tspeed = 2;
            var size = 250;

            var mouseX = 50;
            var mouseY = 25;

            var howElliptical=1;

            var aA=null;

            var i=0;
            var oTag=null;

            aA=oDiv.getElementsByTagName('a');

            for(i=0;i<aA.length;i++)
            {
                oTag={};

                oTag.offsetWidth=aA[i].offsetWidth;
                oTag.offsetHeight=aA[i].offsetHeight;

                mcList.push(oTag);
            }

            sineCosine( 0,0,0 );

            positionAll();

            oDiv.onmouseover=function ()
            {
                active = false;
            };

            oDiv.onmouseout=function ()
            {
                active = true;
            };

            /**
            oDiv.onmousemove=function (ev)
            {
                var oEvent=window.event || ev;

                mouseX=oEvent.clientX-(oDiv.offsetLeft+oDiv.offsetWidth/2);
                mouseY=oEvent.clientY-(oDiv.offsetTop+oDiv.offsetHeight/2);

                mouseX/=5;
                mouseY/=5;
            };**/

            setInterval(update, 30);

            function update()
            {
                var a;
                var b;

                if(active)
                {
                    a = (-Math.min( Math.max( -mouseY, -size ), size ) / radius ) * tspeed;
                    b = (Math.min( Math.max( -mouseX, -size ), size ) / radius ) * tspeed;
                }
                else
                {
                    a = lasta * 0.98;
                    b = lastb * 0.98;
                }

                lasta=a;
                lastb=b;

                if(Math.abs(a)<=0.01 && Math.abs(b)<=0.01)
                {
                    return;
                }

                var c=0;
                sineCosine(a,b,c);
                for(var j=0;j<mcList.length;j++)
                {
                    var rx1=mcList[j].cx;
                    var ry1=mcList[j].cy*ca+mcList[j].cz*(-sa);
                    var rz1=mcList[j].cy*sa+mcList[j].cz*ca;

                    var rx2=rx1*cb+rz1*sb;
                    var ry2=ry1;
                    var rz2=rx1*(-sb)+rz1*cb;

                    var rx3=rx2*cc+ry2*(-sc);
                    var ry3=rx2*sc+ry2*cc;
                    var rz3=rz2;

                    mcList[j].cx=rx3;
                    mcList[j].cy=ry3;
                    mcList[j].cz=rz3;

                    per=d/(d+rz3);

                    mcList[j].x=(howElliptical*rx3*per)-(howElliptical*2);
                    mcList[j].y=ry3*per;
                    mcList[j].scale=per;
                    mcList[j].alpha=per;

                    mcList[j].alpha=(mcList[j].alpha-0.6)*(10/6);
                }

                doPosition();
                depthSort();
            }

            function depthSort()
            {
                var i=0;
                var aTmp=[];

                for(i=0;i<aA.length;i++)
                {
                    aTmp.push(aA[i]);
                }

                aTmp.sort
                (
                    function (vItem1, vItem2)
                    {
                        if(vItem1.cz>vItem2.cz)
                        {
                            return -1;
                        }
                        else if(vItem1.cz<vItem2.cz)
                        {
                            return 1;
                        }
                        else
                        {
                            return 0;
                        }
                    }
                );

                for(i=0;i<aTmp.length;i++)
                {
                    aTmp[i].style.zIndex=i;
                }
            }

            function positionAll()
            {
                var phi=0;
                var theta=0;
                var max=mcList.length;
                var i=0;

                var aTmp=[];
                var oFragment=document.createDocumentFragment();

                //随机排序
                for(i=0;i<aA.length;i++)
                {
                    aTmp.push(aA[i]);
                }

                aTmp.sort
                (
                    function ()
                    {
                        return Math.random()<0.5?1:-1;
                    }
                );

                for(i=0;i<aTmp.length;i++)
                {
                    oFragment.appendChild(aTmp[i]);
                }

                oDiv.appendChild(oFragment);

                for( var i=1; i<max+1; i++){
                    if( distr )
                    {
                        phi = Math.acos(-1+(2*i-1)/max);
                        theta = Math.sqrt(max*Math.PI)*phi;
                    }
                    else
                    {
                        phi = Math.random()*(Math.PI);
                        theta = Math.random()*(2*Math.PI);
                    }
                    //坐标变换
                    mcList[i-1].cx = radius * Math.cos(theta)*Math.sin(phi);
                    mcList[i-1].cy = radius * Math.sin(theta)*Math.sin(phi);
                    mcList[i-1].cz = radius * Math.cos(phi);

                    aA[i-1].style.left=mcList[i-1].cx+oDiv.offsetWidth/2-mcList[i-1].offsetWidth/2+'px';
                    aA[i-1].style.top=mcList[i-1].cy+oDiv.offsetHeight/2-mcList[i-1].offsetHeight/2+'px';
                }
            }

            function doPosition()
            {
                var l=oDiv.offsetWidth/2;
                var t=oDiv.offsetHeight/2;
                for(var i=0;i<mcList.length;i++)
                {
                    aA[i].style.left=mcList[i].cx+l-mcList[i].offsetWidth/2+'px';
                    aA[i].style.top=mcList[i].cy+t-mcList[i].offsetHeight/2+'px';

                    aA[i].style.fontSize=Math.ceil(12*mcList[i].scale/2)+8+'px';

                    aA[i].style.filter="alpha(opacity="+100*mcList[i].alpha+")";
                    aA[i].style.opacity=mcList[i].alpha;
                }
            }

            function sineCosine( a, b, c)
            {
                sa = Math.sin(a * dtr);
                ca = Math.cos(a * dtr);
                sb = Math.sin(b * dtr);
                cb = Math.cos(b * dtr);
                sc = Math.sin(c * dtr);
                cc = Math.cos(c * dtr);
            }
        }
    }
})($);<commit_msg>tag cloud<commit_after>(function($){
    $.fn.tagCloud = function(){

        var $this = $(this);

        for(var i=0; i<$this.length;i++){
            execute($this.get(i));
        }

        return $this;

        function execute(shell){
            //半径
            var radius = 120;
            //是否活动
            var active = true;
            //是否分散
            var scatter = true;
            //旋转速度
            var speed = 2;
            //右旋偏移
            var rightOffset = 50;
            //下旋偏移
            var downOffset = 0;
            //与眼睛的距离
            var distance=300;

            var items = shell.getElementsByTagName('a');
            var itemWraps = [];
            for(var i=0; i<items.length; i++)
            {
                var tag = {};
                var item = items[i];
                tag.item = item;
                tag.offsetWidth = item.offsetWidth;
                tag.offsetHeight = item.offsetHeight;
                itemWraps.push(tag);
            }

            initPosition();

            shell.onmouseover=function ()
            {
                active = false;
            };

            shell.onmouseout=function ()
            {
                active = true;
            };

            setInterval(update, 30);

            function update()
            {
                var a;
                var b;

                if(active)
                {
                    a = (-Math.min( Math.max( -downOffset, -radius ), radius ) / radius ) * speed;
                    b = (Math.min( Math.max( -rightOffset, -radius ), radius ) / radius ) * speed;
                }
                else
                {
                    return;
                }

                with(trigonometric(a,b,0)) {
                    for (var j = 0; j < itemWraps.length; j++) {
                        var rx1 = itemWraps[j].x;
                        var ry1 = itemWraps[j].y * ca + itemWraps[j].z * (-sa);
                        var rz1 = itemWraps[j].y * sa + itemWraps[j].z * ca;

                        var rx2 = rx1 * cb + rz1 * sb;
                        var ry2 = ry1;
                        var rz2 = rx1 * (-sb) + rz1 * cb;

                        var rx3 = rx2 * cc + ry2 * (-sc);
                        var ry3 = rx2 * sc + ry2 * cc;
                        var rz3 = rz2;

                        itemWraps[j].x = rx3;
                        itemWraps[j].y = ry3;
                        itemWraps[j].z = rz3;

                        var per = distance / (distance + rz3);

                        itemWraps[j].scale = per;
                        itemWraps[j].alpha = per;

                        itemWraps[j].alpha = (itemWraps[j].alpha - 0.6) * (10 / 6);
                    }
                }
                doPosition();
            }

            function doPosition()
            {
                var l=shell.offsetWidth/2;
                var t=shell.offsetHeight/2;
                for(var i=0;i<itemWraps.length;i++)
                {
                    items[i].style.left=itemWraps[i].x+l-itemWraps[i].offsetWidth/2+'px';
                    items[i].style.top=itemWraps[i].y+t-itemWraps[i].offsetHeight/2+'px';

                    items[i].style.fontSize=Math.ceil(12*itemWraps[i].scale/2)+8+'px';

                    items[i].style.filter="alpha(opacity="+100*itemWraps[i].alpha+")";
                    items[i].style.opacity=itemWraps[i].alpha;
                }
                ensureDepth();
            }

            function ensureDepth()
            {
                var tmp=[];

                for(var i=0;i<items.length;i++)
                {
                    tmp.push(items[i]);
                }

                tmp.sort
                (
                    function (vItem1, vItem2)
                    {
                        if(vItem1.z>vItem2.z)
                        {
                            return -1;
                        }
                        else if(vItem1.z<vItem2.z)
                        {
                            return 1;
                        }
                        else
                        {
                            return 0;
                        }
                    }
                );

                for(var i=0;i<tmp.length;i++)
                {
                    tmp[i].style.zIndex=i;
                }
            }

            function trigonometric( a, b, c)
            {
                var dtr = Math.PI/radius;
                var rst = {};
                with(rst) {
                    sa = Math.sin(a * dtr);
                    ca = Math.cos(a * dtr);
                    sb = Math.sin(b * dtr);
                    cb = Math.cos(b * dtr);
                    sc = Math.sin(c * dtr);
                    cc = Math.cos(c * dtr);
                }
                return rst;
            }

            function initPosition()
            {
                var phi=0;
                var theta=0;
                var max=itemWraps.length;
                var tmp=[];
                var oFragment=document.createDocumentFragment();

                for(var i=0; i<items.length; i++)
                {
                    tmp.push(items[i]);
                }

                //随机排序
                tmp.sort
                (
                    function ()
                    {
                        return Math.random()<0.5?1:-1;
                    }
                );

                for(var i=0;i<tmp.length;i++)
                {
                    oFragment.appendChild(tmp[i]);
                }

                shell.appendChild(oFragment);

                for( var i=1; i<max+1; i++){
                    if( scatter )
                    {
                        phi = Math.acos(-1+(2*i-1)/max);
                        theta = Math.sqrt(max*Math.PI)*phi;
                    }
                    else
                    {
                        phi = Math.random()*(Math.PI);
                        theta = Math.random()*(2*Math.PI);
                    }
                    //坐标变换
                    itemWraps[i-1].x = radius * Math.cos(theta)*Math.sin(phi);
                    itemWraps[i-1].y = radius * Math.sin(theta)*Math.sin(phi);
                    itemWraps[i-1].z = radius * Math.cos(phi);

                    items[i-1].style.left=itemWraps[i-1].x+shell.offsetWidth/2-itemWraps[i-1].offsetWidth/2+'px';
                    items[i-1].style.top=itemWraps[i-1].y+shell.offsetHeight/2-itemWraps[i-1].offsetHeight/2+'px';
                }
            }

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


<commit_before>// taxbrain.js

//
//  Input page
//

// sidebar - scrollspy
$('body').scrollspy({
  target: '.inputs-sidebar'
});

// Scroll to taxbrain form input areas, instead of jumping
$('.inputs-form a, .btn-explore').click(function(event) {
  if ($(this).attr('id') === 'current-year-link') {
    return;
  }
  // ensure link is indeed a hash link on current page <<-- Zach's stuff
  if (this.host == location.host && this.pathname == location.pathname) {
    event.preventDefault();
    var target = this.hash;
    if (this.href != '#') {
      $('body, html').animate({
        scrollTop: $(target).offset().top
      }, 700);
    }
  }

});

// CPI buttons
$('.form-group > label.btn-cpi').click(function (e) {
  label = $(this)
  field = label.attr('for')
  prev_value = label.hasClass('active')

  // add button-toggle functionality without hijacking click
  prev_value ? label.removeClass('active') : label.addClass('active');
  value = !prev_value

  // find the default value
  select = label.siblings('.form-control');
  placeholder = select.attr('placeholder')
  placeholder_true = placeholder == 'True'
  placeholder_false = placeholder == 'False'
  has_boolean_default = placeholder_true || placeholder_false

  // default-based logic
  if (has_boolean_default) {
    default_value = placeholder_true;
    is_default = (value == default_value);
    console.log(
    'setting CPI field ' + field  +
    ' from value ' + prev_value +
    ' to value ' + value +
    ' with default ' + default_value +
    ' is default ' + is_default )

    // only allow values to be entered if not equal to default
    // if new values entered, show info about default value
    group = label.closest('.form-group')
    if (!is_default) {
      if (value) {select.val('2');} else {select.val('3');}
      group.addClass('edited');

    } else {
      select.val('1'); // set to "Unknown"
      group.removeClass('edited');
    }

  } else {
    // we don't recognize the placeholder value
    console.log('setting CPI field with no default ' + field  +
      ' to value ' + value )

    // pass changes directly to the select
    if (value) {select.val('2');} else {select.val('3');}
  }
});

// Form Inputs
// only allow values to be entered if not equal to default
// if new values entered, show info about default value
$('.form-group > input.form-control').blur(function() {

  input = $(this)
  value = input.val()
  value_default = input.prop('placeholder');
  value_changed = (value != '') && (value != value_default);
  group = input.closest('.form-group')

  //console.log('bluring ' + input  + ' with value ' + value + ' with default ' +
  //  value_default + ' changed ' + value_changed )

  if (value_changed) {
    group.addClass('edited');
  } else {
    input.val(''); // show placeholder instead of value entered that = default
    group.removeClass('edited');
  }
});

//
//  Results page
//

// Select behavior for dropdowns
$(".dropdown-select .dropdown-menu li a").click(function(){
  selection = $(this);
  dropdown_label = $(this).parents('.dropdown-select').find('.dropdown-toggle')
  dropdown_label.html(selection.text() + ' ');
});

// Switch behavior for dropdowns
$(".data-switch .dropdown-menu li a").click(function(){
  switch_key = $(this).text().trim();

  /*
  //Without fade-out-in
  $( ".data-switchable" ).each(function(){
    $this = $(this)
    switch_value = $this.data("switch-options")[switch_key]
    $this.text(switch_value)
  })
  */

  // With fade-out-in
  $('.data-switchable').fadeOut('fast',function(){
    $this = $(this)
    switch_value = $this.data("switch-options")[switch_key]
    $this.text(switch_value).fadeIn('fast');
  });

});

$('input[name="growth_choice"]').click(function () {
  if ($(this).val() === 'factor_target') {
    $('#id_factor_adjustment').val('');
  } else {
    $('#id_factor_target').val('');
  }
});

$('#id_factor_adjustment, #id_factor_target').focus(function() {
  if ($(this).attr('id') === 'id_factor_target') {
    $('input[type="radio"][value="factor_target"]').click();
  } else {
    $('input[type="radio"][value="factor_adjustment"]').click();
  }
});

var currentYear = $('#start-year-select').val();
$('#start-year-select').change(function(e) {
  $('#current-year-link').attr('href', '/taxbrain/?start_year=' + $(this).val());
  $('#current-year-modal').modal('show');
});

$('#current-year-modal').on('hide.bs.modal', function (e) {
  $('#start-year-select option').removeAttr("selected");
  $('#start-year-select option[value="' + currentYear + '"]').attr("selected", "selected");
});
<commit_msg>Remove edited class when reset button is pressed.<commit_after>// taxbrain.js

//
//  Input page
//

// sidebar - scrollspy
$('body').scrollspy({
  target: '.inputs-sidebar'
});

// Scroll to taxbrain form input areas, instead of jumping
$('.inputs-form a, .btn-explore').click(function(event) {
  if ($(this).attr('id') === 'current-year-link') {
    return;
  }
  // ensure link is indeed a hash link on current page <<-- Zach's stuff
  if (this.host == location.host && this.pathname == location.pathname) {
    event.preventDefault();
    var target = this.hash;
    if (this.href != '#') {
      $('body, html').animate({
        scrollTop: $(target).offset().top
      }, 700);
    }
  }

});

// CPI buttons
$('.form-group > label.btn-cpi').click(function (e) {
  label = $(this)
  field = label.attr('for')
  prev_value = label.hasClass('active')

  // add button-toggle functionality without hijacking click
  prev_value ? label.removeClass('active') : label.addClass('active');
  value = !prev_value

  // find the default value
  select = label.siblings('.form-control');
  placeholder = select.attr('placeholder')
  placeholder_true = placeholder == 'True'
  placeholder_false = placeholder == 'False'
  has_boolean_default = placeholder_true || placeholder_false

  // default-based logic
  if (has_boolean_default) {
    default_value = placeholder_true;
    is_default = (value == default_value);
    console.log(
    'setting CPI field ' + field  +
    ' from value ' + prev_value +
    ' to value ' + value +
    ' with default ' + default_value +
    ' is default ' + is_default )

    // only allow values to be entered if not equal to default
    // if new values entered, show info about default value
    group = label.closest('.form-group')
    if (!is_default) {
      if (value) {select.val('2');} else {select.val('3');}
      group.addClass('edited');

    } else {
      select.val('1'); // set to "Unknown"
      group.removeClass('edited');
    }

  } else {
    // we don't recognize the placeholder value
    console.log('setting CPI field with no default ' + field  +
      ' to value ' + value )

    // pass changes directly to the select
    if (value) {select.val('2');} else {select.val('3');}
  }
});

// Form Inputs
// only allow values to be entered if not equal to default
// if new values entered, show info about default value
$('.form-group > input.form-control').blur(function() {

  input = $(this)
  value = input.val()
  value_default = input.prop('placeholder');
  value_changed = (value != '') && (value != value_default);
  group = input.closest('.form-group')

  //console.log('bluring ' + input  + ' with value ' + value + ' with default ' +
  //  value_default + ' changed ' + value_changed )

  if (value_changed) {
    group.addClass('edited');
  } else {
    input.val(''); // show placeholder instead of value entered that = default
    group.removeClass('edited');
  }
});

$("#tax-submit[type='reset']").click(function(){
  $('.form-group.edited').each(function(){
    $(this).removeClass("edited")
  })
});

//
//  Results page
//

// Select behavior for dropdowns
$(".dropdown-select .dropdown-menu li a").click(function(){
  selection = $(this);
  dropdown_label = $(this).parents('.dropdown-select').find('.dropdown-toggle')
  dropdown_label.html(selection.text() + ' ');
});

// Switch behavior for dropdowns
$(".data-switch .dropdown-menu li a").click(function(){
  switch_key = $(this).text().trim();

  /*
  //Without fade-out-in
  $( ".data-switchable" ).each(function(){
    $this = $(this)
    switch_value = $this.data("switch-options")[switch_key]
    $this.text(switch_value)
  })
  */

  // With fade-out-in
  $('.data-switchable').fadeOut('fast',function(){
    $this = $(this)
    switch_value = $this.data("switch-options")[switch_key]
    $this.text(switch_value).fadeIn('fast');
  });

});

$('input[name="growth_choice"]').click(function () {
  if ($(this).val() === 'factor_target') {
    $('#id_factor_adjustment').val('');
  } else {
    $('#id_factor_target').val('');
  }
});

$('#id_factor_adjustment, #id_factor_target').focus(function() {
  if ($(this).attr('id') === 'id_factor_target') {
    $('input[type="radio"][value="factor_target"]').click();
  } else {
    $('input[type="radio"][value="factor_adjustment"]').click();
  }
});

var currentYear = $('#start-year-select').val();
$('#start-year-select').change(function(e) {
  $('#current-year-link').attr('href', '/taxbrain/?start_year=' + $(this).val());
  $('#current-year-modal').modal('show');
});

$('#current-year-modal').on('hide.bs.modal', function (e) {
  $('#start-year-select option').removeAttr("selected");
  $('#start-year-select option[value="' + currentYear + '"]').attr("selected", "selected");
});
<|endoftext|>


<commit_before>

var W={
    datesMarked:[],
    datepickerFormatLayout : "dd M yy"
}

$(document).ready(function(){

    W.Events=[]

    $("#datepicker").datepicker({
	dayNames: [ "Domingo", "Lunes", "Martes", "Miércoles", "Jueves", "Viernes", "Sábado" ],
	dayNamesMin: [ "Do", "Lu", "Ma", "Mi", "Ju", "Vi", "Sa" ],
	dayNamesShort: [ "Dom", "Lun", "Mar", "Mier", "Jue", "Vie", "Sab" ],
	monthNames: [ "Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio",
		      "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre" ],
	firstDay: 1,
	dateFormat:W.datepickerFormatLayout,
	beforeShowDay: markDates,
	onSelect: getEventsForADate
    })

    getMarkDates()
    var today=$.datepicker.formatDate(W.datepickerFormatLayout, new Date())
    getEventsForADate(today)
})

function getMarkDates(){

    $.ajax({
    	url:"/notes/dates",
    	type: 'get',
	dataType: 'json',
    	success: function (dates){
	    for (var i=0;i<dates.length;i++){
		W.datesMarked.push(new Date(dates[i]))
	    }

	    console.log(W.datesMarked)
	    $("#datepicker").datepicker( "refresh" )
	},
    	error: function(error){
	    console.log(error)
	}
    })
}

function getEventsForADate(sdate){

    $.ajax({
    	url:"/notes/events",
    	type: 'post',
	data: sdate,
    	success: function (html){
	    loadHTML(html)
	},
    	error: function(error){
	    console.log(error)
	}
    })
}

function markDates(date) {
    var dates=W.datesMarked
    for (var i = 0; i < dates.length; i++) {
	if (dates[i].getTime() == date.getTime()){
	    return [true, "dp-marked-date"];
        }
    }
    return [true, ""];
}

function loadHTML(html){
    $("#content").html(html)

    //default events
    $("#content a").on("click",function(e){
	e.preventDefault()
    })
}
<commit_msg>add get and event caller in js<commit_after>

var W={
    datesMarked:[],
    datepickerFormatLayout : "dd M yy",
    currentDate:new Date()
}

$(document).ready(function(){

    W.Events=[]

    $("#datepicker").datepicker({
	dayNames: [ "Domingo", "Lunes", "Martes", "Miércoles", "Jueves", "Viernes", "Sábado" ],
	dayNamesMin: [ "Do", "Lu", "Ma", "Mi", "Ju", "Vi", "Sa" ],
	dayNamesShort: [ "Dom", "Lun", "Mar", "Mier", "Jue", "Vie", "Sab" ],
	monthNames: [ "Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio",
		      "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre" ],
	firstDay: 1,
	dateFormat:W.datepickerFormatLayout,
	beforeShowDay: markDates,
	onSelect: getEventsForADate
    })

    getMarkDates()
    var today=$.datepicker.formatDate(W.datepickerFormatLayout, new Date())
    getEventsForADate(today)
})

function getMarkDates(){

    $.ajax({
    	url:"/notes/dates",
    	type: 'get',
	dataType: 'json',
    	success: function (dates){
	    for (var i=0;i<dates.length;i++){
		W.datesMarked.push(new Date(dates[i]))
	    }

	    console.log(W.datesMarked)
	    $("#datepicker").datepicker( "refresh" )
	},
    	error: function(error){
	    console.log(error)
	}
    })
}

function getEventsForADate(sdate){

    $.ajax({
    	url:"/notes/events",
    	type: 'post',
	data: sdate,
    	success: function (html){
	    loadHTML(html)
	    W.currentDate=sdate
	},
    	error: function(error){
	    console.log(error)
	}
    })
}

function markDates(date) {
    var dates=W.datesMarked
    for (var i = 0; i < dates.length; i++) {
	if (dates[i].getTime() == date.getTime()){
	    return [true, "dp-marked-date"];
        }
    }
    return [true, ""];
}

function loadHTML(html){
    $("#content").html(html)

    //default events
    $("#event .close-event").on("click",function(e){
	e.preventDefault()
	getEventsForADate(W.currentDate)
    })

    $("#day-events a").on("click",function(e){
	e.preventDefault()

	var href=$(this).attr("href")
	console.log(href)

	$.ajax({
    	    url:href,
    	    type: 'post',
	    data: "date="+W.currentDate,
    	    success: function (html){
		loadHTML(html)
	    },
    	    error: function(error){
		console.log(error)
	    }
	})
    })
}
<|endoftext|>


<commit_before>/*
//	require script loader
//	require jQuery
//	@uri - script location
//	@callback - callback function for Asynchronous load script
*/
var require = (function(){
	"use strict";
	var $r_cache = {};

	return function (uri, callback) {
		var isAsync = (typeof callback === "function") ? true : false,
			cache = $r_cache,
			request,
			wrapScript,
			ret;

		if (typeof uri !== "string") {
			throw  "script url is undefined";
		}

		if (cache[uri]) {
			if (isAsync) {
				return callback(cache[uri]);
			}
			return cache[uri];
		}

		wrapScript = function(responseText, dataType) {
			var closureFn,
				closureFnText,
				source;
			console.log("dataType: ", dataType);
			if (responseText) {
				closureFnText = '"use strict";\n var module = {}, exports = {}; \n';
				closureFnText += responseText;
				closureFnText += '\n if(!module.exports) module.exports = exports; \n module.id = "'+ uri +'";';
				closureFnText += '\n return module.exports; \n //# sourceURL='+ uri;
				closureFn = new Function(closureFnText);
				cache[uri] = source = closureFn(); // Make the closureFn
				return source;
			} else {
				return null;
			}
		};

		request = $.ajax({
			url: uri,
			type: 'GET',
			dataType: "script",
			async: isAsync,
			cache:true,
			crossDomain: false,
			dataFilter: wrapScript,
			success: function(closureFn) {
				if(!typeof closureFn === "function") {
					return;
				}
				if (isAsync) {
					callback(closureFn);
				} else {
					ret = closureFn;
				}
			},
			error: function(jqXHR, textStatus, errorThrown) {
				throw errorThrown;
			}
		});
		return (isAsync) ? request : ret;
	};
}());<commit_msg>added support isomporphic<commit_after>/*
//	require script loader
//	require jQuery
//	@uri - script location
//	@callback - callback function for Asynchronous load script
*/
(function(env) {
	"use strict";
	var isNode = (env !== window && typeof module !== "undefined" && module.exports),
		supportAmd = (typeof env.define === "function" && env.define.amd),
		require,
		cache = {};

	if (isNode) {
		return;
	}

	require = function (uri, callback) {
		var isAsync = (typeof callback === "function") ? true : false,
			request,
			wrapScript,
			ret;

		if (typeof uri !== "string") {
			throw  "script url is undefined";
		}

		if (cache[uri]) {
			if (isAsync) {
				return callback(cache[uri]);
			}
			return cache[uri];
		}

		wrapScript = function(responseText, dataType) {
			var closureFn,
				closureFnText,
				source;
			//console.log("dataType: ", dataType);
			if (responseText) {
				closureFnText = '"use strict";\n var module = {}, exports = {}; \n';
				closureFnText += responseText;
				closureFnText += '\n if(!module.exports) module.exports = exports; \n module.id = "'+ uri +'";';
				closureFnText += '\n return module.exports; \n //# sourceURL='+ uri;
				closureFn = new Function(closureFnText);
				cache[uri] = source = closureFn(); // Make the closureFn
				return source;
			}
			return null;
		};

		request = $.ajax({
			url: uri,
			type: 'GET',
			dataType: "script",
			async: isAsync,
			cache:true,
			crossDomain: false,
			dataFilter: wrapScript,
			success: function(closureFn) {
				if(!closureFn) {
					return;
				}
				if (isAsync) {
					callback(closureFn);
				} else {
					ret = closureFn;
				}
			},
			error: function(jqXHR, textStatus, errorThrown) {
				throw errorThrown;
			}
		});

		return (isAsync) ? request : ret;
	};

	env.require = require;

}(this));<|endoftext|>