<commit_before>const path = require('path');
const log = require('./utils/log');
const webpack = require('webpack');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const npmSass = require('npm-sass');
const autoprefixer = require('autoprefixer');
const postcssDiscardDuplicates = require('postcss-discard-duplicates');
const ManifestPlugin = require('webpack-manifest-plugin');
const sassImportGlobbing = require('@theme-tools/sass-import-globbing');
const { getBoltManifest, createComponentsManifest } = require('./utils/manifest');
const { promisify } = require('util');
const fs = require('fs');
const readFile = promisify(fs.readFile);
const deepmerge = require('deepmerge');

function createConfig(config) {
  // @TODO: move this setting to .boltrc config
  const sassExportData = require('@theme-tools/sass-export-data')({
    path: path.resolve(process.cwd(), config.dataDir),
  });

  // Default global Sass data defined
  let globalSassData = [
    `$bolt-namespace: ${config.namespace};`,
  ]

  // Default global JS data defined
  let globalJsData = {
    'process.env.NODE_ENV': config.prod ?
      JSON.stringify('production') :
      JSON.stringify('development'),
    bolt: {
      namespace: JSON.stringify(config.namespace),
    },
  };

  // Merge together global Sass data overrides specified in a .boltrc config
  if (config.globalData.scss && config.globalData.scss.length !== 0){
    const overrideItems = [];
    config.globalData.scss.forEach((item) => {
      try {
        const file = fs.readFileSync(item, 'utf8');
        file
          .split('\n')
          .filter(x => x)
          .forEach(x => overrideItems.push(x));
      } catch(err) {
        log.errorAndExit(`Could not find ${item}`, err);
      }
    });

    globalSassData = [...globalSassData, ...overrideItems];
  }

  // Merge together any global JS data overrides
  if (config.globalData.js && config.globalData.js.length !== 0){
    const overrideJsItems = [];
    config.globalData.js.forEach((item) => {
      try {
        const overrideFile = require(path.resolve(process.cwd(), item));
        overrideJsItems.push(overrideFile);
      } catch(err) {
        log.errorAndExit(`Could not find ${item} file`, err);
      }
    });

    globalJsData = deepmerge(globalJsData, ...overrideJsItems);
  }

  /**
   * Build WebPack config's `entry` object
   * @link https://webpack.js.org/configuration/entry-context/#entry
   * @returns {object} entry - WebPack config `entry`
   */
  function buildWebpackEntry() {
    const { components } = getBoltManifest();
    const entry = {};
    if (components.global) {
      entry['bolt-global'] = [];
      components.global.forEach((component) => {
        if (component.assets.style) entry['bolt-global'].push(component.assets.style);
        if (component.assets.main) entry['bolt-global'].push(component.assets.main);
      });
    }
    if (components.individual) {
      components.individual.forEach((component) => {
        const files = [];
        if (component.assets.style) files.push(component.assets.style);
        if (component.assets.main) files.push(component.assets.main);
        if (files) {
          entry[component.basicName] = files;
        }
      });
    }
    if (config.verbosity > 4) {
      log.info('WebPack `entry`:');
      console.log(entry);
    }
    return entry;
  }

  // Map out the global config verbosity setting to the 6 preset levels of Webpack stats: https://webpack.js.org/configuration/stats/#stats + https://github.com/webpack/webpack/blob/b059e07cf90db871fe9497f5c14b9383fc71d2ad/lib/Stats.js#L906

  const webpackStats = {
    0: 'none', // Output nothing
    1: 'errors-only', // only output when errors happen
    2: 'minimal', // only output when errors or new compilation happen
    3: 'normal', // standard output
    4: 'detailed',
    5: 'verbose', // output everything
  };

  function statsPreset(name) {
    /**
     * Accepted values: none, errors-only, minimal, normal, detailed,
     * verbose. Any other falsy value will behave as 'none', truthy
     * values as 'normal'
     */
    const pn = (typeof name === 'string') && name.toLowerCase() || name || 'none';

    switch (pn) {
      case 'none':
        return {
          all: false,
        };
      case 'verbose':
        return {
          entrypoints: true,
          modules: false,
          colors: true,
          chunks: true,
          chunkModules: true,
          chunkOrigins: true,
          depth: true,
          env: true,
          reasons: true,
          usedExports: true,
          providedExports: true,
          optimizationBailout: true,
          errorDetails: true,
          publicPath: true,
          exclude: () => false,
          maxModules: Infinity,
        };
      case 'detailed':
        return {
          entrypoints: true,
          chunks: true,
          colors: true,
          chunkModules: false,
          chunkOrigins: true,
          depth: true,
          usedExports: true,
          providedExports: true,
          optimizationBailout: true,
          errorDetails: true,
          publicPath: true,
          exclude: () => false,
          maxModules: Infinity,
        };
      case 'minimal':
        return {
          all: false,
          colors: true,
          modules: true,
          maxModules: 0,
          errors: true,
          warnings: true,
        };
      case 'errors-only':
        return {
          all: false,
          colors: true,
          errors: true,
          moduleTrace: true,
        };
      default:
        return {
          colors: true,
        };
    }
  }

  // Output CSS module data as JSON.
  // @todo: enable when ready for CSS Modules
  // function getJSONFromCssModules(cssFileName, json) {
  //   const cssName = path.basename(cssFileName, '.css');
  //   const jsonFileName = path.resolve(process.cwd(), config.buildDir, `${cssName}.json`);
  //   fs.writeFileSync(jsonFileName, JSON.stringify(json));
  // }

  /** This workaround has been disabled for now as setting
    * `modules: false` on `css-loader` fixes it; see https://github.com/bolt-design-system/bolt/pull/410
    * Workaround for getting classes with `\@` to compile correctly
    * CSS Classes like `.u-hide\@large` were getting compiled like `.u-hide-large`.
    * Due to this bug: https://github.com/webpack-contrib/css-loader/issues/578
    * Workaround: using the `string-replace-loader` to
    * change `\@` to our `workaroundAtValue` before
    * passing to `css-loader`, then turning it back
    * afterwards.
    */
  const workaroundAtValue = '-theSlashSymbol-';

  const scssLoaders = [
    // {
    //   loader: 'string-replace-loader',
    //   query: {
    //     search: workaroundAtValue,
    //     replace: String.raw`\\`, // needed to ensure `\` comes through
    //     flags: 'g',
    //   },
    // },
    {
      loader: 'css-loader',
      options: {
        sourceMap: true,
        modules: false, // needed for JS referencing classNames directly, such as critical fonts
        importLoaders: 4,
      },
    },
    // {
    //   loader: 'string-replace-loader',
    //   query: {
    //     search: /\\/,
    //     replace: workaroundAtValue,
    //     flags: 'g',
    //   },
    // },
    {
      loader: 'postcss-loader',
      options: {
        sourceMap: true,
        plugins: () => [
          postcssDiscardDuplicates,
          autoprefixer,
        ],
      },
    },
    {
      loader: 'clean-css-loader',
      options: {
        skipWarn: true,
        compatibility: 'ie9',
        level: config.prod ? 1 : 0,
        inline: ['remote'],
        format: 'beautify',
      },
    },
    {
      loader: 'resolve-url-loader',
    },
    {
      loader: 'sass-loader',
      options: {
        sourceMap: true,
        importer: [
          sassImportGlobbing,
          npmSass.importer,
        ],
        functions: sassExportData,
        outputStyle: 'expanded',
        precision: 2,
        data: globalSassData.join('\n'),
      },
    },
  ];

  // The publicPath config sets the client-side base path for all built / asynchronously loaded assets. By default the loader script will automatically figure out the relative path to load your components, but uses publicPath as a fallback. It's recommended to have it start with a `/`. Note: this ONLY sets the base path the browser requests -- it does not set where files are saved during build. To change where files are saved at build time, use the buildDir config.
  // Must start and end with `/`
  // conditional is temp workaround for when servers are disabled via absence of `config.wwwDir`
  const publicPath = config.publicPath ? config.publicPath : (config.wwwDir
    ? `/${path.relative(config.wwwDir, config.buildDir)}/`
    : config.buildDir); // @todo Ensure ends with `/` or we can get `distfonts/` instead of `dist/fonts/`

  // THIS IS IT!! The object that gets passed in as WebPack's config object.
  const webpackConfig = {
    entry: buildWebpackEntry(),
    output: {
      path: path.resolve(process.cwd(), config.buildDir),
      filename: '[name].js',
      chunkFilename: '[name]-bundle.[chunkhash].js',
      publicPath,
    },
    cache: true,
    resolve: {
      extensions: ['.js', '.jsx', '.json', '.svg', '.scss'],
      unsafeCache: true,
    },
    module: {
      rules: [
        {
          test: /\.scss$/,
          oneOf: [
            {
              issuer: /\.js$/,
              use: [
                scssLoaders,
              ].reduce((acc, val) => acc.concat(val), []),
            },
            {
              // no issuer here as it has a bug when its an entry point - https://github.com/webpack/webpack/issues/5906
              use: [
                {
                  loader: MiniCssExtractPlugin.loader,
                },
                scssLoaders,
              ].reduce((acc, val) => acc.concat(val), []),
            },
          ],
        },
        {
          test: /\.js$/,
          exclude: /(node_modules\/\@webcomponents\/webcomponentsjs\/custom-elements-es5-adapter\.js)/,
          use: {
            loader: 'babel-loader',
            options: {
              cacheDirectory: true,
              babelrc: false,
              presets: ['@bolt/babel-preset-bolt'],
            },
          },
        },
        {
          test: /\.(woff|woff2)$/,
          loader: 'file-loader',
          options: {
            name: 'fonts/[name].[ext]',
          },
        },
        {
          test: /\.svg$/,
          loader: 'file-loader',
          options: {
            name: '[name].[ext]',
          },
        },
        // {
        //   test: [/\.json$/],
        //   use: [
        //     {
        //       loader: 'json-loader',
        //     },
        //   ],
        // },
        {
          test: [/\.yml$/, /\.yaml$/],
          use: [
            { loader: 'json-loader' },
            { loader: 'yaml-loader' },
          ],
        },
      ],
    },
    mode: config.prod ? 'production' : 'development',
    optimization: {
      mergeDuplicateChunks: true,
      // splitChunks: {
      //   chunks: 'all',
      //   // cacheGroups: {
      //   //   js: {
      //   //     test: /\.js$/,
      //   //     // name: 'commons',
      //   //     chunks: 'all',
      //   //     minChunks: 2,
      //   //     // test: /node_modules/,
      //   //     // enforce: true,
      //   //   },
      //   //   css: {
      //   //     test: /\.s?css$/,
      //   //     chunks: 'all',
      //   //     minChunks: 2,
      //   //     // enforce: true,
      //   //   },
      //   // },
      // },
    },
    plugins: [
      // Ignore generated output if generated output is on a dependency chain (causes endless loop)
      new webpack.WatchIgnorePlugin([
        /dist\/styleguide/,
        /dist\/annotations/,
        /styleguide/,
        path.join(__dirname, 'node_modules'),
      ]),
      new webpack.IgnorePlugin(/vertx/), // needed to ignore vertx dependency in webcomponentsjs-lite
      new MiniCssExtractPlugin({
        // Options similar to the same options in webpackOptions.output
        // both options are optional
        filename: '[name].css',
        chunkFilename: '[id].css',
        allChunks: true,
      }),
      // @todo This needs to be in `config.dataDir`
      new ManifestPlugin({
        fileName: 'bolt-webpack-manifest.json',
        publicPath,
        writeToFileEmit: true,
        seed: {
          name: 'Bolt Manifest',
        },
      }),
      new webpack.ProvidePlugin({
        Promise: 'es6-promise',
      }),
      new webpack.DefinePlugin(globalJsData),
      // Show build progress
      // Disabling for now as it messes up spinners
      // @todo consider bringing it back
      // new webpack.ProgressPlugin({ profile: false }),
    ],
  };

  if (!config.prod) {
    webpackConfig.plugins.push(
      new webpack.HotModuleReplacementPlugin(),
    );
  }

  if (config.prod) {
    // Optimize JS - https://webpack.js.org/plugins/uglifyjs-webpack-plugin/
    // Config recommendation based off of https://slack.engineering/keep-webpack-fast-a-field-guide-for-better-build-performance-f56a5995e8f1#f548
    webpackConfig.plugins.push(new UglifyJsPlugin({
      sourceMap: true,
      parallel: true,
      cache: true,
      uglifyOptions: {
        cache: true,
        compress: true,

        mangle: true,
      },
    }));

    // https://webpack.js.org/plugins/module-concatenation-plugin/
    webpackConfig.plugins.push(new webpack.optimize.ModuleConcatenationPlugin());

    // Optimize CSS - https://github.com/NMFR/optimize-css-assets-webpack-plugin
    webpackConfig.plugins.push(new OptimizeCssAssetsPlugin({
      canPrint: config.verbosity > 2,
      cssProcessorOptions: {// passes to `cssnano`
        zindex: false, // don't alter `z-index` values
        mergeRules: false, // this MUST be disabled - otherwise certain selectors (ex. ::slotted(*), which IE 11 can't parse) break
      },
    }));

    // @todo Evaluate best source map approach for production
    webpackConfig.devtool = 'hidden-source-map';
  } else { // not prod
    // @todo fix source maps
    // webpackConfig.devtool = 'cheap-module-eval-source-map';
    webpackConfig.devtool = 'eval';
  }

  if (config.wwwDir) {
    webpackConfig.devServer = {
      contentBase: [
        path.resolve(process.cwd(), config.wwwDir),
      // @TODO: add Pattern Lab Styleguidekit Assets Default dist path here
      ],
      compress: true,
      clientLogLevel: 'none',
      port: 8080,
      stats: statsPreset(webpackStats[config.verbosity]),
      overlay: {
        errors: true,
      },
      hot: !!config.prod,
      inline: true,
      noInfo: true, // webpackTasks.watch handles output info related to success & failure
      publicPath,
      watchContentBase: true,
      historyApiFallback: true,
      watchOptions: {
        aggregateTimeout: 200,
    //    ignored: /(annotations|fonts|bower_components|dist\/styleguide|node_modules|styleguide|images|fonts|assets)/
       // Poll using interval (in ms, accepts boolean too)
      },
    };
  }

  return webpackConfig;
}

