<commit_before>var path = require("path");
const merge = require("webpack-merge");
var node_modules = path.resolve(__dirname, "node_modules");
var pathToOriginalJavascriptFilesInLib = path.resolve(__dirname, "lib");
var pathToBookEditJS = path.resolve(__dirname, "bookEdit/js");
var pathToOriginalJavascriptFilesInModified_Libraries = path.resolve(
    __dirname,
    "modified_libraries"
);
var globule = require("globule");

//note: if you change this, change it in gulpfile.js & karma.conf.js as well
var outputDir = "../../output/browser";
const core = require("./webpack.core.js");

// Because our output directory does not have the same parent as our node_modules, we
// need to resolve the babel related presets (and plugins).  This mapping function was
// suggested at https://github.com/babel/babel-loader/issues/166.
function localResolve(preset) {
    return Array.isArray(preset)
        ? [require.resolve(preset[0]), preset[1]]
        : require.resolve(preset);
}

module.exports = merge(core, {
    // mode must be set to either "production" or "development" in webpack 4.
    // Webpack-common is intended to be 'required' by something that provides that.
    context: __dirname,
    //Bloom is not (yet) one webapp; it's actually a several loosely related ones.
    //So we have multiple "entry points" that we need to emit. Fortunately the webpack 4
    //optimization.splitChunks extracts the code that is common to more than one into "commonBundle.js".
    // The root file for each bundle should import errorHandler.ts to enable Bloom's custom
    // error handling for that web page.
    entry: {
        problemReportBundle: "./problemDialog/ProblemDialog.tsx",
        editTabRootBundle: "./bookEdit/editViewFrame.ts",
        readerSetupBundle:
            "./bookEdit/toolbox/readers/readerSetup/readerSetup.ts",
        editablePageBundle: "./bookEdit/editablePage.ts",
        bookPreviewBundle: "./bookPreview/bookPreview.ts",
        toolboxBundle: "./bookEdit/toolbox/toolboxBootstrap.ts",
        pageChooserBundle: "./pageChooser/page-chooser.ts",
        pageThumbnailListBundle:
            "./bookEdit/pageThumbnailList/pageThumbnailList.ts",
        pageControlsBundle:
            "./bookEdit/pageThumbnailList/pageControls/pageControls.tsx",
        publishUIBundle: globule.find(["./publish/**/*.tsx"]),
        enterpriseSettingsBundle: "./collection/enterpriseSettings.tsx",
        testBundle: globule.find([
            "./bookEdit/**/*Spec.ts",
            "./bookEdit/**/*Spec.js",
            "./lib/**/*Spec.ts",
            "./lib/**/*Spec.js",
            "./publish/**/*Spec.ts",
            "./publish/**/*Spec.js"
        ])
        //             testBundle: globule.find(["./**/*Spec.ts", "./**/*Spec.js", "!./node_modules/**"])//This slowed down webpack a ton, becuase the way it works is that it 1st it finds it all, then it excludes node_modules
    },

    output: {
        path: path.join(__dirname, outputDir),
        filename: "[name].js",

        libraryTarget: "var",

        //makes a single entry point module's epxorts accessible via Exports.
        //Note that if you include more than one entry point js in the frame, the second one will overwrite the Exports var
        // (see the other way of doing this, below, if that becomes necessary for some reason)
        // (JT: later: I think what the above means is that the root pug file for a browser control (or an iframe within it)
        // should not import more than commonBundle.js and then, after it, the one root bundle for that frame,
        // one of the bundles specified in the entry: block above. If you do import more than one, only the exports
        // from the LAST one will be accessible in FrameExports, since each bundle will set FrameExports and the
        // last one will win. I can't find the 'other way' of doing things if you need both lots of exports;
        // the preferred solution would be to reorganize the code so that each frame has only one root bundle.)
        library: "FrameExports"
    },

    resolve: {
        // For some reason, webpack began to complain about being given minified source.
        // alias: {
        //   "react-dom": pathToReactDom,
        //   react: pathToReact // the point of this is to use the minified version. https://christianalfoni.github.io/react-webpack-cookbook/Optimizing-rebundling.html
        // },
        modules: [
            ".",
            pathToOriginalJavascriptFilesInLib,
            node_modules,
            pathToBookEditJS,
            pathToOriginalJavascriptFilesInModified_Libraries
        ],
        extensions: [".js", ".jsx", ".ts", ".tsx"] //We may need to add .less here... otherwise maybe it will ignore them unless they are require()'d
    },
    optimization: {
        minimize: false,
        namedModules: true,
        splitChunks: {
            cacheGroups: {
                default: false,
                commons: {
                    name: "commonBundle",
                    chunks: "initial",
                    // Our build process creates 10 independent bundle files.  (See exports.entry
                    // above.)  minChunks specifies how many of those bundles must contain a common
                    // chunk for that common chunk to be moved into commonBundle.js.  The default
                    // value 1 moves everything to a massive commonBundle.js, leaving only a small
                    // stub for each of the 10 original bundle files.  Specifying 10 creates the
                    // smallest commonBundle.js file, which is 6% smaller than the file created for
                    // webpack 1 using the old CommonChunkPlugin.  Specifying 9 (or 7 or 8) creates
                    // a 17% bigger commonBundle file at the cost of the smallest original bundle
                    // having access to some code it doesn't use.  This seemed like a good tradeoff.
                    minChunks: 9,
                    // This is the default value for minSize, the minimum size of a chunk to move
                    // to commonBundle.js.  Changing it didn't seem to have any effect in our build
                    // process.
                    minSize: 30000,
                    reuseExistingChunk: true
                }
            }
        }
    },
    module: {
        rules: [
            {
                // For the most part, we're using typescript and ts-loader handles that.
                // But for things that are still in javascript, the following babel setup allows newer
                // javascript features by compiling to the version JS feature supported by the specific
                // version of FF we currently ship with.
                test: /\.(js|jsx)$/,
                exclude: [
                    /node_modules/,
                    /ckeditor/,
                    /jquery-ui/,
                    /-min/,
                    /qtip/,
                    /xregexp-all-min.js/
                ],
                use: [
                    {
                        loader: "babel-loader",
                        query: {
                            presets: [
                                // Ensure that we target our version of geckofx (mozilla/firefox)
                                [
                                    "@babel/preset-env",
                                    {
                                        targets: {
                                            browsers: [
                                                "Firefox >= 45",
                                                "last 2 versions"
                                            ]
                                        }
                                    }
                                ],
                                "@babel/preset-react"
                            ].map(localResolve)
                        }
                    }
                ]
            },
            // WOFF Font
            {
                test: /\.woff(\?v=\d+\.\d+\.\d+)?$/,
                use: {
                    loader: "url-loader",
                    options: {
                        limit: 10000,
                        mimetype: "application/font-woff"
                    }
                }
            }
        ]
    }
});
<commit_msg>Don't publish storybook to users (BL-7882)<commit_after>var path = require("path");
const merge = require("webpack-merge");
var node_modules = path.resolve(__dirname, "node_modules");
var pathToOriginalJavascriptFilesInLib = path.resolve(__dirname, "lib");
var pathToBookEditJS = path.resolve(__dirname, "bookEdit/js");
var pathToOriginalJavascriptFilesInModified_Libraries = path.resolve(
    __dirname,
    "modified_libraries"
);
var globule = require("globule");

//note: if you change this, change it in gulpfile.js & karma.conf.js as well
var outputDir = "../../output/browser";
const core = require("./webpack.core.js");

// Because our output directory does not have the same parent as our node_modules, we
// need to resolve the babel related presets (and plugins).  This mapping function was
// suggested at https://github.com/babel/babel-loader/issues/166.
function localResolve(preset) {
    return Array.isArray(preset)
        ? [require.resolve(preset[0]), preset[1]]
        : require.resolve(preset);
}

