<commit_before>// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0.
// See the LICENCE file in the repository root for full licence text.

'use strict';

const fs = require('fs');
const path = require('path');
const webpack = require('webpack');

const Autoprefixer = require('autoprefixer');
const CopyPlugin = require('copy-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const SentryPlugin = require('webpack-sentry-plugin');
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const ManifestPlugin = require('webpack-manifest-plugin');

const inProduction = process.env.NODE_ENV === 'production' || process.argv.includes('-p');

let min = '';
let reactMin = 'development';
if (inProduction) {
  min = '.min';
  reactMin = 'production.min';
}

const paymentSandbox = !(process.env.PAYMENT_SANDBOX === '0'
                         || process.env.PAYMENT_SANDBOX === 'false'
                         || !process.env.PAYMENT_SANDBOX);

// relative from root?
const nodeRoot = path.resolve(__dirname, 'node_modules');

const vendor = [
  'clipboard-polyfill/build/clipboard-polyfill.js',
  `url-polyfill/url-polyfill${min}.js`,
  'turbolinks/dist/turbolinks.js',
  `jquery/dist/jquery${min}.js`,
  'jquery-ujs/src/rails.js',
  `qtip2/dist/jquery.qtip${min}.js`,
  'jquery.scrollto/jquery.scrollTo.js',
  'jquery-ui/ui/data.js',
  'jquery-ui/ui/scroll-parent.js',
  'jquery-ui/ui/widget.js',
  'jquery-ui/ui/widgets/mouse.js',
  'jquery-ui/ui/widgets/slider.js',
  'jquery-ui/ui/widgets/sortable.js',
  'jquery-ui/ui/keycode.js',
  'timeago/jquery.timeago.js',
  'blueimp-file-upload/js/jquery.fileupload.js',
  'bootstrap/dist/js/bootstrap.js',
  'lodash/lodash.js',
  'layzr.js/dist/layzr.js',
  `react/umd/react.${reactMin}.js`,
  'react-dom-factories/index.js',
  `react-dom/umd/react-dom.${reactMin}.js`,
  `prop-types/prop-types${min}.js`,
  'photoswipe/dist/photoswipe.js',
  'photoswipe/dist/photoswipe-ui-default.js',
  `d3/dist/d3${min}.js`,
  'moment/moment.js',
  'js-cookie/src/js.cookie.js',
  `imagesloaded/imagesloaded.pkgd${min}.js`,
].map((name) => path.join(nodeRoot, name));

vendor.forEach(function(script) {
  if (!fs.existsSync(script)) {
    throw new Error(`${script} doesn't exist`);
  }
});

if (!fs.readdirSync('resources/assets/build/locales').some((file) => file.endsWith('.js'))) {
  throw new Error('missing locale files.');
}

// TODO: move to own file
class ConcatPlugin {
  constructor(options = {}) {
    this.patterns = options.patterns;
    this.options = options.options || {};
  }

  apply(compiler) {
    const { RawSource } = require('webpack-sources');
    const concatenate = require('concatenate');

    const plugin = { name: 'ConcatPlugin' };

    compiler.hooks.thisCompilation.tap(plugin, (compilation) => {
      const logger = compilation.getLogger('concat-webpack-plugin');
      compilation.hooks.additionalAssets.tapAsync('concat-webpack-plugin', async (callback) => {
        const assets = this.patterns.map((pattern) => {
          const webpackTo = path.resolve(compiler.options.output.path, pattern.output);

          return {
            source: new RawSource(concatenate.sync(pattern.input)),
            targetPath: pattern.output,
            webpackTo: webpackTo,
          };
        });

        assets.forEach((asset) => {
          const {
            targetPath,
            source,
            webpackTo,
          } = asset;

          logger.log(`writing '${webpackTo}'`);
          compilation.emitAsset(targetPath, source);
        });

        callback();
      });
    });
  }
}

const entry = {
  '/js/app': [
    './resources/assets/app.js',
    './resources/assets/less/app.less',
  ],
};

const coffeeReactComponents = [
  'artist-page',
  'beatmap-discussions',
  'beatmap-discussions-history',
  'beatmapset-page',
  'changelog-build',
  'changelog-index',
  'comments-index',
  'comments-show',
  'mp-history',
  'modding-profile',
  'profile-page',
  'admin/contest',
  'contest-entry',
  'contest-voting',
];

const tsReactComponents = [
  'account-edit',
  'beatmaps',
  'chat',
  'friends-index',
  'groups-show',
  'news-index',
  'news-show',
  'notifications-index',
  'scores-show',
  'store-bootstrap',
];

for (const name of coffeeReactComponents) {
  entry[`/js/react/${name}`] = [path.resolve(__dirname, `resources/assets/coffee/react/${name}.coffee`)];
}

for (const name of tsReactComponents) {
  entry[`/js/react/${name}`] = [path.resolve(__dirname, `resources/assets/lib/${name}.ts`)];
}

const webpackConfig = {
  mode: inProduction ? 'production' : 'development',
  devtool: 'source-map',
  entry: entry,
  output: {
    chunkFilename: '[name].[chunkhash:8].js',
    filename: '[name].[contenthash:8].js',
    path: path.resolve(__dirname, 'public'),
  },
  externals: {
    'd3': 'd3',
    'lodash': '_',
    'moment': 'moment',
    'prop-types': 'PropTypes',
    'react': 'React',
    'react-dom': 'ReactDOM',
    'react-dom-factories': 'ReactDOMFactories',
  },
  module: {
    rules: [
      {
        enforce: 'pre',
        exclude: /(node_modules)/,
        loader: 'import-glob-loader',
        test: /\.(js|coffee)$/,
      },
      {
        test: /\.jsx?$/,
        exclude: /(node_modules|bower_components)/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              presets: [
                [
                  '@babel/preset-env',
                  {
                    modules: false,
                    forceAllTransforms: true,
                  },
                ],
              ],
              plugins: [
                '@babel/plugin-syntax-dynamic-import',
                '@babel/plugin-proposal-object-rest-spread',
                [
                  '@babel/plugin-transform-runtime',
                  {
                    helpers: false,
                  },
                ],
              ],
              cacheDirectory: true,
            },
          },
        ],
      },
      {
        test: /\.tsx?$/,
        loader: 'ts-loader',
        exclude: /node_modules/,
      },
      {
        // loader for preexisting global coffeescript
        exclude: [
          path.resolve(__dirname, 'resources/assets/coffee/react'),
        ],
        include: [
          path.resolve(__dirname, 'resources/assets/coffee'),
        ],
        test: /\.coffee$/,
        use: ['imports-loader?this=>window', 'coffee-loader'],
      },
      {
        // loader for import-based coffeescript
        include: [
          path.resolve(__dirname, 'resources/assets/coffee/react'),
          path.resolve(__dirname, 'resources/assets/lib'),
        ],
        test: /\.coffee$/,
        use: ['coffee-loader'],
      },
      {
        test: /\.less$/,
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: 'css-loader',
            options: {
              url: (url) => !url.startsWith('/'),
              sourceMap: true,
              importLoaders: 1,
            },
          },
          {
            loader: 'postcss-loader',
            options: {
              sourceMap: true,
              plugins: [Autoprefixer],
            },
          },
          { loader: 'less-loader', options: { sourceMap: true } },
        ],
      },
      {
        test: /(\.(png|jpe?g|gif|webp)$|^((?!font).)*\.svg$)/,
        loaders: [
          {
            loader: 'file-loader',
            options: {
              name: (path) => {
                if (!/node_modules|bower_components/.test(path)) {
                  return '/images/[name].[ext]?[hash]';
                }

                const cleanPath = path
                  .replace(/\\/g, '/')
                  .replace(/((.*(node_modules|bower_components))|images|image|img|assets)\//g, '');

                return `/images/vendor/${cleanPath}?[hash]`;
              },
            },
          },
          {
            loader: 'img-loader',
          }
        ],
      },
      {
        test: /(\.(woff2?|ttf|eot|otf)$|font.*\.svg$)/,
        loader: 'file-loader',
        options: {
          name: (path) => {
            if (!/node_modules|bower_components/.test(path)) {
              return '/fonts/[name].[ext]?[hash]';
            }

            const cleanPath = path
              .replace(/\\/g, '/')
              .replace(/((.*(node_modules|bower_components))|fonts|font|assets)\//g, '');

            return `/fonts/vendor/${cleanPath}?[hash]`;
          },
        },
      },
    ],
  },
  optimization: {
    runtimeChunk: {
      name: '/js/commons',
    },
    splitChunks: {
      cacheGroups: {
        commons: {
          chunks: 'initial',
          minChunks: 2,
          name: '/js/commons',
        },
      },
    },
  },
  plugins: [
    new webpack.DefinePlugin({
      'process.env.DOCS_URL': JSON.stringify(process.env.DOCS_URL || 'https://docs.ppy.sh'),
      'process.env.PAYMENT_SANDBOX': JSON.stringify(paymentSandbox),
      'process.env.SHOPIFY_DOMAIN': JSON.stringify(process.env.SHOPIFY_DOMAIN),
      'process.env.SHOPIFY_STOREFRONT_TOKEN': JSON.stringify(process.env.SHOPIFY_STOREFRONT_TOKEN),
    }),
    new MiniCssExtractPlugin({
      filename: '/css/app.[hash:8].css',
      chunkFilename: '/css/app.[hash:8].css',
    }),
    new ConcatPlugin({
      patterns: [
        {
          input: [
            'resources/assets/js/ga.js',
            'resources/assets/build/lang.js',
            'resources/assets/js/bootstrap-lang.js',
          ],
          output: '/js/app-deps.js',
        },
        {
          input: vendor, output: '/js/vendor.js',
        },
      ],
    }),
    new CopyPlugin({
      patterns: [
        { from: 'resources/assets/build/locales', to: 'js/locales' },
        { from: 'node_modules/@fortawesome/fontawesome-free/webfonts', to: 'vendor/fonts/font-awesome' },
        { from: 'node_modules/photoswipe/dist/default-skin', to: 'vendor/_photoswipe-default-skin' },
        { from: 'node_modules/moment/locale', to: 'vendor/js/moment-locales' },
      ],
    }),
    new ManifestPlugin({
      fileName: 'mix-manifest.json',
    }),
  ],
  resolve: {
    alias: {
      'ziggy': path.resolve(__dirname, 'resources/assets/js/ziggy.js'),
      'ziggy-route': path.resolve(__dirname, 'vendor/tightenco/ziggy/dist/js/route.js'),
    },
    extensions: ['*', '.js', '.coffee', '.ts', '.tsx'],
    modules: [
      path.resolve(__dirname, 'resources/assets/coffee'),
      path.resolve(__dirname, 'resources/assets/lib'),
      path.resolve(__dirname, 'resources/assets/coffee/react/_components'),
      path.resolve(__dirname, 'node_modules'),
    ],
    plugins: [new TsconfigPathsPlugin()],
  },
};

if (inProduction) {
  webpackConfig.optimization.minimizer = [
    new TerserPlugin({
      sourceMap: true,
      terserOptions: {
        safari10: true,
      },
    }),
  ];
}

if (process.env.SENTRY_RELEASE === '1') {
  webpackConfig.plugins.push(
    new SentryPlugin({
      apiKey: process.env.SENTRY_API_KEY,
      organisation: process.env.SENTRY_ORG,
      project: process.env.SENTRY_PROJ,

      deleteAfterCompile: true,
      exclude: /\.css(\.map)?$/,
      filenameTransform: function(filename) {
        return '~' + filename;
      },
      release: function() {
        return process.env.GIT_SHA;
      },
    }),
  );
}

module.exports = webpackConfig;
<commit_msg>lint file<commit_after>// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0.
// See the LICENCE file in the repository root for full licence text.

'use strict';

const fs = require('fs');
const path = require('path');
const webpack = require('webpack');

