<commit_before>import PropTypes from "prop-types";
import React, { useCallback, useMemo } from "react";
import { useHistory, useRouteMatch } from "react-router-dom";
import styled from "styled-components";

import style from "@agir/front/genericComponents/_variables.scss";

import { useResponsiveMemo } from "@agir/front/genericComponents/grid";

import Button from "@agir/front/genericComponents/Button";
import Panel from "@agir/front/genericComponents/Panel";
import { RawFeatherIcon } from "@agir/front/genericComponents/FeatherIcon";

import { EVENT_TYPES } from "./eventForm.config";

import { routeConfig } from "@agir/front/app/routes.config";

const StyledOption = styled.li`
  display: flex;
  flex-flow: row nowrap;
  height: 2.75rem;
  align-items: flex-start;
  font-size: 1rem;
  line-height: 1.5;
  color: ${({ $selected }) => ($selected ? style.primary500 : style.black1000)};
  font-weight: ${({ $selected }) => ($selected ? 600 : 400)};
  cursor: ${({ $selected }) => ($selected ? "default" : "pointer")};

  button {
    display: ${({ $selected }) => ($selected ? "none" : "inline")};
    margin-left: auto;
  }

  span {
    color: ${style.primary500};
    display: inline-block;
    font-size: 1.5rem;
    padding-right: 0.5rem;
  }
`;

const StyledOptions = styled.div`
  display: flex;
  flex-flow: column nowrap;
  padding-top: 0.5rem;

  ul {
    padding: 0;
    list-style: none;

    strong {
      display: block;
      height: 2.75rem;
      font-weight: 600;
      font-size: 1rem;
      line-height: 1.5;
      display: flex;
      align-items: center;
    }
  }
`;

const StyledDefaultOption = styled.button``;
const StyledDefaultOptions = styled.div`
  display: flex;
  flex-flow: row wrap;
  gap: 0.5rem;

  button {
    border-radius: 0;
    border: none;
    box-shadow: none;
    padding: 0;
    background-color: transparent;
    font-size: 1rem;
    font-weight: 400;
    cursor: pointer;
    color: ${style.primary500};

    &[disabled],
    &[disabled]:hover {
      opacity: 0.5;
      cursor: default;
    }
  }

  ${StyledDefaultOption} {
    padding: 0.5rem 0.75rem;
    color: ${style.black1000};
    display: inline-grid;
    grid-gap: 0.5rem;
    grid-template-columns: auto auto auto;
    align-items: center;
    border: 1px solid ${style.black100};

    &:hover {
      background-color: ${style.black50};
    }

    &[disabled],
    &[disabled]:hover {
      opacity: 1;
      background-color: transparent;
      cursor: default;
    }
  }
`;

const StyledField = styled.div`
  label {
    font-size: 1rem;
    font-weight: 600;
    line-height: 1;
    padding: 4px 0;
  }
`;

const SubtypeOption = (props) => {
  const { option, onClick, selected } = props;

  const handleClick = useCallback(() => {
    onClick(option);
  }, [option, onClick]);

  return (

  );
};

const DefaultOption = (props) => {
  const { option, onClick, selected, disabled } = props;

  const handleClick = useCallback(() => {
    onClick(option);
  }, [option, onClick]);

  return (

  );
};
SubtypeOption.propTypes = DefaultOption.propTypes = {
  option: PropTypes.shape({
    id: PropTypes.number,
    label: PropTypes.string,
    iconName: PropTypes.string,
    color: PropTypes.String,
    description: PropTypes.string,
  }),
  onClick: PropTypes.func,
  selected: PropTypes.bool,
  disabled: PropTypes.bool,
};

const BASE_ROUTE = routeConfig.createEvent.getLink();
const PANEL_ROUTE = BASE_ROUTE + "type/";

const SubtypeField = (props) => {
  const { onChange, value, name, error, disabled } = props;

  const isPanelOpen = useRouteMatch(PANEL_ROUTE);
  const history = useHistory();

  const openPanel = useCallback(() => {
    history.push(PANEL_ROUTE);
  }, [history]);

  const closePanel = useCallback(() => {
    history.replace(BASE_ROUTE);
  }, [history]);

  const panelPosition = useResponsiveMemo("right", "left");

  const handleChange = useCallback(
    (subtype) => {
      onChange(name, subtype);
      closePanel();
    },
    [onChange, name, closePanel]
  );

  const subtypes = useMemo(
    () => (Array.isArray(props.options) ? props.options : []),
    [props.options]
  );

  const options = useMemo(() => {
    const categories = {};
    subtypes.forEach((subtype) => {
      const category =
        subtype.type && EVENT_TYPES[subtype.type] ? subtype.type : "O";
      categories[category] = categories[category] || {
        ...EVENT_TYPES[category],
      };
      categories[category].subtypes = categories[category].subtypes || [];
      categories[category].subtypes.push(subtype);
    });

    return Object.values(categories).filter((category) =>
      Array.isArray(category.subtypes)
    );
  }, [subtypes]);

  const defaultOptions = useMemo(() => subtypes.slice(0, 5), [subtypes]);

  return (

      <Panel
        position={panelPosition}
        shouldShow={isPanelOpen}
        onClose={closePanel}
        onBack={closePanel}
        title="Type de l'événement"
        noScroll
      >

      </Panel>
    </StyledField>
  );
};
SubtypeField.propTypes = {
  onChange: PropTypes.func.isRequired,
  value: PropTypes.object,
  name: PropTypes.string.isRequired,
  options: PropTypes.arrayOf(PropTypes.object),
  error: PropTypes.string,
  disabled: PropTypes.bool,
  required: PropTypes.bool,
};
export default SubtypeField;
<commit_msg>fix: text wrapping on event SubtypeField panel options<commit_after>import PropTypes from "prop-types";
import React, { useCallback, useMemo } from "react";
import { useHistory, useRouteMatch } from "react-router-dom";
import styled from "styled-components";

import style from "@agir/front/genericComponents/_variables.scss";

import { useResponsiveMemo } from "@agir/front/genericComponents/grid";

import Button from "@agir/front/genericComponents/Button";
import Panel from "@agir/front/genericComponents/Panel";
import { RawFeatherIcon } from "@agir/front/genericComponents/FeatherIcon";

import { EVENT_TYPES } from "./eventForm.config";

import { routeConfig } from "@agir/front/app/routes.config";

const StyledOption = styled.li`
  display: flex;
  flex-flow: row nowrap;
  min-height: 2.75rem;
  align-items: flex-start;
  font-size: 1rem;
  line-height: 1.5;
  color: ${({ $selected }) => ($selected ? style.primary500 : style.black1000)};
  font-weight: ${({ $selected }) => ($selected ? 600 : 400)};
  cursor: ${({ $selected }) => ($selected ? "default" : "pointer")};

  & + & {
    padding-top: 0.5rem;
  }

  & > button {
    display: ${({ $selected }) => ($selected ? "none" : "inline")};
    margin-left: auto;
  }

  & > span {
    color: ${style.primary500};
    display: inline-block;
    font-size: 1rem;
    line-height: 1;
    height: 1rem;
    width: 1rem;
    padding-top: 0.25rem;
  }

  & > strong {
    font-weight: 400;
    padding: 0 0.5rem;
  }
`;

const StyledOptions = styled.div`
  display: flex;
  flex-flow: column nowrap;
  padding-top: 0.5rem;

  ul {
    padding: 0;
    list-style: none;

    & > strong {
      display: block;
      height: 2.75rem;
      font-weight: 600;
      font-size: 1rem;
      line-height: 1.5;
      display: flex;
      align-items: center;
    }
  }
`;

const StyledDefaultOption = styled.button``;
const StyledDefaultOptions = styled.div`
  display: flex;
  flex-flow: row wrap;
  gap: 0.5rem;

  button {
    border-radius: 0;
    border: none;
    box-shadow: none;
    padding: 0;
    background-color: transparent;
    font-size: 1rem;
    font-weight: 400;
    cursor: pointer;
    color: ${style.primary500};

    &[disabled],
    &[disabled]:hover {
      opacity: 0.5;
      cursor: default;
    }
  }

  ${StyledDefaultOption} {
    padding: 0.5rem 0.75rem;
    color: ${style.black1000};
    display: inline-grid;
    grid-gap: 0.5rem;
    grid-template-columns: auto auto auto;
    align-items: center;
    border: 1px solid ${style.black100};

    &:hover {
      background-color: ${style.black50};
    }

    &[disabled],
    &[disabled]:hover {
      opacity: 1;
      background-color: transparent;
      cursor: default;
    }
  }
`;

const StyledField = styled.div`
  label {
    font-size: 1rem;
    font-weight: 600;
    line-height: 1;
    padding: 4px 0;
  }
`;

const SubtypeOption = (props) => {
  const { option, onClick, selected } = props;

  const handleClick = useCallback(() => {
    onClick(option);
  }, [option, onClick]);

  return (

  );
};

const DefaultOption = (props) => {
  const { option, onClick, selected, disabled } = props;

  const handleClick = useCallback(() => {
    onClick(option);
  }, [option, onClick]);

  return (

  );
};
SubtypeOption.propTypes = DefaultOption.propTypes = {
  option: PropTypes.shape({
    id: PropTypes.number,
    label: PropTypes.string,
    iconName: PropTypes.string,
    color: PropTypes.String,
    description: PropTypes.string,
  }),
  onClick: PropTypes.func,
  selected: PropTypes.bool,
  disabled: PropTypes.bool,
};

const BASE_ROUTE = routeConfig.createEvent.getLink();
const PANEL_ROUTE = BASE_ROUTE + "type/";

const SubtypeField = (props) => {
  const { onChange, value, name, error, disabled } = props;

  const isPanelOpen = useRouteMatch(PANEL_ROUTE);
  const history = useHistory();

  const openPanel = useCallback(() => {
    history.push(PANEL_ROUTE);
  }, [history]);

  const closePanel = useCallback(() => {
    history.replace(BASE_ROUTE);
  }, [history]);

  const panelPosition = useResponsiveMemo("right", "left");

  const handleChange = useCallback(
    (subtype) => {
      onChange(name, subtype);
      closePanel();
    },
    [onChange, name, closePanel]
  );

  const subtypes = useMemo(
    () => (Array.isArray(props.options) ? props.options : []),
    [props.options]
  );

  const options = useMemo(() => {
    const categories = {};
    subtypes.forEach((subtype) => {
      const category =
        subtype.type && EVENT_TYPES[subtype.type] ? subtype.type : "O";
      categories[category] = categories[category] || {
        ...EVENT_TYPES[category],
      };
      categories[category].subtypes = categories[category].subtypes || [];
      categories[category].subtypes.push(subtype);
    });

    return Object.values(categories).filter((category) =>
      Array.isArray(category.subtypes)
    );
  }, [subtypes]);

  const defaultOptions = useMemo(() => subtypes.slice(0, 5), [subtypes]);

  return (

      <Panel
        position={panelPosition}
        shouldShow={isPanelOpen}
        onClose={closePanel}
        onBack={closePanel}
        title="Type de l'événement"
        noScroll
      >

      </Panel>
    </StyledField>
  );
};
SubtypeField.propTypes = {
  onChange: PropTypes.func.isRequired,
  value: PropTypes.object,
  name: PropTypes.string.isRequired,
  options: PropTypes.arrayOf(PropTypes.object),
  error: PropTypes.string,
  disabled: PropTypes.bool,
  required: PropTypes.bool,
};
export default SubtypeField;
<|endoftext|>


<commit_before>'use strict';

var servicesModule = require('./_index.js');

/**
 * @ngInject
 */