module.exports = merge(core, {
    // mode must be set to either "production" or "development" in webpack 4.
    // Webpack-common is intended to be 'required' by something that provides that.
    context: __dirname,
    //Bloom is not (yet) one webapp; it's actually a several loosely related ones.
    //So we have multiple "entry points" that we need to emit. Fortunately the webpack 4
    //optimization.splitChunks extracts the code that is common to more than one into "commonBundle.js".
    // The root file for each bundle should import errorHandler.ts to enable Bloom's custom
    // error handling for that web page.
    entry: {
        problemReportBundle: "./problemDialog/ProblemDialog.tsx",
        editTabRootBundle: "./bookEdit/editViewFrame.ts",
        readerSetupBundle:
            "./bookEdit/toolbox/readers/readerSetup/readerSetup.ts",
        editablePageBundle: "./bookEdit/editablePage.ts",
        bookPreviewBundle: "./bookPreview/bookPreview.ts",
        toolboxBundle: "./bookEdit/toolbox/toolboxBootstrap.ts",
        pageChooserBundle: "./pageChooser/page-chooser.ts",
        pageThumbnailListBundle:
            "./bookEdit/pageThumbnailList/pageThumbnailList.ts",
        pageControlsBundle:
            "./bookEdit/pageThumbnailList/pageControls/pageControls.tsx",
        publishUIBundle: globule.find([
            "./publish/**/*.tsx",
            "!./publish/**/stories.tsx"
        ]),
        enterpriseSettingsBundle: "./collection/enterpriseSettings.tsx",
        testBundle: globule.find([
            "./bookEdit/**/*Spec.ts",
            "./bookEdit/**/*Spec.js",
            "./lib/**/*Spec.ts",
            "./lib/**/*Spec.js",
            "./publish/**/*Spec.ts",
            "./publish/**/*Spec.js"
        ])
        //             testBundle: globule.find(["./**/*Spec.ts", "./**/*Spec.js", "!./node_modules/**"])//This slowed down webpack a ton, becuase the way it works is that it 1st it finds it all, then it excludes node_modules
    },

    output: {
        path: path.join(__dirname, outputDir),
        filename: "[name].js",

        libraryTarget: "var",

        //makes a single entry point module's epxorts accessible via Exports.
        //Note that if you include more than one entry point js in the frame, the second one will overwrite the Exports var
        // (see the other way of doing this, below, if that becomes necessary for some reason)
        // (JT: later: I think what the above means is that the root pug file for a browser control (or an iframe within it)
        // should not import more than commonBundle.js and then, after it, the one root bundle for that frame,
        // one of the bundles specified in the entry: block above. If you do import more than one, only the exports
        // from the LAST one will be accessible in FrameExports, since each bundle will set FrameExports and the
        // last one will win. I can't find the 'other way' of doing things if you need both lots of exports;
        // the preferred solution would be to reorganize the code so that each frame has only one root bundle.)
        library: "FrameExports"
    },

    resolve: {
        // For some reason, webpack began to complain about being given minified source.
        // alias: {
        //   "react-dom": pathToReactDom,
        //   react: pathToReact // the point of this is to use the minified version. https://christianalfoni.github.io/react-webpack-cookbook/Optimizing-rebundling.html
        // },
        modules: [
            ".",
            pathToOriginalJavascriptFilesInLib,
            node_modules,
            pathToBookEditJS,
            pathToOriginalJavascriptFilesInModified_Libraries
        ],
        extensions: [".js", ".jsx", ".ts", ".tsx"] //We may need to add .less here... otherwise maybe it will ignore them unless they are require()'d
    },
    optimization: {
        minimize: false,
        namedModules: true,
        splitChunks: {
            cacheGroups: {
                default: false,
                commons: {
                    name: "commonBundle",
                    chunks: "initial",
                    // Our build process creates 10 independent bundle files.  (See exports.entry
                    // above.)  minChunks specifies how many of those bundles must contain a common
                    // chunk for that common chunk to be moved into commonBundle.js.  The default
                    // value 1 moves everything to a massive commonBundle.js, leaving only a small
                    // stub for each of the 10 original bundle files.  Specifying 10 creates the
                    // smallest commonBundle.js file, which is 6% smaller than the file created for
                    // webpack 1 using the old CommonChunkPlugin.  Specifying 9 (or 7 or 8) creates
                    // a 17% bigger commonBundle file at the cost of the smallest original bundle
                    // having access to some code it doesn't use.  This seemed like a good tradeoff.
                    minChunks: 9,
                    // This is the default value for minSize, the minimum size of a chunk to move
                    // to commonBundle.js.  Changing it didn't seem to have any effect in our build
                    // process.
                    minSize: 30000,
                    reuseExistingChunk: true
                }
            }
        }
    },
    module: {
        rules: [
            {
                // For the most part, we're using typescript and ts-loader handles that.
                // But for things that are still in javascript, the following babel setup allows newer
                // javascript features by compiling to the version JS feature supported by the specific
                // version of FF we currently ship with.
                test: /\.(js|jsx)$/,
                exclude: [
                    /node_modules/,
                    /ckeditor/,
                    /jquery-ui/,
                    /-min/,
                    /qtip/,
                    /xregexp-all-min.js/
                ],
                use: [
                    {
                        loader: "babel-loader",
                        query: {
                            presets: [
                                // Ensure that we target our version of geckofx (mozilla/firefox)
                                [
                                    "@babel/preset-env",
                                    {
                                        targets: {
                                            browsers: [
                                                "Firefox >= 45",
                                                "last 2 versions"
                                            ]
                                        }
                                    }
                                ],
                                "@babel/preset-react"
                            ].map(localResolve)
                        }
                    }
                ]
            },
            // WOFF Font
            {
                test: /\.woff(\?v=\d+\.\d+\.\d+)?$/,
                use: {
                    loader: "url-loader",
                    options: {
                        limit: 10000,
                        mimetype: "application/font-woff"
                    }
                }
            }
        ]
    }
});
<|endoftext|>


<commit_before>/*
  Copyright 2018 Google LLC

  Use of this source code is governed by an MIT-style
  license that can be found in the LICENSE file or at
  https://opensource.org/licenses/MIT.
*/

const expect = require('chai').expect;
const path = require('path');
const proxyquire = require('proxyquire');
const sinon = require('sinon');

const constants = require('../../../packages/workbox-cli/src/lib/constants');
const errors = require('../../../packages/workbox-cli/src/lib/errors');