const Autoprefixer = require('autoprefixer');
const CopyPlugin = require('copy-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const SentryPlugin = require('webpack-sentry-plugin');
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const ManifestPlugin = require('webpack-manifest-plugin');

const inProduction = process.env.NODE_ENV === 'production' || process.argv.includes('-p');

let min = '';
let reactMin = 'development';
if (inProduction) {
  min = '.min';
  reactMin = 'production.min';
}

const paymentSandbox = !(process.env.PAYMENT_SANDBOX === '0'
                         || process.env.PAYMENT_SANDBOX === 'false'
                         || !process.env.PAYMENT_SANDBOX);

// relative from root?
const nodeRoot = path.resolve(__dirname, 'node_modules');

const vendor = [
  'clipboard-polyfill/build/clipboard-polyfill.js',
  `url-polyfill/url-polyfill${min}.js`,
  'turbolinks/dist/turbolinks.js',
  `jquery/dist/jquery${min}.js`,
  'jquery-ujs/src/rails.js',
  `qtip2/dist/jquery.qtip${min}.js`,
  'jquery.scrollto/jquery.scrollTo.js',
  'jquery-ui/ui/data.js',
  'jquery-ui/ui/scroll-parent.js',
  'jquery-ui/ui/widget.js',
  'jquery-ui/ui/widgets/mouse.js',
  'jquery-ui/ui/widgets/slider.js',
  'jquery-ui/ui/widgets/sortable.js',
  'jquery-ui/ui/keycode.js',
  'timeago/jquery.timeago.js',
  'blueimp-file-upload/js/jquery.fileupload.js',
  'bootstrap/dist/js/bootstrap.js',
  'lodash/lodash.js',
  'layzr.js/dist/layzr.js',
  `react/umd/react.${reactMin}.js`,
  'react-dom-factories/index.js',
  `react-dom/umd/react-dom.${reactMin}.js`,
  `prop-types/prop-types${min}.js`,
  'photoswipe/dist/photoswipe.js',
  'photoswipe/dist/photoswipe-ui-default.js',
  `d3/dist/d3${min}.js`,
  'moment/moment.js',
  'js-cookie/src/js.cookie.js',
  `imagesloaded/imagesloaded.pkgd${min}.js`,
].map((name) => path.join(nodeRoot, name));

vendor.forEach(function(script) {
  if (!fs.existsSync(script)) {
    throw new Error(`${script} doesn't exist`);
  }
});

if (!fs.readdirSync('resources/assets/build/locales').some((file) => file.endsWith('.js'))) {
  throw new Error('missing locale files.');
}

// TODO: move to own file
class ConcatPlugin {
  constructor(options = {}) {
    this.patterns = options.patterns;
    this.options = options.options || {};
  }

  apply(compiler) {
    const { RawSource } = require('webpack-sources');
    const concatenate = require('concatenate');

    const plugin = { name: 'ConcatPlugin' };

    compiler.hooks.thisCompilation.tap(plugin, (compilation) => {
      const logger = compilation.getLogger('concat-webpack-plugin');
      compilation.hooks.additionalAssets.tapAsync('concat-webpack-plugin', async (callback) => {
        const assets = this.patterns.map((pattern) => {
          const webpackTo = path.resolve(compiler.options.output.path, pattern.output);

          return {
            source: new RawSource(concatenate.sync(pattern.input)),
            targetPath: pattern.output,
            webpackTo: webpackTo,
          };
        });

        assets.forEach((asset) => {
          const {
            targetPath,
            source,
            webpackTo,
          } = asset;

          logger.log(`writing '${webpackTo}'`);
          compilation.emitAsset(targetPath, source);
        });

        callback();
      });
    });
  }
}

const entry = {
  '/js/app': [
    './resources/assets/app.js',
    './resources/assets/less/app.less',
  ],
};

const coffeeReactComponents = [
  'artist-page',
  'beatmap-discussions',
  'beatmap-discussions-history',
  'beatmapset-page',
  'changelog-build',
  'changelog-index',
  'comments-index',
  'comments-show',
  'mp-history',
  'modding-profile',
  'profile-page',
  'admin/contest',
  'contest-entry',
  'contest-voting',
];

const tsReactComponents = [
  'account-edit',
  'beatmaps',
  'chat',
  'friends-index',
  'groups-show',
  'news-index',
  'news-show',
  'notifications-index',
  'scores-show',
  'store-bootstrap',
];

for (const name of coffeeReactComponents) {
  entry[`/js/react/${name}`] = [path.resolve(__dirname, `resources/assets/coffee/react/${name}.coffee`)];
}

for (const name of tsReactComponents) {
  entry[`/js/react/${name}`] = [path.resolve(__dirname, `resources/assets/lib/${name}.ts`)];
}

const webpackConfig = {
  devtool: 'source-map',
  entry: entry,
  externals: {
    'd3': 'd3',
    'lodash': '_',
    'moment': 'moment',
    'prop-types': 'PropTypes',
    'react': 'React',
    'react-dom': 'ReactDOM',
    'react-dom-factories': 'ReactDOMFactories',
  },
  mode: inProduction ? 'production' : 'development',
  module: {
    rules: [
      {
        enforce: 'pre',
        exclude: /(node_modules)/,
        loader: 'import-glob-loader',
        test: /\.(js|coffee)$/,
      },
      {
        exclude: /(node_modules|bower_components)/,
        test: /\.jsx?$/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              cacheDirectory: true,
              plugins: [
                '@babel/plugin-syntax-dynamic-import',
                '@babel/plugin-proposal-object-rest-spread',
                [
                  '@babel/plugin-transform-runtime',
                  {
                    helpers: false,
                  },
                ],
              ],
              presets: [
                [
                  '@babel/preset-env',
                  {
                    forceAllTransforms: true,
                    modules: false,
                  },
                ],
              ],
            },
          },
        ],
      },
      {
        exclude: /node_modules/,
        loader: 'ts-loader',
        test: /\.tsx?$/,
      },
      {
        // loader for preexisting global coffeescript
        exclude: [
          path.resolve(__dirname, 'resources/assets/coffee/react'),
        ],
        include: [
          path.resolve(__dirname, 'resources/assets/coffee'),
        ],
        test: /\.coffee$/,
        use: ['imports-loader?this=>window', 'coffee-loader'],
      },
      {
        // loader for import-based coffeescript
        include: [
          path.resolve(__dirname, 'resources/assets/coffee/react'),
          path.resolve(__dirname, 'resources/assets/lib'),
        ],
        test: /\.coffee$/,
        use: ['coffee-loader'],
      },
      {
        test: /\.less$/,
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: 'css-loader',
            options: {
              importLoaders: 1,
              sourceMap: true,
              url: (url) => !url.startsWith('/'),
            },
          },
          {
            loader: 'postcss-loader',
            options: {
              plugins: [Autoprefixer],
              sourceMap: true,
            },
          },
          { loader: 'less-loader', options: { sourceMap: true } },
        ],
      },
      {
        loaders: [
          {
            loader: 'file-loader',
            options: {
              name: (filepath) => {
                if (!/node_modules|bower_components/.test(filepath)) {
                  return '/images/[name].[ext]?[hash]';
                }

                const cleanPath = filepath
                  .replace(/\\/g, '/')
                  .replace(/((.*(node_modules|bower_components))|images|image|img|assets)\//g, '');

                return `/images/vendor/${cleanPath}?[hash]`;
              },
            },
          },
          {
            loader: 'img-loader',
          },
        ],
        test: /(\.(png|jpe?g|gif|webp)$|^((?!font).)*\.svg$)/,
      },
      {
        loader: 'file-loader',
        options: {
          name: (filepath) => {
            if (!/node_modules|bower_components/.test(filepath)) {
              return '/fonts/[name].[ext]?[hash]';
            }

            const cleanPath = filepath
              .replace(/\\/g, '/')
              .replace(/((.*(node_modules|bower_components))|fonts|font|assets)\//g, '');

            return `/fonts/vendor/${cleanPath}?[hash]`;
          },
        },
        test: /(\.(woff2?|ttf|eot|otf)$|font.*\.svg$)/,
      },
    ],
  },
  optimization: {
    runtimeChunk: {
      name: '/js/commons',
    },
    splitChunks: {
      cacheGroups: {
        commons: {
          chunks: 'initial',
          minChunks: 2,
          name: '/js/commons',
        },
      },
    },
  },
  output: {
    chunkFilename: '[name].[chunkhash:8].js',
    filename: '[name].[contenthash:8].js',
    path: path.resolve(__dirname, 'public'),
  },
  plugins: [
    new webpack.DefinePlugin({
      'process.env.DOCS_URL': JSON.stringify(process.env.DOCS_URL || 'https://docs.ppy.sh'),
      'process.env.PAYMENT_SANDBOX': JSON.stringify(paymentSandbox),
      'process.env.SHOPIFY_DOMAIN': JSON.stringify(process.env.SHOPIFY_DOMAIN),
      'process.env.SHOPIFY_STOREFRONT_TOKEN': JSON.stringify(process.env.SHOPIFY_STOREFRONT_TOKEN),
    }),
    new MiniCssExtractPlugin({
      chunkFilename: '/css/app.[hash:8].css',
      filename: '/css/app.[hash:8].css',
    }),
    new ConcatPlugin({
      patterns: [
        {
          input: [
            'resources/assets/js/ga.js',
            'resources/assets/build/lang.js',
            'resources/assets/js/bootstrap-lang.js',
          ],
          output: '/js/app-deps.js',
        },
        {
          input: vendor, output: '/js/vendor.js',
        },
      ],
    }),
    new CopyPlugin({
      patterns: [
        { from: 'resources/assets/build/locales', to: 'js/locales' },
        { from: 'node_modules/@fortawesome/fontawesome-free/webfonts', to: 'vendor/fonts/font-awesome' },
        { from: 'node_modules/photoswipe/dist/default-skin', to: 'vendor/_photoswipe-default-skin' },
        { from: 'node_modules/moment/locale', to: 'vendor/js/moment-locales' },
      ],
    }),
    new ManifestPlugin({
      fileName: 'mix-manifest.json',
    }),
  ],
  resolve: {
    alias: {
      'ziggy': path.resolve(__dirname, 'resources/assets/js/ziggy.js'),
      'ziggy-route': path.resolve(__dirname, 'vendor/tightenco/ziggy/dist/js/route.js'),
    },
    extensions: ['*', '.js', '.coffee', '.ts', '.tsx'],
    modules: [
      path.resolve(__dirname, 'resources/assets/coffee'),
      path.resolve(__dirname, 'resources/assets/lib'),
      path.resolve(__dirname, 'resources/assets/coffee/react/_components'),
      path.resolve(__dirname, 'node_modules'),
    ],
    plugins: [new TsconfigPathsPlugin()],
  },
};

if (inProduction) {
  webpackConfig.optimization.minimizer = [
    new TerserPlugin({
      sourceMap: true,
      terserOptions: {
        safari10: true,
      },
    }),
  ];
}

if (process.env.SENTRY_RELEASE === '1') {
  webpackConfig.plugins.push(
    new SentryPlugin({
      apiKey: process.env.SENTRY_API_KEY,
      organisation: process.env.SENTRY_ORG,
      project: process.env.SENTRY_PROJ,

      deleteAfterCompile: true,
      exclude: /\.css(\.map)?$/,
      filenameTransform: function(filename) {
        return '~' + filename;
      },
      release: function() {
        return process.env.GIT_SHA;
      },
    }),
  );
}

module.exports = webpackConfig;
<|endoftext|>


<commit_before><commit_msg>Also replace MochiKit.Selector with jQuery<commit_after><|endoftext|>