module.exports = createConfig;
<commit_msg>fix: Fix autoprefixer in Drupal builds (WWWD-2139)<commit_after>const path = require('path');
const log = require('./utils/log');
const webpack = require('webpack');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const npmSass = require('npm-sass');
const autoprefixer = require('autoprefixer');
const postcssDiscardDuplicates = require('postcss-discard-duplicates');
const ManifestPlugin = require('webpack-manifest-plugin');
const sassImportGlobbing = require('@theme-tools/sass-import-globbing');
const { getBoltManifest, createComponentsManifest } = require('./utils/manifest');
const { promisify } = require('util');
const fs = require('fs');
const readFile = promisify(fs.readFile);
const deepmerge = require('deepmerge');

function createConfig(config) {
  // @TODO: move this setting to .boltrc config
  const sassExportData = require('@theme-tools/sass-export-data')({
    path: path.resolve(process.cwd(), config.dataDir),
  });

  // Default global Sass data defined
  let globalSassData = [
    `$bolt-namespace: ${config.namespace};`,
  ]

  // Default global JS data defined
  let globalJsData = {
    'process.env.NODE_ENV': config.prod ?
      JSON.stringify('production') :
      JSON.stringify('development'),
    bolt: {
      namespace: JSON.stringify(config.namespace),
    },
  };

  // Merge together global Sass data overrides specified in a .boltrc config
  if (config.globalData.scss && config.globalData.scss.length !== 0){
    const overrideItems = [];
    config.globalData.scss.forEach((item) => {
      try {
        const file = fs.readFileSync(item, 'utf8');
        file
          .split('\n')
          .filter(x => x)
          .forEach(x => overrideItems.push(x));
      } catch(err) {
        log.errorAndExit(`Could not find ${item}`, err);
      }
    });

    globalSassData = [...globalSassData, ...overrideItems];
  }

  // Merge together any global JS data overrides
  if (config.globalData.js && config.globalData.js.length !== 0){
    const overrideJsItems = [];
    config.globalData.js.forEach((item) => {
      try {
        const overrideFile = require(path.resolve(process.cwd(), item));
        overrideJsItems.push(overrideFile);
      } catch(err) {
        log.errorAndExit(`Could not find ${item} file`, err);
      }
    });

    globalJsData = deepmerge(globalJsData, ...overrideJsItems);
  }

  /**
   * Build WebPack config's `entry` object
   * @link https://webpack.js.org/configuration/entry-context/#entry
   * @returns {object} entry - WebPack config `entry`
   */
  function buildWebpackEntry() {
    const { components } = getBoltManifest();
    const entry = {};
    if (components.global) {
      entry['bolt-global'] = [];
      components.global.forEach((component) => {
        if (component.assets.style) entry['bolt-global'].push(component.assets.style);
        if (component.assets.main) entry['bolt-global'].push(component.assets.main);
      });
    }
    if (components.individual) {
      components.individual.forEach((component) => {
        const files = [];
        if (component.assets.style) files.push(component.assets.style);
        if (component.assets.main) files.push(component.assets.main);
        if (files) {
          entry[component.basicName] = files;
        }
      });
    }
    if (config.verbosity > 4) {
      log.info('WebPack `entry`:');
      console.log(entry);
    }
    return entry;
  }

  // Map out the global config verbosity setting to the 6 preset levels of Webpack stats: https://webpack.js.org/configuration/stats/#stats + https://github.com/webpack/webpack/blob/b059e07cf90db871fe9497f5c14b9383fc71d2ad/lib/Stats.js#L906

  const webpackStats = {
    0: 'none', // Output nothing
    1: 'errors-only', // only output when errors happen
    2: 'minimal', // only output when errors or new compilation happen
    3: 'normal', // standard output
    4: 'detailed',
    5: 'verbose', // output everything
  };

  function statsPreset(name) {
    /**
     * Accepted values: none, errors-only, minimal, normal, detailed,
     * verbose. Any other falsy value will behave as 'none', truthy
     * values as 'normal'
     */
    const pn = (typeof name === 'string') && name.toLowerCase() || name || 'none';

    switch (pn) {
      case 'none':
        return {
          all: false,
        };
      case 'verbose':
        return {
          entrypoints: true,
          modules: false,
          colors: true,
          chunks: true,
          chunkModules: true,
          chunkOrigins: true,
          depth: true,
          env: true,
          reasons: true,
          usedExports: true,
          providedExports: true,
          optimizationBailout: true,
          errorDetails: true,
          publicPath: true,
          exclude: () => false,
          maxModules: Infinity,
        };
      case 'detailed':
        return {
          entrypoints: true,
          chunks: true,
          colors: true,
          chunkModules: false,
          chunkOrigins: true,
          depth: true,
          usedExports: true,
          providedExports: true,
          optimizationBailout: true,
          errorDetails: true,
          publicPath: true,
          exclude: () => false,
          maxModules: Infinity,
        };
      case 'minimal':
        return {
          all: false,
          colors: true,
          modules: true,
          maxModules: 0,
          errors: true,
          warnings: true,
        };
      case 'errors-only':
        return {
          all: false,
          colors: true,
          errors: true,
          moduleTrace: true,
        };
      default:
        return {
          colors: true,
        };
    }
  }

  // Output CSS module data as JSON.
  // @todo: enable when ready for CSS Modules
  // function getJSONFromCssModules(cssFileName, json) {
  //   const cssName = path.basename(cssFileName, '.css');
  //   const jsonFileName = path.resolve(process.cwd(), config.buildDir, `${cssName}.json`);
  //   fs.writeFileSync(jsonFileName, JSON.stringify(json));
  // }

  /** This workaround has been disabled for now as setting
    * `modules: false` on `css-loader` fixes it; see https://github.com/bolt-design-system/bolt/pull/410
    * Workaround for getting classes with `\@` to compile correctly
    * CSS Classes like `.u-hide\@large` were getting compiled like `.u-hide-large`.
    * Due to this bug: https://github.com/webpack-contrib/css-loader/issues/578
    * Workaround: using the `string-replace-loader` to
    * change `\@` to our `workaroundAtValue` before
    * passing to `css-loader`, then turning it back
    * afterwards.
    */
  const workaroundAtValue = '-theSlashSymbol-';

  const scssLoaders = [
    // {
    //   loader: 'string-replace-loader',
    //   query: {
    //     search: workaroundAtValue,
    //     replace: String.raw`\\`, // needed to ensure `\` comes through
    //     flags: 'g',
    //   },
    // },
    {
      loader: 'css-loader',
      options: {
        sourceMap: true,
        modules: false, // needed for JS referencing classNames directly, such as critical fonts
        importLoaders: 4,
      },
    },
    // {
    //   loader: 'string-replace-loader',
    //   query: {
    //     search: /\\/,
    //     replace: workaroundAtValue,
    //     flags: 'g',
    //   },
    // },
    {
      loader: 'postcss-loader',
      options: {
        sourceMap: true,
        plugins: () => [
          postcssDiscardDuplicates,
          autoprefixer({browsers: ['last 2 versions', 'IE 11']}),
        ],
      },
    },
    {
      loader: 'clean-css-loader',
      options: {
        skipWarn: true,
        compatibility: 'ie9',
        level: config.prod ? 1 : 0,
        inline: ['remote'],
        format: 'beautify',
      },
    },
    {
      loader: 'resolve-url-loader',
    },
    {
      loader: 'sass-loader',
      options: {
        sourceMap: true,
        importer: [
          sassImportGlobbing,
          npmSass.importer,
        ],
        functions: sassExportData,
        outputStyle: 'expanded',
        precision: 2,
        data: globalSassData.join('\n'),
      },
    },
  ];

  // The publicPath config sets the client-side base path for all built / asynchronously loaded assets. By default the loader script will automatically figure out the relative path to load your components, but uses publicPath as a fallback. It's recommended to have it start with a `/`. Note: this ONLY sets the base path the browser requests -- it does not set where files are saved during build. To change where files are saved at build time, use the buildDir config.
  // Must start and end with `/`
  // conditional is temp workaround for when servers are disabled via absence of `config.wwwDir`
  const publicPath = config.publicPath ? config.publicPath : (config.wwwDir
    ? `/${path.relative(config.wwwDir, config.buildDir)}/`
    : config.buildDir); // @todo Ensure ends with `/` or we can get `distfonts/` instead of `dist/fonts/`

  // THIS IS IT!! The object that gets passed in as WebPack's config object.
  const webpackConfig = {
    entry: buildWebpackEntry(),
    output: {
      path: path.resolve(process.cwd(), config.buildDir),
      filename: '[name].js',
      chunkFilename: '[name]-bundle.[chunkhash].js',
      publicPath,
    },
    cache: true,
    resolve: {
      extensions: ['.js', '.jsx', '.json', '.svg', '.scss'],
      unsafeCache: true,
    },
    module: {
      rules: [
        {
          test: /\.scss$/,
          oneOf: [
            {
              issuer: /\.js$/,
              use: [
                scssLoaders,
              ].reduce((acc, val) => acc.concat(val), []),
            },
            {
              // no issuer here as it has a bug when its an entry point - https://github.com/webpack/webpack/issues/5906
              use: [
                {
                  loader: MiniCssExtractPlugin.loader,
                },
                scssLoaders,
              ].reduce((acc, val) => acc.concat(val), []),
            },
          ],
        },
        {
          test: /\.js$/,
          exclude: /(node_modules\/\@webcomponents\/webcomponentsjs\/custom-elements-es5-adapter\.js)/,
          use: {
            loader: 'babel-loader',
            options: {
              cacheDirectory: true,
              babelrc: false,
              presets: ['@bolt/babel-preset-bolt'],
            },
          },
        },
        {
          test: /\.(woff|woff2)$/,
          loader: 'file-loader',
          options: {
            name: 'fonts/[name].[ext]',
          },
        },
        {
          test: /\.svg$/,
          loader: 'file-loader',
          options: {
            name: '[name].[ext]',
          },
        },
        // {
        //   test: [/\.json$/],
        //   use: [
        //     {
        //       loader: 'json-loader',
        //     },
        //   ],
        // },
        {
          test: [/\.yml$/, /\.yaml$/],
          use: [
            { loader: 'json-loader' },
            { loader: 'yaml-loader' },
          ],
        },
      ],
    },
    mode: config.prod ? 'production' : 'development',
    optimization: {
      mergeDuplicateChunks: true,
      // splitChunks: {
      //   chunks: 'all',
      //   // cacheGroups: {
      //   //   js: {
      //   //     test: /\.js$/,
      //   //     // name: 'commons',
      //   //     chunks: 'all',
      //   //     minChunks: 2,
      //   //     // test: /node_modules/,
      //   //     // enforce: true,
      //   //   },
      //   //   css: {
      //   //     test: /\.s?css$/,
      //   //     chunks: 'all',
      //   //     minChunks: 2,
      //   //     // enforce: true,
      //   //   },
      //   // },
      // },
    },
    plugins: [
      // Ignore generated output if generated output is on a dependency chain (causes endless loop)
      new webpack.WatchIgnorePlugin([
        /dist\/styleguide/,
        /dist\/annotations/,
        /styleguide/,
        path.join(__dirname, 'node_modules'),
      ]),
      new webpack.IgnorePlugin(/vertx/), // needed to ignore vertx dependency in webcomponentsjs-lite
      new MiniCssExtractPlugin({
        // Options similar to the same options in webpackOptions.output
        // both options are optional
        filename: '[name].css',
        chunkFilename: '[id].css',
        allChunks: true,
      }),
      // @todo This needs to be in `config.dataDir`
      new ManifestPlugin({
        fileName: 'bolt-webpack-manifest.json',
        publicPath,
        writeToFileEmit: true,
        seed: {
          name: 'Bolt Manifest',
        },
      }),
      new webpack.ProvidePlugin({
        Promise: 'es6-promise',
      }),
      new webpack.DefinePlugin(globalJsData),
      // Show build progress
      // Disabling for now as it messes up spinners
      // @todo consider bringing it back
      // new webpack.ProgressPlugin({ profile: false }),
    ],
  };

  if (!config.prod) {
    webpackConfig.plugins.push(
      new webpack.HotModuleReplacementPlugin(),
    );
  }

  if (config.prod) {
    // Optimize JS - https://webpack.js.org/plugins/uglifyjs-webpack-plugin/
    // Config recommendation based off of https://slack.engineering/keep-webpack-fast-a-field-guide-for-better-build-performance-f56a5995e8f1#f548
    webpackConfig.plugins.push(new UglifyJsPlugin({
      sourceMap: true,
      parallel: true,
      cache: true,
      uglifyOptions: {
        cache: true,
        compress: true,

        mangle: true,
      },
    }));

    // https://webpack.js.org/plugins/module-concatenation-plugin/
    webpackConfig.plugins.push(new webpack.optimize.ModuleConcatenationPlugin());

    // Optimize CSS - https://github.com/NMFR/optimize-css-assets-webpack-plugin
    webpackConfig.plugins.push(new OptimizeCssAssetsPlugin({
      canPrint: config.verbosity > 2,
      cssProcessorOptions: {// passes to `cssnano`
        zindex: false, // don't alter `z-index` values
        mergeRules: false, // this MUST be disabled - otherwise certain selectors (ex. ::slotted(*), which IE 11 can't parse) break
      },
    }));

    // @todo Evaluate best source map approach for production
    webpackConfig.devtool = 'hidden-source-map';
  } else { // not prod
    // @todo fix source maps
    // webpackConfig.devtool = 'cheap-module-eval-source-map';
    webpackConfig.devtool = 'eval';
  }

  if (config.wwwDir) {
    webpackConfig.devServer = {
      contentBase: [
        path.resolve(process.cwd(), config.wwwDir),
      // @TODO: add Pattern Lab Styleguidekit Assets Default dist path here
      ],
      compress: true,
      clientLogLevel: 'none',
      port: 8080,
      stats: statsPreset(webpackStats[config.verbosity]),
      overlay: {
        errors: true,
      },
      hot: !!config.prod,
      inline: true,
      noInfo: true, // webpackTasks.watch handles output info related to success & failure
      publicPath,
      watchContentBase: true,
      historyApiFallback: true,
      watchOptions: {
        aggregateTimeout: 200,
    //    ignored: /(annotations|fonts|bower_components|dist\/styleguide|node_modules|styleguide|images|fonts|assets)/
       // Poll using interval (in ms, accepts boolean too)
      },
    };
  }

  return webpackConfig;
}