describe(`[workbox-cli] app.js`, function() {
  const MODULE_PATH = '../../../packages/workbox-cli/src/app';
  const PROXIED_CONFIG_FILE = path.resolve(process.cwd(), '/will/be/proxied');
  const PROXIED_DEST_DIR = path.resolve(process.cwd(), 'build');
  const PROXIED_ERROR = new Error('proxied error message');
  const PROXIED_CONFIG = {};
  const INVALID_CONFIG_FILE = path.resolve(process.cwd(), path.join('does', 'not', 'exist'));
  const UNKNOWN_COMMAND = 'unknown-command';
  const WORKBOX_BUILD_COMMANDS = [
    'generateSW',
    'injectManifest',
  ];
  const WORKBOX_BUILD_NO_WARNINGS_RETURN_VALUE = {
    size: 2,
    count: 1,
    warnings: [],
  };
  const WORKBOX_BUILD_WITH_WARNINGS_RETURN_VALUE = {
    size: 2,
    count: 1,
    warnings: ['warning'],
  };

  describe(`failures due to bad parameters`, function() {
    const app = require(MODULE_PATH);

    it(`should reject when both parameters are missing`, async function() {
      try {
        await app();
        throw new Error('Unexpected success.');
      } catch (error) {
        expect(error.message).to.have.string(errors['missing-input']);
      }
    });

    it(`should reject when the command is unknown and options is present`, async function() {
      try {
        await app({input: [UNKNOWN_COMMAND, PROXIED_CONFIG_FILE]});
        throw new Error('Unexpected success.');
      } catch (error) {
        expect(error.message).to.have.string(errors['unknown-command']);
        expect(error.message).to.have.string(UNKNOWN_COMMAND);
      }
    });

    it(`should reject when the command parameter is copyLibraries and options is missing`, async function() {
      try {
        await app({input: ['copyLibraries']});
        throw new Error('Unexpected success.');
      } catch (error) {
        expect(error.message).to.have.string(errors['missing-dest-dir-param']);
      }
    });

    for (const command of WORKBOX_BUILD_COMMANDS) {
      it(`should reject when the command parameter is ${command} and options does not exist`, async function() {
        const loggerErrorStub = sinon.stub();
        const appWithStub = proxyquire(MODULE_PATH, {
          './lib/logger': {
            error: loggerErrorStub,
          },
        });

        try {
          await appWithStub({input: [command, INVALID_CONFIG_FILE]});
          throw new Error('Unexpected success.');
        } catch (error) {
          expect(loggerErrorStub.calledOnce).to.be.true;
          expect(
              loggerErrorStub.alwaysCalledWithExactly(errors['invalid-common-js-module'])
          ).to.be.true;

          // Windows will log with backslashes that need escaping
          const escapedPath = INVALID_CONFIG_FILE.split('\\').join('\\\\');
          expect(error.message).to.have.string(escapedPath);
        }
      });
    }
  });

  describe(`failures due to workbox-build runtime errors`, function() {
    for (const command of WORKBOX_BUILD_COMMANDS) {
      // TODO: Expand this list.
      const badConfigs = [
        {},
        {swDest: null},
        {globPatterns: null, swDest: '/path/to/sw.js'},
        {foo: 'bar'},
      ];

      for (const config of badConfigs) {
        it(`should reject with a validation error when workbox-build.${command}(${JSON.stringify(config)}) is called`, async function() {
          const app = proxyquire(MODULE_PATH, {
            './lib/logger': {
              log: sinon.stub(),
            },
            './lib/read-config': (options) => {
              expect(options).to.eql(PROXIED_CONFIG_FILE);
              return config;
            },
          });

          try {
            await app({input: [command, PROXIED_CONFIG_FILE]});
            throw new Error('Unexpected success.');
          } catch (error) {
            expect(error.message).to.have.string(errors['config-validation-failed']);
          }
        });
      }

      it(`should reject with a generic runtime error when the workbox-build.${command}() rejects for any other reason`, async function() {
        const loggerErrorStub = sinon.stub();
        const app = proxyquire(MODULE_PATH, {
          './lib/logger': {
            log: sinon.stub(),
            error: loggerErrorStub,
          },
          './lib/read-config': (options) => {
            expect(options).to.eql(PROXIED_CONFIG_FILE);
            return PROXIED_CONFIG;
          },
          'workbox-build': {
            [command]: (config) => {
              expect(config).to.eql(PROXIED_CONFIG);
              throw PROXIED_ERROR;
            },
          },
        });

        try {
          await app({input: [command, PROXIED_CONFIG_FILE]});
          throw new Error('Unexpected success.');
        } catch (error) {
          expect(loggerErrorStub.calledOnce).to.be.true;
          expect(
              loggerErrorStub.alwaysCalledWithExactly(errors['workbox-build-runtime-error'])
          ).to.be.true;
          expect(error).to.eql(PROXIED_ERROR);
        }
      });
    }
  });

  describe(`successful calls`, function() {
    for (const command of WORKBOX_BUILD_COMMANDS) {
      it(`should call logger.log() upon successfully running workbox-build.${command}()`, async function() {
        const loggerLogStub = sinon.stub();
        const app = proxyquire(MODULE_PATH, {
          './lib/read-config': (options) => {
            expect(options).to.eql(PROXIED_CONFIG_FILE);
            return PROXIED_CONFIG;
          },
          './lib/logger': {
            log: loggerLogStub,
          },
          'workbox-build': {
            [command]: () => {
              return WORKBOX_BUILD_NO_WARNINGS_RETURN_VALUE;
            },
          },
        });

        await app({input: [command, PROXIED_CONFIG_FILE]});
        expect(loggerLogStub.calledTwice).to.be.true;
      });

      it(`should call logger.warn() to report warnings, and then logger.log() upon successfully running workbox-build.${command}()`, async function() {
        const loggerLogStub = sinon.stub();
        const loggerWarningStub = sinon.stub();
        const app = proxyquire(MODULE_PATH, {
          './lib/read-config': (options) => {
            expect(options).to.eql(PROXIED_CONFIG_FILE);
            return PROXIED_CONFIG;
          },
          './lib/logger': {
            log: loggerLogStub,
            warn: loggerWarningStub,
          },
          'workbox-build': {
            [command]: () => {
              return WORKBOX_BUILD_WITH_WARNINGS_RETURN_VALUE;
            },
          },
        });

        await app({input: [command, PROXIED_CONFIG_FILE]});
        expect(loggerWarningStub.calledOnce).to.be.true;
        expect(loggerLogStub.calledTwice).to.be.true;
      });

      it(`should call logger.log() upon successfully running workbox-build.${command}() using the default config file location`, async function() {
        const loggerLogStub = sinon.stub();
        const app = proxyquire(MODULE_PATH, {
          './lib/read-config': (options) => {
            const defaultConfigPath = path.join(process.cwd(), constants.defaultConfigFile);
            expect(options).to.eql(defaultConfigPath);
            return PROXIED_CONFIG;
          },
          './lib/logger': {
            log: loggerLogStub,
          },
          'workbox-build': {
            [command]: () => {
              return WORKBOX_BUILD_NO_WARNINGS_RETURN_VALUE;
            },
          },
        });

        await app({input: [command]});
        expect(loggerLogStub.calledTwice).to.be.true;
      });
    }

    it(`should call logger.log() upon successfully running workbox-build.copyWorkboxLibraries()`, async function() {
      const loggerLogStub = sinon.stub();
      const app = proxyquire(MODULE_PATH, {
        './lib/logger': {
          log: loggerLogStub,
        },
        'workbox-build': {
          copyWorkboxLibraries: (destDir) => {
            expect(destDir).to.eql(PROXIED_DEST_DIR);
            return path.join(destDir, 'workbox');
          },
        },
      });

      await app({input: ['copyLibraries', PROXIED_DEST_DIR]});
      expect(loggerLogStub.callCount).to.eql(3);
    });

    it(`should call params.showHelp() when passed 'help'`, async function() {
      const app = require(MODULE_PATH);

      const params = {
        input: ['help'],
        showHelp: sinon.stub(),
      };

      await app(params);
      expect(params.showHelp.calledOnce).to.be.true;
    });

    it(`should call params.showHelp() when not passed any command`, async function() {
      const app = require(MODULE_PATH);

      const params = {
        input: [],
        showHelp: sinon.stub(),
      };

      await app(params);
      expect(params.showHelp.calledOnce).to.be.true;
    });
  });
});
<commit_msg>Remove extra escaping. (#2008)<commit_after>/*
  Copyright 2018 Google LLC

  Use of this source code is governed by an MIT-style
  license that can be found in the LICENSE file or at
  https://opensource.org/licenses/MIT.
*/

const expect = require('chai').expect;
const path = require('path');
const proxyquire = require('proxyquire');
const sinon = require('sinon');

const constants = require('../../../packages/workbox-cli/src/lib/constants');
const errors = require('../../../packages/workbox-cli/src/lib/errors');

describe(`[workbox-cli] app.js`, function() {
  const MODULE_PATH = '../../../packages/workbox-cli/src/app';
  const PROXIED_CONFIG_FILE = path.resolve(process.cwd(), '/will/be/proxied');
  const PROXIED_DEST_DIR = path.resolve(process.cwd(), 'build');
  const PROXIED_ERROR = new Error('proxied error message');
  const PROXIED_CONFIG = {};
  const INVALID_CONFIG_FILE = path.resolve(process.cwd(), path.join('does', 'not', 'exist'));
  const UNKNOWN_COMMAND = 'unknown-command';
  const WORKBOX_BUILD_COMMANDS = [
    'generateSW',
    'injectManifest',
  ];
  const WORKBOX_BUILD_NO_WARNINGS_RETURN_VALUE = {
    size: 2,
    count: 1,
    warnings: [],
  };
  const WORKBOX_BUILD_WITH_WARNINGS_RETURN_VALUE = {
    size: 2,
    count: 1,
    warnings: ['warning'],
  };

  describe(`failures due to bad parameters`, function() {
    const app = require(MODULE_PATH);

    it(`should reject when both parameters are missing`, async function() {
      try {
        await app();
        throw new Error('Unexpected success.');
      } catch (error) {
        expect(error.message).to.have.string(errors['missing-input']);
      }
    });

    it(`should reject when the command is unknown and options is present`, async function() {
      try {
        await app({input: [UNKNOWN_COMMAND, PROXIED_CONFIG_FILE]});
        throw new Error('Unexpected success.');
      } catch (error) {
        expect(error.message).to.have.string(errors['unknown-command']);
        expect(error.message).to.have.string(UNKNOWN_COMMAND);
      }
    });

    it(`should reject when the command parameter is copyLibraries and options is missing`, async function() {
      try {
        await app({input: ['copyLibraries']});
        throw new Error('Unexpected success.');
      } catch (error) {
        expect(error.message).to.have.string(errors['missing-dest-dir-param']);
      }
    });

    for (const command of WORKBOX_BUILD_COMMANDS) {
      it(`should reject when the command parameter is ${command} and options does not exist`, async function() {
        const loggerErrorStub = sinon.stub();
        const appWithStub = proxyquire(MODULE_PATH, {
          './lib/logger': {
            error: loggerErrorStub,
          },
        });

        try {
          await appWithStub({input: [command, INVALID_CONFIG_FILE]});
          throw new Error('Unexpected success.');
        } catch (error) {
          expect(loggerErrorStub.calledOnce).to.be.true;
          expect(
              loggerErrorStub.alwaysCalledWithExactly(errors['invalid-common-js-module'])
          ).to.be.true;

          expect(error.message).to.have.string(INVALID_CONFIG_FILE);
        }
      });
    }
  });

  describe(`failures due to workbox-build runtime errors`, function() {
    for (const command of WORKBOX_BUILD_COMMANDS) {
      // TODO: Expand this list.
      const badConfigs = [
        {},
        {swDest: null},
        {globPatterns: null, swDest: '/path/to/sw.js'},
        {foo: 'bar'},
      ];

      for (const config of badConfigs) {
        it(`should reject with a validation error when workbox-build.${command}(${JSON.stringify(config)}) is called`, async function() {
          const app = proxyquire(MODULE_PATH, {
            './lib/logger': {
              log: sinon.stub(),
            },
            './lib/read-config': (options) => {
              expect(options).to.eql(PROXIED_CONFIG_FILE);
              return config;
            },
          });

          try {
            await app({input: [command, PROXIED_CONFIG_FILE]});
            throw new Error('Unexpected success.');
          } catch (error) {
            expect(error.message).to.have.string(errors['config-validation-failed']);
          }
        });
      }

      it(`should reject with a generic runtime error when the workbox-build.${command}() rejects for any other reason`, async function() {
        const loggerErrorStub = sinon.stub();
        const app = proxyquire(MODULE_PATH, {
          './lib/logger': {
            log: sinon.stub(),
            error: loggerErrorStub,
          },
          './lib/read-config': (options) => {
            expect(options).to.eql(PROXIED_CONFIG_FILE);
            return PROXIED_CONFIG;
          },
          'workbox-build': {
            [command]: (config) => {
              expect(config).to.eql(PROXIED_CONFIG);
              throw PROXIED_ERROR;
            },
          },
        });

        try {
          await app({input: [command, PROXIED_CONFIG_FILE]});
          throw new Error('Unexpected success.');
        } catch (error) {
          expect(loggerErrorStub.calledOnce).to.be.true;
          expect(
              loggerErrorStub.alwaysCalledWithExactly(errors['workbox-build-runtime-error'])
          ).to.be.true;
          expect(error).to.eql(PROXIED_ERROR);
        }
      });
    }
  });

  describe(`successful calls`, function() {
    for (const command of WORKBOX_BUILD_COMMANDS) {
      it(`should call logger.log() upon successfully running workbox-build.${command}()`, async function() {
        const loggerLogStub = sinon.stub();
        const app = proxyquire(MODULE_PATH, {
          './lib/read-config': (options) => {
            expect(options).to.eql(PROXIED_CONFIG_FILE);
            return PROXIED_CONFIG;
          },
          './lib/logger': {
            log: loggerLogStub,
          },
          'workbox-build': {
            [command]: () => {
              return WORKBOX_BUILD_NO_WARNINGS_RETURN_VALUE;
            },
          },
        });

        await app({input: [command, PROXIED_CONFIG_FILE]});
        expect(loggerLogStub.calledTwice).to.be.true;
      });

      it(`should call logger.warn() to report warnings, and then logger.log() upon successfully running workbox-build.${command}()`, async function() {
        const loggerLogStub = sinon.stub();
        const loggerWarningStub = sinon.stub();
        const app = proxyquire(MODULE_PATH, {
          './lib/read-config': (options) => {
            expect(options).to.eql(PROXIED_CONFIG_FILE);
            return PROXIED_CONFIG;
          },
          './lib/logger': {
            log: loggerLogStub,
            warn: loggerWarningStub,
          },
          'workbox-build': {
            [command]: () => {
              return WORKBOX_BUILD_WITH_WARNINGS_RETURN_VALUE;
            },
          },
        });

        await app({input: [command, PROXIED_CONFIG_FILE]});
        expect(loggerWarningStub.calledOnce).to.be.true;
        expect(loggerLogStub.calledTwice).to.be.true;
      });

      it(`should call logger.log() upon successfully running workbox-build.${command}() using the default config file location`, async function() {
        const loggerLogStub = sinon.stub();
        const app = proxyquire(MODULE_PATH, {
          './lib/read-config': (options) => {
            const defaultConfigPath = path.join(process.cwd(), constants.defaultConfigFile);
            expect(options).to.eql(defaultConfigPath);
            return PROXIED_CONFIG;
          },
          './lib/logger': {
            log: loggerLogStub,
          },
          'workbox-build': {
            [command]: () => {
              return WORKBOX_BUILD_NO_WARNINGS_RETURN_VALUE;
            },
          },
        });

        await app({input: [command]});
        expect(loggerLogStub.calledTwice).to.be.true;
      });
    }

    it(`should call logger.log() upon successfully running workbox-build.copyWorkboxLibraries()`, async function() {
      const loggerLogStub = sinon.stub();
      const app = proxyquire(MODULE_PATH, {
        './lib/logger': {
          log: loggerLogStub,
        },
        'workbox-build': {
          copyWorkboxLibraries: (destDir) => {
            expect(destDir).to.eql(PROXIED_DEST_DIR);
            return path.join(destDir, 'workbox');
          },
        },
      });

      await app({input: ['copyLibraries', PROXIED_DEST_DIR]});
      expect(loggerLogStub.callCount).to.eql(3);
    });

    it(`should call params.showHelp() when passed 'help'`, async function() {
      const app = require(MODULE_PATH);

      const params = {
        input: ['help'],
        showHelp: sinon.stub(),
      };

      await app(params);
      expect(params.showHelp.calledOnce).to.be.true;
    });

    it(`should call params.showHelp() when not passed any command`, async function() {
      const app = require(MODULE_PATH);

      const params = {
        input: [],
        showHelp: sinon.stub(),
      };

      await app(params);
      expect(params.showHelp.calledOnce).to.be.true;
    });
  });
});
<|endoftext|>


<commit_before>/**
 * @file EditableSelect component
 */

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

// Components
import TextField from '../TextField';
import Popup from '../Popup';
import List from '../List';
import DynamicRenderList from '../DynamicRenderList';
import GroupList from '../GroupList';

// Statics
import Theme from '../Theme';
import Position from '../_statics/Position';

// Vendors
import {findDOMNode} from 'react-dom';
import classNames from 'classnames';
import Util from '../_vendors/Util';
import DropdownCalculation from '../_vendors/DropdownCalculation';
import ComponentUtil from '../_vendors/ComponentUtil';

class EditableSelect extends Component {

    static Theme = Theme;
    static Position = Position;

    static getDerivedStateFromProps(props, state) {
        return {
            prevProps: props,
            value: ComponentUtil.getDerivedState(props, state, 'value')
        };
    }

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.wrapper = createRef();
        this.wrapperEl = null;
        this.trigger = createRef();
        this.triggerEl = null;
        this.popup = createRef();
        this.popupEl = null;

        this.state = {
            value: props.value,
            listValue: props.value,
            filter: '',
            popupVisible: false,
            isAbove: false
        };

    }

    componentDidMount() {
        this.wrapperEl = this.wrapper?.current;
        this.popupEl = this.popup?.current;
        this.triggerEl = findDOMNode(this.trigger?.current);
    }

    componentDidUpdate(prevProps, prevState, snapshot) {
        if (this.state.popupVisible) {
            this.popup?.current?.resetPosition?.();
        }
    }

    handleChangeValue = value => {
        const {useFilter, onChange} = this.props;

        this.setState({
            value,
            filter: useFilter ? value : this.state.filter,
            listValue: ''
        }, () => {
            onChange && onChange(value);
            this.popup?.current?.resetPosition?.();
        });
    };

    showPopup = () => {
        this.setState({
            popupVisible: true
        }, () => {
            this.props.onTriggerClick && this.props.onTriggerClick(this.state.popupVisible);
            this.props.onFocus && this.props.onFocus();
        });
    };

    closePopup = () => {
        this.setState({
            popupVisible: false
        }, () => {
            this.trigger?.current.blur();
            this.props.onTriggerClick && this.props.onTriggerClick(this.state.popupVisible);
            this.props.onPopupClosed && this.props.onPopupClosed();
        });
    };

    triggerHandler = (el, triggerEl, popupEl, currentVisible, isBlurClose) => {

        if (!triggerEl) {
            return true;
        }

        while (el) {
            if (el === popupEl) {
                return currentVisible;
            } else if (el === triggerEl) {
                return true;
            }
            el = el.parentNode;
        }

        return isBlurClose ? false : currentVisible;

    };

    filterData = () => {

        const {data, filterCallback} = this.props, {filter} = this.state;

        if (!filter) {
            return data;
        }

        if (filterCallback) {
            return filterCallback(filter, data);
        }

        const {displayField, isGrouped} = this.props,
            filterFunc = originData => {
                return originData.filter(item => typeof item === 'object' && !!item[displayField] ?
                    item[displayField].toString().toUpperCase().includes(filter.toUpperCase())
                    :
                    item.toString().toUpperCase().includes(filter.toUpperCase()));
            };

        if (isGrouped) {

            let result = Object.assign(data);

            for (let i = 0, len = result.length; i < len; i++) {
                let group = result[i];
                group.children = filterFunc(group.children);
                if (group.children.length < 1) {
                    result.splice(i, 1);
                    i--;
                }
            }

        }

        return filterFunc(data);

    };

    handlePopupRender = popupEl => {
        const isAbove = DropdownCalculation.isAbove(this.wrapperEl, this.triggerEl, findDOMNode(popupEl));
        if (isAbove !== this.state.isAbove) {
            this.setState({
                isAbove
            });
        }
    };

    handleChange = value => {
        const {valueField, autoClose, renderer} = this.props,
            itemValue = renderer ? renderer(value) : (typeof value === 'object' ? value[valueField] : value);

        if (autoClose) {
            this.closePopup();
        }

        this.setState({
            value: itemValue,
            listValue: value
        }, () => {
            const {onChange} = this.props;
            onChange && onChange(itemValue);
        });

    };

    render() {

        const {
                className, popupClassName, style, popupStyle, name, placeholder, disabled, valueField, displayField,
                clearButtonIconCls, clearButtonVisible, descriptionField, position, rightIconCls, resetPopPositionWait,
                triggerTheme, isGrouped, onItemClick, renderer, noMatchedPopupVisible, noMatchedMsg,
                useDynamicRenderList, onMouseOver, onMouseOut, parentEl
            } = this.props,
            {value, listValue, popupVisible, isAbove} = this.state,

            isAboveFinally = position === Position.TOP || position === Position.TOP_LEFT
                || position === Position.TOP_RIGHT || (!position && isAbove),
            listData = this.filterData();

        return (

                {
                    name ?
                        <input type="hidden"
                               name={name}
                               value={value}/>
                        :
                        null
                }

                <TextField ref={this.trigger}
                           className={classNames('editable-select-trigger', isAboveFinally ? 'above' : 'blow', {
                               activated: popupVisible && (listData?.length > 0 || noMatchedPopupVisible),
                               empty: !value
                           })}
                           value={value}
                           rightIconCls={`${rightIconCls} editable-select-trigger-icon`}
                           placeholder={placeholder}
                           disabled={disabled}
                           theme={triggerTheme}
                           onMouseOver={onMouseOver}
                           onMouseOut={onMouseOut}
                           clearButtonVisible={clearButtonVisible}
                           clearButtonIconCls={clearButtonIconCls}
                           onChange={this.handleChangeValue}
                           onFocus={this.showPopup}/>

                {
                    !noMatchedPopupVisible && listData?.length < 1 ?
                        null
                        :
                        <Popup className={classNames('editable-select-popup', isAboveFinally ? 'above' : 'blow', {
                            'no-matched-popup': listData?.length < 1,
                            [popupClassName]: popupClassName
                        })}
                               style={Object.assign({
                                   width: this.triggerEl && getComputedStyle(this.triggerEl).width
                               }, popupStyle)}
                               visible={popupVisible}
                               triggerEl={this.triggerEl}
                               triggerHandler={this.triggerHandler}
                               parentEl={parentEl}
                               ref={this.popup}
                               hasTriangle={false}
                               position={position ? position : (isAbove ? Position.TOP_LEFT : Position.BOTTOM_LEFT)}
                               resetPositionWait={resetPopPositionWait}
                               onRender={this.handlePopupRender}
                               onRequestClose={this.closePopup}>

                            {
                                listData?.length < 1 ?

                                        {
                                            noMatchedMsg ?
                                                noMatchedMsg
                                                :

                                                    No matched value.

                                        }

                                    :
                                    isGrouped ?
                                        <GroupList className="editable-select-list"
                                                   data={listData}
                                                   value={listValue}
                                                   valueField={valueField}
                                                   displayField={displayField}
                                                   descriptionField={descriptionField}
                                                   renderer={renderer}
                                                   onItemClick={onItemClick}
                                                   onChange={this.handleChange}/>
                                        :
                                        useDynamicRenderList ?
                                            <DynamicRenderList className="editable-select-list"
                                                               data={listData}
                                                               value={listValue}
                                                               valueField={valueField}
                                                               displayField={displayField}
                                                               descriptionField={descriptionField}
                                                               renderer={renderer}
                                                               onItemClick={onItemClick}
                                                               onChange={this.handleChange}/>
                                            :
                                            <List className="editable-select-list"
                                                  data={listData}
                                                  valueField={valueField}
                                                  value={listValue}
                                                  displayField={displayField}
                                                  descriptionField={descriptionField}
                                                  renderer={renderer}
                                                  onItemClick={onItemClick}
                                                  onChange={this.handleChange}/>
                            }

                        </Popup>
                }

        );

    }
}