servicesModule.service('dataService', function ($q, $timeout, sqliteService, $log) {
    var service = {};

    service.getCurrentElements = function () {
        var query = "SELECT * FROM CurrentElement AS ce " +
            "JOIN Player AS p ON p.Id = ce.PlayerId " +
            "JOIN Element AS e ON e.Id = ce.ElementId " +
            "WHERE p.Id = 1" +
            ";";

        return sqliteService.query(query, []);
    };

    service.getAllElements = function () {
        var query = "SELECT * FROM Elements ";
        return sqliteService.query(query);
    };

    service.getCombinedElement = function (element1, element2) {
        var parameters = [element1.Id, element2.Id, element1.Id, element2.Id];
        var query = "SELECT * FROM Element AS e " +
            "JOIN Recipe AS r ON r.ResultId = e.Id " +
            "WHERE (r.Element1Id = ? AND r.Element2Id = ?) OR (r.Element2Id = ? AND r.Element1Id = ?)";

        return sqliteService.query(query, parameters);
    };

    service.getElementParts = function (element) {
        var parameters = [element.Id];
        var query = "SELECT * FROM Element AS e " +
            "JOIN Recipe AS r ON r.ResultId = e.Id " +
            "WHERE r.ResultId = ? ";

        return sqliteService.query(query, parameters);
    };

    service.getBaseElements = function () {
        var parameters = [];
        var query = "SELECT * FROM Element AS e " +
            "LEFT JOIN Recipe AS r ON r.ResultId = e.Id " +
            "WHERE r.ResultId = null ";

        return sqliteService.query(query, parameters);
    };

    return service;
});

<commit_msg>forgot dis one<commit_after>'use strict';

var servicesModule = require('./_index.js');

/**
 * @ngInject
 */
servicesModule.service('dataService', function ($q, $timeout, sqliteService, $log) {
    var service = {};

    service.getCurrentElements = function () {
        var query = "SELECT e.Id, e.Name, e.Description, e.Image, p.CurrentEnergy, ce.Location FROM CurrentElement AS ce " +
            "JOIN Player AS p ON p.Id = ce.PlayerId " +
            "JOIN Element AS e ON e.Id = ce.ElementId " +
            "WHERE p.Id = 1" +
            ";";

        return sqliteService.query(query, []);
    };

    // TODO: Use
    service.getAllElements = function () {
        var query = "SELECT * FROM Elements ";
        return sqliteService.query(query);
    };

    service.getCombinedElement = function (element1, element2) {
        var parameters = [element1.Id, element2.Id, element1.Id, element2.Id];
        var query = "SELECT e.Id, e.Name, e.Description, e.Image, r.EnergyUsage FROM Element AS e " +
            "JOIN Recipe AS r ON r.ResultId = e.Id " +
            "WHERE (r.Element1Id = ? AND r.Element2Id = ?) OR (r.Element2Id = ? AND r.Element1Id = ?)";

        return sqliteService.query(query, parameters);
    };

    service.getElementParts = function (element) {
        var parameters = [element.Id];
        var query = "SELECT e.Id, e.Name, e.Description, e.Image, r.EnergyUsage FROM Element AS e " +
            "JOIN Recipe AS r ON r.Element1Id = e.Id OR r.Element2Id = e.Id " +
            "WHERE r.ResultId = ?";

        return sqliteService.query(query, parameters);
    };

    service.getBaseElements = function () {
        var parameters = [];
        var query = "SELECT * FROM Element AS e " +
            "LEFT JOIN Recipe AS r ON r.ResultId = e.Id " +
            "WHERE r.ResultId = null ";

        return sqliteService.query(query, parameters);
    };

    return service;
});

<|endoftext|>


<commit_before>//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------

// Limits taken from WasmLimits.h
const MaxTypes = 1000000;
const MaxFunctions = 1000000;
const MaxImports = 100000;
const MaxExports = 100000;
const MaxGlobals = 1000000;
const MaxDataSegments = 100000;
const MaxElementSegments = 10000000;
const MaxTableSize = 10000000;

const MaxStringSize = 100000;
const MaxFunctionLocals = 50000;
const MaxFunctionParams = 1000;
const MaxBrTableElems = 1000000;

const MaxMemoryInitialPages = 32767;
const MaxMemoryMaximumPages = 65536;
const MaxModuleSize = 1024 * 1024 * 1024;
const MaxFunctionSize = 7654321;

/* global assert,testRunner */ // eslint rule
WScript.LoadScriptFile("../UnitTestFramework/UnitTestFramework.js");
WScript.LoadScriptFile("../WasmSpec/testsuite/harness/wasm-constants.js");
WScript.LoadScriptFile("../WasmSpec/testsuite/harness/wasm-module-builder.js");
WScript.Flag("-off:wasmdeferred");

function compile(moduleStr) {
  const buf = WebAssembly.wabt.convertWast2Wasm(moduleStr);
  return new WebAssembly.Module(buf);
}

const notTooLongName = "a".repeat(MaxStringSize);
const tooLongName = "a".repeat(MaxStringSize + 1);

function makeLimitTests(opt) {
  return [{
    name: "valid: " + opt.name,
    validTest: true,
    body() {
      const builder = new WasmModuleBuilder();
      opt.makeModule(builder, opt.limit);
      assert.doesNotThrow(() => new WebAssembly.Module(builder.toBuffer()), `WebAssembly should allow up to ${opt.limit} ${opt.type}`);
    }
  }, {
    name: "invalid: " + opt.name,
    invalidTest: true,
    body() {
      const builder = new WasmModuleBuilder();
      opt.makeModule(builder, opt.limit + 1);
      assert.throws(() => new WebAssembly.Module(builder.toBuffer()), WebAssembly.CompileError, `WebAssembly should not allow ${opt.limit + 1} ${opt.type}`, opt.errorMsg);
    }
  }];
}

const tests = [
  // Tests 0,1
  ...makeLimitTests({
    name: "test max types",
    limit: MaxTypes,
    type: "types",
    errorMsg: "Too many signatures",
    makeModule: (builder, limit) => {for (let i = 0; i < limit; ++i) {builder.addType(kSig_v_v);}}
  }),
  // Tests 2,3
  ...makeLimitTests({
    name: "test max functions",
    limit: MaxFunctions,
    type: "functions",
    errorMsg: "Too many functions",
    makeModule: (builder, limit) => {
      const typeIndex = builder.addType(kSig_v_v);
      for (let i = 0; i < limit; ++i) {builder.addFunction(null, typeIndex).addBody([]);}
    }
  }),
  // Tests 4,5
  ...makeLimitTests({
    name: "test max imports",
    limit: MaxImports,
    type: "imports",
    errorMsg: "Too many imports",
    makeModule: (builder, limit) => {
      const typeIndex = builder.addType(kSig_v_v);
      for (let i = 0; i < limit; ++i) {builder.addImport("", "", typeIndex);}
    }
  }),
  // Tests 6,7
  ...makeLimitTests({
    name: "test max exports",
    limit: MaxExports,
    type: "exports",
    errorMsg: "Too many exports",
    makeModule: (builder, limit) => {
      builder.addFunction(null, kSig_v_v).addBody([]);
      for (let i = 0; i < limit; ++i) {builder.addExport("" + i, 0);}
    }
  }),
  // Tests 8,9
  ...makeLimitTests({
    name: "test max globals",
    limit: MaxGlobals,
    type: "globals",
    errorMsg: "Too many globals",
    makeModule: (builder, limit) => {for (let i = 0; i < limit; ++i) {builder.addGlobal(kWasmI32, /* mutable */ false);}}
  }),
  // Tests 10,11
  ...makeLimitTests({
    name: "test max data segments",
    limit: MaxDataSegments,
    type: "data segments",
    errorMsg: "Too many data segments",
    makeModule: (builder, limit) => {
      builder.addMemory(0);
      for (let i = 0; i < limit; ++i) {builder.addDataSegment(0, "");}
    }
  }),
  // Tests 12,13
  ...makeLimitTests({
    name: "test max element segments",
    limit: MaxElementSegments,
    type: "element segments",
    errorMsg: "Too many element segments",
    makeModule: (builder, limit) => {
      builder.addFunction(null, kSig_v_v).addBody([]);
      builder.setTableLength(1);
      builder.element_segments.length = limit;
      builder.element_segments.fill({base:0, is_global:false, array:[0]});
    }
  }),
  // Test 14
  {
    name: "test max table size",
    body() {
      assert.doesNotThrow(() => new WebAssembly.Table({element: "anyfunc", initial: MaxTableSize}));
      assert.doesNotThrow(() => new WebAssembly.Table({element: "anyfunc", initial: MaxTableSize, maximum: MaxTableSize}));
      assert.throws(() => new WebAssembly.Table({element: "anyfunc", maximum: MaxTableSize}));
      assert.throws(() => new WebAssembly.Table({element: "anyfunc", initial: MaxTableSize + 1}));
      assert.throws(() => new WebAssembly.Table({element: "anyfunc", initial: MaxTableSize + 1, maximum: MaxTableSize + 1}));
      assert.throws(() => new WebAssembly.Table({element: "anyfunc", maximum: MaxTableSize + 1}));

      assert.doesNotThrow(() => compile(`(module (table ${MaxTableSize} anyfunc))`));
      assert.doesNotThrow(() => compile(`(module (table ${MaxTableSize} ${MaxTableSize} anyfunc))`));
      assert.doesNotThrow(() => compile(`(module (table 0 ${MaxTableSize} anyfunc))`));
      assert.throws(() => compile(`(module (table ${MaxTableSize + 1} anyfunc))`), WebAssembly.CompileError, "table too big");
      assert.throws(() => compile(`(module (table ${MaxTableSize + 1} ${MaxTableSize + 1} anyfunc))`), WebAssembly.CompileError, "table too big");
      assert.throws(() => compile(`(module (table 0 ${MaxTableSize + 1} anyfunc))`), WebAssembly.CompileError, "table too big");
    }
  },
  // Test 15
  {
    name: "test custom sections with long names",
    body() {
      function makeCustomSection(name) {
        const customSection = new Binary();
        customSection.emit_section(0, section => {
          section.emit_string(name)
          section.emit_string("payload")
        });
        const builder = new WasmModuleBuilder();
        builder.addExplicitSection(customSection);
        return new WebAssembly.Module(builder.toBuffer());
      }
      assert.doesNotThrow(() => makeCustomSection(notTooLongName));
      assert.throws(() => makeCustomSection(tooLongName), WebAssembly.CompileError, "Name too long");
    }
  },
  // Test 16
  {
    name: "test exports with long names",
    body() {
      function makeExportName(name)
      {
        const builder = new WasmModuleBuilder();
        builder
          .addFunction(name, kSig_v_v)
          .exportFunc()
          .addBody([]);
        return new WebAssembly.Module(builder.toBuffer());
      }
      assert.doesNotThrow(() => makeExportName(notTooLongName));
      assert.throws(() => makeExportName(tooLongName), WebAssembly.CompileError, "Name too long");
    }
  },
  // Test 17
  {
    name: "test imports with long names",
    body() {
      function makeImportName(name)
      {
        const builder = new WasmModuleBuilder();
        builder.addImport(name, 'b', kSig_v_v);
        return new WebAssembly.Module(builder.toBuffer());
      }
      assert.doesNotThrow(() => makeImportName(notTooLongName));
      assert.throws(() => makeImportName(tooLongName), WebAssembly.CompileError, "Name too long");
    }
  },
  // Test 18
  {
    name: "test Name section with long names",
    body() {
      function makeName(name)
      {
        const builder = new WasmModuleBuilder();
        builder
          .addFunction(notTooLongName, kSig_v_v)
          .addBody([]);
        return new WebAssembly.Module(builder.toBuffer());
      }
      assert.doesNotThrow(() => makeName(notTooLongName));
      // todo:: enable test once we start using the Name section
      // assert.throws(() => makeName(tooLongName), WebAssembly.CompileError, "Name too long");
    }
  },
  // Tests 19,20
  ...makeLimitTests({
    name: "test max function locals",
    limit: MaxFunctionLocals,
    type: "locals",
    makeModule: (builder, limit) => {
      builder.addFunction(null, kSig_v_v).addLocals({i32_count: limit}).addBody([]);
    }
  }),
  // Tests 21,22
  ...makeLimitTests({
    name: "test max function params",
    limit: MaxFunctionParams,
    type: "params",
    errorMsg: "Too many arguments in signature",
    makeModule: (builder, limit) => {
      builder.addFunction(null, {params: (new Array(limit)).fill(kWasmI32), results: []}).addBody([]);
    }
  }),
  // Tests 23
  {
    name: "test max memory pages",
    body() {
      // We can't actually allocate so much memory on a test machine and except things to go well
      //assert.doesNotThrow(() => new WebAssembly.Memory({initial: MaxMemoryInitialPages}));
      //assert.doesNotThrow(() => new WebAssembly.Memory({initial: MaxMemoryInitialPages, maximum: MaxMemoryMaximumPages}));
      //assert.doesNotThrow(() => new WebAssembly.Memory({maximum: MaxMemoryMaximumPages}));
      assert.throws(() => new WebAssembly.Memory({initial: MaxMemoryInitialPages + 1}));
      assert.throws(() => new WebAssembly.Memory({initial: MaxMemoryInitialPages + 1, maximum: MaxMemoryMaximumPages + 1}));
      assert.throws(() => new WebAssembly.Memory({maximum: MaxMemoryMaximumPages + 1}));

      const makeModule = (min, max) => {
        const builder = new WasmModuleBuilder();
        builder.addMemory(min, max);
        return new WebAssembly.Module(builder.toBuffer());
      };

      assert.doesNotThrow(() => makeModule(MaxMemoryInitialPages));
      assert.doesNotThrow(() => makeModule(MaxMemoryInitialPages, MaxMemoryMaximumPages));
      assert.doesNotThrow(() => makeModule(0, MaxMemoryMaximumPages));
      assert.throws(() => makeModule(MaxMemoryInitialPages + 1), WebAssembly.CompileError, "Minimum memory size too big");
      assert.throws(() => makeModule(MaxMemoryInitialPages + 1, MaxMemoryMaximumPages + 1), WebAssembly.CompileError, "Minimum memory size too big");
      assert.throws(() => makeModule(0, MaxMemoryMaximumPages + 1), WebAssembly.CompileError, "Maximum memory size too big");
    }
  },
  // Tests 24
  {
    name: "test max module size",
    body() {
      assert.throws(() => new WebAssembly.Module(new ArrayBuffer(MaxModuleSize)), WebAssembly.CompileError, "Malformed WASM module header");
      assert.throws(() => new WebAssembly.Module(new ArrayBuffer(MaxModuleSize + 1)), WebAssembly.CompileError, "Module too big");
    }
  },
  // Tests 25,26
  ...makeLimitTests({
    name: "test max function size",
    limit: MaxFunctionSize,
    type: "size",
    errorMsg: "Function body too big",
    makeModule: (builder, limit) => {
      // limit -1 (number of locals byte) -1 (endOpCode)
      builder.addFunction(null, kSig_v_v).addBody((new Array(limit - 2)).fill(kExprNop));
    }
  }),
  // todo:: test MaxBrTableElems
];