module.exports = createConfig;
<|endoftext|>


<commit_before>/**
 * TagSpaces - universal file and folder organizer
 * Copyright (C) 2017-present TagSpaces UG (haftungsbeschraenkt)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License (version 3) as
 * published by the Free Software Foundation.
 *
 * 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 <https://www.gnu.org/licenses/>.
 *
 * @flow
 */

import React, { useState } from 'react';
import Button from '@material-ui/core/Button';
import moment from 'moment';
import TextField from '@material-ui/core/TextField';
import DialogActions from '@material-ui/core/DialogActions';
import DialogContent from '@material-ui/core/DialogContent';
import DialogTitle from '@material-ui/core/DialogTitle';
import FormControl from '@material-ui/core/FormControl';
import FormHelperText from '@material-ui/core/FormHelperText';
import Switch from '@material-ui/core/Switch';
import GenericDialog, { onEnterKeyHandler } from './GenericDialog';
import ColorPickerDialog from './ColorPickerDialog';
import { type TagGroup } from '../../reducers/taglibrary';
import i18n from '../../services/i18n';
import TransparentBackground from '../TransparentBackground';

type Props = {
  // classes: Object,
  open?: boolean,
  editTagGroup: (tagGroup: TagGroup) => void,
  selectedTagGroupEntry?: TagGroup,
  onClose: () => void
};

/*type State = {
  inputError?: boolean,
  displayColorPicker?: boolean,
  displayTextColorPicker?: boolean,
  disableConfirmButton?: boolean,
  title?: string,
  applyChanges?: boolean,
  color?: string,
  textcolor?: string,
  modifiedDate?: string
};*/

/*class EditTagGroupDialog extends React.Component<Props, State> {
  state = {
    displayColorPicker: false,
    displayTextColorPicker: false,
    inputError: false,
    disableConfirmButton: true,
    applyChanges: false,
    title: '',
    color: '',
    textcolor: '',
    modifiedDate: ''
  };*/