EditableSelect.propTypes = {

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

    /**
     * The class name of the popup element.
     */
    popupClassName: PropTypes.string,

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

    rightIconCls: PropTypes.string,

    /**
     * Override the styles of the popup element.
     */
    popupStyle: PropTypes.object,

    position: PropTypes.oneOf(Util.enumerateValue(Position)),

    /**
     * The name of the editableSelect.
     */
    name: PropTypes.string,

    /**
     * The value of the editableSelect.
     */
    value: PropTypes.any,

    /**
     * You can create a complicated renderer callback instead of value and desc prop.
     */
    renderer: PropTypes.func,

    /**
     * The placeholder of the editableSelect.
     */
    placeholder: PropTypes.string,

    /**
     * The options data.
     */
    data: PropTypes.oneOfType([

        // not grouped
        PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.shape({

            /**
             * The CSS class name of the list button.
             */
            className: PropTypes.string,

            /**
             * Override the styles of the list button.
             */
            style: PropTypes.object,

            /**
             * The theme of the list button.
             */
            theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

            /**
             * The text value of the list button.Type can be string or number.
             */
            value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

            /**
             * The desc value of the list button. Type can be string or number.
             */
            desc: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

            /**
             * If true, the list button will be disabled.
             */
            disabled: PropTypes.bool,

            /**
             * If true,the button will be have loading effect.
             */
            isLoading: PropTypes.bool,

            /**
             * If true,the element's ripple effect will be disabled.
             */
            disableTouchRipple: PropTypes.bool,

            /**
             * Use this property to display an icon. It will display on the left.
             */
            iconCls: PropTypes.string,

            /**
             * Use this property to display an icon. It will display on the right.
             */
            rightIconCls: PropTypes.string,

            /**
             * You can create a complicated renderer callback instead of value and desc prop.
             */
            renderer: PropTypes.func,

            /**
             * Callback function fired when a list item touch-tapped.
             */
            onClick: PropTypes.func

        }), PropTypes.string, PropTypes.number])),

        // grouped
        PropTypes.array

    ]).isRequired,

    /**
     * The invalid message of editableSelect.
     */
    invalidMsg: PropTypes.string,

    /**
     * If true,the editableSelect will be disabled.
     */
    disabled: PropTypes.bool,

    /**
     * The visiblity of no matched popup.
     */
    noMatchedPopupVisible: PropTypes.bool,

    /**
     * If true,clearButton will display when the textField is not empty.
     */
    clearButtonVisible: PropTypes.bool,
    clearButtonIconCls: PropTypes.string,

    /**
     * The value field name in data. (default: "value")
     */
    valueField: PropTypes.string,

    displayField: PropTypes.string,

    /**
     * The description field name in data. (default: "desc")
     */
    descriptionField: PropTypes.string,

    /**
     * The message of the editableSelect.
     */
    infoMsg: PropTypes.string,

    /**
     * Use this function to format the options's text.
     */
    textFormat: PropTypes.func,

    /**
     * If true,the drop-down box automatically closed after selection.
     */
    autoClose: PropTypes.bool,

    /**
     * If true,the drop-down box will have search input.
     */
    useFilter: PropTypes.bool,

    /**
     * The theme of editableSelect.Can be primary,highlight,success,warning,error.
     */
    triggerTheme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * If true,the drop-down box will be have group selection.
     */
    isGrouped: PropTypes.bool,

    resetPopPositionWait: PropTypes.number,
    noMatchedMsg: PropTypes.string,
    useDynamicRenderList: PropTypes.bool,
    parentEl: PropTypes.object,

    /**
     * Callback function fired when the button is touch-tapped.
     */
    onItemClick: PropTypes.func,

    /**
     * Callback function fired when filter changed.
     */
    filterCallback: PropTypes.func,

    /**
     * Callback function fired when a menu item is selected.
     */
    onChange: PropTypes.func,

    onMouseOver: PropTypes.func,
    onMouseOut: PropTypes.func,
    onTriggerClick: PropTypes.func,
    onFocus: PropTypes.func,
    onPopupClosed: PropTypes.func

};