<commit_before>/**
 * @file Table component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';

// import Header from '../_TableHeader';
import Content from '../_TableContent';
// import Footer from '../_TableFooter';
import Pagination from '../_TablePagination';
import CircularLoading from '../CircularLoading';

import Theme from '../Theme';
import HorizontalAlign from '../_statics/HorizontalAlign';
import SelectMode from '../_statics/SelectMode';
import SelectAllMode from '../_statics/SelectAllMode';
import SortingType from '../_statics/SortingType';

import Util from '../_vendors/Util';
import Calculation from '../_vendors/Calculation';
import ComponentUtil from '../_vendors/ComponentUtil';
import TableCalculation from '../_vendors/TableCalculation';

class Table extends Component {

    static Theme = Theme;
    static Align = HorizontalAlign;
    static Fixed = HorizontalAlign;
    static SelectMode = SelectMode;
    static SelectAllMode = SelectAllMode;
    static SortingType = SortingType;

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            init: props.hasInitFadeOut,
            sorting: props.sorting,
            page: props.page,
            pageSize: props.pageSize,
            expandRows: props.expandRows,
            value: Calculation.getInitValue(props)
        };

    }

    /**
     * public
     */
    collapseAllRows = () => {
        this.setState({
            expandRows: []
        }, () => {
            const {onExpandChange} = this.props;
            onExpandChange && onExpandChange([]);
        });
    };

    /**
     * keep table loading after do first render
     */
    handleInit = () => {
        this.state.init && this.setState({
            init: false
        });
    };

    /**
     * handle selection change
     */
    handleChange = (value, rowIndex) => {
        this.setState({
            value
        }, () => {
            const {onChange} = this.props;
            onChange && onChange(value, rowIndex);
        });
    };

    /**
     * handle sort change
     */
    handleSortChange = sorting => {
        this.setState({
            sorting
        }, () => {
            const {onSortChange} = this.props;
            onSortChange && onSortChange(sorting);
        });
    };

    /**
     * handle pagination change
     */
    handlePaginationChange = pagination => {
        this.setState(pagination, () => {
            const {onPaginationChange} = this.props;
            onPaginationChange && onPaginationChange(pagination);
        });
    };

    /**
     * handle expand change
     */
    handleExpandChange = (expandRows, callback) => {
        this.setState({
            expandRows
        }, () => {
            const {onExpandChange} = this.props;
            onExpandChange && onExpandChange(expandRows);
            callback && callback();
        });
    };

    static getDerivedStateFromProps(props, state) {

        const page = ComponentUtil.getDerivedState(props, state, 'page'),
            pageSize = ComponentUtil.getDerivedState(props, state, 'pageSize');

        return {
            prevProps: props,
            sorting: ComponentUtil.getDerivedState(props, state, 'sorting'),
            page: TableCalculation.handlePage(page, pageSize, props.data),
            pageSize,
            expandRows: ComponentUtil.getDerivedState(props, state, 'expandRows'),
            value: Calculation.getInitValue({
                value: ComponentUtil.getDerivedState(props, state, 'value'),
                selectMode: props.selectMode
            })
        };

    }

    render() {

        const {

                className, style,

                // pagination
                isPaginated, pageSizes, useFullPagination, pageSizeValueField, pageSizeDisplayField,
                paginationSelectionVisible, paginationTotalVisible, paginationPageSizeVisible,
                paginationPageSizesVisible, paginationPageSizeRightIconCls,
                paginationPrevIconCls, paginationNextIconCls, paginationFirstIconCls, paginationLastIconCls,
                paginationTotalRenderer, onPageChange, onPageSizeChange,

                // not passing down these props
                hasInitFadeOut, pageSize: propsPageSize,
                onPaginationChange,

                ...restProps

            } = this.props,
            {init, sorting, page, pageSize, expandRows, value} = this.state;

        return (

                {/* table header, display when select */}
                {/**/}

                {/* table area */}
                <Content {...restProps}
                         sorting={sorting}
                         isPaginated={isPaginated}
                         page={page}
                         pageSize={pageSize}
                         expandRows={expandRows}
                         value={value}
                         onInit={this.handleInit}
                         onChange={this.handleChange}
                         onSortChange={this.handleSortChange}
                         onExpandChange={this.handleExpandChange}/>

                {/* table footer */}
                {/**/}

                {/* table pagination */}
                {
                    isPaginated ?
                        <Pagination {...restProps}
                                    page={page}
                                    pageSize={pageSize}
                                    pageSizes={pageSizes}
                                    value={value}
                                    useFullPagination={useFullPagination}
                                    pageSizeValueField={pageSizeValueField}
                                    pageSizeDisplayField={pageSizeDisplayField}
                                    paginationSelectionVisible={paginationSelectionVisible}
                                    paginationTotalVisible={paginationTotalVisible}
                                    paginationPageSizeVisible={paginationPageSizeVisible}
                                    paginationPageSizesVisible={paginationPageSizesVisible}
                                    paginationPageSizeRightIconCls={paginationPageSizeRightIconCls}
                                    paginationPrevIconCls={paginationPrevIconCls}
                                    paginationNextIconCls={paginationNextIconCls}
                                    paginationFirstIconCls={paginationFirstIconCls}
                                    paginationLastIconCls={paginationLastIconCls}
                                    paginationTotalRenderer={paginationTotalRenderer}
                                    onPageChange={onPageChange}
                                    onPageSizeChange={onPageSizeChange}
                                    onPaginationChange={this.handlePaginationChange}/>
                        :
                        null
                }

                    {
                        init ?
                            <CircularLoading className="table-init-loading"/>
                            :
                            null
                    }

        );

    }
}

Table.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * Children passed into table header.
     */
    columns: PropTypes.arrayOf(PropTypes.shape({

        /**
         * fixed position of column ( true / 'left' / 'right' ).
         */
        fixed: PropTypes.oneOfType([PropTypes.bool, PropTypes.oneOf(Util.enumerateValue(HorizontalAlign))]),

        /**
         * width of column.
         */
        width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

        /**
         * minimum width of column.
         */
        minWidth: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

        /**
         * align of current column.
         */
        align: PropTypes.oneOf(Util.enumerateValue(HorizontalAlign)),

        /**
         * no wrap of current column.
         */
        noWrap: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),

        /**
         * The class name of header.
         */
        headClassName: PropTypes.string,

        /**
         * Override the styles of header.
         */
        headStyle: PropTypes.object,

        /**
         * align of table header cell
         */
        headAlign: PropTypes.oneOf(Util.enumerateValue(HorizontalAlign)),

        /**
         * The render content in table head.
         *  (1) callback:
         *      function (tableData, colIndex) {
         *          return colIndex;
         *      }
         *
         *  (2) others:
         *      render whatever you pass
         */
        headRenderer: PropTypes.any,

        /**
         * column span of table header.
         *  (1) function callback:
         *      function (tableData, colIndex) {
         *          return null;
         *      }
         *
         *  (2) number:
         *      render whatever you pass
         */
        headSpan: PropTypes.oneOfType([PropTypes.number, PropTypes.func]),

        /**
         * no wrap of table header.
         */
        headNoWrap: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),

        /**
         * The class name of td.
         */
        bodyClassName: PropTypes.string,

        /**
         * Override the styles of td.
         */
        bodyStyle: PropTypes.object,

        /**
         * align of table body cell.
         */
        bodyAlign: PropTypes.oneOf(Util.enumerateValue(HorizontalAlign)),

        /**
         * The render content in table body.
         *  (1) callback:
         *      function (rowData, rowIndex, colIndex) {
         *          return rowData.id;
         *      }
         *
         *  (2) others:
         *      render whatever you pass
         */
        bodyRenderer: PropTypes.any,

        /**
         * column span of table body.
         *  (1) function callback:
         *      function (rowData, colIndex, rowIndex) {
         *          return null;
         *      }
         *
         *  (2) number:
         *      render whatever you pass
         */
        bodySpan: PropTypes.oneOfType([PropTypes.number, PropTypes.func]),

        /**
         * no wrap of table body.
         */
        bodyNoWrap: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),

        /**
         * The class name of footer.
         */
        footClassName: PropTypes.string,

        /**
         * Override the styles of footer.
         */
        footStyle: PropTypes.object,

        /**
         * align of table footer cell
         */
        footAlign: PropTypes.oneOf(Util.enumerateValue(HorizontalAlign)),

        /**
         * The render content in table foot.
         *  (1) callback:
         *      function (tableData, colIndex) {
         *          return colIndex;
         *      }
         *
         *  (2) others:
         *      render whatever you pass
         */
        footRenderer: PropTypes.any,

        /**
         * column span of table foot.
         *  (1) function callback:
         *      function (tableData, colIndex) {
         *          return null;
         *      }
         *
         *  (2) number:
         *      render whatever you pass
         */
        footSpan: PropTypes.oneOfType([PropTypes.number, PropTypes.func]),

        /**
         * no wrap of table foot.
         */
        footNoWrap: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),

        /**
         * If true,this column can be sorted.
         */
        sortable: PropTypes.bool,

        /**
         * Sorting property.
         */
        sortingProp: PropTypes.string,

        defaultSortingType: PropTypes.oneOf(Util.enumerateValue(SortingType))

    })).isRequired,

    data: PropTypes.array,
    value: PropTypes.array,
    idProp: PropTypes.string,
    disabled: PropTypes.bool,
    hasInitFadeOut: PropTypes.bool,
    noDataText: PropTypes.string,

    /**
     * expand
     */
    expandRows: PropTypes.array,
    expandIconCls: PropTypes.string,

    /**
     * multi select
     */
    selectTheme: PropTypes.oneOf(Util.enumerateValue(Theme)),
    selectMode: PropTypes.oneOf(Util.enumerateValue(SelectMode)),
    selectAllMode: PropTypes.oneOf(Util.enumerateValue(SelectAllMode)),
    selectColumn: PropTypes.shape({

        /**
         * fixed position of column ( true / 'left' / 'right' )
         */
        fixed: PropTypes.oneOfType([PropTypes.bool, PropTypes.oneOf(Util.enumerateValue(HorizontalAlign))]),

        /**
         * width of column
         */
        width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

        /**
         * minimum width of column
         */
        minWidth: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

        /**
         * align of current column
         */
        align: PropTypes.oneOf(Util.enumerateValue(HorizontalAlign)),

        /**
         * The class name of header.
         */
        headClassName: PropTypes.string,

        /**
         * Override the styles of header.
         */
        headStyle: PropTypes.object,

        /**
         * align of table header cell
         */
        headAlign: PropTypes.oneOf(Util.enumerateValue(HorizontalAlign)),

        /**
         * The class name of td.
         */
        bodyClassName: PropTypes.string,

        /**
         * Override the styles of td.
         */
        bodyStyle: PropTypes.object,

        /**
         * align of table body cell
         */
        bodyAlign: PropTypes.oneOf(Util.enumerateValue(HorizontalAlign))

    }),
    isSelectRecursive: PropTypes.bool,
    selectUncheckedIconCls: PropTypes.string,
    selectCheckedIconCls: PropTypes.string,
    selectIndeterminateIconCls: PropTypes.string,

    /**
     * sorting
     */
    sorting: PropTypes.shape({
        prop: PropTypes.string,
        type: PropTypes.oneOf(Util.enumerateValue(SortingType))
    }),
    defaultSortingType: PropTypes.oneOf(Util.enumerateValue(SortingType)),
    sortingAscIconCls: PropTypes.string,
    sortingDescIconCls: PropTypes.string,
    autoSorting: PropTypes.bool,
    sortingFunc: PropTypes.func,

    /**
     * pagination
     */
    isPaginated: PropTypes.bool,
    page: PropTypes.number,
    pageSize: PropTypes.number,
    pageSizes: PropTypes.array,
    pageSizeValueField: PropTypes.string,
    pageSizeDisplayField: PropTypes.string,
    useFullPagination: PropTypes.bool,
    paginationSelectionVisible: PropTypes.bool,
    paginationTotalVisible: PropTypes.bool,
    paginationPageSizeVisible: PropTypes.bool,
    paginationPageSizesVisible: PropTypes.bool,
    paginationPageSizeRightIconCls: PropTypes.string,
    paginationPrevIconCls: PropTypes.string,
    paginationNextIconCls: PropTypes.string,
    paginationFirstIconCls: PropTypes.string,
    paginationLastIconCls: PropTypes.string,
    paginationSelectionRenderer: PropTypes.func,
    paginationTotalRenderer: PropTypes.func,

    /**
     * hidden
     */
    isHeadHidden: PropTypes.bool,
    isFootHidden: PropTypes.bool,

    /**
     * fixed
     */
    isHeadFixed: PropTypes.bool,
    isFootFixed: PropTypes.bool,

    /**
     * scroll
     */
    scroll: PropTypes.shape({
        width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
        height: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
        maxHeight: PropTypes.oneOfType([PropTypes.number, PropTypes.string])
    }),

    /**
     * callback
     */
    onChange: PropTypes.func,
    onSelect: PropTypes.func,
    onDeselect: PropTypes.func,
    onSelectAll: PropTypes.func,
    onDeselectAll: PropTypes.func,
    onExpand: PropTypes.func,
    onCollapse: PropTypes.func,
    onExpandChange: PropTypes.func,
    onSortChange: PropTypes.func,
    onDataUpdate: PropTypes.func,
    onPaginationChange: PropTypes.func,
    onPageChange: PropTypes.func,
    onPageSizeChange: PropTypes.func

};