WScript.LoadScriptFile("../UnitTestFrameWork/yargs.js");
const argv = yargsParse(WScript.Arguments, {
  boolean: ["valid", "invalid", "verbose"],
  number: ["start", "end"],
  default: {
    verbose: true,
    valid: true,
    invalid: true,
    start: 0,
    end: tests.length
  }
}).argv;

const todoTests = tests
  .slice(argv.start, argv.end)
  .filter(test => (!test.invalidTest || argv.invalid) &&
                  (!test.validTest || argv.valid));

testRunner.run(todoTests, {verbose: argv.verbose});
<commit_msg>[MERGE #6070 @wyrichte] Fixed wasm test that was failing in linux due to filepath<commit_after>//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------

// Limits taken from WasmLimits.h
const MaxTypes = 1000000;
const MaxFunctions = 1000000;
const MaxImports = 100000;
const MaxExports = 100000;
const MaxGlobals = 1000000;
const MaxDataSegments = 100000;
const MaxElementSegments = 10000000;
const MaxTableSize = 10000000;

const MaxStringSize = 100000;
const MaxFunctionLocals = 50000;
const MaxFunctionParams = 1000;
const MaxBrTableElems = 1000000;

const MaxMemoryInitialPages = 32767;
const MaxMemoryMaximumPages = 65536;
const MaxModuleSize = 1024 * 1024 * 1024;
const MaxFunctionSize = 7654321;

/* global assert,testRunner */ // eslint rule
WScript.LoadScriptFile("../UnitTestFramework/UnitTestFramework.js");
WScript.LoadScriptFile("../WasmSpec/testsuite/harness/wasm-constants.js");
WScript.LoadScriptFile("../WasmSpec/testsuite/harness/wasm-module-builder.js");
WScript.Flag("-off:wasmdeferred");

function compile(moduleStr) {
  const buf = WebAssembly.wabt.convertWast2Wasm(moduleStr);
  return new WebAssembly.Module(buf);
}

const notTooLongName = "a".repeat(MaxStringSize);
const tooLongName = "a".repeat(MaxStringSize + 1);

function makeLimitTests(opt) {
  return [{
    name: "valid: " + opt.name,
    validTest: true,
    body() {
      const builder = new WasmModuleBuilder();
      opt.makeModule(builder, opt.limit);
      assert.doesNotThrow(() => new WebAssembly.Module(builder.toBuffer()), `WebAssembly should allow up to ${opt.limit} ${opt.type}`);
    }
  }, {
    name: "invalid: " + opt.name,
    invalidTest: true,
    body() {
      const builder = new WasmModuleBuilder();
      opt.makeModule(builder, opt.limit + 1);
      assert.throws(() => new WebAssembly.Module(builder.toBuffer()), WebAssembly.CompileError, `WebAssembly should not allow ${opt.limit + 1} ${opt.type}`, opt.errorMsg);
    }
  }];
}

const tests = [
  // Tests 0,1
  ...makeLimitTests({
    name: "test max types",
    limit: MaxTypes,
    type: "types",
    errorMsg: "Too many signatures",
    makeModule: (builder, limit) => {for (let i = 0; i < limit; ++i) {builder.addType(kSig_v_v);}}
  }),
  // Tests 2,3
  ...makeLimitTests({
    name: "test max functions",
    limit: MaxFunctions,
    type: "functions",
    errorMsg: "Too many functions",
    makeModule: (builder, limit) => {
      const typeIndex = builder.addType(kSig_v_v);
      for (let i = 0; i < limit; ++i) {builder.addFunction(null, typeIndex).addBody([]);}
    }
  }),
  // Tests 4,5
  ...makeLimitTests({
    name: "test max imports",
    limit: MaxImports,
    type: "imports",
    errorMsg: "Too many imports",
    makeModule: (builder, limit) => {
      const typeIndex = builder.addType(kSig_v_v);
      for (let i = 0; i < limit; ++i) {builder.addImport("", "", typeIndex);}
    }
  }),
  // Tests 6,7
  ...makeLimitTests({
    name: "test max exports",
    limit: MaxExports,
    type: "exports",
    errorMsg: "Too many exports",
    makeModule: (builder, limit) => {
      builder.addFunction(null, kSig_v_v).addBody([]);
      for (let i = 0; i < limit; ++i) {builder.addExport("" + i, 0);}
    }
  }),
  // Tests 8,9
  ...makeLimitTests({
    name: "test max globals",
    limit: MaxGlobals,
    type: "globals",
    errorMsg: "Too many globals",
    makeModule: (builder, limit) => {for (let i = 0; i < limit; ++i) {builder.addGlobal(kWasmI32, /* mutable */ false);}}
  }),
  // Tests 10,11
  ...makeLimitTests({
    name: "test max data segments",
    limit: MaxDataSegments,
    type: "data segments",
    errorMsg: "Too many data segments",
    makeModule: (builder, limit) => {
      builder.addMemory(0);
      for (let i = 0; i < limit; ++i) {builder.addDataSegment(0, "");}
    }
  }),
  // Tests 12,13
  ...makeLimitTests({
    name: "test max element segments",
    limit: MaxElementSegments,
    type: "element segments",
    errorMsg: "Too many element segments",
    makeModule: (builder, limit) => {
      builder.addFunction(null, kSig_v_v).addBody([]);
      builder.setTableLength(1);
      builder.element_segments.length = limit;
      builder.element_segments.fill({base:0, is_global:false, array:[0]});
    }
  }),
  // Test 14
  {
    name: "test max table size",
    body() {
      assert.doesNotThrow(() => new WebAssembly.Table({element: "anyfunc", initial: MaxTableSize}));
      assert.doesNotThrow(() => new WebAssembly.Table({element: "anyfunc", initial: MaxTableSize, maximum: MaxTableSize}));
      assert.throws(() => new WebAssembly.Table({element: "anyfunc", maximum: MaxTableSize}));
      assert.throws(() => new WebAssembly.Table({element: "anyfunc", initial: MaxTableSize + 1}));
      assert.throws(() => new WebAssembly.Table({element: "anyfunc", initial: MaxTableSize + 1, maximum: MaxTableSize + 1}));
      assert.throws(() => new WebAssembly.Table({element: "anyfunc", maximum: MaxTableSize + 1}));

      assert.doesNotThrow(() => compile(`(module (table ${MaxTableSize} anyfunc))`));
      assert.doesNotThrow(() => compile(`(module (table ${MaxTableSize} ${MaxTableSize} anyfunc))`));
      assert.doesNotThrow(() => compile(`(module (table 0 ${MaxTableSize} anyfunc))`));
      assert.throws(() => compile(`(module (table ${MaxTableSize + 1} anyfunc))`), WebAssembly.CompileError, "table too big");
      assert.throws(() => compile(`(module (table ${MaxTableSize + 1} ${MaxTableSize + 1} anyfunc))`), WebAssembly.CompileError, "table too big");
      assert.throws(() => compile(`(module (table 0 ${MaxTableSize + 1} anyfunc))`), WebAssembly.CompileError, "table too big");
    }
  },
  // Test 15
  {
    name: "test custom sections with long names",
    body() {
      function makeCustomSection(name) {
        const customSection = new Binary();
        customSection.emit_section(0, section => {
          section.emit_string(name)
          section.emit_string("payload")
        });
        const builder = new WasmModuleBuilder();
        builder.addExplicitSection(customSection);
        return new WebAssembly.Module(builder.toBuffer());
      }
      assert.doesNotThrow(() => makeCustomSection(notTooLongName));
      assert.throws(() => makeCustomSection(tooLongName), WebAssembly.CompileError, "Name too long");
    }
  },
  // Test 16
  {
    name: "test exports with long names",
    body() {
      function makeExportName(name)
      {
        const builder = new WasmModuleBuilder();
        builder
          .addFunction(name, kSig_v_v)
          .exportFunc()
          .addBody([]);
        return new WebAssembly.Module(builder.toBuffer());
      }
      assert.doesNotThrow(() => makeExportName(notTooLongName));
      assert.throws(() => makeExportName(tooLongName), WebAssembly.CompileError, "Name too long");
    }
  },
  // Test 17
  {
    name: "test imports with long names",
    body() {
      function makeImportName(name)
      {
        const builder = new WasmModuleBuilder();
        builder.addImport(name, 'b', kSig_v_v);
        return new WebAssembly.Module(builder.toBuffer());
      }
      assert.doesNotThrow(() => makeImportName(notTooLongName));
      assert.throws(() => makeImportName(tooLongName), WebAssembly.CompileError, "Name too long");
    }
  },
  // Test 18
  {
    name: "test Name section with long names",
    body() {
      function makeName(name)
      {
        const builder = new WasmModuleBuilder();
        builder
          .addFunction(notTooLongName, kSig_v_v)
          .addBody([]);
        return new WebAssembly.Module(builder.toBuffer());
      }
      assert.doesNotThrow(() => makeName(notTooLongName));
      // todo:: enable test once we start using the Name section
      // assert.throws(() => makeName(tooLongName), WebAssembly.CompileError, "Name too long");
    }
  },
  // Tests 19,20
  ...makeLimitTests({
    name: "test max function locals",
    limit: MaxFunctionLocals,
    type: "locals",
    makeModule: (builder, limit) => {
      builder.addFunction(null, kSig_v_v).addLocals({i32_count: limit}).addBody([]);
    }
  }),
  // Tests 21,22
  ...makeLimitTests({
    name: "test max function params",
    limit: MaxFunctionParams,
    type: "params",
    errorMsg: "Too many arguments in signature",
    makeModule: (builder, limit) => {
      builder.addFunction(null, {params: (new Array(limit)).fill(kWasmI32), results: []}).addBody([]);
    }
  }),
  // Tests 23
  {
    name: "test max memory pages",
    body() {
      // We can't actually allocate so much memory on a test machine and except things to go well
      //assert.doesNotThrow(() => new WebAssembly.Memory({initial: MaxMemoryInitialPages}));
      //assert.doesNotThrow(() => new WebAssembly.Memory({initial: MaxMemoryInitialPages, maximum: MaxMemoryMaximumPages}));
      //assert.doesNotThrow(() => new WebAssembly.Memory({maximum: MaxMemoryMaximumPages}));
      assert.throws(() => new WebAssembly.Memory({initial: MaxMemoryInitialPages + 1}));
      assert.throws(() => new WebAssembly.Memory({initial: MaxMemoryInitialPages + 1, maximum: MaxMemoryMaximumPages + 1}));
      assert.throws(() => new WebAssembly.Memory({maximum: MaxMemoryMaximumPages + 1}));

      const makeModule = (min, max) => {
        const builder = new WasmModuleBuilder();
        builder.addMemory(min, max);
        return new WebAssembly.Module(builder.toBuffer());
      };

      assert.doesNotThrow(() => makeModule(MaxMemoryInitialPages));
      assert.doesNotThrow(() => makeModule(MaxMemoryInitialPages, MaxMemoryMaximumPages));
      assert.doesNotThrow(() => makeModule(0, MaxMemoryMaximumPages));
      assert.throws(() => makeModule(MaxMemoryInitialPages + 1), WebAssembly.CompileError, "Minimum memory size too big");
      assert.throws(() => makeModule(MaxMemoryInitialPages + 1, MaxMemoryMaximumPages + 1), WebAssembly.CompileError, "Minimum memory size too big");
      assert.throws(() => makeModule(0, MaxMemoryMaximumPages + 1), WebAssembly.CompileError, "Maximum memory size too big");
    }
  },
  // Tests 24
  {
    name: "test max module size",
    body() {
      assert.throws(() => new WebAssembly.Module(new ArrayBuffer(MaxModuleSize)), WebAssembly.CompileError, "Malformed WASM module header");
      assert.throws(() => new WebAssembly.Module(new ArrayBuffer(MaxModuleSize + 1)), WebAssembly.CompileError, "Module too big");
    }
  },
  // Tests 25,26
  ...makeLimitTests({
    name: "test max function size",
    limit: MaxFunctionSize,
    type: "size",
    errorMsg: "Function body too big",
    makeModule: (builder, limit) => {
      // limit -1 (number of locals byte) -1 (endOpCode)
      builder.addFunction(null, kSig_v_v).addBody((new Array(limit - 2)).fill(kExprNop));
    }
  }),
  // todo:: test MaxBrTableElems
];

WScript.LoadScriptFile("../UnitTestFramework/yargs.js");
const argv = yargsParse(WScript.Arguments, {
  boolean: ["valid", "invalid", "verbose"],
  number: ["start", "end"],
  default: {
    verbose: true,
    valid: true,
    invalid: true,
    start: 0,
    end: tests.length
  }
}).argv;

const todoTests = tests
  .slice(argv.start, argv.end)
  .filter(test => (!test.invalidTest || argv.invalid) &&
                  (!test.validTest || argv.valid));

testRunner.run(todoTests, {verbose: argv.verbose});
<|endoftext|>


<commit_before>'use strict';

const touch = require('./touch');

const NSThread = ObjC.classes.NSThread;
const UIButtonClass = ObjC.classes.UIButton.class();
const UILabelClass = ObjC.classes.UILabel.class();
const UITextFieldClass = ObjC.classes.UITextField.class();
const UITextViewClass = ObjC.classes.UITextView.class();
let UIWebViewClass = null;
const UIWindow = ObjC.classes.UIWindow;
const UIWindowClass = UIWindow.class();

const textField_shouldChangeCharactersInRange_replacementString_ = ObjC.selector('textField:shouldChangeCharactersInRange:replacementString:');

function UINode(view) {
  this.instance = view;

  if (UIWebViewClass === null) {
    // Cannot access this type early
    UIWebViewClass = ObjC.classes.UIWebView.class();
  }

  if (view.isKindOfClass_(UIWindowClass)) {
    this.type = 'UIWindow';
  } else if (view.isKindOfClass_(UIButtonClass)) {
    this.type = 'UIButton';
  } else if (view.isKindOfClass_(UILabelClass)) {
    this.type = 'UILabel';
  } else if (view.isKindOfClass_(UITextFieldClass)) {
    this.type = 'UITextField';
  } else if (view.isKindOfClass_(UITextViewClass)) {
    this.type = 'UITextView';
  } else if (view.isKindOfClass_(UIWebViewClass)) {
    this.type = 'UIWebView';
  } else {
    this.type = 'UIView';
  }
  this.className = view.$className;
  this._enabled = null;
  this._label = null;

  const children = [];
  const subviews = view.subviews();
  const count = subviews.count().valueOf();
  for (let i = 0; i !== count; i++) {
    children.push(new UINode(subviews.objectAtIndex_(i)));
  }
  this.children = children;
}

UINode.prototype = {
  get enabled() {
    if (this._enabled === null) {
      const instance = this.instance;
      if ('enabled' in instance) {
        this._enabled = !!instance.enabled();
      } else {
        this._enabled = true;
      }
    }
    return this._enabled;
  },
  get label() {
    if (this._label === null) {
      const instance = this.instance;
      if ('accessibilityLabel' in instance) {
        const accLabel = instance.accessibilityLabel();
        if (accLabel !== null) {
          this._label = accLabel.toString();
        }
      }
      if (this._label === null) {
        if ('placeholder' in instance) {
          this._label = readStringProperty(instance.placeholder());
        } else if ('text' in instance) {
          this._label = readStringProperty(instance.text());
        } else {
          this._label = '';
        }
      }
    }
    return this._label;
  },
  forEach(fn) {
    fn(this);
    this.children.forEach(child => child.forEach(fn));
  },
  find(predicate) {
    if (predicate(this)) {
      return this;
    }

    const children = this.children;
    for (let i = 0; i !== children.length; i++) {
      const child = children[i].find(predicate);
      if (child !== null) {
        return child;
      }
    }

    return null;
  },
  setText(text) {
    return performOnMainThread(() => {
      const instance = this.instance;

      const delegate = instance.delegate();
      let valid = true;
      if (delegate !== null && delegate.respondsToSelector_(textField_shouldChangeCharactersInRange_replacementString_)) {
        const oldText = instance.text().toString();
        valid = delegate.textField_shouldChangeCharactersInRange_replacementString_(instance, [0, oldText.length], text);
      }

      if (!valid)
        return;

      instance.becomeFirstResponder();
      instance.setText_('');
      instance.deleteBackward();
      instance.insertText_(text);
      instance.resignFirstResponder();
    });
  },
  tap() {
    const view = this.instance;
    return new Promise(function (resolve, reject) {
      performOnMainThread(() => {
        const [_, [width, height]] = view.frame();
        const x = width / 2;
        const y = height / 2;
        touch.tap(view, x, y).then(resolve, reject);
      })
    });
  }
};

function performOnMainThread(action) {
  return new Promise(function (resolve, reject) {
    if (NSThread.isMainThread()) {
      performAction();
    } else {
      ObjC.schedule(ObjC.mainQueue, performAction);
    }

    function performAction() {
      try {
        const result = action();
        resolve(result);
      } catch (e) {
        reject(e);
      }
    }
  });
}

function readStringProperty(value) {
  return (value !== null) ? value.toString() : '';
}

module.exports = UINode;
<commit_msg>Fix UAF (#5)<commit_after>'use strict';

const touch = require('./touch');

const NSThread = ObjC.classes.NSThread;
const UIButtonClass = ObjC.classes.UIButton.class();
const UILabelClass = ObjC.classes.UILabel.class();
const UITextFieldClass = ObjC.classes.UITextField.class();
const UITextViewClass = ObjC.classes.UITextView.class();
let UIWebViewClass = null;
let WKWebViewClass = null;
const UIWindow = ObjC.classes.UIWindow;
const UIWindowClass = UIWindow.class();

const textField_shouldChangeCharactersInRange_replacementString_ = ObjC.selector('textField:shouldChangeCharactersInRange:replacementString:');
const willMoveToWindow = ObjC.classes.UIView['- willMoveToWindow:'];
const originalWillMoveToWindow = willMoveToWindow.implementation;

const activeInstances = {};
let keepingAlive = false;

function keepViewsAlive() {
  if (keepingAlive) {
    return;
  }
  keepingAlive = true;

  willMoveToWindow.implementation = ObjC.implement(willMoveToWindow, (handle, selector, window) => {
    originalWillMoveToWindow(handle, selector, window);

    const key = handle.toString();
    const node = activeInstances[key];

    if (window.isNull() && node !== undefined) {
      const view = node.instance;
      node.instance = null;
      delete activeInstances[key];
      view.release();
    }
  });
}

function UINode(view) {
  this.instance = view;
  keepViewsAlive();
  view.retain();
  activeInstances[view.handle.toString()] = this;

  if (UIWebViewClass === null) {
    // Cannot access this type early
    UIWebViewClass = ObjC.classes.UIWebView.class();
  }

  if (WKWebViewClass === null) {
    // Cannot access this type early
    WKWebViewClass = ObjC.classes.WKWebView.class();
  }

  if (view.isKindOfClass_(UIWindowClass)) {
    this.type = 'UIWindow';
  } else if (view.isKindOfClass_(UIButtonClass)) {
    this.type = 'UIButton';
  } else if (view.isKindOfClass_(UILabelClass)) {
    this.type = 'UILabel';
  } else if (view.isKindOfClass_(UITextFieldClass)) {
    this.type = 'UITextField';
  } else if (view.isKindOfClass_(UITextViewClass)) {
    this.type = 'UITextView';
  } else if (view.isKindOfClass_(UIWebViewClass)) {
    this.type = 'UIWebView';
  } else if (view.isKindOfClass_(WKWebViewClass)) {
    this.type = 'WKWebView';
  } else {
    this.type = 'UIView';
  }
  this.className = view.$className;
  this._enabled = null;
  this._label = null;

  const children = [];
  const subviews = view.subviews();
  const count = subviews.count().valueOf();
  for (let i = 0; i !== count; i++) {
    children.push(new UINode(subviews.objectAtIndex_(i)));
  }
  this.children = children;
}

UINode.prototype = {
  get enabled() {
    if (this._enabled === null) {
      const instance = this.instance;
      if (instance === null) {
        return false;
      }
      if ('enabled' in instance) {
        this._enabled = !!instance.enabled();
      } else {
        this._enabled = true;
      }
    }
    return this._enabled;
  },
  get label() {
    if (this._label === null) {
      const instance = this.instance;
      if (instance === null) {
        return '';
      }
      if ('accessibilityLabel' in instance) {
        const accLabel = instance.accessibilityLabel();
        if (accLabel !== null) {
          this._label = accLabel.toString();
        }
      }
      if (this._label === null) {
        if ('placeholder' in instance) {
          this._label = readStringProperty(instance.placeholder());
        } else if ('text' in instance) {
          this._label = readStringProperty(instance.text());
        } else {
          this._label = '';
        }
      }
    }
    return this._label;
  },
  forEach(fn) {
    fn(this);
    this.children.forEach(child => child.forEach(fn));
  },
  find(predicate) {
    if (predicate(this)) {
      return this;
    }

    const children = this.children;
    for (let i = 0; i !== children.length; i++) {
      const child = children[i].find(predicate);
      if (child !== null) {
        return child;
      }
    }

    return null;
  },
  setText(text) {
    return performOnMainThread(() => {
      const instance = this.instance;
      if (instance === null) {
        throw new Error('View is gone');
      }

      const delegate = instance.delegate();
      let valid = true;
      if (delegate !== null && delegate.respondsToSelector_(textField_shouldChangeCharactersInRange_replacementString_)) {
        const oldText = instance.text().toString();
        valid = delegate.textField_shouldChangeCharactersInRange_replacementString_(instance, [0, oldText.length], text);
      }

      if (!valid)
        return;

      instance.becomeFirstResponder();
      instance.setText_('');
      instance.deleteBackward();
      instance.insertText_(text);
      instance.resignFirstResponder();
    });
  },
  tap() {
    const view = this.instance;
    if (view === null) {
      return Promise.reject(new Error('View is gone'));
    }
    return new Promise(function (resolve, reject) {
      performOnMainThread(() => {
        const [_, [width, height]] = view.frame();
        const x = width / 2;
        const y = height / 2;
        touch.tap(view, x, y).then(resolve, reject);
      })
    });
  }
};

function performOnMainThread(action) {
  return new Promise(function (resolve, reject) {
    if (NSThread.isMainThread()) {
      performAction();
    } else {
      ObjC.schedule(ObjC.mainQueue, performAction);
    }

    function performAction() {
      try {
        const result = action();
        resolve(result);
      } catch (e) {
        reject(e);
      }
    }
  });
}

function readStringProperty(value) {
  return (value !== null) ? value.toString() : '';
}

module.exports = UINode;
<|endoftext|>


<commit_before>/*
 Copyright (c) 2015, GEM Foundation.

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU Affero General Public License as
 published by the Free Software Foundation, either version 3 of the
 License, or (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU Affero General Public License for more details.

 You should have received a copy of the GNU Affero General Public License
 along with this program.  If not, see <https://www.gnu.org/licenses/agpl.html>.
 */

(function($, Backbone, _, oq_server_url, calc_id) {
    var progressHandlingFunction = function(progress) {
	var percent = progress.loaded / progress.total * 100;
 	$('.bar').css('width', percent + '%');
	if (percent == 100) {
            dialog.hidePleaseWait();
	}
    };

    var dialog = (function ()
                  {
                      var pleaseWaitDiv = $('

Processing...

');
                      return {
                          showPleaseWait: function(msg, progress) {
	                      $('h1', pleaseWaitDiv).text(msg);
	                      if (progress) {
		                  progressHandlingFunction({loaded: 0, total: 1});
	                      } else {
		                  progressHandlingFunction({loaded: 1, total: 1});
	                      }
                              pleaseWaitDiv.modal();
                          },
                          hidePleaseWait: function () {
                              pleaseWaitDiv.modal('hide');
                          }
                      };
                  })();

    var diaerror = (function ()
                  {
                      var errorDiv = $('
\

\

\

\

Calculation not accepted: traceback
\

\

\

\

\
                      <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>\

\

\

\

');

//

Error

Close
');
//                      errorDiv.on("click", function(e) {
//                          this.hideDiaError();
//                      })
                      return {
                          showDiaError: function(title, msg) {
                              if (title != null) {
                                  $('.modal-title', errorDiv).html(title);
                              }
                              if (msg != null) {
	                          $('.modal-body-pre', errorDiv).html(msg);
                              }
                              errorDiv.modal();
                          },
                          hideDiaError: function () {
                              errorDiv.modal('hide');
                          }
                      };
                  })();

    var OutputTable = Backbone.View.extend(
        {
            /* the html element where the table is rendered */
            el: $('#my-outputs'),

            initialize: function(options) {

                /* whatever happens to any calculation, re-render the table */
                _.bindAll(this, 'render');
                this.outputs = options.outputs;
                this.outputs.bind('reset', this.render);
                this.outputs.bind('add', this.render);
                this.outputs.bind('remove', this.render);

                /* if false, it prevents the table to be refreshed */
                this.can_be_rendered = true;

                this.render();
            },

            events: {
                "click .btn-danger": "remove_calculation",
                "click .btn-traceback": "show_traceback",
                "click .btn-file": "on_run_risk_clicked",
                "change .btn-file input": "on_run_risk_queued"
            },

            /* When an input dialog is opened, it is very important to not re-render the table */
            on_run_risk_clicked: function(e) {
                /* if a file input dialog has been opened do not refresh the calc table */
                this.can_be_rendered = false;
            },

            on_run_risk_queued: function(e) {
                this.can_be_rendered = true;
            },

            remove_calculation: function(e) {
                e.preventDefault();
                var calc_id = $(e.target).attr('data-calc-id');
                var view = this;
                diaerror.showDiaError("Removing calculation  " + calc_id + ".", "...");
                $.post(oq_server_url + "/v1/calc/" + calc_id + "/remove"
                     ).success(
                         function(data, textStatus, jqXHR)
                         {
                             diaerror.showDiaError("Remove calculation  " + calc_id + ".", "done");
                             view.outputs.remove([view.outputs.get(calc_id)]);
                         }
                     ).error(
                         function(jqXHR, textStatus, errorThrown)
                         {
                             if (jqXHR.status == 404) {
                                 diaerror.showDiaError("Remove calculation  " + calc_id + ".", "Failed: calculation " + calc_id + " not found.");
                             }
                             else {
                                 diaerror.showDiaError("Remove calculation  " + calc_id + ".", "Failed: " + textStatus);
                             }
                         }
                     );
            },

            show_traceback: function(e) {
                var calc_id = $(e.target).attr('data-calc-id');

                var myXhr = $.ajax({url: oq_server_url + "/v1/calc/" + calc_id + "/traceback",
                                    error: function (jqXHR, textStatus, errorThrown) {
                                        if (jqXHR.status == 404) {
                                            diaerror.showDiaError("Calculation " + calc_id + " not found.");
                                        }
                                        else {
                                            diaerror.showDiaError("Error retrieving traceback for calculation " + calc_id + ".", textStatus);
                                        }
                                        // alert("Error: " + textStatus);
                                    },
                                    success: function (data, textStatus, jqXHR) {
                                        if (data.length == 0) {
                                            diaerror.showDiaError("Traceback not found for calculation " + calc_id + ".", []);
                                        }
                                        else {
                                            out = ""
                                            for (s in data) {
                                                if (data[s] == "")
                                                    continue;
                                                out += data[s] + '\n';
                                            }
                                            diaerror.showDiaError("Traceback of calculation " + calc_id + ".", out);
                                        }
                                        // alert("Success: " + textStatus);
                                    }});
            },

            render: function() {
                if (!this.can_be_rendered) {
                    return;
                };
                this.$el.html(_.template($('#output-table-template').html(),
                                         { outputs: this.outputs.models }));
            }
        });

    var Calculation = Backbone.Model.extend({
        defaults: {
            map: null,
            layers: []
        }
    });

/*
    var OutputTable = Backbone.View.extend(
        {
            el: $('#tab2'),

            initialize: function(options) {
                _.bindAll(this, 'render');
                this.outputs = options.outputs;
                this.outputs.bind('reset', this.render);
                this.outputs.bind('add', this.render);
                this.render();
            },

            render: function() {
                this.$el.html(_.template($('#output-table-template').html(),
                                         { outputs: this.outputs.models }));
            }
        });
*/

    var Output = Backbone.Model.extend(
        {
            calc: function() {
                return outputs.get(this.get('calculation')) || new Output({ 'calculation_type': undefined });
            }
        });

    var Outputs = Backbone.Collection.extend(
        {
            model: Output,
            url: oq_server_url + "/v1/calc/" + calc_id + "/result/list"
        });
    var outputs = new Outputs();

    var refresh_calcs;

    function setTimer() {
        refresh_calcs = setInterval(function() { outputs.fetch({reset: true}) }, 5000);
    }

    /* classic event management */
    $(document).ready(
        function() {
            // var calculation_table = new CalculationTable({ outputs: outputs });
            // outputs.fetch({reset: true});
            // setInterval(function() { outputs.fetch({reset: true}) }, 15000);

            var output_table = new OutputTable({ outputs: outputs });
            outputs.fetch({reset: true});

            // /* TODO. output collection should observe the calculation one */
            setInterval(function() { outputs.fetch({reset: true}) }, 10000);

            /* XXX. Reset the input file value to ensure the change event
               will be always triggered */
            $(document).on("click", 'input[name=archive]',
                           function(e) { this.value = null; });
            $(document).on("change", 'input[name=archive]',
                           function(e) {
                               dialog.showPleaseWait('Uploading calculation', true);
                               var input = $(e.target);
                               var form = input.parents('form')[0];

	                       $(form).ajaxSubmit(
                                   {
		                       xhr: function() {  // custom xhr to add progress bar management
                                           var myXhr = $.ajaxSettings.xhr();
                                           if(myXhr.upload){ // if upload property exists
                                               myXhr.upload.addEventListener('progress', progressHandlingFunction, false);
                                           }
                                           return myXhr;
                                       },
		                       success: function(data) {
                                           outputs.add(new Calculation(data));
                                       },
		                       error: function(xhr) {
                                           dialog.hidePleaseWait();
                                           var s, out, ret = $.parseJSON(xhr.responseText);
                                           out = ""
                                           for (s in ret) {
                                               if (ret[s] == "")
                                                   continue;
                                               out += ret[s] + '\n';
                                           }
                                           diaerror.showDiaError("Calculation not accepted: traceback", out);
		                       }});
                           });

            $(document).on('hidden.bs.modal', 'div[id^=traceback-]',
                           function(e) {
                               setTimer();
                           });

        });
})($, Backbone, _, gem_oq_server_url, gem_calc_id);

<commit_msg>Fix engineweb_get_outputs.js mixed spaces and tabs<commit_after>/*
 Copyright (c) 2015, GEM Foundation.

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU Affero General Public License as
 published by the Free Software Foundation, either version 3 of the
 License, or (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU Affero General Public License for more details.

 You should have received a copy of the GNU Affero General Public License
 along with this program.  If not, see <https://www.gnu.org/licenses/agpl.html>.
 */

(function($, Backbone, _, oq_server_url, calc_id) {
    var progressHandlingFunction = function(progress) {
        var percent = progress.loaded / progress.total * 100;
        $('.bar').css('width', percent + '%');
        if (percent == 100) {
                dialog.hidePleaseWait();
        }
    };

    var dialog = (function ()
                  {
                      var pleaseWaitDiv = $('

Processing...

');
                      return {
                          showPleaseWait: function(msg, progress) {
                          $('h1', pleaseWaitDiv).text(msg);
                          if (progress) {
                              progressHandlingFunction({loaded: 0, total: 1});
                          } else {
                              progressHandlingFunction({loaded: 1, total: 1});
                          }
                              pleaseWaitDiv.modal();
                          },
                          hidePleaseWait: function () {
                              pleaseWaitDiv.modal('hide');
                          }
                      };
                  })();

    var diaerror = (function ()
                  {
                      var errorDiv = $('
\

\

\

\

Calculation not accepted: traceback
\

\

\

\

\
                      <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>\

\

\

\

');

//

Error

Close
');
//                      errorDiv.on("click", function(e) {
//                          this.hideDiaError();
//                      })
                      return {
                          showDiaError: function(title, msg) {
                              if (title != null) {
                                  $('.modal-title', errorDiv).html(title);
                              }
                              if (msg != null) {
                                  $('.modal-body-pre', errorDiv).html(msg);
                              }
                              errorDiv.modal();
                          },
                          hideDiaError: function () {
                              errorDiv.modal('hide');
                          }
                      };
                  })();

    var OutputTable = Backbone.View.extend(
        {
            /* the html element where the table is rendered */
            el: $('#my-outputs'),

            initialize: function(options) {

                /* whatever happens to any calculation, re-render the table */
                _.bindAll(this, 'render');
                this.outputs = options.outputs;
                this.outputs.bind('reset', this.render);
                this.outputs.bind('add', this.render);
                this.outputs.bind('remove', this.render);

                /* if false, it prevents the table to be refreshed */
                this.can_be_rendered = true;

                this.render();
            },

            events: {
                "click .btn-danger": "remove_calculation",
                "click .btn-traceback": "show_traceback",
                "click .btn-file": "on_run_risk_clicked",
                "change .btn-file input": "on_run_risk_queued"
            },

            /* When an input dialog is opened, it is very important to not re-render the table */
            on_run_risk_clicked: function(e) {
                /* if a file input dialog has been opened do not refresh the calc table */
                this.can_be_rendered = false;
            },

            on_run_risk_queued: function(e) {
                this.can_be_rendered = true;
            },

            remove_calculation: function(e) {
                e.preventDefault();
                var calc_id = $(e.target).attr('data-calc-id');
                var view = this;
                diaerror.showDiaError("Removing calculation  " + calc_id + ".", "...");
                $.post(oq_server_url + "/v1/calc/" + calc_id + "/remove"
                     ).success(
                         function(data, textStatus, jqXHR)
                         {
                             diaerror.showDiaError("Remove calculation  " + calc_id + ".", "done");
                             view.outputs.remove([view.outputs.get(calc_id)]);
                         }
                     ).error(
                         function(jqXHR, textStatus, errorThrown)
                         {
                             if (jqXHR.status == 404) {
                                 diaerror.showDiaError("Remove calculation  " + calc_id + ".", "Failed: calculation " + calc_id + " not found.");
                             }
                             else {
                                 diaerror.showDiaError("Remove calculation  " + calc_id + ".", "Failed: " + textStatus);
                             }
                         }
                     );
            },

            show_traceback: function(e) {
                var calc_id = $(e.target).attr('data-calc-id');

                var myXhr = $.ajax({url: oq_server_url + "/v1/calc/" + calc_id + "/traceback",
                                    error: function (jqXHR, textStatus, errorThrown) {
                                        if (jqXHR.status == 404) {
                                            diaerror.showDiaError("Calculation " + calc_id + " not found.");
                                        }
                                        else {
                                            diaerror.showDiaError("Error retrieving traceback for calculation " + calc_id + ".", textStatus);
                                        }
                                        // alert("Error: " + textStatus);
                                    },
                                    success: function (data, textStatus, jqXHR) {
                                        if (data.length == 0) {
                                            diaerror.showDiaError("Traceback not found for calculation " + calc_id + ".", []);
                                        }
                                        else {
                                            out = ""
                                            for (s in data) {
                                                if (data[s] == "")
                                                    continue;
                                                out += data[s] + '\n';
                                            }
                                            diaerror.showDiaError("Traceback of calculation " + calc_id + ".", out);
                                        }
                                        // alert("Success: " + textStatus);
                                    }});
            },

            render: function() {
                if (!this.can_be_rendered) {
                    return;
                };
                this.$el.html(_.template($('#output-table-template').html(),
                                         { outputs: this.outputs.models }));
            }
        });

    var Calculation = Backbone.Model.extend({
        defaults: {
            map: null,
            layers: []
        }
    });

/*
    var OutputTable = Backbone.View.extend(
        {
            el: $('#tab2'),

            initialize: function(options) {
                _.bindAll(this, 'render');
                this.outputs = options.outputs;
                this.outputs.bind('reset', this.render);
                this.outputs.bind('add', this.render);
                this.render();
            },

            render: function() {
                this.$el.html(_.template($('#output-table-template').html(),
                                         { outputs: this.outputs.models }));
            }
        });
*/

    var Output = Backbone.Model.extend(
        {
            calc: function() {
                return outputs.get(this.get('calculation')) || new Output({ 'calculation_type': undefined });
            }
        });

    var Outputs = Backbone.Collection.extend(
        {
            model: Output,
            url: oq_server_url + "/v1/calc/" + calc_id + "/result/list"
        });
    var outputs = new Outputs();

    var refresh_calcs;

    function setTimer() {
        refresh_calcs = setInterval(function() { outputs.fetch({reset: true}) }, 5000);
    }

    /* classic event management */
    $(document).ready(
        function() {
            // var calculation_table = new CalculationTable({ outputs: outputs });
            // outputs.fetch({reset: true});
            // setInterval(function() { outputs.fetch({reset: true}) }, 15000);

            var output_table = new OutputTable({ outputs: outputs });
            outputs.fetch({reset: true});

            // /* TODO. output collection should observe the calculation one */
            setInterval(function() { outputs.fetch({reset: true}) }, 10000);

            /* XXX. Reset the input file value to ensure the change event
               will be always triggered */
            $(document).on("click", 'input[name=archive]',
                           function(e) { this.value = null; });
            $(document).on("change", 'input[name=archive]',
                           function(e) {
                               dialog.showPleaseWait('Uploading calculation', true);
                               var input = $(e.target);
                               var form = input.parents('form')[0];

                               $(form).ajaxSubmit(
                                   {
                                    xhr: function() {  // custom xhr to add progress bar management
                                        var myXhr = $.ajaxSettings.xhr();
                                        if(myXhr.upload){ // if upload property exists
                                            myXhr.upload.addEventListener('progress', progressHandlingFunction, false);
                                        }
                                        return myXhr;
                                    },
                                    success: function(data) {
                                        outputs.add(new Calculation(data));
                                    },
                                    error: function(xhr) {
                                        dialog.hidePleaseWait();
                                        var s, out, ret = $.parseJSON(xhr.responseText);
                                        out = ""
                                        for (s in ret) {
                                            if (ret[s] == "")
                                                continue;
                                            out += ret[s] + '\n';
                                        }
                                        diaerror.showDiaError("Calculation not accepted: traceback", out);
                                    }});
                           });

            $(document).on('hidden.bs.modal', 'div[id^=traceback-]',
                           function(e) {
                               setTimer();
                           });

        });
})($, Backbone, _, gem_oq_server_url, gem_calc_id);
<|endoftext|>


<commit_before>const path = require('path')
const webpack = require('webpack')
const { merge } = require('webpack-merge')
const WebpackChain = require('webpack-chain')
const { VueLoaderPlugin } = require('vue-loader')

const WebpackProgressPlugin = require('./plugin.progress')
const BootDefaultExport = require('./plugin.boot-default-export')
const parseBuildEnv = require('../helpers/parse-build-env')

const appPaths = require('../app-paths')
const injectStyleRules = require('./inject.style-rules')
const { webpackNames } = require('./symbols')

function getDependenciesRegex (list) {
  const deps = list.map(dep => {
    if (typeof dep === 'string') {
      return path.join('node_modules', dep, '/')
        .replace(/\\/g, '[\\\\/]') // windows support
    }
    else if (dep instanceof RegExp) {
      return dep.source
    }
  })

  return new RegExp(deps.join('|'))
}

function getRootDefines (rootDefines, configName) {
  if (configName === webpackNames.ssr.serverSide) {
    return { ...rootDefines, __QUASAR_SSR_SERVER__: true }
  }

  if (configName === webpackNames.ssr.clientSide) {
    return { ...rootDefines, __QUASAR_SSR_CLIENT__: true }
  }

  return rootDefines
}

module.exports = function (cfg, configName) {
  const chain = new WebpackChain()

  const needsHash = !cfg.ctx.dev && !['electron', 'cordova', 'capacitor', 'bex'].includes(cfg.ctx.modeName)
  const fileHash = needsHash ? '.[contenthash:8]' : ''
  const chunkHash = needsHash ? '.[chunkhash:8]' : ''
  const resolveModules = [
    'node_modules',
    appPaths.resolve.app('node_modules'),
    appPaths.resolve.cli('node_modules')
  ]

  chain.entry('app').add(appPaths.resolve.app('.quasar/client-entry.js'))
  chain.mode(cfg.ctx.dev ? 'development' : 'production')
  chain.devtool(cfg.build.sourceMap ? cfg.build.devtool : false)

  if (cfg.ctx.prod || cfg.ctx.mode.ssr) {
    chain.output
      .path(
        cfg.ctx.mode.ssr
          ? path.join(cfg.build.distDir, 'www')
          : cfg.build.distDir
      )
      .publicPath(cfg.build.publicPath)
      .filename(`js/[name]${fileHash}.js`)
      .chunkFilename(`js/[name]${chunkHash}.js`)
  }

  chain.resolve.extensions
    .merge(
      cfg.supportTS !== false
        ? [ '.mjs', '.ts', '.js', '.vue', '.json', '.wasm' ]
        : [ '.mjs', '.js', '.vue', '.json', '.wasm' ]
    )

  chain.resolve.modules
    .merge(resolveModules)

  chain.resolve.alias
    .merge({
      src: appPaths.srcDir,
      app: appPaths.appDir,
      components: appPaths.resolve.src(`components`),
      layouts: appPaths.resolve.src(`layouts`),
      pages: appPaths.resolve.src(`pages`),
      assets: appPaths.resolve.src(`assets`),
      boot: appPaths.resolve.src(`boot`),

      'src-bex': appPaths.bexDir // needed for app/templates
    })

  const vueFile = configName === webpackNames.ssr.serverSide
    ? (cfg.ctx.prod ? 'vue.cjs.prod.js' : 'vue.cjs.js')
    : (
      cfg.build.vueCompiler
        ? 'vue.esm-bundler.js'
        : 'vue.runtime.esm-bundler.js'
    )

  chain.resolve.alias.set('vue$', 'vue/dist/' + vueFile)

  const vueI18nFile = configName === webpackNames.ssr.serverSide
    ? (cfg.ctx.prod ? 'vue-i18n.cjs.prod.js' : 'vue-i18n.cjs.js')
    : 'vue-i18n.esm-bundler.js'

  chain.resolve.alias.set('vue-i18n$', 'vue-i18n/dist/' + vueI18nFile)

  chain.resolveLoader.modules
    .merge(resolveModules)

  chain.module.noParse(
    /^(vue|vue-router|vuex|vuex-router-sync|@quasar[\\/]extras|quasar[\\/]dist)$/
  )

  const vueRule = chain.module.rule('vue')
    .test(/\.vue$/)

  vueRule.use('vue-auto-import-quasar')
    .loader(path.join(__dirname, 'loader.vue.auto-import-quasar.js'))
    .options({
      autoImportComponentCase: cfg.framework.autoImportComponentCase,
      isServerBuild: configName === webpackNames.ssr.serverSide
    })

  vueRule.use('vue-loader')
    .loader('vue-loader')
    .options(
      merge(
        {},
        cfg.build.vueLoaderOptions,
        {
          isServerBuild: configName === webpackNames.ssr.serverSide,
          compilerOptions: configName === webpackNames.ssr.serverSide
            ? { directiveTransforms: cfg.ssr.directiveTransforms, ssr: true }
            : {}
        }
      )
    )

  if (configName !== webpackNames.ssr.serverSide) {
    chain.module.rule('js-transform-quasar-imports')
      .test(/\.(t|j)sx?$/)
      .use('transform-quasar-imports')
        .loader(path.join(__dirname, 'loader.js.transform-quasar-imports.js'))
  }

  if (cfg.build.transpile === true) {
    const nodeModulesRegex = /[\\/]node_modules[\\/]/
    const exceptionsRegex = getDependenciesRegex(
      [ /\.vue\.js$/, configName === webpackNames.ssr.serverSide ? 'quasar/src' : 'quasar', '@babel/runtime' ]
        .concat(cfg.build.transpileDependencies)
    )

    chain.module.rule('babel')
      .test(/\.js$/)
      .exclude
        .add(filepath => (
          // Transpile the exceptions:
          exceptionsRegex.test(filepath) === false &&
          // Don't transpile anything else in node_modules:
          nodeModulesRegex.test(filepath)
        ))
        .end()
      .use('babel-loader')
        .loader('babel-loader')
          .options({
            compact: false,
            extends: appPaths.resolve.app('babel.config.js')
          })
  }

  if (cfg.supportTS !== false) {
    chain.module
      .rule('typescript')
      .test(/\.ts$/)
      .use('ts-loader')
        .loader('ts-loader')
        .options({
          // custom config is merged if present, but vue setup and type checking disable are always applied
          ...(cfg.supportTS.tsLoaderConfig || {}),
          appendTsSuffixTo: [ /\.vue$/ ],
          // Type checking is handled by fork-ts-checker-webpack-plugin
          transpileOnly: true
        })

    const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin')
    chain
      .plugin('ts-checker')
      // https://github.com/TypeStrong/fork-ts-checker-webpack-plugin#options
      .use(ForkTsCheckerWebpackPlugin, [
        // custom config is merged if present, but vue option is always enabled
        merge({}, cfg.supportTS.tsCheckerConfig || {}, {
          typescript: {
            extensions: {
              vue: {
                enabled: true,
                compiler: '@vue/compiler-sfc'
              }
            }
          }
        })
      ])
  }

  // TODO: change to Asset Management when webpack-chain is webpack5 compatible
  chain.module.rule('images')
    .test(/\.(png|jpe?g|gif|svg|webp|avif|ico)(\?.*)?$/)
    .type('javascript/auto')
    .use('url-loader')
      .loader('url-loader')
      .options({
        esModule: false,
        limit: 10000,
        name: `img/[name]${fileHash}.[ext]`
      })

  // TODO: change to Asset Management when webpack-chain is webpack5 compatible
  chain.module.rule('fonts')
    .test(/\.(woff2?|eot|ttf|otf)(\?.*)?$/)
    .type('javascript/auto')
    .use('url-loader')
      .loader('url-loader')
      .options({
        esModule: false,
        limit: 10000,
        name: `fonts/[name]${fileHash}.[ext]`
      })

  // TODO: change to Asset Management when webpack-chain is webpack5 compatible
  chain.module.rule('media')
    .test(/\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/)
    .type('javascript/auto')
    .use('url-loader')
      .loader('url-loader')
      .options({
        esModule: false,
        limit: 10000,
        name: `media/[name]${fileHash}.[ext]`
      })

  injectStyleRules(chain, {
    isServerBuild: configName === webpackNames.ssr.serverSide,
    rtl: cfg.build.rtl,
    sourceMap: cfg.build.sourceMap,
    extract: cfg.build.extractCSS,
    minify: cfg.build.minify,
    stylusLoaderOptions: cfg.build.stylusLoaderOptions,
    sassLoaderOptions: cfg.build.sassLoaderOptions,
    scssLoaderOptions: cfg.build.scssLoaderOptions,
    lessLoaderOptions: cfg.build.lessLoaderOptions
  })

  chain.module // fixes https://github.com/graphql/graphql-js/issues/1272
    .rule('mjs')
    .test(/\.mjs$/)
    .type('javascript/auto')
    .include
      .add(/[\\/]node_modules[\\/]/)

  chain.plugin('vue-loader')
    .use(VueLoaderPlugin)

  chain.plugin('define')
    .use(webpack.DefinePlugin, [
      parseBuildEnv(cfg.build.env, getRootDefines(cfg.__rootDefines, configName))
    ])

  chain.optimization
    .nodeEnv(false)

  if (cfg.ctx.dev && configName !== webpackNames.ssr.serverSide && cfg.ctx.mode.pwa && cfg.pwa.workboxPluginMode === 'InjectManifest') {
    // need to place it here before the status plugin
    const CustomSwWarningPlugin = require('./pwa/plugin.custom-sw-warning')
    chain.plugin('custom-sw-warning')
      .use(CustomSwWarningPlugin)
  }

  chain.plugin('progress')
    .use(WebpackProgressPlugin, [{ name: configName, cfg }])

  chain.plugin('boot-default-export')
    .use(BootDefaultExport)

  chain.performance
    .hints(false)
    .maxAssetSize(500000)

  if (configName !== webpackNames.ssr.serverSide && cfg.vendor.disable !== true) {
    const { add, remove } = cfg.vendor
    const regex = /[\\/]node_modules[\\/]/

    chain.optimization.splitChunks({
      cacheGroups: {
        defaultVendors: {
          name: 'vendor',
          chunks: 'all',
          priority: -10,
          // a module is extracted into the vendor chunk if...
          test: add !== void 0 || remove !== void 0
            ? module => {
              if (module.resource) {
                if (remove !== void 0 && remove.test(module.resource)) { return false }
                if (add !== void 0 && add.test(module.resource)) { return true }
              }
              return regex.test(module.resource)
            }
            : regex
        },
        common: {
          name: `chunk-common`,
          minChunks: 2,
          priority: -20,
          chunks: 'all',
          reuseExistingChunk: true
        }
      }
    })
  }

  // extract css into its own file
  if (configName !== webpackNames.ssr.serverSide && cfg.build.extractCSS) {
    const MiniCssExtractPlugin = require('mini-css-extract-plugin')

    chain.plugin('mini-css-extract')
      .use(MiniCssExtractPlugin, [{
        filename: `css/[name]${fileHash}.css`
      }])
  }

  if (cfg.ctx.prod) {
    if (
      cfg.build.ignorePublicFolder !== true &&
      configName !== webpackNames.ssr.serverSide
    ) {
      // copy /public to dist folder
      const CopyWebpackPlugin = require('copy-webpack-plugin')

      const ignore = [
        '**/.DS_Store',
        '**/.Thumbs.db',
        '**/*.sublime*',
        '**/.idea',
        '**/.editorconfig',
        '**/.vscode'
      ]

      // avoid useless files to be copied
      if (['electron', 'cordova', 'capacitor'].includes(cfg.ctx.modeName)) {
        ignore.push(
          '**/public/icons', '**/public/favicon.ico'
        )
      }

      const patterns = [{
        from: appPaths.resolve.app('public'),
        noErrorOnMissing: true,
        globOptions: { ignore }
      }]

      chain.plugin('copy-webpack')
        .use(CopyWebpackPlugin, [{ patterns }])
    }

    chain.optimization
      .concatenateModules(cfg.ctx.mode.ssr !== true)

    if (cfg.ctx.debug) {
      // reset default webpack 4 minimizer
      chain.optimization.minimizers.delete('js')
      // also:
      chain.optimization.minimize(false)
    }
    else if (cfg.build.minify) {
      const TerserPlugin = require('terser-webpack-plugin')

      chain.optimization
        .minimizer('js')
        .use(TerserPlugin, [{
          terserOptions: cfg.build.uglifyOptions,
          extractComments: false,
          parallel: true
        }])
    }

    if (configName !== webpackNames.ssr.serverSide) {
      // dedupe & minify CSS (only if extracted)
      if (cfg.build.extractCSS && cfg.build.minify) {
        const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')

        // We are using this plugin so that possible
        // duplicated CSS = require(different components) can be deduped.
        chain.optimization
          .minimizer('css')
          .use(CssMinimizerPlugin, [{
            parallel: true
          }])
      }

      // also produce a gzipped version
      if (cfg.build.gzip) {
        const CompressionWebpackPlugin = require('compression-webpack-plugin')
        chain.plugin('compress-webpack')
          .use(CompressionWebpackPlugin, [ cfg.build.gzip ])
      }

      if (cfg.build.analyze) {
        const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
        chain.plugin('bundle-analyzer')
          .use(BundleAnalyzerPlugin, [ Object.assign({}, cfg.build.analyze) ])
      }
    }
  }

  return chain
}
<commit_msg>fix(app): Ensure webpack does not clash assets when they have same name but in different folders (#11711)<commit_after>const path = require('path')
const webpack = require('webpack')
const { merge } = require('webpack-merge')
const WebpackChain = require('webpack-chain')
const { VueLoaderPlugin } = require('vue-loader')

const WebpackProgressPlugin = require('./plugin.progress')
const BootDefaultExport = require('./plugin.boot-default-export')
const parseBuildEnv = require('../helpers/parse-build-env')

const appPaths = require('../app-paths')
const injectStyleRules = require('./inject.style-rules')
const { webpackNames } = require('./symbols')

function getDependenciesRegex (list) {
  const deps = list.map(dep => {
    if (typeof dep === 'string') {
      return path.join('node_modules', dep, '/')
        .replace(/\\/g, '[\\\\/]') // windows support
    }
    else if (dep instanceof RegExp) {
      return dep.source
    }
  })

  return new RegExp(deps.join('|'))
}

function getRootDefines (rootDefines, configName) {
  if (configName === webpackNames.ssr.serverSide) {
    return { ...rootDefines, __QUASAR_SSR_SERVER__: true }
  }

  if (configName === webpackNames.ssr.clientSide) {
    return { ...rootDefines, __QUASAR_SSR_CLIENT__: true }
  }

  return rootDefines
}

module.exports = function (cfg, configName) {
  const chain = new WebpackChain()

  const needsHash = !cfg.ctx.dev && !['electron', 'cordova', 'capacitor', 'bex'].includes(cfg.ctx.modeName)
  const fileHash = needsHash ? '.[contenthash:8]' : ''
  const chunkHash = needsHash ? '.[chunkhash:8]' : ''
  const resolveModules = [
    'node_modules',
    appPaths.resolve.app('node_modules'),
    appPaths.resolve.cli('node_modules')
  ]

  chain.entry('app').add(appPaths.resolve.app('.quasar/client-entry.js'))
  chain.mode(cfg.ctx.dev ? 'development' : 'production')
  chain.devtool(cfg.build.sourceMap ? cfg.build.devtool : false)

  if (cfg.ctx.prod || cfg.ctx.mode.ssr) {
    chain.output
      .path(
        cfg.ctx.mode.ssr
          ? path.join(cfg.build.distDir, 'www')
          : cfg.build.distDir
      )
      .publicPath(cfg.build.publicPath)
      .filename(`js/[name]${fileHash}.js`)
      .chunkFilename(`js/[name]${chunkHash}.js`)
  }

  chain.resolve.extensions
    .merge(
      cfg.supportTS !== false
        ? [ '.mjs', '.ts', '.js', '.vue', '.json', '.wasm' ]
        : [ '.mjs', '.js', '.vue', '.json', '.wasm' ]
    )

  chain.resolve.modules
    .merge(resolveModules)

  chain.resolve.alias
    .merge({
      src: appPaths.srcDir,
      app: appPaths.appDir,
      components: appPaths.resolve.src(`components`),
      layouts: appPaths.resolve.src(`layouts`),
      pages: appPaths.resolve.src(`pages`),
      assets: appPaths.resolve.src(`assets`),
      boot: appPaths.resolve.src(`boot`),

      'src-bex': appPaths.bexDir // needed for app/templates
    })

  const vueFile = configName === webpackNames.ssr.serverSide
    ? (cfg.ctx.prod ? 'vue.cjs.prod.js' : 'vue.cjs.js')
    : (
      cfg.build.vueCompiler
        ? 'vue.esm-bundler.js'
        : 'vue.runtime.esm-bundler.js'
    )

  chain.resolve.alias.set('vue$', 'vue/dist/' + vueFile)

  const vueI18nFile = configName === webpackNames.ssr.serverSide
    ? (cfg.ctx.prod ? 'vue-i18n.cjs.prod.js' : 'vue-i18n.cjs.js')
    : 'vue-i18n.esm-bundler.js'

  chain.resolve.alias.set('vue-i18n$', 'vue-i18n/dist/' + vueI18nFile)

  chain.resolveLoader.modules
    .merge(resolveModules)

  chain.module.noParse(
    /^(vue|vue-router|vuex|vuex-router-sync|@quasar[\\/]extras|quasar[\\/]dist)$/
  )

  const vueRule = chain.module.rule('vue')
    .test(/\.vue$/)

  vueRule.use('vue-auto-import-quasar')
    .loader(path.join(__dirname, 'loader.vue.auto-import-quasar.js'))
    .options({
      autoImportComponentCase: cfg.framework.autoImportComponentCase,
      isServerBuild: configName === webpackNames.ssr.serverSide
    })

  vueRule.use('vue-loader')
    .loader('vue-loader')
    .options(
      merge(
        {},
        cfg.build.vueLoaderOptions,
        {
          isServerBuild: configName === webpackNames.ssr.serverSide,
          compilerOptions: configName === webpackNames.ssr.serverSide
            ? { directiveTransforms: cfg.ssr.directiveTransforms, ssr: true }
            : {}
        }
      )
    )

  if (configName !== webpackNames.ssr.serverSide) {
    chain.module.rule('js-transform-quasar-imports')
      .test(/\.(t|j)sx?$/)
      .use('transform-quasar-imports')
        .loader(path.join(__dirname, 'loader.js.transform-quasar-imports.js'))
  }

  if (cfg.build.transpile === true) {
    const nodeModulesRegex = /[\\/]node_modules[\\/]/
    const exceptionsRegex = getDependenciesRegex(
      [ /\.vue\.js$/, configName === webpackNames.ssr.serverSide ? 'quasar/src' : 'quasar', '@babel/runtime' ]
        .concat(cfg.build.transpileDependencies)
    )

    chain.module.rule('babel')
      .test(/\.js$/)
      .exclude
        .add(filepath => (
          // Transpile the exceptions:
          exceptionsRegex.test(filepath) === false &&
          // Don't transpile anything else in node_modules:
          nodeModulesRegex.test(filepath)
        ))
        .end()
      .use('babel-loader')
        .loader('babel-loader')
          .options({
            compact: false,
            extends: appPaths.resolve.app('babel.config.js')
          })
  }

  if (cfg.supportTS !== false) {
    chain.module
      .rule('typescript')
      .test(/\.ts$/)
      .use('ts-loader')
        .loader('ts-loader')
        .options({
          // custom config is merged if present, but vue setup and type checking disable are always applied
          ...(cfg.supportTS.tsLoaderConfig || {}),
          appendTsSuffixTo: [ /\.vue$/ ],
          // Type checking is handled by fork-ts-checker-webpack-plugin
          transpileOnly: true
        })

    const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin')
    chain
      .plugin('ts-checker')
      // https://github.com/TypeStrong/fork-ts-checker-webpack-plugin#options
      .use(ForkTsCheckerWebpackPlugin, [
        // custom config is merged if present, but vue option is always enabled
        merge({}, cfg.supportTS.tsCheckerConfig || {}, {
          typescript: {
            extensions: {
              vue: {
                enabled: true,
                compiler: '@vue/compiler-sfc'
              }
            }
          }
        })
      ])
  }

  // TODO: change to Asset Management when webpack-chain is webpack5 compatible
  chain.module.rule('images')
    .test(/\.(png|jpe?g|gif|svg|webp|avif|ico)(\?.*)?$/)
    .type('javascript/auto')
    .use('url-loader')
      .loader('url-loader')
      .options({
        esModule: false,
        limit: 10000,
        name: `img/[path][name]${fileHash}.[ext]`
      })

  // TODO: change to Asset Management when webpack-chain is webpack5 compatible
  chain.module.rule('fonts')
    .test(/\.(woff2?|eot|ttf|otf)(\?.*)?$/)
    .type('javascript/auto')
    .use('url-loader')
      .loader('url-loader')
      .options({
        esModule: false,
        limit: 10000,
        name: `fonts/[path][name]${fileHash}.[ext]`
      })

  // TODO: change to Asset Management when webpack-chain is webpack5 compatible
  chain.module.rule('media')
    .test(/\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/)
    .type('javascript/auto')
    .use('url-loader')
      .loader('url-loader')
      .options({
        esModule: false,
        limit: 10000,
        name: `media/[path][name]${fileHash}.[ext]`
      })

  injectStyleRules(chain, {
    isServerBuild: configName === webpackNames.ssr.serverSide,
    rtl: cfg.build.rtl,
    sourceMap: cfg.build.sourceMap,
    extract: cfg.build.extractCSS,
    minify: cfg.build.minify,
    stylusLoaderOptions: cfg.build.stylusLoaderOptions,
    sassLoaderOptions: cfg.build.sassLoaderOptions,
    scssLoaderOptions: cfg.build.scssLoaderOptions,
    lessLoaderOptions: cfg.build.lessLoaderOptions
  })

  chain.module // fixes https://github.com/graphql/graphql-js/issues/1272
    .rule('mjs')
    .test(/\.mjs$/)
    .type('javascript/auto')
    .include
      .add(/[\\/]node_modules[\\/]/)

  chain.plugin('vue-loader')
    .use(VueLoaderPlugin)

  chain.plugin('define')
    .use(webpack.DefinePlugin, [
      parseBuildEnv(cfg.build.env, getRootDefines(cfg.__rootDefines, configName))
    ])

  chain.optimization
    .nodeEnv(false)

  if (cfg.ctx.dev && configName !== webpackNames.ssr.serverSide && cfg.ctx.mode.pwa && cfg.pwa.workboxPluginMode === 'InjectManifest') {
    // need to place it here before the status plugin
    const CustomSwWarningPlugin = require('./pwa/plugin.custom-sw-warning')
    chain.plugin('custom-sw-warning')
      .use(CustomSwWarningPlugin)
  }

  chain.plugin('progress')
    .use(WebpackProgressPlugin, [{ name: configName, cfg }])

  chain.plugin('boot-default-export')
    .use(BootDefaultExport)

  chain.performance
    .hints(false)
    .maxAssetSize(500000)

  if (configName !== webpackNames.ssr.serverSide && cfg.vendor.disable !== true) {
    const { add, remove } = cfg.vendor
    const regex = /[\\/]node_modules[\\/]/

    chain.optimization.splitChunks({
      cacheGroups: {
        defaultVendors: {
          name: 'vendor',
          chunks: 'all',
          priority: -10,
          // a module is extracted into the vendor chunk if...
          test: add !== void 0 || remove !== void 0
            ? module => {
              if (module.resource) {
                if (remove !== void 0 && remove.test(module.resource)) { return false }
                if (add !== void 0 && add.test(module.resource)) { return true }
              }
              return regex.test(module.resource)
            }
            : regex
        },
        common: {
          name: `chunk-common`,
          minChunks: 2,
          priority: -20,
          chunks: 'all',
          reuseExistingChunk: true
        }
      }
    })
  }

  // extract css into its own file
  if (configName !== webpackNames.ssr.serverSide && cfg.build.extractCSS) {
    const MiniCssExtractPlugin = require('mini-css-extract-plugin')

    chain.plugin('mini-css-extract')
      .use(MiniCssExtractPlugin, [{
        filename: `css/[name]${fileHash}.css`
      }])
  }

  if (cfg.ctx.prod) {
    if (
      cfg.build.ignorePublicFolder !== true &&
      configName !== webpackNames.ssr.serverSide
    ) {
      // copy /public to dist folder
      const CopyWebpackPlugin = require('copy-webpack-plugin')

      const ignore = [
        '**/.DS_Store',
        '**/.Thumbs.db',
        '**/*.sublime*',
        '**/.idea',
        '**/.editorconfig',
        '**/.vscode'
      ]

      // avoid useless files to be copied
      if (['electron', 'cordova', 'capacitor'].includes(cfg.ctx.modeName)) {
        ignore.push(
          '**/public/icons', '**/public/favicon.ico'
        )
      }

      const patterns = [{
        from: appPaths.resolve.app('public'),
        noErrorOnMissing: true,
        globOptions: { ignore }
      }]

      chain.plugin('copy-webpack')
        .use(CopyWebpackPlugin, [{ patterns }])
    }

    chain.optimization
      .concatenateModules(cfg.ctx.mode.ssr !== true)

    if (cfg.ctx.debug) {
      // reset default webpack 4 minimizer
      chain.optimization.minimizers.delete('js')
      // also:
      chain.optimization.minimize(false)
    }
    else if (cfg.build.minify) {
      const TerserPlugin = require('terser-webpack-plugin')

      chain.optimization
        .minimizer('js')
        .use(TerserPlugin, [{
          terserOptions: cfg.build.uglifyOptions,
          extractComments: false,
          parallel: true
        }])
    }

    if (configName !== webpackNames.ssr.serverSide) {
      // dedupe & minify CSS (only if extracted)
      if (cfg.build.extractCSS && cfg.build.minify) {
        const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')

        // We are using this plugin so that possible
        // duplicated CSS = require(different components) can be deduped.
        chain.optimization
          .minimizer('css')
          .use(CssMinimizerPlugin, [{
            parallel: true
          }])
      }

      // also produce a gzipped version
      if (cfg.build.gzip) {
        const CompressionWebpackPlugin = require('compression-webpack-plugin')
        chain.plugin('compress-webpack')
          .use(CompressionWebpackPlugin, [ cfg.build.gzip ])
      }

      if (cfg.build.analyze) {
        const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
        chain.plugin('bundle-analyzer')
          .use(BundleAnalyzerPlugin, [ Object.assign({}, cfg.build.analyze) ])
      }
    }
  }

  return chain
}
<|endoftext|>