EditableSelect.defaultProps = {

    name: '',
    value: '',
    placeholder: 'Please select ...',
    rightIconCls: 'fas fa-angle-down',
    data: [],
    invalidMsg: '',
    disabled: false,

    displayField: 'text',
    descriptionField: 'desc',

    infoMsg: '',
    autoClose: true,
    useFilter: false,
    useDynamicRenderList: false,
    noMatchedPopupVisible: true,
    clearButtonVisible: true,
    clearButtonIconCls: 'fas fa-times-circle',
    triggerTheme: Theme.DEFAULT,
    isGrouped: false,
    resetPopPositionWait: 250

};

export default EditableSelect;
<commit_msg>fix editable select<commit_after>/**
 * @file EditableSelect component
 */

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

// Components
import TextField from '../TextField';
import Popup from '../Popup';
import List from '../List';
import DynamicRenderList from '../DynamicRenderList';
import GroupList from '../GroupList';

// Statics
import Theme from '../Theme';
import Position from '../_statics/Position';

// Vendors
import {findDOMNode} from 'react-dom';
import classNames from 'classnames';
import Util from '../_vendors/Util';
import DropdownCalculation from '../_vendors/DropdownCalculation';
import ComponentUtil from '../_vendors/ComponentUtil';

class EditableSelect extends Component {

    static Theme = Theme;
    static Position = Position;

    static getDerivedStateFromProps(props, state) {
        return {
            prevProps: props,
            value: ComponentUtil.getDerivedState(props, state, 'value')
        };
    }

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.wrapper = createRef();
        this.wrapperEl = null;
        this.trigger = createRef();
        this.triggerEl = null;
        this.popup = createRef();
        this.popupEl = null;

        this.state = {
            value: props.value,
            listValue: props.value,
            filter: '',
            popupVisible: false,
            isAbove: false
        };

    }

    componentDidMount() {
        this.wrapperEl = this.wrapper?.current;
        this.popupEl = this.popup?.current;
        this.triggerEl = findDOMNode(this.trigger?.current);
    }

    componentDidUpdate(prevProps, prevState, snapshot) {
        if (this.state.popupVisible) {
            this.popup?.current?.resetPosition?.();
        }
    }

    handleChangeValue = value => {
        const {useFilter, onChange} = this.props;

        this.setState({
            value,
            filter: useFilter ? value : this.state.filter,
            listValue: ''
        }, () => {
            onChange && onChange(value);
            this.popup?.current?.resetPosition?.();
        });
    };

    showPopup = () => {
        this.setState({
            popupVisible: true
        }, () => {
            this.props.onTriggerClick && this.props.onTriggerClick(this.state.popupVisible);
            this.props.onFocus && this.props.onFocus();
        });
    };

    closePopup = () => {
        this.setState({
            popupVisible: false
        }, () => {
            this.trigger?.current.blur();
            this.props.onTriggerClick && this.props.onTriggerClick(this.state.popupVisible);
            this.props.onPopupClosed && this.props.onPopupClosed();
        });
    };

    triggerHandler = (el, triggerEl, popupEl, currentVisible, isBlurClose) => {

        if (!triggerEl) {
            return true;
        }

        while (el) {
            if (el === popupEl) {
                return currentVisible;
            } else if (el === triggerEl) {
                return true;
            }
            el = el.parentNode;
        }

        return isBlurClose ? false : currentVisible;

    };

    filterData = () => {

        const {data, filterCallback} = this.props, {filter} = this.state;

        if (!filter) {
            return data;
        }

        if (filterCallback) {
            return filterCallback(filter, data);
        }

        const {displayField, isGrouped} = this.props,
            filterFunc = originData => {
                return originData.filter(item => typeof item === 'object' && !!item[displayField] ?
                    item[displayField].toString().toUpperCase().includes(filter.toUpperCase())
                    :
                    item.toString().toUpperCase().includes(filter.toUpperCase()));
            };

        if (isGrouped) {

            let result = Object.assign(data);

            for (let i = 0, len = result.length; i < len; i++) {
                let group = result[i];
                group.children = filterFunc(group.children);
                if (group.children.length < 1) {
                    result.splice(i, 1);
                    i--;
                }
            }

        }

        return filterFunc(data);

    };

    handlePopupRender = popupEl => {
        const isAbove = DropdownCalculation.isAbove(this.wrapperEl, this.triggerEl, findDOMNode(popupEl));
        if (isAbove !== this.state.isAbove) {
            this.setState({
                isAbove
            });
        }
    };

    handleChange = value => {
        const {valueField, autoClose, renderer} = this.props,
            itemValue = renderer ? renderer(value) : (typeof value === 'object' ? value[valueField] : value);

        if (autoClose) {
            this.closePopup();
        }

        this.setState({
            value: itemValue,
            filter: itemValue,
            listValue: value
        }, () => {
            const {onChange} = this.props;
            onChange && onChange(itemValue);
        });

    };

    render() {

        const {
                className, popupClassName, style, popupStyle, name, placeholder, disabled, valueField, displayField,
                clearButtonIconCls, clearButtonVisible, descriptionField, position, rightIconCls, resetPopPositionWait,
                triggerTheme, isGrouped, onItemClick, renderer, noMatchedPopupVisible, noMatchedMsg,
                useDynamicRenderList, onMouseOver, onMouseOut, parentEl
            } = this.props,
            {value, listValue, popupVisible, isAbove} = this.state,

            isAboveFinally = position === Position.TOP || position === Position.TOP_LEFT
                || position === Position.TOP_RIGHT || (!position && isAbove),
            listData = this.filterData();

        return (

                {
                    name ?
                        <input type="hidden"
                               name={name}
                               value={value}/>
                        :
                        null
                }

                <TextField ref={this.trigger}
                           className={classNames('editable-select-trigger', isAboveFinally ? 'above' : 'blow', {
                               activated: popupVisible && (listData?.length > 0 || noMatchedPopupVisible),
                               empty: !value
                           })}
                           value={value}
                           rightIconCls={`${rightIconCls} editable-select-trigger-icon`}
                           placeholder={placeholder}
                           disabled={disabled}
                           theme={triggerTheme}
                           onMouseOver={onMouseOver}
                           onMouseOut={onMouseOut}
                           clearButtonVisible={clearButtonVisible}
                           clearButtonIconCls={clearButtonIconCls}
                           onChange={this.handleChangeValue}
                           onFocus={this.showPopup}/>

                {
                    !noMatchedPopupVisible && listData?.length < 1 ?
                        null
                        :
                        <Popup className={classNames('editable-select-popup', isAboveFinally ? 'above' : 'blow', {
                            'no-matched-popup': listData?.length < 1,
                            [popupClassName]: popupClassName
                        })}
                               style={Object.assign({
                                   width: this.triggerEl && getComputedStyle(this.triggerEl).width
                               }, popupStyle)}
                               visible={popupVisible}
                               triggerEl={this.triggerEl}
                               triggerHandler={this.triggerHandler}
                               parentEl={parentEl}
                               ref={this.popup}
                               hasTriangle={false}
                               position={position ? position : (isAbove ? Position.TOP_LEFT : Position.BOTTOM_LEFT)}
                               resetPositionWait={resetPopPositionWait}
                               onRender={this.handlePopupRender}
                               onRequestClose={this.closePopup}>

                            {
                                listData?.length < 1 ?

                                        {
                                            noMatchedMsg ?
                                                noMatchedMsg
                                                :

                                                    No matched value.

                                        }

                                    :
                                    isGrouped ?
                                        <GroupList className="editable-select-list"
                                                   data={listData}
                                                   value={listValue}
                                                   valueField={valueField}
                                                   displayField={displayField}
                                                   descriptionField={descriptionField}
                                                   renderer={renderer}
                                                   onItemClick={onItemClick}
                                                   onChange={this.handleChange}/>
                                        :
                                        useDynamicRenderList ?
                                            <DynamicRenderList className="editable-select-list"
                                                               data={listData}
                                                               value={listValue}
                                                               valueField={valueField}
                                                               displayField={displayField}
                                                               descriptionField={descriptionField}
                                                               renderer={renderer}
                                                               onItemClick={onItemClick}
                                                               onChange={this.handleChange}/>
                                            :
                                            <List className="editable-select-list"
                                                  data={listData}
                                                  valueField={valueField}
                                                  value={listValue}
                                                  displayField={displayField}
                                                  descriptionField={descriptionField}
                                                  renderer={renderer}
                                                  onItemClick={onItemClick}
                                                  onChange={this.handleChange}/>
                            }

                        </Popup>
                }

        );

    }
}