Table.defaultProps = {

    disabled: false,
    hasInitFadeOut: true,
    noDataText: 'No Data',

    /**
     * expand
     */
    expandRows: [],
    expandIconCls: 'fas fa-chevron-right',

    /**
     * multi select
     */
    selectMode: SelectMode.SINGLE_SELECT,
    selectAllMode: SelectAllMode.CURRENT_PAGE,
    isSelectRecursive: false,
    selectUncheckedIconCls: 'far fa-square',
    selectCheckedIconCls: 'fas fa-check-square',
    selectIndeterminateIconCls: 'fas fa-minus-square',

    /**
     * sorting
     */
    defaultSortingType: SortingType.ASC,
    sortingAscIconCls: 'fas fa-sort-up',
    sortingDescIconCls: 'fas fa-sort-down',
    autoSorting: true,

    /**
     * pagination
     */
    isPaginated: true,
    page: 0,
    pageSize: 10,
    pageSizes: [5, 10, 15, 20],
    useFullPagination: false,
    pageSizeValueField: 'value',
    pageSizeDisplayField: 'text',
    paginationSelectionVisible: false,
    paginationTotalVisible: true,
    paginationPageSizeVisible: true,
    paginationPageSizesVisible: true,
    paginationPageSizeRightIconCls: 'fas fa-angle-down',
    paginationPrevIconCls: 'fas fa-angle-left',
    paginationNextIconCls: 'fas fa-angle-right',
    paginationFirstIconCls: 'fas fa-angle-double-left',
    paginationLastIconCls: 'fas fa-angle-double-right',

    /**
     * hidden
     */
    isHeadHidden: false,
    isFootHidden: false,

    /**
     * fixed
     */
    isHeadFixed: false,
    isFootFixed: false

};

export default Table;
<commit_msg>update Table<commit_after>/**
 * @file Table component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';

// import Header from '../_TableHeader';
import Content from '../_TableContent';
// import Footer from '../_TableFooter';
import Pagination from '../_TablePagination';
import CircularLoading from '../CircularLoading';

import Theme from '../Theme';
import HorizontalAlign from '../_statics/HorizontalAlign';
import SelectMode from '../_statics/SelectMode';
import SelectAllMode from '../_statics/SelectAllMode';
import SortingType from '../_statics/SortingType';

import Util from '../_vendors/Util';
import Calculation from '../_vendors/Calculation';
import ComponentUtil from '../_vendors/ComponentUtil';
import TableCalculation from '../_vendors/TableCalculation';

class Table extends Component {

    static Theme = Theme;
    static Align = HorizontalAlign;
    static Fixed = HorizontalAlign;
    static SelectMode = SelectMode;
    static SelectAllMode = SelectAllMode;
    static SortingType = SortingType;

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            init: props.hasInitFadeOut,
            sorting: props.sorting,
            page: props.page,
            pageSize: props.pageSize,
            expandRows: props.expandRows,
            value: props.value
        };

    }

    /**
     * public
     */
    collapseAllRows = () => {
        this.setState({
            expandRows: []
        }, () => {
            const {onExpandChange} = this.props;
            onExpandChange && onExpandChange([]);
        });
    };

    /**
     * keep table loading after do first render
     */
    handleInit = () => {
        this.state.init && this.setState({
            init: false
        });
    };

    /**
     * handle selection change
     */
    handleChange = (value, rowIndex) => {
        this.setState({
            value
        }, () => {
            const {onChange} = this.props;
            onChange && onChange(value, rowIndex);
        });
    };

    /**
     * handle sort change
     */
    handleSortChange = sorting => {
        this.setState({
            sorting
        }, () => {
            const {onSortChange} = this.props;
            onSortChange && onSortChange(sorting);
        });
    };

    /**
     * handle pagination change
     */
    handlePaginationChange = pagination => {
        this.setState(pagination, () => {
            const {onPaginationChange} = this.props;
            onPaginationChange && onPaginationChange(pagination);
        });
    };

    /**
     * handle expand change
     */
    handleExpandChange = (expandRows, callback) => {
        this.setState({
            expandRows
        }, () => {
            const {onExpandChange} = this.props;
            onExpandChange && onExpandChange(expandRows);
            callback && callback();
        });
    };

    static getDerivedStateFromProps(props, state) {

        const page = ComponentUtil.getDerivedState(props, state, 'page'),
            pageSize = ComponentUtil.getDerivedState(props, state, 'pageSize');

        return {
            prevProps: props,
            sorting: ComponentUtil.getDerivedState(props, state, 'sorting'),
            page: TableCalculation.handlePage(page, pageSize, props.data),
            pageSize,
            expandRows: ComponentUtil.getDerivedState(props, state, 'expandRows'),
            value: ComponentUtil.getDerivedState(props, state, 'value')
        };

    }

    render() {

        const {

                className, style,

                // pagination
                isPaginated, pageSizes, useFullPagination, pageSizeValueField, pageSizeDisplayField,
                paginationSelectionVisible, paginationTotalVisible, paginationPageSizeVisible,
                paginationPageSizesVisible, paginationPageSizeRightIconCls,
                paginationPrevIconCls, paginationNextIconCls, paginationFirstIconCls, paginationLastIconCls,
                paginationTotalRenderer, onPageChange, onPageSizeChange,

                // not passing down these props
                hasInitFadeOut, pageSize: propsPageSize,
                onPaginationChange,

                ...restProps

            } = this.props,
            {init, sorting, page, pageSize, expandRows, value} = this.state;

        return (

                {/* table header, display when select */}
                {/**/}

                {/* table area */}
                <Content {...restProps}
                         sorting={sorting}
                         isPaginated={isPaginated}
                         page={page}
                         pageSize={pageSize}
                         expandRows={expandRows}
                         value={value}
                         onInit={this.handleInit}
                         onChange={this.handleChange}
                         onSortChange={this.handleSortChange}
                         onExpandChange={this.handleExpandChange}/>

                {/* table footer */}
                {/**/}

                {/* table pagination */}
                {
                    isPaginated ?
                        <Pagination {...restProps}
                                    page={page}
                                    pageSize={pageSize}
                                    pageSizes={pageSizes}
                                    value={value}
                                    useFullPagination={useFullPagination}
                                    pageSizeValueField={pageSizeValueField}
                                    pageSizeDisplayField={pageSizeDisplayField}
                                    paginationSelectionVisible={paginationSelectionVisible}
                                    paginationTotalVisible={paginationTotalVisible}
                                    paginationPageSizeVisible={paginationPageSizeVisible}
                                    paginationPageSizesVisible={paginationPageSizesVisible}
                                    paginationPageSizeRightIconCls={paginationPageSizeRightIconCls}
                                    paginationPrevIconCls={paginationPrevIconCls}
                                    paginationNextIconCls={paginationNextIconCls}
                                    paginationFirstIconCls={paginationFirstIconCls}
                                    paginationLastIconCls={paginationLastIconCls}
                                    paginationTotalRenderer={paginationTotalRenderer}
                                    onPageChange={onPageChange}
                                    onPageSizeChange={onPageSizeChange}
                                    onPaginationChange={this.handlePaginationChange}/>
                        :
                        null
                }

                    {
                        init ?
                            <CircularLoading className="table-init-loading"/>
                            :
                            null
                    }

        );

    }
}