const EditTagGroupDialog = (props: Props) => {
  const [displayColorPicker, setDisplayColorPicker] = useState(false);
  const [displayTextColorPicker, setDisplayTextColorPicker] = useState(false);
  cosnt [inputError, setInputError] = useState(false);
  const [disableConfirmButton, setDisableConfirmButton] = useState(true);
  const [applyChanges, setApplyChanges] = useState(false);
  const [title, setTitle] = useState('');
  const [color, setColor] = useState('');
  const [textcolor, setTextcolor] = useState('');
  const [modifiedDate, setModifiedData] = useState('');

  componentWillReceiveProps = ({ open, selectedTagGroupEntry }) => {
    if (open === true) {
      this.setState({
        disableConfirmButton: !selectedTagGroupEntry.title,
        applyChanges: false,
        title: selectedTagGroupEntry.title,
        color: selectedTagGroupEntry.color,
        textcolor: selectedTagGroupEntry.textcolor,
        modifiedDate: selectedTagGroupEntry.modified_date
      });
    }
  }

  /* function componentWillReceiveProps({ open, selectedTagGroupEntry }) {
    if (open === true) {
      setDisplayTextColorPicker(!selectedTagGroupEntry.title);
      setApplyChanges(false);
      setTitle(selectedTagGroupEntry.title);
      setColor(selectedTagGroupEntry.color);
      setTestcolor(selectedTagGroupEntry.textcolor);
      setModifiedData(selectedTagGroupEntry.modified_date);
    }
  } */

  handleInputChange = (event: Object) => {
    const target = event.target;
    const value = target.type === 'checkbox' ? target.checked : target.value;
    const name = target.name;

    this.setState({
      [name]: value
    }, this.handleValidation);
  }

  /* handleValidation() {
    if (this.state.title && this.state.title.length > 0) {
      this.setState({ inputError: false, disableConfirmButton: false });
    } else {
      this.setState({ inputError: true, disableConfirmButton: true });
    }
  } */

  function handleValidation() {
    if (title && title.length > 0) {
      setInputError(false);
      setDisableConfirmButton(false);
    } else {
      setInputError(true);
      setDisableConfirmButton(true);
    }
  }

  /* onConfirm = () => {
    const { disableConfirmButton, applyChanges } = this.state;
    const { selectedTagGroupEntry } = this.props;
    if (disableConfirmButton) {
      return;
    }

    if (selectedTagGroupEntry && selectedTagGroupEntry.children) {
      this.props.editTagGroup({
        ...selectedTagGroupEntry,
        title: this.state.title,
        color: this.state.color,
        textcolor: this.state.textcolor,
        children: selectedTagGroupEntry.children.map((tag) => ({
          ...tag,
          color: applyChanges ? this.state.color : tag.color,
          textcolor: applyChanges ? this.state.textcolor : tag.textcolor,
          style: tag.style
        }))
      });
      this.setState({ inputError: false, disableConfirmButton: true });
      this.props.onClose();
    }
  }; */

  function onConfirm() {
    const { disableConfirmButton, applyChanges } = state;
    const { selectedTagGroupEntry } = props;
    if (disableConfirmButton) {
      return;
    }

    if (selectedTagGroupEntry && selectedTagGroupEntry.children) {
      props.editTagGroup({
        ...selectedTagGroupEntry,
        title: title,
        color: color,
        textcolor: textcolor,
        children: selectedTagGroupEntry.children.map((tag) => ({
          ...tag,
          color: applyChanges ? color : tag.color,
          textcolor: applyChanges ? textcolor : tag.textcolor,
          style: tag.style
        }))
      });
      setInputError(false);
      setDisableConfirmButton(true);
      props.onClose();
    }
  }

  /* toggleDefaultTagBackgroundColorPicker = () => {
    this.setState({ displayColorPicker: !this.state.displayColorPicker });
  }; */

  function toggleDefaultTagBackgroundColorPicker() {
    setDisplayColorPicker(!displayColorPicker);
  }

  /* toggleDefaultTagTextColorPicker = () => {
    this.setState({ displayTextColorPicker: !this.state.displayTextColorPicker });
  }; */

  function toggleDefaultTagTextColorPicker() {
    setDisplayTextColorPicker(!displayTextColorPicker );
  }

  /* handleChangeColor = (color: string) => {
    this.setState({ color });
  }; */

  function handleChangeColor(color) {
    setColor(color);
  }

  /*  handleChangeTextColor = (color: string) => {
      this.setState({ textcolor: color });
  };  */

  function handleChangeTextColor(color) {
    setTextcolor(color);
  }

  /*  setApplyChanges = (applyColorChanges: boolean) => {
      this.setState({ applyChanges: applyColorChanges });
  };  */

  function setApplyChanges() {
    setApplyChanges(applyColorChanges);
  }

  /*  renderTitle = () => (
      <DialogTitle style={{ overflow: 'visible' }}>{i18n.t('core:editTagGroupTitle')}{` '${this.state.title}'`}</DialogTitle>
  );  */

  function renderTitle() {
    return (
      <DialogTitle style={{ overflow: 'visible' }}>{i18n.t('core:editTagGroupTitle')}{`'${title}'`}</DialogTitle>
    );
  }

  /*  renderContent = () => {
      const { color, textcolor, modifiedDate } = this.state;
      const styles = {
        color: {
          width: '100%',
          height: 30,
          borderRadius: 2,
          borderWidth: 1,
          borderStyle: 'solid',
          borderColor: 'gray',
          padding: '5px',
          background: color
        },
        textcolor: {
          width: '100%',
          height: 30,
          borderRadius: 2,
          borderWidth: 1,
          borderStyle: 'solid',
          borderColor: 'gray',
          padding: '5px',
          background: textcolor
        },
        helpText: {
          marginBottom: '5px',
          fontSize: '1rem'
        },
      };  */

  function renderContent() {
    const { color, textcolor, modifiedDate } = this.state;
    const styles = {
      color: {
        width: '100%',
        height: 30,
        borderRadius: 2,
        borderWidth: 1,
        borderStyle: 'solid',
        borderColor: 'gray',
        padding: '5px',
        background: color
      },
      textcolor: {
        width: '100%',
        height: 30,
        borderRadius: 2,
        borderWidth: 1,
        borderStyle: 'solid',
        borderColor: 'gray',
        padding: '5px',
        background: textcolor
      },
      helpText: {
        marginBottom: '5px',
        fontSize: '1rem'
      },
    };
  }

  return (
    <DialogContent style={{ overflow: 'visible' }}>
      <FormControl
        fullWidth={true}
        error={this.state.inputError}
        style={{ overflow: 'visible' }}
      >
        {modifiedDate !== '' && (

            {`${i18n.t('core:modifiedDate')}: `}
            <time>{moment(modifiedDate).format('MMM. DD, YYYY')}</time>

        )}
        <TextField
          error={this.state.inputError}
          margin="dense"
          name="title"
          label={i18n.t('core:editTagGroupNewName')}
          onChange={this.handleInputChange}
          value={this.state.title}
          data-tid="editTagGroupInput"
          fullWidth={true}
        />
        {this.state.inputError && <FormHelperText>{i18n.t('core:taggroupTitleHelper')}</FormHelperText>}
      </FormControl>
      <FormControl
        fullWidth={true}
      >
        <FormHelperText style={styles.helpText}>{i18n.t('core:tagBackgroundColor')}</FormHelperText>
        <TransparentBackground>
          <Button
            onClick={this.toggleDefaultTagBackgroundColorPicker}
            data-tid="editTagGroupBackgroundColor"
            style={styles.color}
          >
          </Button>
        </TransparentBackground>
        <ColorPickerDialog
          open={this.state.displayColorPicker}
          setColor={this.handleChangeColor}
          onClose={this.toggleDefaultTagBackgroundColorPicker}
          color={this.state.color}
        />
      </FormControl>
      <FormControl
        fullWidth={true}
      >
        <FormHelperText style={styles.helpText}>{i18n.t('core:tagForegroundColor')}</FormHelperText>
        <TransparentBackground>
          <Button
            onClick={this.toggleDefaultTagTextColorPicker}
            data-tid="editTagGroupForegroundColor"
            style={styles.textcolor}
            role="presentation"
          >
          </Button>
        </TransparentBackground>
        <ColorPickerDialog
          open={this.state.displayTextColorPicker}
          setColor={this.handleChangeTextColor}
          onClose={this.toggleDefaultTagTextColorPicker}
          color={this.state.textcolor}
        />
      </FormControl>
      <FormControl>
        <FormHelperText style={styles.helpText}>{i18n.t('core:colorChangesToAllTags')}</FormHelperText>
        <Switch
          data-tid="editTagGroupSwitch"
          onClick={() => this.setApplyChanges(!this.state.applyChanges)}
          checked={this.state.applyChanges}
        />
      </FormControl>
    </DialogContent>
  );

  /*renderActions = () => (
    <DialogActions>
      <Button
        onClick={this.props.onClose}
        color="primary"
      >
        {i18n.t('core:cancel')}
      </Button>
      <Button
        disabled={this.state.disableConfirmButton}
        onClick={this.onConfirm}
        data-tid="editTagGroupConfirmButton"
        color="primary"
      >
        {i18n.t('core:ok')}
      </Button>
    </DialogActions>
  );*/

  function renderActions() {
    return (
      <DialogActions>
        <Button
          onClick={props.onClose}
          color="primary"
        >
          {i18n.t('core:cancel')}
        </Button>
        <Button
          disabled={disableConfirmButton}
          onClick={onConfirm}
          data-tid="editTagGroupConfirmButton"
          color="primary"
        >
          {i18n.t('core:ok')}
        </Button>
      </DialogActions>
    );
  }

  function render() {
    return (
      <GenericDialog
        open={props.open}
        onClose={props.onClose}
        onEnterKey={(event) => onEnterKeyHandler(event, onConfirm)}
        renderTitle={renderTitle}
        renderContent={renderContent}
        renderActions={renderActions}
      />
    );
  }
}

export default EditTagGroupDialog;
<commit_msg>TSN305 - return the original EditTagGroupDialog.js<commit_after>/**
 * TagSpaces - universal file and folder organizer
 * Copyright (C) 2017-present TagSpaces UG (haftungsbeschraenkt)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License (version 3) as
 * published by the Free Software Foundation.
 *
 * 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 <https://www.gnu.org/licenses/>.
 *
 * @flow
 */

import React from 'react';
import Button from '@material-ui/core/Button';
import moment from 'moment';
import TextField from '@material-ui/core/TextField';
import DialogActions from '@material-ui/core/DialogActions';
import DialogContent from '@material-ui/core/DialogContent';
import DialogTitle from '@material-ui/core/DialogTitle';
import FormControl from '@material-ui/core/FormControl';
import FormHelperText from '@material-ui/core/FormHelperText';
import Switch from '@material-ui/core/Switch';
import GenericDialog, { onEnterKeyHandler } from './GenericDialog';
import ColorPickerDialog from './ColorPickerDialog';
import { type TagGroup } from '../../reducers/taglibrary';
import i18n from '../../services/i18n';
import TransparentBackground from '../TransparentBackground';

type Props = {
  open?: boolean,
  editTagGroup: (tagGroup: TagGroup) => void,
  selectedTagGroupEntry?: TagGroup,
  onClose: () => void
};

type State = {
  inputError?: boolean,
  displayColorPicker?: boolean,
  displayTextColorPicker?: boolean,
  disableConfirmButton?: boolean,
  title?: string,
  applyChanges?: boolean,
  color?: string,
  textcolor?: string,
  modifiedDate?: string
};

class EditTagGroupDialog extends React.Component<Props, State> {
  state = {
    displayColorPicker: false,
    displayTextColorPicker: false,
    inputError: false,
    disableConfirmButton: true,
    applyChanges: false,
    title: '',
    color: '',
    textcolor: '',
    modifiedDate: ''
  };

  componentWillReceiveProps = ({ open, selectedTagGroupEntry }) => {
    if (open === true) {
      this.setState({
        disableConfirmButton: !selectedTagGroupEntry.title,
        applyChanges: false,
        title: selectedTagGroupEntry.title,
        color: selectedTagGroupEntry.color,
        textcolor: selectedTagGroupEntry.textcolor,
        modifiedDate: selectedTagGroupEntry.modified_date
      });
    }
  };

  handleInputChange = (event: Object) => {
    const target = event.target;
    const value = target.type === 'checkbox' ? target.checked : target.value;
    const name = target.name;

    this.setState({
      [name]: value
    }, this.handleValidation);
  };

  handleValidation() {
    if (this.state.title && this.state.title.length > 0) {
      this.setState({ inputError: false, disableConfirmButton: false });
    } else {
      this.setState({ inputError: true, disableConfirmButton: true });
    }
  }

  onConfirm = () => {
    const { disableConfirmButton, applyChanges } = this.state;
    const { selectedTagGroupEntry } = this.props;
    if (disableConfirmButton) {
      return;
    }

    if (selectedTagGroupEntry && selectedTagGroupEntry.children) {
      this.props.editTagGroup({
        ...selectedTagGroupEntry,
        title: this.state.title,
        color: this.state.color,
        textcolor: this.state.textcolor,
        children: selectedTagGroupEntry.children.map((tag) => ({
          ...tag,
          color: applyChanges ? this.state.color : tag.color,
          textcolor: applyChanges ? this.state.textcolor : tag.textcolor,
          style: tag.style
        }))
      });
      this.setState({ inputError: false, disableConfirmButton: true });
      this.props.onClose();
    }
  };

  toggleDefaultTagBackgroundColorPicker = () => {
    this.setState({ displayColorPicker: !this.state.displayColorPicker });
  };

  toggleDefaultTagTextColorPicker = () => {
    this.setState({ displayTextColorPicker: !this.state.displayTextColorPicker });
  };

  handleChangeColor = (color: string) => {
    this.setState({ color });
  };

  handleChangeTextColor = (color: string) => {
    this.setState({ textcolor: color });
  };

  setApplyChanges = (applyColorChanges: boolean) => {
    this.setState({ applyChanges: applyColorChanges });
  };

  renderTitle = () => (
    <DialogTitle style={{ overflow: 'visible' }}>{i18n.t('core:editTagGroupTitle')}{` '${this.state.title}'`}</DialogTitle>
  );