EditableSelect.propTypes = {

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

    /**
     * The class name of the popup element.
     */
    popupClassName: PropTypes.string,

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

    rightIconCls: PropTypes.string,

    /**
     * Override the styles of the popup element.
     */
    popupStyle: PropTypes.object,

    position: PropTypes.oneOf(Util.enumerateValue(Position)),

    /**
     * The name of the editableSelect.
     */
    name: PropTypes.string,

    /**
     * The value of the editableSelect.
     */
    value: PropTypes.any,

    /**
     * You can create a complicated renderer callback instead of value and desc prop.
     */
    renderer: PropTypes.func,

    /**
     * The placeholder of the editableSelect.
     */
    placeholder: PropTypes.string,

    /**
     * The options data.
     */
    data: PropTypes.oneOfType([

        // not grouped
        PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.shape({

            /**
             * The CSS class name of the list button.
             */
            className: PropTypes.string,

            /**
             * Override the styles of the list button.
             */
            style: PropTypes.object,

            /**
             * The theme of the list button.
             */
            theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

            /**
             * The text value of the list button.Type can be string or number.
             */
            value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

            /**
             * The desc value of the list button. Type can be string or number.
             */
            desc: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

            /**
             * If true, the list button will be disabled.
             */
            disabled: PropTypes.bool,

            /**
             * If true,the button will be have loading effect.
             */
            isLoading: PropTypes.bool,

            /**
             * If true,the element's ripple effect will be disabled.
             */
            disableTouchRipple: PropTypes.bool,

            /**
             * Use this property to display an icon. It will display on the left.
             */
            iconCls: PropTypes.string,

            /**
             * Use this property to display an icon. It will display on the right.
             */
            rightIconCls: PropTypes.string,

            /**
             * You can create a complicated renderer callback instead of value and desc prop.
             */
            renderer: PropTypes.func,

            /**
             * Callback function fired when a list item touch-tapped.
             */
            onClick: PropTypes.func

        }), PropTypes.string, PropTypes.number])),

        // grouped
        PropTypes.array

    ]).isRequired,

    /**
     * The invalid message of editableSelect.
     */
    invalidMsg: PropTypes.string,

    /**
     * If true,the editableSelect will be disabled.
     */
    disabled: PropTypes.bool,

    /**
     * The visiblity of no matched popup.
     */
    noMatchedPopupVisible: PropTypes.bool,

    /**
     * If true,clearButton will display when the textField is not empty.
     */
    clearButtonVisible: PropTypes.bool,
    clearButtonIconCls: PropTypes.string,

    /**
     * The value field name in data. (default: "value")
     */
    valueField: PropTypes.string,

    displayField: PropTypes.string,

    /**
     * The description field name in data. (default: "desc")
     */
    descriptionField: PropTypes.string,

    /**
     * The message of the editableSelect.
     */
    infoMsg: PropTypes.string,

    /**
     * Use this function to format the options's text.
     */
    textFormat: PropTypes.func,

    /**
     * If true,the drop-down box automatically closed after selection.
     */
    autoClose: PropTypes.bool,

    /**
     * If true,the drop-down box will have search input.
     */
    useFilter: PropTypes.bool,

    /**
     * The theme of editableSelect.Can be primary,highlight,success,warning,error.
     */
    triggerTheme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * If true,the drop-down box will be have group selection.
     */
    isGrouped: PropTypes.bool,

    resetPopPositionWait: PropTypes.number,
    noMatchedMsg: PropTypes.string,
    useDynamicRenderList: PropTypes.bool,
    parentEl: PropTypes.object,

    /**
     * Callback function fired when the button is touch-tapped.
     */
    onItemClick: PropTypes.func,

    /**
     * Callback function fired when filter changed.
     */
    filterCallback: PropTypes.func,

    /**
     * Callback function fired when a menu item is selected.
     */
    onChange: PropTypes.func,

    onMouseOver: PropTypes.func,
    onMouseOut: PropTypes.func,
    onTriggerClick: PropTypes.func,
    onFocus: PropTypes.func,
    onPopupClosed: PropTypes.func

};

EditableSelect.defaultProps = {

    name: '',
    value: '',
    placeholder: 'Please select ...',
    rightIconCls: 'fas fa-angle-down',
    data: [],
    invalidMsg: '',
    disabled: false,

    displayField: 'text',
    descriptionField: 'desc',

    infoMsg: '',
    autoClose: true,
    useFilter: false,
    useDynamicRenderList: false,
    noMatchedPopupVisible: true,
    clearButtonVisible: true,
    clearButtonIconCls: 'fas fa-times-circle',
    triggerTheme: Theme.DEFAULT,
    isGrouped: false,
    resetPopPositionWait: 250

};

export default EditableSelect;
<|endoftext|>


<commit_before>/**
* Contains the declaration for the {@link module:layout/FittableLayout~FittableLayout}, {@link module:layout/FittableLayout~FittableColumnsLayout}
* and {@link module:layout/FittableLayout~FittableRowsLayout} kinds.
* @module layout/FittableLayout
*/

var
	kind = require('enyo/kind'),
	dom = require('enyo/dom'),
	Control = require('enyo/Control'),
	Layout = require('enyo/Layout');

var detector = document.createElement('div'),
	flexAvailable =
		(detector.style.flexBasis !== undefined) ||
		(detector.style.webkitFlexBasis !== undefined) ||
		(detector.style.mozFlexBasis !== undefined) ||
		(detector.style.msFlexBasis !== undefined);

/**
* {@link module:layout/FittableLayout~FittableLayout} provides the base positioning and boundary logic for
* the fittable layout strategy. The fittable layout strategy is based on
* laying out items in either a set of rows or a set of columns, with most of
* the items having natural size, but one item expanding to fill the remaining
* space. The item that expands is labeled with the attribute `fit: true`.
*
* The subkinds {@link module:layout/FittableLayout~FittableColumnsLayout} and {@link module:layout/FittableLayout~FittableRowsLayout}
* (or _their_ subkinds) are used for layout rather than `enyo.FittableLayout` because
* they specify properties that the framework expects to be available when laying items
* out.
*
* When available on the platform, you can opt-in to have `enyo.FittableLayout` use CSS
* flexible box (flexbox) to implement fitting behavior on the platform for better
* performance; Enyo will fall back to JavaScript-based layout on older platforms.
* Three subtle differences between the flexbox and JavaScript implementations
* should be noted:

* - When using flexbox, vertical margins (i.e., `margin-top`, `margin-bottom`) will
* not collapse; when using JavaScript layout, vertical margins will collapse according
* to static layout rules.
*
* - When using flexbox, non-fitting children of the Fittable must not be sized
* using percentages of the container (even if set to `position: relative`);
* this is explicitly not supported by the flexbox 2013 spec.
*
* - The flexbox-based Fittable implementation will respect multiple children
* with `fit: true` (the fitting space will be divided equally between them).
* This is NOT supported by the JavaScript implementation, and you should not rely
* upon this behavior if you are deploying to platforms without flexbox support.
*
* The flexbox implementation was added to Enyo 2.5.0 as an optional performance
* optimization; to use the optimization, set `useFlex: true` on the Fittable
* container.  This will cause flexbox to be used when possible.
*
* @class FittableLayout
* @extends module:enyo/Layout~Layout
* @public
*/
var FittableLayout = module.exports = kind(/** @lends module:layout/FittableLayout~FittableLayout.prototype */{
	name: 'enyo.FittableLayout',

	/**
	* @private
	*/
	kind: Layout,

	/**
	* @private
	*/
	noDefer: true,

	/**
	* @method
	* @private
	*/
	constructor: function () {
		Layout.prototype._constructor.apply(this, arguments);

		// Add the force-ltr class if we're in RTL mode, but this control is set explicitly to NOT be in RTL mode.
		this.container.addRemoveClass('force-left-to-right', (Control.prototype.rtl && !this.container.get('rtl')) );

		// Flexbox optimization is determined by global flexAvailable and per-instance opt-in useFlex flag
		this.useFlex = flexAvailable && (this.container.useFlex === true);
		if (this.useFlex) {
			this.container.addClass(this.flexLayoutClass);
		} else {
			this.container.addClass(this.fitLayoutClass);
		}
	},

	/**
	* @private
	*/
	calcFitIndex: function () {
		var aChildren = this.container.children,
			oChild,
			n;

		for (n=0; n<aChildren.length; n++) {
			oChild = aChildren[n];
			if (oChild.fit && oChild.showing) {
				return n;
			}
		}
	},

	/**
	* @private
	*/
	getFitControl: function () {
		var aChildren = this.container.children,
			oFitChild = aChildren[this.fitIndex];

		if (!(oFitChild && oFitChild.fit && oFitChild.showing)) {
			this.fitIndex = this.calcFitIndex();
			oFitChild = aChildren[this.fitIndex];
		}
		return oFitChild;
	},

	/**
	* @private
	*/
	shouldReverse: function () {
		return this.container.rtl && this.orient === 'h';
	},

	/**
	* @private
	*/
	destroy: function () {
		Layout.prototype.destroy.apply(this, arguments);

		if (this.container) {
			this.container.removeClass(this.useFlex ? this.flexLayoutClass : this.fitLayoutClass);
		}
	},

	/**
	* @private
	*/
	getFirstChild: function() {
		var aChildren = this.getShowingChildren();

		if (this.shouldReverse()) {
			return aChildren[aChildren.length - 1];
		} else {
			return aChildren[0];
		}
	},

	/**
	* @private
	*/
	getLastChild: function() {
		var aChildren = this.getShowingChildren();

		if (this.shouldReverse()) {
			return aChildren[0];
		} else {
			return aChildren[aChildren.length - 1];
		}
	},

	/**
	* @private
	*/
	getShowingChildren: function() {
		var a = [],
			n = 0,
			aChildren = this.container.children,
			nLength   = aChildren.length;

		for (;n<nLength; n++) {
			if (aChildren[n].showing) {
				a.push(aChildren[n]);
			}
		}

		return a;
	},

	/**
	* @private
	*/
	_reflow: function(sMeasureName, sClienMeasure, sAttrBefore, sAttrAfter) {
		this.container.addRemoveClass('enyo-stretch', !this.container.noStretch);

		var oFitChild       = this.getFitControl(),
			oContainerNode  = this.container.hasNode(),  // Container node
			nTotalSize     = 0,                          // Total container width or height without padding
			nBeforeOffset   = 0,                         // Offset before fit child
			nAfterOffset    = 0,                         // Offset after fit child
			oPadding,                                    // Object containing t,b,r,l paddings
			oBounds,                                     // Bounds object of fit control
			oLastChild,
			oFirstChild,
			nFitSize;

		if (!oFitChild || !oContainerNode) { return; }

		oPadding   = dom.calcPaddingExtents(oContainerNode);
		oBounds    = oFitChild.getBounds();
		nTotalSize = oContainerNode[sClienMeasure] - (oPadding[sAttrBefore] + oPadding[sAttrAfter]);

		if (this.shouldReverse()) {
			oFirstChild  = this.getFirstChild();
			nAfterOffset = nTotalSize - (oBounds[sAttrBefore] + oBounds[sMeasureName]);

			var nMarginBeforeFirstChild = dom.getComputedBoxValue(oFirstChild.hasNode(), 'margin', sAttrBefore) || 0;

			if (oFirstChild == oFitChild) {
				nBeforeOffset = nMarginBeforeFirstChild;
			} else {
				var oFirstChildBounds      = oFirstChild.getBounds(),
					nSpaceBeforeFirstChild = oFirstChildBounds[sAttrBefore];

				nBeforeOffset = oBounds[sAttrBefore] + nMarginBeforeFirstChild - nSpaceBeforeFirstChild;
			}
		} else {
			oLastChild    = this.getLastChild();
			nBeforeOffset = oBounds[sAttrBefore] - (oPadding[sAttrBefore] || 0);

			var nMarginAfterLastChild = dom.getComputedBoxValue(oLastChild.hasNode(), 'margin', sAttrAfter) || 0;

			if (oLastChild == oFitChild) {
				nAfterOffset = nMarginAfterLastChild;
			} else {
				var oLastChildBounds = oLastChild.getBounds(),
					nFitChildEnd     = oBounds[sAttrBefore] + oBounds[sMeasureName],
					nLastChildEnd    = oLastChildBounds[sAttrBefore] + oLastChildBounds[sMeasureName] +  nMarginAfterLastChild;

				nAfterOffset = nLastChildEnd - nFitChildEnd;
			}
		}

		nFitSize = nTotalSize - (nBeforeOffset + nAfterOffset);
		oFitChild.applyStyle(sMeasureName, nFitSize + 'px');
	},

	/**
	* Assigns any static layout properties not dependent on changes to the
	* rendered component or container sizes, etc.
	*
	* @public
	*/
	flow: function() {
		if (this.useFlex) {
			var i,
				children = this.container.children,
				child;
			this.container.addClass(this.flexLayoutClass);
			this.container.addRemoveClass('nostretch', this.container.noStretch);
			for (i=0; i<children.length; i++) {
				child = children[i];
				child.addClass('enyo-flex-item');
				child.addRemoveClass('flex', child.fit);
			}
		}
	},

	/**
	* Updates the layout to reflect any changes made to the layout container or
	* the contained components.
	*
	* @public
	*/
	reflow: function() {
		if (!this.useFlex) {
			if (this.orient == 'h') {
				this._reflow('width', 'clientWidth', 'left', 'right');
			} else {
				this._reflow('height', 'clientHeight', 'top', 'bottom');
			}
		}
	},

	/**
	* @private
	* @lends module:layout/FittableLayout~FittableLayout.prototype
	*/
	statics: {
		/**
		* Indicates whether flexbox optimization can be used.
		*
		* @type {Boolean}
		* @default  false
		* @private
		*/
		flexAvailable: flexAvailable
	}
});