Table.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * Children passed into table header.
     */
    columns: PropTypes.arrayOf(PropTypes.shape({

        /**
         * fixed position of column ( true / 'left' / 'right' ).
         */
        fixed: PropTypes.oneOfType([PropTypes.bool, PropTypes.oneOf(Util.enumerateValue(HorizontalAlign))]),

        /**
         * width of column.
         */
        width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

        /**
         * minimum width of column.
         */
        minWidth: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

        /**
         * align of current column.
         */
        align: PropTypes.oneOf(Util.enumerateValue(HorizontalAlign)),

        /**
         * no wrap of current column.
         */
        noWrap: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),

        /**
         * The class name of header.
         */
        headClassName: PropTypes.string,

        /**
         * Override the styles of header.
         */
        headStyle: PropTypes.object,

        /**
         * align of table header cell
         */
        headAlign: PropTypes.oneOf(Util.enumerateValue(HorizontalAlign)),

        /**
         * The render content in table head.
         *  (1) callback:
         *      function (tableData, colIndex) {
         *          return colIndex;
         *      }
         *
         *  (2) others:
         *      render whatever you pass
         */
        headRenderer: PropTypes.any,

        /**
         * column span of table header.
         *  (1) function callback:
         *      function (tableData, colIndex) {
         *          return null;
         *      }
         *
         *  (2) number:
         *      render whatever you pass
         */
        headSpan: PropTypes.oneOfType([PropTypes.number, PropTypes.func]),

        /**
         * no wrap of table header.
         */
        headNoWrap: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),

        /**
         * The class name of td.
         */
        bodyClassName: PropTypes.string,

        /**
         * Override the styles of td.
         */
        bodyStyle: PropTypes.object,

        /**
         * align of table body cell.
         */
        bodyAlign: PropTypes.oneOf(Util.enumerateValue(HorizontalAlign)),

        /**
         * The render content in table body.
         *  (1) callback:
         *      function (rowData, rowIndex, colIndex) {
         *          return rowData.id;
         *      }
         *
         *  (2) others:
         *      render whatever you pass
         */
        bodyRenderer: PropTypes.any,

        /**
         * column span of table body.
         *  (1) function callback:
         *      function (rowData, colIndex, rowIndex) {
         *          return null;
         *      }
         *
         *  (2) number:
         *      render whatever you pass
         */
        bodySpan: PropTypes.oneOfType([PropTypes.number, PropTypes.func]),

        /**
         * no wrap of table body.
         */
        bodyNoWrap: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),

        /**
         * The class name of footer.
         */
        footClassName: PropTypes.string,

        /**
         * Override the styles of footer.
         */
        footStyle: PropTypes.object,

        /**
         * align of table footer cell
         */
        footAlign: PropTypes.oneOf(Util.enumerateValue(HorizontalAlign)),

        /**
         * The render content in table foot.
         *  (1) callback:
         *      function (tableData, colIndex) {
         *          return colIndex;
         *      }
         *
         *  (2) others:
         *      render whatever you pass
         */
        footRenderer: PropTypes.any,

        /**
         * column span of table foot.
         *  (1) function callback:
         *      function (tableData, colIndex) {
         *          return null;
         *      }
         *
         *  (2) number:
         *      render whatever you pass
         */
        footSpan: PropTypes.oneOfType([PropTypes.number, PropTypes.func]),

        /**
         * no wrap of table foot.
         */
        footNoWrap: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),

        /**
         * If true,this column can be sorted.
         */
        sortable: PropTypes.bool,

        /**
         * Sorting property.
         */
        sortingProp: PropTypes.string,

        defaultSortingType: PropTypes.oneOf(Util.enumerateValue(SortingType))

    })).isRequired,

    data: PropTypes.array,
    value: PropTypes.array,
    idProp: PropTypes.string,
    disabled: PropTypes.bool,
    hasInitFadeOut: PropTypes.bool,
    noDataText: PropTypes.string,

    /**
     * expand
     */
    expandRows: PropTypes.array,
    expandIconCls: PropTypes.string,

    /**
     * multi select
     */
    selectTheme: PropTypes.oneOf(Util.enumerateValue(Theme)),
    selectMode: PropTypes.oneOf(Util.enumerateValue(SelectMode)),
    selectAllMode: PropTypes.oneOf(Util.enumerateValue(SelectAllMode)),
    selectColumn: PropTypes.shape({

        /**
         * fixed position of column ( true / 'left' / 'right' )
         */
        fixed: PropTypes.oneOfType([PropTypes.bool, PropTypes.oneOf(Util.enumerateValue(HorizontalAlign))]),

        /**
         * width of column
         */
        width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

        /**
         * minimum width of column
         */
        minWidth: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

        /**
         * align of current column
         */
        align: PropTypes.oneOf(Util.enumerateValue(HorizontalAlign)),

        /**
         * The class name of header.
         */
        headClassName: PropTypes.string,

        /**
         * Override the styles of header.
         */
        headStyle: PropTypes.object,

        /**
         * align of table header cell
         */
        headAlign: PropTypes.oneOf(Util.enumerateValue(HorizontalAlign)),

        /**
         * The class name of td.
         */
        bodyClassName: PropTypes.string,

        /**
         * Override the styles of td.
         */
        bodyStyle: PropTypes.object,

        /**
         * align of table body cell
         */
        bodyAlign: PropTypes.oneOf(Util.enumerateValue(HorizontalAlign))

    }),
    isSelectRecursive: PropTypes.bool,
    selectUncheckedIconCls: PropTypes.string,
    selectCheckedIconCls: PropTypes.string,
    selectIndeterminateIconCls: PropTypes.string,

    /**
     * sorting
     */
    sorting: PropTypes.shape({
        prop: PropTypes.string,
        type: PropTypes.oneOf(Util.enumerateValue(SortingType))
    }),
    defaultSortingType: PropTypes.oneOf(Util.enumerateValue(SortingType)),
    sortingAscIconCls: PropTypes.string,
    sortingDescIconCls: PropTypes.string,
    autoSorting: PropTypes.bool,
    sortingFunc: PropTypes.func,

    /**
     * pagination
     */
    isPaginated: PropTypes.bool,
    page: PropTypes.number,
    pageSize: PropTypes.number,
    pageSizes: PropTypes.array,
    pageSizeValueField: PropTypes.string,
    pageSizeDisplayField: PropTypes.string,
    useFullPagination: PropTypes.bool,
    paginationSelectionVisible: PropTypes.bool,
    paginationTotalVisible: PropTypes.bool,
    paginationPageSizeVisible: PropTypes.bool,
    paginationPageSizesVisible: PropTypes.bool,
    paginationPageSizeRightIconCls: PropTypes.string,
    paginationPrevIconCls: PropTypes.string,
    paginationNextIconCls: PropTypes.string,
    paginationFirstIconCls: PropTypes.string,
    paginationLastIconCls: PropTypes.string,
    paginationSelectionRenderer: PropTypes.func,
    paginationTotalRenderer: PropTypes.func,

    /**
     * hidden
     */
    isHeadHidden: PropTypes.bool,
    isFootHidden: PropTypes.bool,

    /**
     * fixed
     */
    isHeadFixed: PropTypes.bool,
    isFootFixed: PropTypes.bool,

    /**
     * scroll
     */
    scroll: PropTypes.shape({
        width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
        height: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
        maxHeight: PropTypes.oneOfType([PropTypes.number, PropTypes.string])
    }),

    /**
     * callback
     */
    onChange: PropTypes.func,
    onSelect: PropTypes.func,
    onDeselect: PropTypes.func,
    onSelectAll: PropTypes.func,
    onDeselectAll: PropTypes.func,
    onExpand: PropTypes.func,
    onCollapse: PropTypes.func,
    onExpandChange: PropTypes.func,
    onSortChange: PropTypes.func,
    onDataUpdate: PropTypes.func,
    onPaginationChange: PropTypes.func,
    onPageChange: PropTypes.func,
    onPageSizeChange: PropTypes.func

};

Table.defaultProps = {

    disabled: false,
    hasInitFadeOut: true,
    noDataText: 'No Data',

    /**
     * expand
     */
    expandRows: [],
    expandIconCls: 'fas fa-chevron-right',

    /**
     * multi select
     */
    selectMode: SelectMode.SINGLE_SELECT,
    selectAllMode: SelectAllMode.CURRENT_PAGE,
    isSelectRecursive: false,
    selectUncheckedIconCls: 'far fa-square',
    selectCheckedIconCls: 'fas fa-check-square',
    selectIndeterminateIconCls: 'fas fa-minus-square',

    /**
     * sorting
     */
    defaultSortingType: SortingType.ASC,
    sortingAscIconCls: 'fas fa-sort-up',
    sortingDescIconCls: 'fas fa-sort-down',
    autoSorting: true,

    /**
     * pagination
     */
    isPaginated: true,
    page: 0,
    pageSize: 10,
    pageSizes: [5, 10, 15, 20],
    useFullPagination: false,
    pageSizeValueField: 'value',
    pageSizeDisplayField: 'text',
    paginationSelectionVisible: false,
    paginationTotalVisible: true,
    paginationPageSizeVisible: true,
    paginationPageSizesVisible: true,
    paginationPageSizeRightIconCls: 'fas fa-angle-down',
    paginationPrevIconCls: 'fas fa-angle-left',
    paginationNextIconCls: 'fas fa-angle-right',
    paginationFirstIconCls: 'fas fa-angle-double-left',
    paginationLastIconCls: 'fas fa-angle-double-right',

    /**
     * hidden
     */
    isHeadHidden: false,
    isFootHidden: false,

    /**
     * fixed
     */
    isHeadFixed: false,
    isFootFixed: false

};

export default Table;
<|endoftext|>


<commit_before>// ===================================================================================================
//                           _  __     _ _
//                          | |/ /__ _| | |_ _  _ _ _ __ _
//                          | ' </ _` | |  _| || | '_/ _` |
//                          |_|\_\__,_|_|\__|\_,_|_| \__,_|
//
// This file is part of the Kaltura Collaborative Media Suite which allows users
// to do with audio, video, and animation what Wiki platfroms allow them to do with
// text.
//
// Copyright (C) 2006-2020  Kaltura Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
// @ignore
// ===================================================================================================
/**
 * The Kaltura Client - this is the facade through which all service actions should be called.
 * @param config the Kaltura configuration object holding partner credentials (type: KalturaConfiguration).
 */
var util = require('util');
var kaltura = require('./KalturaClientBase');
kaltura.services = require('./KalturaServices');
kaltura.objects = require('./KalturaModel');
kaltura.enums = require('./KalturaTypes');

class Client extends kaltura.ClientBase {

	/**
	 * @param Configuration config
	 */
	constructor(config) {
		super(config);
		this.setApiVersion('16.10.0');
		this.setClientTag('node:20-10-31');
	}
}

module.exports = kaltura;
module.exports.Client = Client;

<commit_msg>New clientlib ver<commit_after>// ===================================================================================================
//                           _  __     _ _
//                          | |/ /__ _| | |_ _  _ _ _ __ _
//                          | ' </ _` | |  _| || | '_/ _` |
//                          |_|\_\__,_|_|\__|\_,_|_| \__,_|
//
// This file is part of the Kaltura Collaborative Media Suite which allows users
// to do with audio, video, and animation what Wiki platfroms allow them to do with
// text.
//
// Copyright (C) 2006-2020  Kaltura Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
// @ignore
// ===================================================================================================
/**
 * The Kaltura Client - this is the facade through which all service actions should be called.
 * @param config the Kaltura configuration object holding partner credentials (type: KalturaConfiguration).
 */
var util = require('util');
var kaltura = require('./KalturaClientBase');
kaltura.services = require('./KalturaServices');
kaltura.objects = require('./KalturaModel');
kaltura.enums = require('./KalturaTypes');

class Client extends kaltura.ClientBase {

	/**
	 * @param Configuration config
	 */
	constructor(config) {
		super(config);
		this.setApiVersion('16.10.0');
		this.setClientTag('node:20-11-01');
	}
}

module.exports = kaltura;
module.exports.Client = Client;

<|endoftext|>


<commit_before>'use strict'

if (process.env.NODE_ENV !== 'production') {
  require('dotenv').config()
}

var Discord = require('discord.io');
var logger  = require('winston');
var fetch   = require('isomorphic-unfetch');
var express = require('express');
var app     = express();
var config = {
  "consumerKey": process.env.CONSUMER_KEY,
  "consumerSecret": process.env.CONSUMER_SECRET,
  "accessToken": process.env.ACCESS_TOKEN,
  "accessTokenSecret": process.env.ACCESS_TOKEN_SECRET,
  "callbackUrl": process.env.CALLBACK_URL
}
var allowedUserIds = process.env.ALLOWED_USER_IDS.split(',');
var Twitter = require('twitter-node-client').Twitter;
var twitter = new Twitter(config);

/* @TODO: Look into these - not firing */
app.get('/', function (req, res) {
  res.send('Hello World');
});

app.all('/twitter/callback', function (req, res) {
  res.send('Hello');
});

app.listen(process.env.PORT);
console.log('listening on ', process.env.PORT);

// Callback functions
/* @TODO: Move into utility file */
function error (err, response, body) {
  console.log('ERROR [%s]', err);
};
function success (data) {
  console.log('Data [%s]', data);
};
function postAsPromised (data) {
  return new Promise(function (resolve, reject) {
    twitter.postMedia({
      media_data: data
    }, reject, resolve)
  });
}
function isApprovedUser (userID) {
  return allowedUserIds.indexOf(userID) === -1 ? false : true;
}
function isTweetEmoji (emoji) {
  /* Could set this as process.env variable or array if wanted */
  return emoji.id === '359826768204660737';
}
function tweetWithAttachments (evt, message) {
  /* Not sure you can actually upload more than one thing at a time.... */
  return Promise.all(evt.d.attachments.map(item => {
    // do stuff
    return fetch(item.url)
      .then(res => res.buffer())
      .then(buffer => buffer.toString('base64'))
      .then(data => postAsPromised(data))
      .then(data => JSON.parse(data).media_id_string)
      .catch(console.log); /* @TODO: Better error handling, possibly don't even tweet without image? */
  }))
  .then(array => {
    const string = array.join(',');
    twitter.postTweet({
      status: message,
      media_ids: string
    }, error, success)
  })
}
function tweet (message) {
  return twitter.postTweet({
    status: message
  }, error, success);
}

// Configure logger settings
logger.remove(logger.transports.Console);
logger.add(logger.transports.Console, {
  colorize: true
});
logger.level = 'debug';

/* @TODO: Make this more dynamic - could be active in more channels */
var activeChannelID;

// Initialize Discord Bot
var bot = new Discord.Client({
  token: process.env.AUTH_TOKEN,
  autorun: true
});

bot.on('ready', function (evt) {
  logger.info('Connected');
  logger.info('Logged in as: ');
  logger.info(bot.username + ' - (' + bot.id + ')');
});