  renderContent = () => {
    const { color, textcolor, modifiedDate } = this.state;
    const styles = {
      color: {
        width: '100%',
        height: 30,
        borderRadius: 2,
        borderWidth: 1,
        borderStyle: 'solid',
        borderColor: 'gray',
        padding: '5px',
        background: color
      },
      textcolor: {
        width: '100%',
        height: 30,
        borderRadius: 2,
        borderWidth: 1,
        borderStyle: 'solid',
        borderColor: 'gray',
        padding: '5px',
        background: textcolor
      },
      helpText: {
        marginBottom: '5px',
        fontSize: '1rem'
      },
    };

    return (
      <DialogContent style={{ overflow: 'visible' }}>
        <FormControl
          fullWidth={true}
          error={this.state.inputError}
          style={{ overflow: 'visible' }}
        >
          {modifiedDate !== '' && (

              {`${i18n.t('core:modifiedDate')}: `}
              <time>{moment(modifiedDate).format('MMM. DD, YYYY')}</time>

          )}
          <TextField
            error={this.state.inputError}
            margin="dense"
            name="title"
            label={i18n.t('core:editTagGroupNewName')}
            onChange={this.handleInputChange}
            value={this.state.title}
            data-tid="editTagGroupInput"
            fullWidth={true}
          />
          {this.state.inputError && <FormHelperText>{i18n.t('core:taggroupTitleHelper')}</FormHelperText>}
        </FormControl>
        <FormControl
          fullWidth={true}
        >
          <FormHelperText style={styles.helpText}>{i18n.t('core:tagBackgroundColor')}</FormHelperText>
          <TransparentBackground>
            <Button
              onClick={this.toggleDefaultTagBackgroundColorPicker}
              data-tid="editTagGroupBackgroundColor"
              style={styles.color}
            >
            </Button>
          </TransparentBackground>
          <ColorPickerDialog
            open={this.state.displayColorPicker}
            setColor={this.handleChangeColor}
            onClose={this.toggleDefaultTagBackgroundColorPicker}
            color={this.state.color}
          />
        </FormControl>
        <FormControl
          fullWidth={true}
        >
          <FormHelperText style={styles.helpText}>{i18n.t('core:tagForegroundColor')}</FormHelperText>
          <TransparentBackground>
            <Button
              onClick={this.toggleDefaultTagTextColorPicker}
              data-tid="editTagGroupForegroundColor"
              style={styles.textcolor}
              role="presentation"
            >
            </Button>
          </TransparentBackground>
          <ColorPickerDialog
            open={this.state.displayTextColorPicker}
            setColor={this.handleChangeTextColor}
            onClose={this.toggleDefaultTagTextColorPicker}
            color={this.state.textcolor}
          />
        </FormControl>
        <FormControl>
          <FormHelperText style={styles.helpText}>{i18n.t('core:colorChangesToAllTags')}</FormHelperText>
          <Switch
            data-tid="editTagGroupSwitch"
            onClick={() => this.setApplyChanges(!this.state.applyChanges)}
            checked={this.state.applyChanges}
          />
        </FormControl>
      </DialogContent>
    );
  };

  renderActions = () => (
    <DialogActions>
      <Button
        onClick={this.props.onClose}
        color="primary"
      >
        {i18n.t('core:cancel')}
      </Button>
      <Button
        disabled={this.state.disableConfirmButton}
        onClick={this.onConfirm}
        data-tid="editTagGroupConfirmButton"
        color="primary"
      >
        {i18n.t('core:ok')}
      </Button>
    </DialogActions>
  );

  render() {
    return (
      <GenericDialog
        open={this.props.open}
        onClose={this.props.onClose}
        onEnterKey={(event) => onEnterKeyHandler(event, this.onConfirm)}
        renderTitle={this.renderTitle}
        renderContent={this.renderContent}
        renderActions={this.renderActions}
      />
    );
  }
}