/**
* {@link module:layout/FittableLayout~FittableColumnsLayout} provides a container in which items are laid
* out in a set of vertical columns, with most of the items having natural
* size, but one expanding to fill the remaining space. The one that expands is
* labeled with the attribute `fit: true`.
*
* `enyo.FittableColumnsLayout` is meant to be used as a value for the
* `layoutKind` property of other kinds. `layoutKind` provides a way to add
* layout behavior in a pluggable fashion while retaining the ability to use a
* specific base kind.
*
* For more information, see the documentation on
* [Fittables]{@linkplain $dev-guide/building-apps/layout/fittables.html} in the
* Enyo Developer Guide.
*
* @class FittableColumnsLayout
* @extends module:layout/FittableLayout~FittableLayout
* @public
*/

/**
* The declaration for {@link module:layout/FittableLayout~FittableColumnsLayout}
*/
module.exports.Columns = kind(/** @lends module:layout/FittableLayout~FittableColumnsLayout.prototype */{
	name        : 'enyo.FittableColumnsLayout',
	kind        : FittableLayout,
	orient      : 'h',
	fitLayoutClass : 'enyo-fittable-columns-layout',
	flexLayoutClass: 'enyo-flex-container columns'
});

/**
* {@link module:layout/FittableLayout~FittableRowsLayout} provides a container in which items are laid out
* in a set of horizontal rows, with most of the items having natural size, but
* one expanding to fill the remaining space. The one that expands is labeled
* with the attribute `fit: true`.
*
* `enyo.FittableRowsLayout` is meant to be used as a value for the
* `layoutKind` property of other kinds. `layoutKind` provides a way to add
* layout behavior in a pluggable fashion while retaining the ability to use a
* specific base kind.
*
* For more information, see the documentation on
* [Fittables]{@linkplain $dev-guide/building-apps/layout/fittables.html} in the
* Enyo Developer Guide.
*
* @class FittableRowsLayout
* @extends module:layout/FittableLayout~FittableLayout
* @public
*/

/**
* The declaration for {@link module:layout/FittableLayout~FittableRowsLayout}
*/
module.exports.Rows = kind(
	/** @lends module:layout/FittableLayout~FittableRowsLayout.prototype */ {

	/**
	* @private
	*/
	name        : 'enyo.FittableRowsLayout',

	/**
	* @private
	*/
	kind        : FittableLayout,

	/**
	* Layout CSS class used to fit rows.
	*
	* @type {String}
	* @default 'enyo-fittable-rows-layout'
	* @public
	*/
	fitLayoutClass : 'enyo-fittable-rows-layout',

	/**
	* The orientation of the layout.
	*
	* @type {String}
	* @default 'v'
	* @public
	*/
	orient      : 'v',

	/**
	* @private
	*/
	flexLayoutClass: 'enyo-flex-container rows'
});
<commit_msg>ENYO-2258-ListActions: Scroller in list action is not showed<commit_after>/**
* Contains the declaration for the {@link module:layout/FittableLayout~FittableLayout}, {@link module:layout/FittableLayout~FittableColumnsLayout}
* and {@link module:layout/FittableLayout~FittableRowsLayout} kinds.
* @module layout/FittableLayout
*/

var
	kind = require('enyo/kind'),
	dom = require('enyo/dom'),
	Control = require('enyo/Control'),
	utils = require('enyo/utils'),
	Layout = require('enyo/Layout');

var detector = document.createElement('div'),
	flexAvailable =
		(detector.style.flexBasis !== undefined) ||
		(detector.style.webkitFlexBasis !== undefined) ||
		(detector.style.mozFlexBasis !== undefined) ||
		(detector.style.msFlexBasis !== undefined);