bot.on('message', function (user, userID, channelID, message, evt) {
  const authorId = evt.d.author.id;
  if (!isApprovedUser(authorId)) { return; }
  /* On every message in active channel, if bot is active, post message to Twitter */
  if (activeChannelID === channelID && message.substring(0,1) !== '!') {

    /* If there are attachments, upload first and then upload tweet */
    if (evt.d && evt.d.attachments.length) {
      tweetWithAttachments(evt, message);
    } else {
      /* No attachments, straight tweet */
      tweet(message);
    }
  }

  // Our bot needs to know if it will execute a command
  // It will listen for messages that will start with `!`
  if (message.substring(0, 1) == '!') {
    var args = message.substring(1).split(' ');
    var cmd = args[0];

    args = args.splice(1);
    switch(cmd) {
      case 'activate':
      /* Only allow one active channel at a time for now - @TODO: multiple active channels */
      if (!activeChannelID) {
        activeChannelID = channelID;
      }
        break;
      case 'deactivate':
        if (activeChannelID === channelID) {
          activeChannelID = undefined;
        }
        break;
      case 'ping':
        bot.sendMessage({
          to: channelID,
          message: 'Pong!'
        });
      default:
      break;
    }
  }
});

bot.on('any', function (event) {
  const type = event.t;
  const data = event.d;
  let userID;
  let channelID;
  if (data) {
    userID = data.user_id;
    channelID = data.channel_id;
  }

  if (type === 'MESSAGE_REACTION_ADD') {
    if (isApprovedUser(userID) && isTweetEmoji(data.emoji)) {
      bot.getMessage({ channelID: channelID, messageID: data.message_id }, (err, msg) => {

        /* Only tweet once */
        const reactions = msg.reactions;
        const tweetCount = reactions.find(item => isTweetEmoji(item.emoji)).count;
        if (tweetCount > 1) { return; }

        if (msg.attachments.length) {
          tweetWithAttachments({ d: msg }, msg.content);
        } else {
          tweet(msg.content);
        }
      });
    }
  }
});

bot.on('disconnect', function (errMsg, code) {
  logger.error('DISCONNECTED', errMsg);
  logger.error('CODE: ', code);

  bot.connect();
});<commit_msg>Adding logic to let emoji be found by name<commit_after>'use strict'

if (process.env.NODE_ENV !== 'production') {
  require('dotenv').config()
}

var Discord = require('discord.io');
var logger  = require('winston');
var fetch   = require('isomorphic-unfetch');
var express = require('express');
var app     = express();
var config = {
  "consumerKey": process.env.CONSUMER_KEY,
  "consumerSecret": process.env.CONSUMER_SECRET,
  "accessToken": process.env.ACCESS_TOKEN,
  "accessTokenSecret": process.env.ACCESS_TOKEN_SECRET,
  "callbackUrl": process.env.CALLBACK_URL
}
var allowedUserIds = process.env.ALLOWED_USER_IDS.split(',');
var Twitter = require('twitter-node-client').Twitter;
var twitter = new Twitter(config);

/* @TODO: Look into these - not firing */
app.get('/', function (req, res) {
  res.send('Hello World');
});

app.all('/twitter/callback', function (req, res) {
  res.send('Hello');
});

app.listen(process.env.PORT);
console.log('listening on ', process.env.PORT);

// Callback functions
/* @TODO: Move into utility file */
function error (err, response, body) {
  console.log('ERROR [%s]', err);
};
function success (data) {
  console.log('Data [%s]', data);
};
function postAsPromised (data) {
  return new Promise(function (resolve, reject) {
    twitter.postMedia({
      media_data: data
    }, reject, resolve)
  });
}
function isApprovedUser (userID) {
  return allowedUserIds.indexOf(userID) === -1 ? false : true;
}
function isTweetEmoji (emoji) {
  /* Could set this as process.env variable or array if wanted */
  return emoji.id === '359826768204660737' || emoji.name === 'tweet';
}
function tweetWithAttachments (evt, message) {
  /* Not sure you can actually upload more than one thing at a time.... */
  return Promise.all(evt.d.attachments.map(item => {
    // do stuff
    return fetch(item.url)
      .then(res => res.buffer())
      .then(buffer => buffer.toString('base64'))
      .then(data => postAsPromised(data))
      .then(data => JSON.parse(data).media_id_string)
      .catch(console.log); /* @TODO: Better error handling, possibly don't even tweet without image? */
  }))
  .then(array => {
    const string = array.join(',');
    twitter.postTweet({
      status: message,
      media_ids: string
    }, error, success)
  })
}
function tweet (message) {
  return twitter.postTweet({
    status: message
  }, error, success);
}

// Configure logger settings
logger.remove(logger.transports.Console);
logger.add(logger.transports.Console, {
  colorize: true
});
logger.level = 'debug';

/* @TODO: Make this more dynamic - could be active in more channels */
var activeChannelID;

// Initialize Discord Bot
var bot = new Discord.Client({
  token: process.env.AUTH_TOKEN,
  autorun: true
});

bot.on('ready', function (evt) {
  logger.info('Connected');
  logger.info('Logged in as: ');
  logger.info(bot.username + ' - (' + bot.id + ')');
});

bot.on('message', function (user, userID, channelID, message, evt) {
  const authorId = evt.d.author.id;
  if (!isApprovedUser(authorId)) { return; }
  /* On every message in active channel, if bot is active, post message to Twitter */
  if (activeChannelID === channelID && message.substring(0,1) !== '!') {

    /* If there are attachments, upload first and then upload tweet */
    if (evt.d && evt.d.attachments.length) {
      tweetWithAttachments(evt, message);
    } else {
      /* No attachments, straight tweet */
      tweet(message);
    }
  }

  // Our bot needs to know if it will execute a command
  // It will listen for messages that will start with `!`
  if (message.substring(0, 1) == '!') {
    var args = message.substring(1).split(' ');
    var cmd = args[0];

    args = args.splice(1);
    switch(cmd) {
      case 'activate':
      /* Only allow one active channel at a time for now - @TODO: multiple active channels */
      if (!activeChannelID) {
        activeChannelID = channelID;
      }
        break;
      case 'deactivate':
        if (activeChannelID === channelID) {
          activeChannelID = undefined;
        }
        break;
      case 'ping':
        bot.sendMessage({
          to: channelID,
          message: 'Pong!'
        });
      default:
      break;
    }
  }
});

bot.on('any', function (event) {
  const type = event.t;
  const data = event.d;
  let userID;
  let channelID;
  if (data) {
    userID = data.user_id;
    channelID = data.channel_id;
  }

  if (type === 'MESSAGE_REACTION_ADD') {
    if (isApprovedUser(userID) && isTweetEmoji(data.emoji)) {
      bot.getMessage({ channelID: channelID, messageID: data.message_id }, (err, msg) => {

        /* Only tweet once */
        const reactions = msg.reactions;
        console.log('reactions', reactions);
        const tweetCount = reactions.find(item => isTweetEmoji(item.emoji)).count;
        if (tweetCount > 1) { return; }

        if (msg.attachments.length) {
          // tweetWithAttachments({ d: msg }, msg.content);
        } else {
          // tweet(msg.content);
        }
      });
    }
  }
});

bot.on('disconnect', function (errMsg, code) {
  logger.error('DISCONNECTED', errMsg);
  logger.error('CODE: ', code);

  bot.connect();
});<|endoftext|>


<commit_before>
class Controller
{
    constructor($rootScope)
    {
    }
}

Controller.$inject = ['$rootScope'];

export {Controller};

<commit_msg>set default title<commit_after>
class Controller
{
    constructor($rootScope)
    {
        this.title = 'Default generic administrator';
    }
}

Controller.$inject = ['$rootScope'];

export {Controller};

<|endoftext|>


<commit_before>import { test, moduleForComponent } from 'ember-qunit';
import Ember from 'ember';

moduleForComponent('jobs-item', 'JobsItemComponent', {
  needs: ['helper:format-duration', 'helper:pretty-date', 'component:status-icon']
});

test('it renders', function (assert) {
  const attributes = {
    id: 10,
    state: 'passed',
    number: '2',
    config: {
      rvm: '2.1.2',
      jdk: 'openjdk6',
      os: 'linux',
      env: 'TESTS=unit'
    },
    duration: 100
  };
  const job = Ember.Object.create(attributes);
  const component = this.subject({
    job: job
  });
  this.render();
  assert.ok(component.$().hasClass('passed'), 'component should have a state class (passed)');
  assert.equal(component.$('.job-number').text().trim(), '2', 'job number should be displayed');
  assert.equal(component.$('.job-lang').text().trim(), 'JDK: openjdk6 Ruby: 2.1.2', 'langauges list should be displayed');
  assert.equal(component.$('.job-env').text().trim(), 'TESTS=unit', 'env should be displayed');
  assert.ok(component.$('.job-os').hasClass('linux'), 'OS class should be added for OS icon');
  assert.equal(component.$('.job-duration').text().trim(), '1 min 40 sec', 'duration should be displayed');
});

test('outputs info on not set properties', function (assert) {
  const job = Ember.Object.create();
  const component = this.subject({
    job: job
  });
  this.render();
  assert.ok(component.$('.job-env').text().match(/no environment variables set/), 'a message for no env vars should be displayed');
  assert.ok(component.$('.job-lang').text().match(/no language set/), 'a message about no language being set should be displayed');
});

test('when env is not set, gemfile is displayed in the env section', function (assert) {
  const attributes = {
    id: 10,
    state: 'passed',
    number: '2',
    config: {
      rvm: '2.1.2',
      gemfile: 'foo/Gemfile'
    },
    duration: 100
  };
  const job = Ember.Object.create(attributes);
  const component = this.subject({
    job: job
  });
  this.render();
  assert.equal(component.$('.job-lang .label-align').text().trim(), 'Ruby: 2.1.2', 'langauges list should be displayed');
  assert.equal(component.$('.job-env .label-align').text().trim(), 'Gemfile: foo/Gemfile', 'env should be displayed');
});

test('when env is set, gemfile is displayed in the language section', function (assert) {
  const attributes = {
    id: 10,
    state: 'passed',
    number: '2',
    config: {
      rvm: '2.1.2',
      gemfile: 'foo/Gemfile',
      env: 'FOO=bar'
    },
    duration: 100
  };
  const job = Ember.Object.create(attributes);
  const component = this.subject({
    job: job
  });
  this.render();
  assert.equal(component.$('.job-lang .label-align').text().trim(), 'Ruby: 2.1.2 Gemfile: foo/Gemfile', 'Gemfile should be displayed in languages section');
  assert.equal(component.$('.job-env .label-align').text().trim(), 'FOO=bar', 'env should be displayed');
});
<commit_msg>Fix typo in unit test<commit_after>import { test, moduleForComponent } from 'ember-qunit';
import Ember from 'ember';

moduleForComponent('jobs-item', 'JobsItemComponent', {
  needs: ['helper:format-duration', 'helper:pretty-date', 'component:status-icon']
});

test('it renders', function (assert) {
  const attributes = {
    id: 10,
    state: 'passed',
    number: '2',
    config: {
      rvm: '2.1.2',
      jdk: 'openjdk6',
      os: 'linux',
      env: 'TESTS=unit'
    },
    duration: 100
  };
  const job = Ember.Object.create(attributes);
  const component = this.subject({
    job: job
  });
  this.render();
  assert.ok(component.$().hasClass('passed'), 'component should have a state class (passed)');
  assert.equal(component.$('.job-number').text().trim(), '2', 'job number should be displayed');
  assert.equal(component.$('.job-lang').text().trim(), 'JDK: openjdk6 Ruby: 2.1.2', 'languages list should be displayed');
  assert.equal(component.$('.job-env').text().trim(), 'TESTS=unit', 'env should be displayed');
  assert.ok(component.$('.job-os').hasClass('linux'), 'OS class should be added for OS icon');
  assert.equal(component.$('.job-duration').text().trim(), '1 min 40 sec', 'duration should be displayed');
});

test('outputs info on not set properties', function (assert) {
  const job = Ember.Object.create();
  const component = this.subject({
    job: job
  });
  this.render();
  assert.ok(component.$('.job-env').text().match(/no environment variables set/), 'a message for no env vars should be displayed');
  assert.ok(component.$('.job-lang').text().match(/no language set/), 'a message about no language being set should be displayed');
});