export default EditTagGroupDialog;
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2019 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.nanos.crunch',
  name: 'UserCapabilityJunctionDAO',
  extends: 'foam.dao.ProxyDAO',

  documentation: `Authenticated DAO decorator to only show capabilities owned by a user. Updates can only be performed by system.`,

  javaImports: [
    'foam.core.FObject',
    'foam.dao.ArraySink',
    'foam.dao.DAO',
    'foam.nanos.auth.*',
    'foam.nanos.logger.Logger',

    'java.util.Calendar',
    'java.util.Date',
    'java.util.List',

    'static foam.mlang.MLang.*'
  ],

  messages: [
    { name: 'ERROR_MSG', message: 'Error on UserCapabilityJunction checkOwnership create UCJ denied to user ' }
  ],

  methods: [
    {
      name: 'getUser',
      args: [
        {
          name: 'x',
          type: 'Context'
        }
      ],
      type: 'foam.nanos.auth.User',
      javaCode: `
        User user = ((Subject) x.get("subject")).getUser();
        if ( user == null ) throw new AuthenticationException("user not found");
        return user;
      `
    },
    {
      name: 'checkOwnership',
      args: [
        {
          name: 'x',
          type: 'Context'
        },
        {
          name: 'obj',
          type: 'foam.nanos.crunch.UserCapabilityJunction'
        }
      ],
      documentation: `Check if current user has permission to add this junction`,
      javaCode: `
        User user = getUser(x);
        User agent = ((Subject) x.get("subject")).getRealUser();
        AuthService auth = (AuthService) x.get("auth");
        boolean isOwner = obj.getSourceId() == user.getId() || ( agent != null && obj.getSourceId() == agent.getId() );
        if ( ! isOwner && ! auth.check(x, "ucj.addPermission") ) throw new AuthorizationException(ERROR_MSG + agent.getId()+":"+user.getId());
      `
    },
    {
      name: 'getFilteredDAO',
      args: [
        {
          name: 'x',
          type: 'Context'
        }
      ],
      type: 'foam.dao.DAO',
      documentation: `Return list of junctions the current user has read access to`,
      javaCode: `
        User user = getUser(x);
        AuthService auth = (AuthService) x.get("auth");
        if ( auth.check(x, "service.*") ) return getDelegate();
        return getDelegate().where(
          EQ(UserCapabilityJunction.SOURCE_ID, user.getId())
        );
      `
    },
    {
      name: 'put_',
      args: [
        {
          name: 'x',
          type: 'Context'
        },
        {
          name: 'obj',
          type: 'foam.core.FObject'
        }
      ],
      type: 'foam.core.FObject',
      documentation: `
        Set the status of the junction before put by checking if prerequisites are fulfilled, data required is validated,
        and whether review is required and set.
      `,
      javaCode: `
        UserCapabilityJunction ucJunction = (UserCapabilityJunction) obj;
        UserCapabilityJunction old = (UserCapabilityJunction) getDelegate().find_(x, ucJunction.getId());

        DAO capabilityDAO = (DAO) x.get("capabilityDAO");
        Capability capability = (Capability) capabilityDAO.find_(x, ucJunction.getTargetId());

        checkOwnership(x, ucJunction);

        // if the junction is being updated from GRANTED to something else, put the updated ucj,
        // then try to reput the ucj as new ucj
        if ( old != null && old.getStatus() == CapabilityJunctionStatus.GRANTED && ucJunction.getStatus() != CapabilityJunctionStatus.GRANTED ) {
          getDelegate().put_(x, ucJunction);
          old = null;
        }

        boolean requiresData = capability.getOf() != null;
        boolean requiresReview = capability.getReviewRequired();

        boolean ucjExpiredButCapabilityNot = ucJunction.getStatus() == CapabilityJunctionStatus.EXPIRED && ! capability.isExpired();

        // Update current UCJ status
        if ( old == null
          || ucJunction.getStatus() == CapabilityJunctionStatus.ACTION_REQUIRED
          || ucjExpiredButCapabilityNot )
        {
          CapabilityJunctionStatus chainedStatus = checkPrereqsChainedStatus(x, ucJunction);
          if ( ( ! requiresData || ( ucJunction.getData() != null && validateData(x, ucJunction) ) )
            && chainedStatus == CapabilityJunctionStatus.GRANTED )
          {
            // if review is required for this Capability, set the status to pending so that rules can be triggered
            if ( requiresReview )
              ucJunction.setStatus(CapabilityJunctionStatus.PENDING);
            else
              ucJunction.setStatus(CapabilityJunctionStatus.GRANTED);
          } else {
            ucJunction.setStatus(CapabilityJunctionStatus.ACTION_REQUIRED);
          }
        }

        if ( ucJunction.getStatus() == CapabilityJunctionStatus.PENDING ) {
          if ( ! requiresReview ) ucJunction.setStatus(CapabilityJunctionStatus.GRANTED);
        }

        if ( ucJunction.getStatus() == CapabilityJunctionStatus.GRANTED ) {
          if ( requiresData && capability.getDaoKey() != null ) saveDataToDAO(x, capability, ucJunction, true);
          configureJunctionExpiry(x, ucJunction, old, capability);
        }

        return getDelegate().put_(x, ucJunction);
      `
    },
    {
      name: 'saveDataToDAO',
      args: [
        {
          name: 'x',
          type: 'Context'
        },
        {
          name: 'capability',
          type: 'Capability'
        },
        {
          name: 'obj',
          type: 'foam.nanos.crunch.UserCapabilityJunction'
        },
        {
          name: 'putObject',
          type: 'Boolean'
        }
      ],
      javaType: 'foam.core.FObject',
      documentation: `
        We may or may not want to store the data in its own dao, based on the nature of the data.
        For example, if the data for some UserCapabilityJunction is a businessOnboarding object, we may want to store this object in
        the businessOnboardingDAO for easier access.
        If the data on an UserCapabilityJunction should be stored in some DAO, the daoKey should be provided on its corresponding Capability object.
      `,
      javaCode: `
        if ( obj.getData() == null )
          throw new RuntimeException("UserCapabilityJunction data not submitted for capability: " + obj.getTargetId());

        String daoKey = capability.getDaoKey();
        if ( daoKey == null ) return null;

        DAO dao = (DAO) x.get(daoKey);
        if ( dao == null ) return null;

        FObject objectToSave;                                                  // Identify or create data to go into dao.
        String contextDAOFindKey = (String) capability.getContextDAOFindKey();

        if ( contextDAOFindKey != null && ! contextDAOFindKey.isEmpty() ) {
          if ( contextDAOFindKey.toLowerCase().contains("subject") ) {         // 1- Case if subject lookup
            String[] words = foam.util.StringUtil.split(contextDAOFindKey, '.');
            objectToSave = (FObject) x.get("subject");

            if ( objectToSave == null || words.length < 2 )
              throw new RuntimeException("@UserCapabilityJunction capability.contextDAOFindKey not found in context. Please check capability: " + obj.getTargetId() + " and its contextDAOFindKey: " + contextDAOFindKey);

            if ( words[1].toLowerCase().equals("user") ) {
              objectToSave = ((Subject) objectToSave).getUser();
            } else if ( words[1].toLowerCase().equals("realuser") ) {
              objectToSave = ((Subject) objectToSave).getRealUser();
            }
          } else {                                                              // 2- Case if anything other then subject specified
            objectToSave = (FObject) x.get(contextDAOFindKey);

            if ( objectToSave == null )
              throw new RuntimeException("@UserCapabilityJunction capability.contextDAOFindKey not found in context. Please check capability: " + obj.getTargetId() + " and its contextDAOFindKey: " + contextDAOFindKey);
          }
        } else {
          try {                                                                 // 3- Case where contextDAOFindKey not specified:
            // Create new object of DAO type to copy over properties
            objectToSave = (FObject) dao.getOf().newInstance();
          } catch (Exception e) {                                               // 4- default case, try using ucj data directly.
            objectToSave = (FObject) obj.getData();
          }
        }
        objectToSave = objectToSave.fclone().copyFrom(obj.getData());           // finally copy user inputed data into objectToSave <- typed to the safest possibility from above cases

        try {                                                                   // save data to dao
          if ( putObject ) dao.put(objectToSave);
        } catch (Exception e) {
          Logger logger = (Logger) x.get("logger");
          logger.warning("Data cannot be added to " + capability.getDaoKey() + " for UserCapabilityJunction object : " + obj.getId() );
        }

        return objectToSave;
      `
    },
    {
      name: 'configureJunctionExpiry',
      args: [
        {
          name: 'x',
          type: 'Context'
        },
        {
          name: 'obj',
          type: 'foam.nanos.crunch.UserCapabilityJunction'
        },
        {
          name: 'old',
          type: 'foam.nanos.crunch.UserCapabilityJunction'
        },
        {
          name: 'capability',
          type: 'foam.nanos.crunch.Capability'
        }
      ],
      type: 'foam.core.FObject',
      documentation: `
        Set the expiry of a userCapabilityJunction based on the duration or expiry set on the capability, which
        ever one comes first
      `,
      javaCode: `
        // Only update the expiry for non-active junctions, i.e., non-expired, non-pending, or granted junctions whose expiry is not yet set
        if ( ( old != null && old.getStatus() == CapabilityJunctionStatus.GRANTED && old.getExpiry() != null ) || obj.getStatus() != CapabilityJunctionStatus.GRANTED )
          return obj;

        Date junctionExpiry = capability.getExpiry();

        if ( capability.getDuration() > 0 ) {
          Date today = new Date();
          Calendar calendar = Calendar.getInstance();
          calendar.setTime(today);
          calendar.add(Calendar.DATE, capability.getDuration());

          if ( junctionExpiry == null ) {
            junctionExpiry = calendar.getTime();
          } else {
            junctionExpiry = junctionExpiry.after(calendar.getTime()) ? calendar.getTime() : junctionExpiry;
          }
        }
        obj.setExpiry(junctionExpiry);
        return obj;
      `
    },
    {
      name: 'getPrereqs',
      args: [
        {
          name: 'x',
          type: 'Context'
        },
        {
          name: 'obj',
          type: 'foam.core.FObject'
        }
      ],
      javaType: 'java.util.List<CapabilityCapabilityJunction>',
      documentation: `
        Returns the list of prerequisiteCapabilityJunctions for the target capability of the ucj
      `,
      javaCode: `
        DAO prerequisiteCapabilityJunctionDAO = (DAO) (x.get("prerequisiteCapabilityJunctionDAO"));

        // get a list of the prerequisite junctions where the current capability is the dependent
        List<CapabilityCapabilityJunction> ccJunctions = (List<CapabilityCapabilityJunction>) ((ArraySink) prerequisiteCapabilityJunctionDAO
        .where(EQ(CapabilityCapabilityJunction.SOURCE_ID, ((UserCapabilityJunction) obj).getTargetId()))
        .select(new ArraySink()))
        .getArray();

        return ccJunctions;
      `
    },
    {
      name: 'checkPrereqsChainedStatus',
      args: [
        {
          name: 'x',
          type: 'Context'
        },
        {
          name: 'obj',
          type: 'foam.core.FObject'
        }
      ],
      type: 'CapabilityJunctionStatus',
      documentation: `
        Check statuses of all preRequist capabilities - returning:
        GRANTED: If all pre-reqs are in granted status
        PENDING: At least one pre-req is still in pending status
        ACTION_REQUIRED: If not any of the above
      `,
      javaCode: `
        boolean allGranted = true;
        Capability cap;
        DAO capDAO = (DAO) x.get("capabilityDAO");
        List<CapabilityCapabilityJunction> ccJunctions = ( List<CapabilityCapabilityJunction> ) getPrereqs(x, obj);
        UserCapabilityJunction ucj = (UserCapabilityJunction) obj;

        for ( CapabilityCapabilityJunction ccJunction : ccJunctions ) {
          cap = (Capability) capDAO.find((String) ccJunction.getSourceId());
          if ( ! cap.getEnabled() ) continue;
          UserCapabilityJunction ucJunction = (UserCapabilityJunction) getDelegate().find(AND(
            EQ(UserCapabilityJunction.SOURCE_ID, ucj.getSourceId()),
            EQ(UserCapabilityJunction.TARGET_ID, (String) ccJunction.getTargetId())
          ));

          // CONFIRM ucJunction status with re-put the ucj
          ucJunction = ucJunction == null ?
            new UserCapabilityJunction.Builder(x)
              .setSourceId(ucj.getSourceId())
              .setTargetId(ccJunction.getTargetId())
              .build() :
            ucJunction;
          try {
            ucJunction = (UserCapabilityJunction) ((DAO) x.get("userCapabilityJunctionDAO")).put_(x, ucJunction);
          } catch ( RuntimeException e ) {
            return CapabilityJunctionStatus.ACTION_REQUIRED;
          }

          if ( ucJunction == null ) return CapabilityJunctionStatus.ACTION_REQUIRED;
          if ( ucJunction.getStatus() != CapabilityJunctionStatus.GRANTED
            && ucJunction.getStatus() != CapabilityJunctionStatus.PENDING ) return CapabilityJunctionStatus.ACTION_REQUIRED;
          if ( ucJunction.getStatus() == CapabilityJunctionStatus.PENDING ) allGranted = false;
        }
        return allGranted ? CapabilityJunctionStatus.GRANTED : CapabilityJunctionStatus.PENDING;
      `
    },
    {
      name: 'validateData',
      args: [
        {
          name: 'x',
          type: 'Context'
        },
        {
          name: 'obj',
          type: 'foam.core.FObject'
        }
      ],
      type: 'Boolean',
      documentation: `call the validate method on data and if not "return true" then set the junction status to pending`,
      javaCode: `
        FObject data = ((UserCapabilityJunction) obj).getData();
        if ( data != null ) {
          try {
            data.validate(x);
          } catch(Exception e) {
            return false;
          }
          return true;
        }
        return false;
      `
    },
    {
      name: 'remove_',
      javaCode: `
        throw new UnsupportedOperationException("UserCapabilityJunctions should be disabled via status change.");
      `
    },
    {
      name: 'removeAll_',
      javaCode: `
        throw new UnsupportedOperationException("UserCapabilityJunctions should be disabled via status change.");
      `
    },
    {
      name: 'select_',
      javaCode: `
        DAO dao = getFilteredDAO(x);
        return dao.select_(x, sink, skip, limit, order, predicate);
      `
    },
    {
      name: 'find_',
      javaCode:`
        FObject result = super.find_(x, id);
        if ( result != null ) checkOwnership(x, (UserCapabilityJunction) result);
        return result;
      `
    }
  ]
});
<commit_msg>small fix<commit_after>/**
 * @license
 * Copyright 2019 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.nanos.crunch',
  name: 'UserCapabilityJunctionDAO',
  extends: 'foam.dao.ProxyDAO',

  documentation: `Authenticated DAO decorator to only show capabilities owned by a user. Updates can only be performed by system.`,

  javaImports: [
    'foam.core.FObject',
    'foam.dao.ArraySink',
    'foam.dao.DAO',
    'foam.nanos.auth.*',
    'foam.nanos.logger.Logger',

    'java.util.Calendar',
    'java.util.Date',
    'java.util.List',

    'static foam.mlang.MLang.*'
  ],

  messages: [
    { name: 'ERROR_MSG', message: 'Error on UserCapabilityJunction checkOwnership create UCJ denied to user ' }
  ],

  methods: [
    {
      name: 'getUser',
      args: [
        {
          name: 'x',
          type: 'Context'
        }
      ],
      type: 'foam.nanos.auth.User',
      javaCode: `
        User user = ((Subject) x.get("subject")).getUser();
        if ( user == null ) throw new AuthenticationException("user not found");
        return user;
      `
    },
    {
      name: 'checkOwnership',
      args: [
        {
          name: 'x',
          type: 'Context'
        },
        {
          name: 'obj',
          type: 'foam.nanos.crunch.UserCapabilityJunction'
        }
      ],
      documentation: `Check if current user has permission to add this junction`,
      javaCode: `
        User user = getUser(x);
        User agent = ((Subject) x.get("subject")).getRealUser();
        AuthService auth = (AuthService) x.get("auth");
        boolean isOwner = obj.getSourceId() == user.getId() || ( agent != null && obj.getSourceId() == agent.getId() );
        if ( ! isOwner && ! auth.check(x, "ucj.addPermission") ) throw new AuthorizationException(ERROR_MSG + agent.getId()+":"+user.getId());
      `
    },
    {
      name: 'getFilteredDAO',
      args: [
        {
          name: 'x',
          type: 'Context'
        }
      ],
      type: 'foam.dao.DAO',
      documentation: `Return list of junctions the current user has read access to`,
      javaCode: `
        User user = getUser(x);
        AuthService auth = (AuthService) x.get("auth");
        if ( auth.check(x, "service.*") ) return getDelegate();
        return getDelegate().where(
          EQ(UserCapabilityJunction.SOURCE_ID, user.getId())
        );
      `
    },
    {
      name: 'put_',
      args: [
        {
          name: 'x',
          type: 'Context'
        },
        {
          name: 'obj',
          type: 'foam.core.FObject'
        }
      ],
      type: 'foam.core.FObject',
      documentation: `
        Set the status of the junction before put by checking if prerequisites are fulfilled, data required is validated,
        and whether review is required and set.
      `,
      javaCode: `
        UserCapabilityJunction ucJunction = (UserCapabilityJunction) obj;
        UserCapabilityJunction old = (UserCapabilityJunction) getDelegate().find_(x, ucJunction.getId());

        DAO capabilityDAO = (DAO) x.get("capabilityDAO");
        Capability capability = (Capability) capabilityDAO.find_(x, ucJunction.getTargetId());

        checkOwnership(x, ucJunction);

        // if the junction is being updated from GRANTED to something else, put the updated ucj,
        // then try to reput the ucj as new ucj
        if ( old != null && old.getStatus() == CapabilityJunctionStatus.GRANTED && ucJunction.getStatus() != CapabilityJunctionStatus.GRANTED ) {
          getDelegate().put_(x, ucJunction);
          old = null;
        }

        boolean requiresData = capability.getOf() != null;
        boolean requiresReview = capability.getReviewRequired();

        boolean ucjExpiredButCapabilityNot = ucJunction.getStatus() == CapabilityJunctionStatus.EXPIRED && ! capability.isExpired();

        // Update current UCJ status
        if ( old == null
          || ucJunction.getStatus() == CapabilityJunctionStatus.ACTION_REQUIRED
          || ucjExpiredButCapabilityNot )
        {
          CapabilityJunctionStatus chainedStatus = checkPrereqsChainedStatus(x, ucJunction);
          if ( ( ! requiresData || ( ucJunction.getData() != null && validateData(x, ucJunction) ) )
            && chainedStatus == CapabilityJunctionStatus.GRANTED )
          {
            // if review is required for this Capability, set the status to pending so that rules can be triggered
            if ( requiresReview )
              ucJunction.setStatus(CapabilityJunctionStatus.PENDING);
            else
              ucJunction.setStatus(CapabilityJunctionStatus.GRANTED);
          } else {
            ucJunction.setStatus(CapabilityJunctionStatus.ACTION_REQUIRED);
          }
        }

        if ( ucJunction.getStatus() == CapabilityJunctionStatus.PENDING ) {
          if ( ! requiresReview ) ucJunction.setStatus(CapabilityJunctionStatus.GRANTED);
        }

        if ( ucJunction.getStatus() == CapabilityJunctionStatus.GRANTED ) {
          if ( requiresData && capability.getDaoKey() != null ) saveDataToDAO(x, capability, ucJunction, true);
          configureJunctionExpiry(x, ucJunction, old, capability);
        }

        return getDelegate().put_(x, ucJunction);
      `
    },
    {
      name: 'saveDataToDAO',
      args: [
        {
          name: 'x',
          type: 'Context'
        },
        {
          name: 'capability',
          type: 'Capability'
        },
        {
          name: 'obj',
          type: 'foam.nanos.crunch.UserCapabilityJunction'
        },
        {
          name: 'putObject',
          type: 'Boolean'
        }
      ],
      javaType: 'foam.core.FObject',
      documentation: `
        We may or may not want to store the data in its own dao, based on the nature of the data.
        For example, if the data for some UserCapabilityJunction is a businessOnboarding object, we may want to store this object in
        the businessOnboardingDAO for easier access.
        If the data on an UserCapabilityJunction should be stored in some DAO, the daoKey should be provided on its corresponding Capability object.
      `,
      javaCode: `
        if ( obj.getData() == null )
          throw new RuntimeException("UserCapabilityJunction data not submitted for capability: " + obj.getTargetId());

        String daoKey = capability.getDaoKey();
        if ( daoKey == null ) return null;

        DAO dao = (DAO) x.get(daoKey);
        if ( dao == null ) return null;

        FObject objectToSave;                                                  // Identify or create data to go into dao.
        String contextDAOFindKey = (String) capability.getContextDAOFindKey();

        if ( contextDAOFindKey != null && ! contextDAOFindKey.isEmpty() ) {
          if ( contextDAOFindKey.toLowerCase().contains("subject") ) {         // 1- Case if subject lookup
            String[] words = foam.util.StringUtil.split(contextDAOFindKey, '.');
            objectToSave = (FObject) x.get("subject");

            if ( objectToSave == null || words.length < 2 )
              throw new RuntimeException("@UserCapabilityJunction capability.contextDAOFindKey not found in context. Please check capability: " + obj.getTargetId() + " and its contextDAOFindKey: " + contextDAOFindKey);

            if ( words[1].toLowerCase().equals("user") ) {
              objectToSave = ((Subject) objectToSave).getUser();
            } else if ( words[1].toLowerCase().equals("realuser") ) {
              objectToSave = ((Subject) objectToSave).getRealUser();
            }
          } else {                                                              // 2- Case if anything other then subject specified
            objectToSave = (FObject) x.get(contextDAOFindKey);

            if ( objectToSave == null )
              throw new RuntimeException("@UserCapabilityJunction capability.contextDAOFindKey not found in context. Please check capability: " + obj.getTargetId() + " and its contextDAOFindKey: " + contextDAOFindKey);
          }
        } else {
          try {                                                                 // 3- Case where contextDAOFindKey not specified:
            // Create new object of DAO type to copy over properties
            objectToSave = (FObject) dao.getOf().newInstance();
          } catch (Exception e) {                                               // 4- default case, try using ucj data directly.
            objectToSave = (FObject) obj.getData();
          }
        }
        objectToSave = objectToSave.fclone().copyFrom(obj.getData());           // finally copy user inputed data into objectToSave <- typed to the safest possibility from above cases

        try {                                                                   // save data to dao
          if ( putObject ) dao.put(objectToSave);
        } catch (Exception e) {
          Logger logger = (Logger) x.get("logger");
          logger.warning("Data cannot be added to " + capability.getDaoKey() + " for UserCapabilityJunction object : " + obj.getId() );
        }

        return objectToSave;
      `
    },
    {
      name: 'configureJunctionExpiry',
      args: [
        {
          name: 'x',
          type: 'Context'
        },
        {
          name: 'obj',
          type: 'foam.nanos.crunch.UserCapabilityJunction'
        },
        {
          name: 'old',
          type: 'foam.nanos.crunch.UserCapabilityJunction'
        },
        {
          name: 'capability',
          type: 'foam.nanos.crunch.Capability'
        }
      ],
      type: 'foam.core.FObject',
      documentation: `
        Set the expiry of a userCapabilityJunction based on the duration or expiry set on the capability, which
        ever one comes first
      `,
      javaCode: `
        // Only update the expiry for non-active junctions, i.e., non-expired, non-pending, or granted junctions whose expiry is not yet set
        if ( ( old != null && old.getStatus() == CapabilityJunctionStatus.GRANTED && old.getExpiry() != null ) || obj.getStatus() != CapabilityJunctionStatus.GRANTED )
          return obj;

        Date junctionExpiry = capability.getExpiry();

        if ( capability.getDuration() > 0 ) {
          Date today = new Date();
          Calendar calendar = Calendar.getInstance();
          calendar.setTime(today);
          calendar.add(Calendar.DATE, capability.getDuration());

          if ( junctionExpiry == null ) {
            junctionExpiry = calendar.getTime();
          } else {
            junctionExpiry = junctionExpiry.after(calendar.getTime()) ? calendar.getTime() : junctionExpiry;
          }
        }
        obj.setExpiry(junctionExpiry);
        return obj;
      `
    },
    {
      name: 'getPrereqs',
      args: [
        {
          name: 'x',
          type: 'Context'
        },
        {
          name: 'obj',
          type: 'foam.core.FObject'
        }
      ],
      javaType: 'java.util.List<CapabilityCapabilityJunction>',
      documentation: `
        Returns the list of prerequisiteCapabilityJunctions for the target capability of the ucj
      `,
      javaCode: `
        DAO prerequisiteCapabilityJunctionDAO = (DAO) (x.get("prerequisiteCapabilityJunctionDAO"));

        // get a list of the prerequisite junctions where the current capability is the dependent
        List<CapabilityCapabilityJunction> ccJunctions = (List<CapabilityCapabilityJunction>) ((ArraySink) prerequisiteCapabilityJunctionDAO
        .where(EQ(CapabilityCapabilityJunction.SOURCE_ID, ((UserCapabilityJunction) obj).getTargetId()))
        .select(new ArraySink()))
        .getArray();

        return ccJunctions;
      `
    },
    {
      name: 'checkPrereqsChainedStatus',
      args: [
        {
          name: 'x',
          type: 'Context'
        },
        {
          name: 'obj',
          type: 'foam.core.FObject'
        }
      ],
      type: 'CapabilityJunctionStatus',
      documentation: `
        Check statuses of all preRequist capabilities - returning:
        GRANTED: If all pre-reqs are in granted status
        PENDING: At least one pre-req is still in pending status
        ACTION_REQUIRED: If not any of the above
      `,
      javaCode: `
        boolean allGranted = true;
        Capability cap;
        DAO capDAO = (DAO) x.get("capabilityDAO");
        List<CapabilityCapabilityJunction> ccJunctions = ( List<CapabilityCapabilityJunction> ) getPrereqs(x, obj);
        UserCapabilityJunction ucj = (UserCapabilityJunction) obj;

        for ( CapabilityCapabilityJunction ccJunction : ccJunctions ) {
          cap = (Capability) capDAO.find((String) ccJunction.getSourceId());
          if ( ! cap.getEnabled() ) continue;
          UserCapabilityJunction ucJunction = (UserCapabilityJunction) getDelegate().find(AND(
            EQ(UserCapabilityJunction.SOURCE_ID, ucj.getSourceId()),
            EQ(UserCapabilityJunction.TARGET_ID, (String) ccJunction.getTargetId())
          ));

          if ( ucJunction != null && ucJunction.getStatus() == CapabilityJunctionStatus.GRANTED ) continue;

          // CONFIRM ucJunction status with re-put the ucj
          ucJunction = ucJunction == null ?
            new UserCapabilityJunction.Builder(x)
              .setSourceId(ucj.getSourceId())
              .setTargetId(ccJunction.getTargetId())
              .build() :
            ucJunction;
          try {
            ucJunction = (UserCapabilityJunction) ((DAO) x.get("userCapabilityJunctionDAO")).put_(x, ucJunction);
          } catch ( RuntimeException e ) {
            return CapabilityJunctionStatus.ACTION_REQUIRED;
          }

          if ( ucJunction == null ) return CapabilityJunctionStatus.ACTION_REQUIRED;
          if ( ucJunction.getStatus() != CapabilityJunctionStatus.GRANTED
            && ucJunction.getStatus() != CapabilityJunctionStatus.PENDING ) return CapabilityJunctionStatus.ACTION_REQUIRED;
          if ( ucJunction.getStatus() == CapabilityJunctionStatus.PENDING ) allGranted = false;
        }
        return allGranted ? CapabilityJunctionStatus.GRANTED : CapabilityJunctionStatus.PENDING;
      `
    },
    {
      name: 'validateData',
      args: [
        {
          name: 'x',
          type: 'Context'
        },
        {
          name: 'obj',
          type: 'foam.core.FObject'
        }
      ],
      type: 'Boolean',
      documentation: `call the validate method on data and if not "return true" then set the junction status to pending`,
      javaCode: `
        FObject data = ((UserCapabilityJunction) obj).getData();
        if ( data != null ) {
          try {
            data.validate(x);
          } catch(Exception e) {
            return false;
          }
          return true;
        }
        return false;
      `
    },
    {
      name: 'remove_',
      javaCode: `
        throw new UnsupportedOperationException("UserCapabilityJunctions should be disabled via status change.");
      `
    },
    {
      name: 'removeAll_',
      javaCode: `
        throw new UnsupportedOperationException("UserCapabilityJunctions should be disabled via status change.");
      `
    },
    {
      name: 'select_',
      javaCode: `
        DAO dao = getFilteredDAO(x);
        return dao.select_(x, sink, skip, limit, order, predicate);
      `
    },
    {
      name: 'find_',
      javaCode:`
        FObject result = super.find_(x, id);
        if ( result != null ) checkOwnership(x, (UserCapabilityJunction) result);
        return result;
      `
    }
  ]
});
<|endoftext|>


<commit_before>'use strict';

/**
 * template cache
 * @type {Object}
 */