/**
* {@link module:layout/FittableLayout~FittableLayout} provides the base positioning and boundary logic for
* the fittable layout strategy. The fittable layout strategy is based on
* laying out items in either a set of rows or a set of columns, with most of
* the items having natural size, but one item expanding to fill the remaining
* space. The item that expands is labeled with the attribute `fit: true`.
*
* The subkinds {@link module:layout/FittableLayout~FittableColumnsLayout} and {@link module:layout/FittableLayout~FittableRowsLayout}
* (or _their_ subkinds) are used for layout rather than `enyo.FittableLayout` because
* they specify properties that the framework expects to be available when laying items
* out.
*
* When available on the platform, you can opt-in to have `enyo.FittableLayout` use CSS
* flexible box (flexbox) to implement fitting behavior on the platform for better
* performance; Enyo will fall back to JavaScript-based layout on older platforms.
* Three subtle differences between the flexbox and JavaScript implementations
* should be noted:

* - When using flexbox, vertical margins (i.e., `margin-top`, `margin-bottom`) will
* not collapse; when using JavaScript layout, vertical margins will collapse according
* to static layout rules.
*
* - When using flexbox, non-fitting children of the Fittable must not be sized
* using percentages of the container (even if set to `position: relative`);
* this is explicitly not supported by the flexbox 2013 spec.
*
* - The flexbox-based Fittable implementation will respect multiple children
* with `fit: true` (the fitting space will be divided equally between them).
* This is NOT supported by the JavaScript implementation, and you should not rely
* upon this behavior if you are deploying to platforms without flexbox support.
*
* The flexbox implementation was added to Enyo 2.5.0 as an optional performance
* optimization; to use the optimization, set `useFlex: true` on the Fittable
* container.  This will cause flexbox to be used when possible.
*
* @class FittableLayout
* @extends module:enyo/Layout~Layout
* @public
*/
var FittableLayout = module.exports = kind(/** @lends module:layout/FittableLayout~FittableLayout.prototype */{
	name: 'enyo.FittableLayout',

	/**
	* @private
	*/
	kind: Layout,

	/**
	* @private
	*/
	noDefer: true,

	/**
	* @method
	* @private
	*/
	constructor: function () {
		Layout.prototype._constructor.apply(this, arguments);

		// If the layout was flowed on container hidden, reflow the layout while showing it.
		this.container.addObserver('showing', utils.bindSafely(this, '_reflowOnShowing'));

		// Add the force-ltr class if we're in RTL mode, but this control is set explicitly to NOT be in RTL mode.
		this.container.addRemoveClass('force-left-to-right', (Control.prototype.rtl && !this.container.get('rtl')) );

		// Flexbox optimization is determined by global flexAvailable and per-instance opt-in useFlex flag
		this.useFlex = flexAvailable && (this.container.useFlex === true);
		if (this.useFlex) {
			this.container.addClass(this.flexLayoutClass);
		} else {
			this.container.addClass(this.fitLayoutClass);
		}
	},

	/**
	* @private
	*/
	_reflowOnShowing: function (was, is, prop) {
		if (is && this._hiddenReflow) {
			this.reflow();
			this._hiddenReflow = false;
		}
	},

	/**
	* @private
	*/
	calcFitIndex: function () {
		var aChildren = this.container.children,
			oChild,
			n;

		for (n=0; n<aChildren.length; n++) {
			oChild = aChildren[n];
			if (oChild.fit && oChild.showing) {
				return n;
			}
		}
	},

	/**
	* @private
	*/
	getFitControl: function () {
		var aChildren = this.container.children,
			oFitChild = aChildren[this.fitIndex];

		if (!(oFitChild && oFitChild.fit && oFitChild.showing)) {
			this.fitIndex = this.calcFitIndex();
			oFitChild = aChildren[this.fitIndex];
		}
		return oFitChild;
	},

	/**
	* @private
	*/
	shouldReverse: function () {
		return this.container.rtl && this.orient === 'h';
	},

	/**
	* @private
	*/
	destroy: function () {
		Layout.prototype.destroy.apply(this, arguments);

		if (this.container) {
			this.container.removeClass(this.useFlex ? this.flexLayoutClass : this.fitLayoutClass);
		}
	},

	/**
	* @private
	*/
	getFirstChild: function() {
		var aChildren = this.getShowingChildren();

		if (this.shouldReverse()) {
			return aChildren[aChildren.length - 1];
		} else {
			return aChildren[0];
		}
	},

	/**
	* @private
	*/
	getLastChild: function() {
		var aChildren = this.getShowingChildren();

		if (this.shouldReverse()) {
			return aChildren[0];
		} else {
			return aChildren[aChildren.length - 1];
		}
	},

	/**
	* @private
	*/
	getShowingChildren: function() {
		var a = [],
			n = 0,
			aChildren = this.container.children,
			nLength   = aChildren.length;

		for (;n<nLength; n++) {
			if (aChildren[n].showing) {
				a.push(aChildren[n]);
			}
		}

		return a;
	},

	/**
	* @private
	*/
	_reflow: function(sMeasureName, sClienMeasure, sAttrBefore, sAttrAfter) {
		this.container.addRemoveClass('enyo-stretch', !this.container.noStretch);

		// check if it overflows while container is not showing and remember the state
		if (!this.container.showing) this._hiddenReflow = true;

		var oFitChild       = this.getFitControl(),
			oContainerNode  = this.container.hasNode(),  // Container node
			nTotalSize     = 0,                          // Total container width or height without padding
			nBeforeOffset   = 0,                         // Offset before fit child
			nAfterOffset    = 0,                         // Offset after fit child
			oPadding,                                    // Object containing t,b,r,l paddings
			oBounds,                                     // Bounds object of fit control
			oLastChild,
			oFirstChild,
			nFitSize;

		if (!oFitChild || !oContainerNode) { return; }

		oPadding   = dom.calcPaddingExtents(oContainerNode);
		oBounds    = oFitChild.getBounds();
		nTotalSize = oContainerNode[sClienMeasure] - (oPadding[sAttrBefore] + oPadding[sAttrAfter]);

		if (this.shouldReverse()) {
			oFirstChild  = this.getFirstChild();
			nAfterOffset = nTotalSize - (oBounds[sAttrBefore] + oBounds[sMeasureName]);

			var nMarginBeforeFirstChild = dom.getComputedBoxValue(oFirstChild.hasNode(), 'margin', sAttrBefore) || 0;

			if (oFirstChild == oFitChild) {
				nBeforeOffset = nMarginBeforeFirstChild;
			} else {
				var oFirstChildBounds      = oFirstChild.getBounds(),
					nSpaceBeforeFirstChild = oFirstChildBounds[sAttrBefore];

				nBeforeOffset = oBounds[sAttrBefore] + nMarginBeforeFirstChild - nSpaceBeforeFirstChild;
			}
		} else {
			oLastChild    = this.getLastChild();
			nBeforeOffset = oBounds[sAttrBefore] - (oPadding[sAttrBefore] || 0);

			var nMarginAfterLastChild = dom.getComputedBoxValue(oLastChild.hasNode(), 'margin', sAttrAfter) || 0;

			if (oLastChild == oFitChild) {
				nAfterOffset = nMarginAfterLastChild;
			} else {
				var oLastChildBounds = oLastChild.getBounds(),
					nFitChildEnd     = oBounds[sAttrBefore] + oBounds[sMeasureName],
					nLastChildEnd    = oLastChildBounds[sAttrBefore] + oLastChildBounds[sMeasureName] +  nMarginAfterLastChild;

				nAfterOffset = nLastChildEnd - nFitChildEnd;
			}
		}

		nFitSize = nTotalSize - (nBeforeOffset + nAfterOffset);
		oFitChild.applyStyle(sMeasureName, nFitSize + 'px');
	},

	/**
	* Assigns any static layout properties not dependent on changes to the
	* rendered component or container sizes, etc.
	*
	* @public
	*/
	flow: function() {
		if (this.useFlex) {
			var i,
				children = this.container.children,
				child;
			this.container.addClass(this.flexLayoutClass);
			this.container.addRemoveClass('nostretch', this.container.noStretch);
			for (i=0; i<children.length; i++) {
				child = children[i];
				child.addClass('enyo-flex-item');
				child.addRemoveClass('flex', child.fit);
			}
		}
	},

	/**
	* Updates the layout to reflect any changes made to the layout container or
	* the contained components.
	*
	* @public
	*/
	reflow: function() {
		if (!this.useFlex) {
			if (this.orient == 'h') {
				this._reflow('width', 'clientWidth', 'left', 'right');
			} else {
				this._reflow('height', 'clientHeight', 'top', 'bottom');
			}
		}
	},

	/**
	* @private
	* @lends module:layout/FittableLayout~FittableLayout.prototype
	*/
	statics: {
		/**
		* Indicates whether flexbox optimization can be used.
		*
		* @type {Boolean}
		* @default  false
		* @private
		*/
		flexAvailable: flexAvailable
	}
});

/**
* {@link module:layout/FittableLayout~FittableColumnsLayout} provides a container in which items are laid
* out in a set of vertical columns, with most of the items having natural
* size, but one expanding to fill the remaining space. The one that expands is
* labeled with the attribute `fit: true`.
*
* `enyo.FittableColumnsLayout` is meant to be used as a value for the
* `layoutKind` property of other kinds. `layoutKind` provides a way to add
* layout behavior in a pluggable fashion while retaining the ability to use a
* specific base kind.
*
* For more information, see the documentation on
* [Fittables]{@linkplain $dev-guide/building-apps/layout/fittables.html} in the
* Enyo Developer Guide.
*
* @class FittableColumnsLayout
* @extends module:layout/FittableLayout~FittableLayout
* @public
*/

/**
* The declaration for {@link module:layout/FittableLayout~FittableColumnsLayout}
*/
module.exports.Columns = kind(/** @lends module:layout/FittableLayout~FittableColumnsLayout.prototype */{
	name        : 'enyo.FittableColumnsLayout',
	kind        : FittableLayout,
	orient      : 'h',
	fitLayoutClass : 'enyo-fittable-columns-layout',
	flexLayoutClass: 'enyo-flex-container columns'
});

/**
* {@link module:layout/FittableLayout~FittableRowsLayout} provides a container in which items are laid out
* in a set of horizontal rows, with most of the items having natural size, but
* one expanding to fill the remaining space. The one that expands is labeled
* with the attribute `fit: true`.
*
* `enyo.FittableRowsLayout` is meant to be used as a value for the
* `layoutKind` property of other kinds. `layoutKind` provides a way to add
* layout behavior in a pluggable fashion while retaining the ability to use a
* specific base kind.
*
* For more information, see the documentation on
* [Fittables]{@linkplain $dev-guide/building-apps/layout/fittables.html} in the
* Enyo Developer Guide.
*
* @class FittableRowsLayout
* @extends module:layout/FittableLayout~FittableLayout
* @public
*/

/**
* The declaration for {@link module:layout/FittableLayout~FittableRowsLayout}
*/
module.exports.Rows = kind(
	/** @lends module:layout/FittableLayout~FittableRowsLayout.prototype */ {

	/**
	* @private
	*/
	name        : 'enyo.FittableRowsLayout',

	/**
	* @private
	*/
	kind        : FittableLayout,

	/**
	* Layout CSS class used to fit rows.
	*
	* @type {String}
	* @default 'enyo-fittable-rows-layout'
	* @public
	*/
	fitLayoutClass : 'enyo-fittable-rows-layout',

	/**
	* The orientation of the layout.
	*
	* @type {String}
	* @default 'v'
	* @public
	*/
	orient      : 'v',

	/**
	* @private
	*/
	flexLayoutClass: 'enyo-flex-container rows'
});
<|endoftext|>


<commit_before>
    $.plot.plugins.push({
        init: init,
        options: options,
        name: 'datatable',
        version: '1.0.3'
    });
})(jQuery);
<commit_msg>fix plugin version<commit_after>
    $.plot.plugins.push({
        init: init,
        options: options,
        name: 'datatable',
        version: '1.0.4'
    });
})(jQuery);
<|endoftext|>


<commit_before>/**
 * This directive will facilitate the lookups values for entities on the server.
 *
 * User: rowan.massey
 * Date: 07/04/2015
 */

(function () {
    "use strict";

    var customLookup = function ($q) {

        var template = '
' +
                '<input type="text" ng-keyup="search()">' +
                '
' +
                '
' +
                '{{record[lookupTextField]}}' +
                '
' +
                '
' +
                '
',

            link = function (scope, element, attributes, ngModelController) {
                var input = element.find("input");

                scope.onItemSelect = function (item) {
                    input.val(item[scope.lookupTextField]);
                    ngModelController.$setViewValue(item[scope.lookupValueField]);
                    scope.foundRecords = [];
                };

                scope.search = function () {
                    var defer = $q.defer();
                    defer.resolve(scope.lookupDatasource()(input.val()));
                    defer.promise.then(function (searchResults) {
                        scope.foundRecords = searchResults;
                    });
                };
            };

        return {
            restrict: "E",
            scope: {
                lookupDatasource: "&",
                lookupTextField: "@",
                lookupValueField: "@"
            },
            replace: true,
            require: "ngModel",
            template: template,
            link: link
        };
    };
    angular.module("ruaghain.lookup-directive", []).directive('customLookup', ["$q", customLookup]);
})();<commit_msg>Changed the directive to not resolve the function that's passed in, this should be done by the relevant service.<commit_after>/**
 * This directive will facilitate the lookups values for entities on the server.
 *
 * User: rowan.massey
 * Date: 07/04/2015
 */

(function () {
    "use strict";

    var customLookup = function () {

        var template = '
' +
                '<input type="text" ng-keyup="search()">' +
                '
' +
                '
' +
                '{{record[lookupTextField]}}' +
                '
' +
                '
' +
                '
',

            link = function (scope, element, attributes, ngModelController) {
                var input = element.find("input");

                scope.onItemSelect = function (item) {
                    input.val(item[scope.lookupTextField]);
                    ngModelController.$setViewValue(item[scope.lookupValueField]);
                    scope.foundRecords = [];
                };

                //This method executes the passed in function.
                scope.search = function () {
                    scope.foundRecords = scope.lookupDatasource()(input.val());
                };
            };

        return {
            restrict: "E",
            scope: {
                lookupDatasource: "&",
                lookupTextField: "@",
                lookupValueField: "@"
            },
            replace: true,
            require: "ngModel",
            template: template,
            link: link
        };
    };
    angular.module("ruaghain.lookup-directive", []).directive('customLookup', [customLookup]);
})();<|endoftext|>