test('when env is not set, gemfile is displayed in the env section', function (assert) {
  const attributes = {
    id: 10,
    state: 'passed',
    number: '2',
    config: {
      rvm: '2.1.2',
      gemfile: 'foo/Gemfile'
    },
    duration: 100
  };
  const job = Ember.Object.create(attributes);
  const component = this.subject({
    job: job
  });
  this.render();
  assert.equal(component.$('.job-lang .label-align').text().trim(), 'Ruby: 2.1.2', 'languages list should be displayed');
  assert.equal(component.$('.job-env .label-align').text().trim(), 'Gemfile: foo/Gemfile', 'env should be displayed');
});

test('when env is set, gemfile is displayed in the language section', function (assert) {
  const attributes = {
    id: 10,
    state: 'passed',
    number: '2',
    config: {
      rvm: '2.1.2',
      gemfile: 'foo/Gemfile',
      env: 'FOO=bar'
    },
    duration: 100
  };
  const job = Ember.Object.create(attributes);
  const component = this.subject({
    job: job
  });
  this.render();
  assert.equal(component.$('.job-lang .label-align').text().trim(), 'Ruby: 2.1.2 Gemfile: foo/Gemfile', 'Gemfile should be displayed in languages section');
  assert.equal(component.$('.job-env .label-align').text().trim(), 'FOO=bar', 'env should be displayed');
});
<|endoftext|>


<commit_before>'use strict';

angular.module('asc.racing', ['ngRoute'])

.config(['$routeProvider', function($routeProvider) {
  $routeProvider.when('/racing', {
    templateUrl: 'racing/racing.html',
    conroller: 'RacingCtrl'
  });
}])

.controller('RacingCtrl', ['$scope', function($scope) {
    $scope.links = [
        { name: 'Schedule', templatePath: 'racing/articles/race-schedule.html' },
        { name: 'Results', templatePath: 'racing/articles/race-results.html' },
        { name: 'Camps', templatePath: 'racing/articles/race-camps.html' },
        { name: 'Sponsors', templatePath: 'racing/articles/race-sponsors.html' },
        { name: 'Gatekeeper Training', templatePath: 'racing/articles/race-gatekeeping.html' },
        { name: 'Waiver', templatePath: 'forms/Crescent_Racing_Liability_Waiver.pdf' }
    ];

    $scope.changeSelected = function (link) {
        $scope.selectedPath = link.templatePath;
        for (var i = 0; i < $scope.links.length; i++) {
            $scope.links[i].selected = false;
        }
        link.selected = true;
    };

    function onLoad() {
        $scope.changeSelected($scope.links[0]);
    }

    onLoad();
}])

.controller('RacingScheduleCtrl', ['$scope', function($scope) {
    $scope.schedule = [
        { date: 'December 4', place: 'Appalachian Mt, NC (Race Development)', time: '9:45 - 3:00'},
        { date: 'December 17 - 18', place: 'Appalachian Mt, NC', time: '9:30'},
        { date: 'January 07 - 08', place: 'Sugar Mt, NC', time: '9:00'},
        { date: 'January 28 - 29', place: 'Wintergreen Resort, VA', time: ''},
        { date: 'February 1', place: 'Cupp Run Challenge at Snowshoe, WV', time: ''},
        { date: 'February 11 - 12', place: 'Beech Mt, NC', time: '10:00'},
        { date: 'February 25 - 26', place: 'Sugar Mt, NC', time: '9:00'},
        { date: 'March 10 - 12', place: 'Crescent Cup, SilverCreek, WV', time: '8:45 am'}
    ];
}])

.controller('RacingCampsCtrl', ['$scope', function($scope) {
    $scope.camps = [
        { date: 'December 5 - 12', place: 'Steamboat Race Camp', info: 'Sports Am.Tours: 800-876-8551'},
        { date: 'December', place: 'Killington Race Camp', info: '800-621-6867'},
        { date: 'December 6', place: 'CSC Development Training at Appalachian', info: '9:46 am - 3:00 pm'},
        { date: 'December 11 - 13', place: 'Sugar Mt. Race Camp', info: 'Kim Jöchl: 828-898-4521 x224'},
        { date: 'December', place: 'Timberline, WV  Ski School', info: '304-866-4801, Multiple sessions'}
    ];
}])

.controller('RacingSponsorsCtrl', ['$scope', function($scope) {
    $scope.sponsors = [
        { img: 'img/sponsors/aspen-snowmass-logo.png', link: 'https://www.aspensnowmass.com/' },
        { img: 'img/sponsors/dynastar-logo.png', link: 'http://www.dynastar.com/US/US/' },
        { img: 'img/sponsors/jackson-hole-logo.gif', link: 'http://www.jacksonhole.com/' },
        { img: 'img/sponsors/bayou-logo.png', link: 'http://bayousmokehouse.com/#about-us' },
        { img: 'img/sponsors/copper-logo.png', link: 'http://www.coppercolorado.com/winter/index.html' },
        { img: 'img/sponsors/saucer-wax-logo.png', link: 'https://www.saucerwax.net/' },
        { img: 'img/sponsors/alpine-ski-center-logo.png', link: 'https://www.alpineskicenter.com/' },
        { img: 'img/sponsors/samuel-adams-logo.jpg', link: 'https://www.samueladams.com/' },
        { img: 'img/sponsors/canaan-valley-logo.png', link: 'http://canaanresort.com/winter/' },
        { img: 'img/sponsors/steamboat-logo.jpg', link: 'http://www.steamboat.com/' },
        { img: 'img/sponsors/wolfridge-resort-logo.png', link: 'http://www.skiwolfridgenc.com/' },
        { img: 'img/sponsors/telluride-logo.png', link: 'http://www.tellurideskiresort.com/' },
        { img: 'img/sponsors/whitefish-logo.gif', link: 'http://skiwhitefish.com/' },
        { img: 'img/sponsors/banana-boat-logo.png', link: 'http://www.bananaboat.com/' },
        { img: 'img/sponsors/sports-america-logo.png', link: 'https://www.sportsamerica.com/' },
        { img: 'img/sponsors/steamboat-resorts-logo.png', link: 'http://www.steamboat.com/plan-your-trip/lodging/s/steamboat-resorts.aspx' },
        { img: 'img/sponsors/podium-wax-logo.gif', link: 'https://www.podiumwax.com/' },
        { img: 'img/sponsors/leki-logo.png', link: 'http://usa.leki.com/' },
        { img: 'img/sponsors/polarmax-logo.png', link: 'http://www.polarmax.com/' },
        { img: 'img/sponsors/snowmass-village-logo.jpg', link: 'http://www.tosv.com/' },
        { img: 'img/sponsors/point6-logo.jpg', link: 'http://point6.com/' },
        { img: 'img/sponsors/descente-logo.png', link: 'http://www.descente.com/' },
        { img: 'img/sponsors/sportube-logo.jpg', link: 'https://www.sportube.com/' },
        { img: 'img/sponsors/artech-logo.jpg', link: 'http://www.artechski.com/' },
        { img: 'img/sponsors/ski-country-sports-logo.jpg', link: 'https://www.skicountrysports.com/' },
        { img: 'img/sponsors/snowshoe-logo.png', link: 'http://www.snowshoemtn.com/' },
        { img: 'img/sponsors/peet-dryer-logo.png', link: 'http://www.peetdryer.com/' },
        { img: 'img/sponsors/head-logo.png', link: 'http://www.head.com/us/home/' },
        { img: 'img/sponsors/red-bull-logo.png', link: 'http://www.redbull.com/us/en' },
        { img: 'img/sponsors/cataloochee-logo.jpg', link: 'http://cataloochee.com/' },
        { img: 'img/sponsors/skigroup-logo.png', link: 'http://www.skigroup.net/' },
        { img: 'img/sponsors/winter-park-logo.jpg', link: 'http://www.winterparkresort.com/' },
        { img: 'img/sponsors/taos-logo.png', link: 'http://www.skitaos.org/' },
        { img: 'img/sponsors/atomic-logo.png', link: 'https://www.atomic.com/en-us' },
        { img: 'img/sponsors/clif-bar-logo.png', link: 'http://www.clifbar.com/' },
        { img: 'img/sponsors/zardoz-logo.png', link: 'http://www.zardoznotwax.com/' },
        { img: 'img/sponsors/ski-banff-logo.jpg', link: 'http://www.skibanff.com/' },
        { img: 'img/sponsors/spyder-logo.jpg', link: 'http://www.spyder.com/' },
        { img: 'img/sponsors/holidaze-logo.jpg', link: 'http://holidaze.com/' }
    ];
}])
;<commit_msg>sponsor update<commit_after>'use strict';

angular.module('asc.racing', ['ngRoute'])

.config(['$routeProvider', function($routeProvider) {
  $routeProvider.when('/racing', {
    templateUrl: 'racing/racing.html',
    conroller: 'RacingCtrl'
  });
}])

.controller('RacingCtrl', ['$scope', function($scope) {
    $scope.links = [
        { name: 'Schedule', templatePath: 'racing/articles/race-schedule.html' },
        { name: 'Results', templatePath: 'racing/articles/race-results.html' },
        { name: 'Camps', templatePath: 'racing/articles/race-camps.html' },
        { name: 'Sponsors', templatePath: 'racing/articles/race-sponsors.html' },
        { name: 'Gatekeeper Training', templatePath: 'racing/articles/race-gatekeeping.html' },
        { name: 'Waiver', templatePath: 'forms/Crescent_Racing_Liability_Waiver.pdf' }
    ];

    $scope.changeSelected = function (link) {
        $scope.selectedPath = link.templatePath;
        for (var i = 0; i < $scope.links.length; i++) {
            $scope.links[i].selected = false;
        }
        link.selected = true;
    };

    function onLoad() {
        $scope.changeSelected($scope.links[0]);
    }

    onLoad();
}])

.controller('RacingScheduleCtrl', ['$scope', function($scope) {
    $scope.schedule = [
        { date: 'December 4', place: 'Appalachian Mt, NC (Race Development)', time: '9:45 - 3:00'},
        { date: 'December 17 - 18', place: 'Appalachian Mt, NC', time: '9:30'},
        { date: 'January 07 - 08', place: 'Sugar Mt, NC', time: '9:00'},
        { date: 'January 28 - 29', place: 'Wintergreen Resort, VA', time: ''},
        { date: 'February 1', place: 'Cupp Run Challenge at Snowshoe, WV', time: ''},
        { date: 'February 11 - 12', place: 'Beech Mt, NC', time: '10:00'},
        { date: 'February 25 - 26', place: 'Sugar Mt, NC', time: '9:00'},
        { date: 'March 10 - 12', place: 'Crescent Cup, SilverCreek, WV', time: '8:45 am'}
    ];
}])

.controller('RacingCampsCtrl', ['$scope', function($scope) {
    $scope.camps = [
        { date: 'December 5 - 12', place: 'Steamboat Race Camp', info: 'Sports Am.Tours: 800-876-8551'},
        { date: 'December', place: 'Killington Race Camp', info: '800-621-6867'},
        { date: 'December 6', place: 'CSC Development Training at Appalachian', info: '9:46 am - 3:00 pm'},
        { date: 'December 11 - 13', place: 'Sugar Mt. Race Camp', info: 'Kim Jöchl: 828-898-4521 x224'},
        { date: 'December', place: 'Timberline, WV  Ski School', info: '304-866-4801, Multiple sessions'}
    ];
}])