var cache = {};

/**
 * render a html/js template
 * inspired by john resig's micro templating:
 * http://ejohn.org/blog/javascript-micro-templating/
 *
 * @param  {string} str     template string
 * @param  {object} data    template data object
 * @return {HTMLCollection} rendered template
 */
function renderTemplate(str, data) {
    var dom = document.implementation.createHTMLDocument(),
        fn = cache[str] = cache[str] ||
            new Function('obj', // eslint-disable-line no-new-func
                'var p=[],print=function(){p.push.apply(p,arguments);};' +
                'with(obj){p.push(\'' +
                str.replace(/[\r\t\n]/g, ' ')
                    .split('{%').join('\t')
                    .replace(/((^|%\})[^\t]*)"/g, '$1\r')
                    .replace(/\t=(.*?)%\}/g, '\',$1,\'')
                    .split('\t').join('\');')
                    .split('%}').join('p.push(\'')
                    .split('\r').join('"') +
                '\');}return p.join("");');
    data = typeof data === 'object' ? data : {};
    dom.body.innerHTML = fn(data);
    return dom.body.children.length > 1 ?
        dom.body.children : dom.body.children[0];
}

module.exports = renderTemplate;
<commit_msg>fix indentation in module render-template<commit_after>'use strict';

/**
 * template cache
 * @type {Object}
 */