.controller('RacingSponsorsCtrl', ['$scope', function($scope) {
    $scope.sponsors = [
        { img: 'img/sponsors/aspen-snowmass-logo.png', link: 'https://www.aspensnowmass.com/' },
        { img: 'img/sponsors/dynastar-logo.png', link: 'http://www.dynastar.com/US/US/' },
        { img: 'img/sponsors/jackson-hole-logo.gif', link: 'http://www.jacksonhole.com/' },
        { img: 'img/sponsors/bayou-logo.png', link: 'http://bayousmokehouse.com/#about-us' },
        { img: 'img/sponsors/copper-logo.png', link: 'http://www.coppercolorado.com/winter/index.html' },
        { img: 'img/sponsors/saucer-wax-logo.png', link: 'https://www.saucerwax.net/' },
        { img: 'img/sponsors/alpine-ski-center-logo.png', link: 'https://www.alpineskicenter.com/' },
        { img: 'img/sponsors/fuxi-logo.png', link: 'http://www.fuxiracingusa.com/' },
        { img: 'img/sponsors/putnam-logo.jpg', link: 'https://www.putnam.com/' },
        { img: 'img/sponsors/steamboat-logo.jpg', link: 'http://www.steamboat.com/' },
        { img: 'img/sponsors/wolfridge-resort-logo.png', link: 'http://www.skiwolfridgenc.com/' },
        { img: 'img/sponsors/highcountrytaproom-logo.png', link: 'http://highcountrytaproom.weebly.com/' },
        { img: 'img/sponsors/whitefish-logo.gif', link: 'http://skiwhitefish.com/' },
        { img: 'img/sponsors/banana-boat-logo.png', link: 'http://www.bananaboat.com/' },
        { img: 'img/sponsors/sports-america-logo.png', link: 'https://www.sportsamerica.com/' },
        { img: 'img/sponsors/steamboat-resorts-logo.png', link: 'http://www.steamboat.com/plan-your-trip/lodging/s/steamboat-resorts.aspx' },
        { img: 'img/sponsors/podium-wax-logo.gif', link: 'https://www.podiumwax.com/' },
        { img: 'img/sponsors/leki-logo.png', link: 'http://usa.leki.com/' },
        { img: 'img/sponsors/polarmax-logo.png', link: 'http://www.polarmax.com/' },
        { img: 'img/sponsors/addisonfarmsvineyard-logo.png', link: 'http://www.addisonfarms.net/' },
        { img: 'img/sponsors/point6-logo.jpg', link: 'http://point6.com/' },
        { img: 'img/sponsors/descente-logo.png', link: 'http://www.descente.com/' },
        { img: 'img/sponsors/sportube-logo.jpg', link: 'https://www.sportube.com/' },
        { img: 'img/sponsors/swany-logo.jpg', link: 'http://www.swanyamerica.com/' },
        { img: 'img/sponsors/ski-country-sports-logo.jpg', link: 'https://www.skicountrysports.com/' },
        { img: 'img/sponsors/snowshoe-logo.png', link: 'http://www.snowshoemtn.com/' },
        { img: 'img/sponsors/debooter-logo.png', link: 'https://www.outdoorlogicsolutions.com/' },
        { img: 'img/sponsors/head-logo.png', link: 'http://www.head.com/us/home/' },
        { img: 'img/sponsors/mammothlakes-logo.png', link: 'http://www.ci.mammoth-lakes.ca.us/' },
        { img: 'img/sponsors/cataloochee-logo.jpg', link: 'http://cataloochee.com/' },
        { img: 'img/sponsors/mammoth-logo.png', link: 'http://www.mammothmountain.com/' },
        { img: 'img/sponsors/winter-park-logo.jpg', link: 'http://www.winterparkresort.com/' },
        { img: 'img/sponsors/wintergreen-logo.png', link: 'http://www.wintergreenresort.com/' },
        { img: 'img/sponsors/atomic-logo.png', link: 'https://www.atomic.com/en-us' },
        { img: 'img/sponsors/clif-bar-logo.png', link: 'http://www.clifbar.com/' },
        { img: 'img/sponsors/zardoz-logo.png', link: 'http://www.zardoznotwax.com/' },
        { img: 'img/sponsors/spyder-logo.jpg', link: 'http://www.spyder.com/' }
    ];
}])
;<|endoftext|>


<commit_before>defineClass(org.helma.wrappers.ByteArray);
export('ByteArray');

__shared__ = true;

Object.defineProperty(String.prototype, 'toByteArray', {
    value: function(charset) {
        charset = charset || 'utf8';
        return new ByteArray(String(this), charset);
    }
});

Object.defineProperty(ByteArray.prototype, 'reverse', {
    value: function() {
        return Array.reverse(this);
    }
});

Object.defineProperty(ByteArray.prototype, 'sort', {
    value: function(fn) {
        fn = fn || function(a, b) a - b;
        return Array.sort(this, fn);
    }
});

Object.defineProperty(ByteArray.prototype, 'forEach', {
    value: function(fn, thisObj) {
        Array.forEach(this, fn, thisObj);
    }
});

Object.defineProperty(ByteArray.prototype, 'filter', {
    value: function(fn, thisObj) {
        return Array.filter(this, fn, thisObj);
    }
});

Object.defineProperty(ByteArray.prototype, 'some', {
    value: function(fn, thisObj) {
        return Array.some(this, fn, thisObj);
    }
});

Object.defineProperty(ByteArray.prototype, 'every', {
    value: function(fn, thisObj) {
        return Array.every(this, fn, thisObj);
    }
});

Object.defineProperty(ByteArray.prototype, 'map', {
    value: function(fn, thisObj) {
        return Array.map(this, fn, thisObj);
    }
});

Object.defineProperty(ByteArray.prototype, 'reduce', {
    value: function(fn, initialValue) {
        return initialValue === undefined ?
               Array.reduce(this, fn) :
               Array.reduce(this, fn, initialValue);
    }
});

Object.defineProperty(ByteArray.prototype, 'reduceRight', {
    value: function(fn, initialValue) {
        return initialValue === undefined ?
               Array.reduceRight(this, fn) :
               Array.reduceRight(this, fn, initialValue);
    }
});<commit_msg>Implement ByteArray.pop() and .push()<commit_after>defineClass(org.helma.wrappers.ByteArray);
export('ByteArray');

__shared__ = true;

Object.defineProperty(String.prototype, 'toByteArray', {
    value: function(charset) {
        charset = charset || 'utf8';
        return new ByteArray(String(this), charset);
    }
});

Object.defineProperty(ByteArray.prototype, 'reverse', {
    value: function() {
        return Array.reverse(this);
    }
});

Object.defineProperty(ByteArray.prototype, 'sort', {
    value: function(fn) {
        fn = fn || function(a, b) a - b;
        return Array.sort(this, fn);
    }
});

Object.defineProperty(ByteArray.prototype, 'forEach', {
    value: function(fn, thisObj) {
        Array.forEach(this, fn, thisObj);
    }
});

Object.defineProperty(ByteArray.prototype, 'filter', {
    value: function(fn, thisObj) {
        return Array.filter(this, fn, thisObj);
    }
});

Object.defineProperty(ByteArray.prototype, 'some', {
    value: function(fn, thisObj) {
        return Array.some(this, fn, thisObj);
    }
});

Object.defineProperty(ByteArray.prototype, 'every', {
    value: function(fn, thisObj) {
        return Array.every(this, fn, thisObj);
    }
});

Object.defineProperty(ByteArray.prototype, 'map', {
    value: function(fn, thisObj) {
        return Array.map(this, fn, thisObj);
    }
});

Object.defineProperty(ByteArray.prototype, 'reduce', {
    value: function(fn, initialValue) {
        return initialValue === undefined ?
               Array.reduce(this, fn) :
               Array.reduce(this, fn, initialValue);
    }
});

Object.defineProperty(ByteArray.prototype, 'reduceRight', {
    value: function(fn, initialValue) {
        return initialValue === undefined ?
               Array.reduceRight(this, fn) :
               Array.reduceRight(this, fn, initialValue);
    }
});

Object.defineProperty(ByteArray.prototype, 'pop', {
    value: function() {
        return Array.pop(this);
    }
});

Object.defineProperty(ByteArray.prototype, 'push', {
    value: function() {
        for (var i = 0; i < arguments.length; i++) {
            this[this.length] = arguments[i];
        }
        return this.length;
    }
});
<|endoftext|>


<commit_before>(() => {
    if (self && self.isCriticalInit) return true

    self.importJS = (uri) => {
        let s = document.createElement('script')
        s.src = uri
        s.type = "text/javascript"
        s.async = false
        document.getElementsByTagName('head')[0].appendChild(s)
    }

    // if Object.assign not supported, load /client/critical-old-ie.js
    if (typeof Object.assign != 'function') self.importJS(
        typeof __CRITICAL_EXTRA_OLD_IE_FILENAME__ == 'undefined' ? "/client/critical-extra-old-ie.js" : __CRITICAL_EXTRA_OLD_IE_FILENAME__
    )

    require('./critical.g.less')

    document.addEventListener("DOMContentLoaded", function () {
        let tagHtml = document.getElementsByTagName('html')
        self.isMobile = false
        let platform = 'not-specified'

        if (tagHtml && tagHtml.length) {
            tagHtml = tagHtml[0]
            if (typeof navigator !== 'undefined') {
                const UA = navigator.userAgent
                if (/Android|HTC/i.test(UA)) {
                    self.isMobile = true
                    platform = 'android'
                } else if (/iPad/i.test(UA)) {
                    // iPad
                    self.isMobile = true
                    platform = 'ios'
                } else if (/iPod|iPhone/i.test(UA)) {
                    // iPhone
                    self.isMobile = true
                    platform = 'ios'
                } else if (/Mobile/i.test(UA) && /Safari/i.test(UA)) {
                    // general iOS
                    self.isMobile = true
                    platform = 'ios'
                }
            }
            if (self.isMobile) {
                tagHtml.classList.add('is-mobile')
            }
            if (platform) {
                tagHtml.classList.add('platform-' + platform)
            }
        }
    })

    self.isCriticalInit = true
})()
<commit_msg>re-implement<commit_after>import bindEvent from 'bind-event'

(() => {
    if (self && self.isCriticalInit) return true

    self.importJS = (uri) => {
        let s = document.createElement('script')
        s.src = uri
        s.type = "text/javascript"
        s.async = false
        document.getElementsByTagName('head')[0].appendChild(s)
    }

    // if Object.assign not supported, load /client/critical-old-ie.js
    if (typeof Object.assign != 'function') self.importJS(
        typeof __CRITICAL_EXTRA_OLD_IE_FILENAME__ == 'undefined' ? "/client/critical-extra-old-ie.js" : __CRITICAL_EXTRA_OLD_IE_FILENAME__
    )

    require('./critical.g.less')

    document.addEventListener("DOMContentLoaded", function () {
        let boatLoader = document.createElement('div')
        let tagHtml = document.getElementsByTagName('html')
        self.isMobile = false
        let platform = 'not-specified'

        boatLoader.id = 'boat-loader'
        document.body.appendChild(boatLoader)
        bindEvent(
            boatLoader,
            'transitionend',
            function (evt) {
                // console.log(evt, evt.target.style.opacity)
                if (evt.propertyName == 'opacity' && !evt.target.style.opacity)
                    evt.target.parentNode.removeChild(evt.target)
            }
        )

        if (tagHtml && tagHtml.length) {
            tagHtml = tagHtml[0]
            if (typeof navigator !== 'undefined') {
                const UA = navigator.userAgent
                if (/Android|HTC/i.test(UA)) {
                    self.isMobile = true
                    platform = 'android'
                } else if (/iPad/i.test(UA)) {
                    // iPad
                    self.isMobile = true
                    platform = 'ios'
                } else if (/iPod|iPhone/i.test(UA)) {
                    // iPhone
                    self.isMobile = true
                    platform = 'ios'
                } else if (/Mobile/i.test(UA) && /Safari/i.test(UA)) {
                    // general iOS
                    self.isMobile = true
                    platform = 'ios'
                }
            }
            if (self.isMobile) {
                tagHtml.classList.add('is-mobile')
            }
            if (platform) {
                tagHtml.classList.add('platform-' + platform)
            }
        }
    })

    // [nw.js] show and focus window
    if (self.nw && self.nw.win) {
        self.nw.win.show()
        self.nw.win.focus()
    }

    self.isCriticalInit = true
})()
<|endoftext|>