var cache = {};

/**
 * render a html/js template
 * inspired by john resig's micro templating:
 * http://ejohn.org/blog/javascript-micro-templating/
 *
 * @param  {string} str     template string
 * @param  {object} data    template data object
 * @return {HTMLCollection} rendered template
 */
function renderTemplate(str, data) {
  var dom = document.implementation.createHTMLDocument(),
      fn = cache[str] = cache[str] ||
        new Function('obj', // eslint-disable-line no-new-func
          'var p=[],print=function(){p.push.apply(p,arguments);};' +
          'with(obj){p.push(\'' +
          str.replace(/[\r\t\n]/g, ' ')
            .split('{%').join('\t')
            .replace(/((^|%\})[^\t]*)"/g, '$1\r')
            .replace(/\t=(.*?)%\}/g, '\',$1,\'')
            .split('\t').join('\');')
            .split('%}').join('p.push(\'')
            .split('\r').join('"') +
          '\');}return p.join("");');
  data = typeof data === 'object' ? data : {};
  dom.body.innerHTML = fn(data);
  return dom.body.children.length > 1 ?
    dom.body.children : dom.body.children[0];
}

module.exports = renderTemplate;
<|endoftext|>


<commit_before>/**
* @license Apache-2.0
*
* Copyright (c) 2018 The Stdlib Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*    http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

'use strict';

/*
* When adding modules to the namespace, ensure that they are added in alphabetical order according to module name.
*/

// MODULES //

var setReadOnly = require( '@stdlib/utils/define-read-only-property' );

// MAIN //

/**
* Top-level namespace.
*
* @namespace ns
*/
var ns = {};

/**
* @name arcsine
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/arcsine}
*/
setReadOnly( ns, 'arcsine', require( '@stdlib/random/streams/arcsine' ) );

/**
* @name beta
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/beta}
*/
setReadOnly( ns, 'beta', require( '@stdlib/random/streams/beta' ) );

/**
* @name betaprime
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/betaprime}
*/
setReadOnly( ns, 'betaprime', require( '@stdlib/random/streams/betaprime' ) );

/**
* @name boxMuller
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/box-muller}
*/
setReadOnly( ns, 'boxMuller', require( '@stdlib/random/streams/box-muller' ) );

/**
* @name cauchy
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/cauchy}
*/
setReadOnly( ns, 'cauchy', require( '@stdlib/random/streams/cauchy' ) );

/**
* @name cosine
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/cosine}
*/
setReadOnly( ns, 'cosine', require( '@stdlib/random/streams/cosine' ) );

/**
* @name erlang
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/erlang}
*/
setReadOnly( ns, 'erlang', require( '@stdlib/random/streams/erlang' ) );

/**
* @name gamma
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/gamma}
*/
setReadOnly( ns, 'gamma', require( '@stdlib/random/streams/gamma' ) );

/**
* @name gumbel
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/gumbel}
*/
setReadOnly( ns, 'gumbel', require( '@stdlib/random/streams/gumbel' ) );

/**
* @name improvedZiggurat
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/improved-ziggurat}
*/
setReadOnly( ns, 'improvedZiggurat', require( '@stdlib/random/streams/improved-ziggurat' ) );

/**
* @name invgamma
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/invgamma}
*/
setReadOnly( ns, 'invgamma', require( '@stdlib/random/streams/invgamma' ) );

/**
* @name kumaraswamy
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/kumaraswamy}
*/
setReadOnly( ns, 'kumaraswamy', require( '@stdlib/random/streams/kumaraswamy' ) );

/**
* @name laplace
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/laplace}
*/
setReadOnly( ns, 'laplace', require( '@stdlib/random/streams/laplace' ) );

/**
* @name levy
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/levy}
*/
setReadOnly( ns, 'levy', require( '@stdlib/random/streams/levy' ) );

/**
* @name logistic
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/logistic}
*/
setReadOnly( ns, 'logistic', require( '@stdlib/random/streams/logistic' ) );

/**
* @name lognormal
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/lognormal}
*/
setReadOnly( ns, 'lognormal', require( '@stdlib/random/streams/lognormal' ) );

/**
* @name minstd
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/minstd}
*/
setReadOnly( ns, 'minstd', require( '@stdlib/random/streams/minstd' ) );

/**
* @name minstdShuffle
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/minstd-shuffle}
*/
setReadOnly( ns, 'minstdShuffle', require( '@stdlib/random/streams/minstd-shuffle' ) );

/**
* @name mt19937
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/mt19937}
*/
setReadOnly( ns, 'mt19937', require( '@stdlib/random/streams/mt19937' ) );

/**
* @name normal
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/normal}
*/
setReadOnly( ns, 'normal', require( '@stdlib/random/streams/normal' ) );

/**
* @name randi
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/randi}
*/
setReadOnly( ns, 'randi', require( '@stdlib/random/streams/randi' ) );

/**
* @name randn
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/randn}
*/
setReadOnly( ns, 'randn', require( '@stdlib/random/streams/randn' ) );

/**
* @name randu
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/randu}
*/
setReadOnly( ns, 'randu', require( '@stdlib/random/streams/randu' ) );

/**
* @name rayleigh
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/rayleigh}
*/
setReadOnly( ns, 'rayleigh', require( '@stdlib/random/streams/rayleigh' ) );

/**
* @name t
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/t}
*/
setReadOnly( ns, 't', require( '@stdlib/random/streams/t' ) );

/**
* @name uniform
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/uniform}
*/
setReadOnly( ns, 'uniform', require( '@stdlib/random/streams/uniform' ) );

/**
* @name weibull
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/weibull}
*/
setReadOnly( ns, 'weibull', require( '@stdlib/random/streams/weibull' ) );

// EXPORTS //

module.exports = ns;
<commit_msg>Add chi to streams namespace<commit_after>/**
* @license Apache-2.0
*
* Copyright (c) 2018 The Stdlib Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*    http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

'use strict';

/*
* When adding modules to the namespace, ensure that they are added in alphabetical order according to module name.
*/

// MODULES //

var setReadOnly = require( '@stdlib/utils/define-read-only-property' );

// MAIN //

/**
* Top-level namespace.
*
* @namespace ns
*/
var ns = {};

/**
* @name arcsine
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/arcsine}
*/
setReadOnly( ns, 'arcsine', require( '@stdlib/random/streams/arcsine' ) );

/**
* @name beta
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/beta}
*/
setReadOnly( ns, 'beta', require( '@stdlib/random/streams/beta' ) );

/**
* @name betaprime
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/betaprime}
*/
setReadOnly( ns, 'betaprime', require( '@stdlib/random/streams/betaprime' ) );

/**
* @name boxMuller
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/box-muller}
*/
setReadOnly( ns, 'boxMuller', require( '@stdlib/random/streams/box-muller' ) );

/**
* @name cauchy
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/cauchy}
*/
setReadOnly( ns, 'cauchy', require( '@stdlib/random/streams/cauchy' ) );

/**
* @name chi
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/chi}
*/
setReadOnly( ns, 'chi', require( '@stdlib/random/streams/chi' ) );

/**
* @name cosine
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/cosine}
*/
setReadOnly( ns, 'cosine', require( '@stdlib/random/streams/cosine' ) );

/**
* @name erlang
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/erlang}
*/
setReadOnly( ns, 'erlang', require( '@stdlib/random/streams/erlang' ) );

/**
* @name gamma
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/gamma}
*/
setReadOnly( ns, 'gamma', require( '@stdlib/random/streams/gamma' ) );

/**
* @name gumbel
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/gumbel}
*/
setReadOnly( ns, 'gumbel', require( '@stdlib/random/streams/gumbel' ) );

/**
* @name improvedZiggurat
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/improved-ziggurat}
*/
setReadOnly( ns, 'improvedZiggurat', require( '@stdlib/random/streams/improved-ziggurat' ) );

/**
* @name invgamma
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/invgamma}
*/
setReadOnly( ns, 'invgamma', require( '@stdlib/random/streams/invgamma' ) );

/**
* @name kumaraswamy
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/kumaraswamy}
*/
setReadOnly( ns, 'kumaraswamy', require( '@stdlib/random/streams/kumaraswamy' ) );

/**
* @name laplace
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/laplace}
*/
setReadOnly( ns, 'laplace', require( '@stdlib/random/streams/laplace' ) );

/**
* @name levy
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/levy}
*/
setReadOnly( ns, 'levy', require( '@stdlib/random/streams/levy' ) );

/**
* @name logistic
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/logistic}
*/
setReadOnly( ns, 'logistic', require( '@stdlib/random/streams/logistic' ) );

/**
* @name lognormal
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/lognormal}
*/
setReadOnly( ns, 'lognormal', require( '@stdlib/random/streams/lognormal' ) );

/**
* @name minstd
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/minstd}
*/
setReadOnly( ns, 'minstd', require( '@stdlib/random/streams/minstd' ) );

/**
* @name minstdShuffle
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/minstd-shuffle}
*/
setReadOnly( ns, 'minstdShuffle', require( '@stdlib/random/streams/minstd-shuffle' ) );

/**
* @name mt19937
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/mt19937}
*/
setReadOnly( ns, 'mt19937', require( '@stdlib/random/streams/mt19937' ) );

/**
* @name normal
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/normal}
*/
setReadOnly( ns, 'normal', require( '@stdlib/random/streams/normal' ) );

/**
* @name randi
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/randi}
*/
setReadOnly( ns, 'randi', require( '@stdlib/random/streams/randi' ) );

/**
* @name randn
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/randn}
*/
setReadOnly( ns, 'randn', require( '@stdlib/random/streams/randn' ) );

/**
* @name randu
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/randu}
*/
setReadOnly( ns, 'randu', require( '@stdlib/random/streams/randu' ) );

/**
* @name rayleigh
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/rayleigh}
*/
setReadOnly( ns, 'rayleigh', require( '@stdlib/random/streams/rayleigh' ) );

/**
* @name t
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/t}
*/
setReadOnly( ns, 't', require( '@stdlib/random/streams/t' ) );

/**
* @name uniform
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/uniform}
*/
setReadOnly( ns, 'uniform', require( '@stdlib/random/streams/uniform' ) );

/**
* @name weibull
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/random/streams/weibull}
*/
setReadOnly( ns, 'weibull', require( '@stdlib/random/streams/weibull' ) );

// EXPORTS //

module.exports = ns;
<|endoftext|>