<commit_before>import {
  clone,
  defer,
  GltfBufferViewLoader,
  GltfStructuralMetadataLoader,
  GltfTextureLoader,
  MetadataSchemaLoader,
  Resource,
  ResourceCache,
  SupportedImageFormats,
} from "../../Source/Cesium.js";
import createScene from "../createScene.js";
import loaderProcess from "../loaderProcess.js";
import MetadataTester from "../MetadataTester.js";
import waitForLoaderProcess from "../waitForLoaderProcess.js";

describe(
  "Scene/GltfStructuralMetadataLoader",
  function () {
    if (!MetadataTester.isSupported()) {
      return;
    }

    const image = new Image();
    image.src =
      "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+ip1sAAAAASUVORK5CYII=";

    const gltfUri = "https://example.com/model.glb";
    const gltfResource = new Resource({
      url: gltfUri,
    });

    const schemaJson = {
      classes: {
        building: {
          properties: {
            name: {
              type: "STRING",
            },
            height: {
              type: "SCALAR",
              componentType: "FLOAT64",
            },
          },
        },
        tree: {
          properties: {
            species: {
              type: "STRING",
              array: true,
            },
          },
        },
        map: {
          properties: {
            color: {
              type: "SCALAR",
              componentType: "UINT8",
              array: true,
              count: 3,
            },
            intensity: {
              type: "SCALAR",
              componentType: "UINT8",
            },
          },
        },
        ortho: {
          properties: {
            vegetation: {
              type: "SCALAR",
              componentType: "UINT8",
              normalized: true,
            },
          },
        },
      },
    };

    const results = MetadataTester.createGltf({
      schema: schemaJson,
      propertyTables: [
        {
          name: "Buildings",
          class: "building",
          properties: {
            name: ["House", "Hospital"],
            height: [10.0, 20.0],
          },
        },
        {
          name: "Trees",
          class: "tree",
          properties: {
            species: [["Sparrow", "Squirrel"], ["Crow"]],
          },
        },
      ],
      images: [
        {
          uri: "map.png",
        },
        {
          uri: "ortho.png",
        },
      ],
      textures: [
        {
          source: 0,
        },
        {
          source: 1,
        },
      ],
      propertyTextures: [
        {
          name: "Map",
          class: "map",
          properties: {
            color: {
              channels: [0, 1, 2],
              index: 0,
              texCoord: 0,
            },
            intensity: {
              channels: [3],
              index: 0,
              texCoord: 0,
            },
          },
        },
        {
          name: "Ortho",
          class: "ortho",
          properties: {
            vegetation: {
              channels: [0],
              index: 1,
              texCoord: 1,
            },
          },
        },
      ],
    });

    const gltf = results.gltf;
    const extension = gltf.extensions.EXT_structural_metadata;
    const buffer = results.buffer.buffer;

    const gltfSchemaUri = clone(gltf, true);
    const extensionSchemaUri = gltfSchemaUri.extensions.EXT_structural_metadata;
    extensionSchemaUri.schemaUri = "schema.json";
    delete extensionSchemaUri.schema;

    let scene;

    beforeAll(function () {
      scene = createScene();
    });

    afterAll(function () {
      scene.destroyForSpecs();
    });

    afterEach(function () {
      ResourceCache.clearForSpecs();
    });

    it("throws if gltf is undefined", function () {
      expect(function () {
        return new GltfStructuralMetadataLoader({
          gltf: undefined,
          extension: extension,
          gltfResource: gltfResource,
          baseResource: gltfResource,
          supportedImageFormats: new SupportedImageFormats(),
        });
      }).toThrowDeveloperError();
    });

    it("throws if neither extension nor extensionLegacy is defined", function () {
      expect(function () {
        return new GltfStructuralMetadataLoader({
          gltf: gltf,
          extension: undefined,
          extensionLegacy: undefined,
          gltfResource: gltfResource,
          baseResource: gltfResource,
          supportedImageFormats: new SupportedImageFormats(),
        });
      }).toThrowDeveloperError();
    });

    it("throws if gltfResource is undefined", function () {
      expect(function () {
        return new GltfStructuralMetadataLoader({
          gltf: gltf,
          extension: extension,
          gltfResource: undefined,
          baseResource: gltfResource,
          supportedImageFormats: new SupportedImageFormats(),
        });
      }).toThrowDeveloperError();
    });

    it("throws if baseResource is undefined", function () {
      expect(function () {
        return new GltfStructuralMetadataLoader({
          gltf: gltf,
          extension: extension,
          gltfResource: gltfResource,
          baseResource: undefined,
          supportedImageFormats: new SupportedImageFormats(),
        });
      }).toThrowDeveloperError();
    });

    it("throws if gltf is undefined", function () {
      expect(function () {
        return new GltfStructuralMetadataLoader({
          gltf: gltf,
          extension: extension,
          gltfResource: gltfResource,
          baseResource: gltfResource,
          supportedImageFormats: undefined,
        });
      }).toThrowDeveloperError();
    });

    it("rejects promise if buffer view fails to load", function () {
      spyOn(Resource.prototype, "fetchArrayBuffer").and.callFake(function () {
        const error = new Error("404 Not Found");
        return Promise.reject(error);
      });

      spyOn(Resource.prototype, "fetchImage").and.returnValue(
        Promise.resolve(image)
      );

      const structuralMetadataLoader = new GltfStructuralMetadataLoader({
        gltf: gltf,
        extension: extension,
        gltfResource: gltfResource,
        baseResource: gltfResource,
        supportedImageFormats: new SupportedImageFormats(),
      });

      structuralMetadataLoader.load();

      return structuralMetadataLoader.promise
        .then(function (structuralMetadataLoader) {
          fail();
        })
        .catch(function (runtimeError) {
          expect(runtimeError.message).toBe(
            "Failed to load structural metadata\nFailed to load buffer view\nFailed to load external buffer: https://example.com/external.bin\n404 Not Found"
          );
        });
    });

    it("rejects promise if texture fails to load", function () {
      spyOn(Resource.prototype, "fetchArrayBuffer").and.returnValue(
        Promise.resolve(buffer)
      );

      spyOn(Resource.prototype, "fetchImage").and.callFake(function () {
        const error = new Error("404 Not Found");
        return Promise.reject(error);
      });

      const structuralMetadataLoader = new GltfStructuralMetadataLoader({
        gltf: gltf,
        extension: extension,
        gltfResource: gltfResource,
        baseResource: gltfResource,
        supportedImageFormats: new SupportedImageFormats(),
      });

      structuralMetadataLoader.load();

      return structuralMetadataLoader.promise
        .then(function (structuralMetadataLoader) {
          fail();
        })
        .catch(function (runtimeError) {
          expect(runtimeError.message).toBe(
            "Failed to load structural metadata\nFailed to load texture\nFailed to load image: map.png\n404 Not Found"
          );
        });
    });

    it("rejects promise if external schema fails to load", function () {
      spyOn(Resource.prototype, "fetchArrayBuffer").and.returnValue(
        Promise.resolve(buffer)
      );

      spyOn(Resource.prototype, "fetchImage").and.returnValue(
        Promise.resolve(image)
      );

      spyOn(Resource.prototype, "fetchJson").and.callFake(function () {
        const error = new Error("404 Not Found");
        return Promise.reject(error);
      });

      const structuralMetadataLoader = new GltfStructuralMetadataLoader({
        gltf: gltfSchemaUri,
        extension: extensionSchemaUri,
        gltfResource: gltfResource,
        baseResource: gltfResource,
        supportedImageFormats: new SupportedImageFormats(),
      });

      structuralMetadataLoader.load();

      return structuralMetadataLoader.promise
        .then(function () {
          fail();
        })
        .catch(function (runtimeError) {
          expect(runtimeError.message).toBe(
            "Failed to load structural metadata\nFailed to load schema: https://example.com/schema.json\n404 Not Found"
          );
        });
    });

    it("loads structural metadata", function () {
      spyOn(Resource.prototype, "fetchArrayBuffer").and.returnValue(
        Promise.resolve(buffer)
      );

      spyOn(Resource.prototype, "fetchImage").and.returnValue(
        Promise.resolve(image)
      );

      const structuralMetadataLoader = new GltfStructuralMetadataLoader({
        gltf: gltf,
        extension: extension,
        gltfResource: gltfResource,
        baseResource: gltfResource,
        supportedImageFormats: new SupportedImageFormats(),
      });

      structuralMetadataLoader.load();

      return waitForLoaderProcess(structuralMetadataLoader, scene).then(
        function (structuralMetadataLoader) {
          loaderProcess(structuralMetadataLoader, scene); // Check that calling process after load doesn't break anything
          const structuralMetadata =
            structuralMetadataLoader.structuralMetadata;
          const buildingsTable = structuralMetadata.getPropertyTable(0);
          expect(buildingsTable.id).toBe(0);
          const treesTable = structuralMetadata.getPropertyTable(1);
          expect(treesTable.id).toBe(1);
          const mapTexture = structuralMetadata.getPropertyTexture(0);
          expect(mapTexture.id).toBe(0);
          const orthoTexture = structuralMetadata.getPropertyTexture(1);
          expect(orthoTexture.id).toBe(1);

          expect(buildingsTable.getProperty(0, "name")).toBe("House");
          expect(buildingsTable.getProperty(1, "name")).toBe("Hospital");
          expect(treesTable.getProperty(0, "species")).toEqual([
            "Sparrow",
            "Squirrel",
          ]);
          expect(treesTable.getProperty(1, "species")).toEqual(["Crow"]);

          const colorProperty = mapTexture.getProperty("color");
          const intensityProperty = mapTexture.getProperty("intensity");
          const vegetationProperty = orthoTexture.getProperty("vegetation");

          expect(colorProperty.textureReader.texture.width).toBe(1);
          expect(colorProperty.textureReader.texture.height).toBe(1);
          expect(colorProperty.textureReader.texture).toBe(
            intensityProperty.textureReader.texture
          );

          expect(vegetationProperty.textureReader.texture.width).toBe(1);
          expect(vegetationProperty.textureReader.texture.height).toBe(1);
          expect(vegetationProperty.textureReader.texture).not.toBe(
            colorProperty.textureReader.texture
          );

          expect(
            Object.keys(structuralMetadata.schema.classes).sort()
          ).toEqual(["building", "map", "ortho", "tree"]);
        }
      );
    });

    it("loads structural metadata with external schema", function () {
      spyOn(Resource.prototype, "fetchArrayBuffer").and.returnValue(
        Promise.resolve(buffer)
      );

      spyOn(Resource.prototype, "fetchImage").and.returnValue(
        Promise.resolve(image)
      );

      spyOn(Resource.prototype, "fetchJson").and.returnValue(
        Promise.resolve(schemaJson)
      );

      const structuralMetadataLoader = new GltfStructuralMetadataLoader({
        gltf: gltfSchemaUri,
        extension: extensionSchemaUri,
        gltfResource: gltfResource,
        baseResource: gltfResource,
        supportedImageFormats: new SupportedImageFormats(),
      });

      structuralMetadataLoader.load();

      return waitForLoaderProcess(structuralMetadataLoader, scene).then(
        function (structuralMetadataLoader) {
          const structuralMetadata =
            structuralMetadataLoader.structuralMetadata;
          expect(
            Object.keys(structuralMetadata.schema.classes).sort()
          ).toEqual(["building", "map", "ortho", "tree"]);
        }
      );
    });

    it("destroys structural metadata", function () {
      spyOn(Resource.prototype, "fetchArrayBuffer").and.returnValue(
        Promise.resolve(buffer)
      );

      spyOn(Resource.prototype, "fetchImage").and.returnValue(
        Promise.resolve(image)
      );

      spyOn(Resource.prototype, "fetchJson").and.returnValue(
        Promise.resolve(schemaJson)
      );

      const destroyBufferView = spyOn(
        GltfBufferViewLoader.prototype,
        "destroy"
      ).and.callThrough();

      const destroyTexture = spyOn(
        GltfTextureLoader.prototype,
        "destroy"
      ).and.callThrough();

      const destroySchema = spyOn(
        MetadataSchemaLoader.prototype,
        "destroy"
      ).and.callThrough();

      const structuralMetadataLoader = new GltfStructuralMetadataLoader({
        gltf: gltf,
        extension: extension,
        gltfResource: gltfResource,
        baseResource: gltfResource,
        supportedImageFormats: new SupportedImageFormats(),
      });

      structuralMetadataLoader.load();

      return waitForLoaderProcess(structuralMetadataLoader, scene).then(
        function (structuralMetadataLoader) {
          expect(structuralMetadataLoader.structuralMetadata).toBeDefined();
          expect(structuralMetadataLoader.isDestroyed()).toBe(false);

          structuralMetadataLoader.destroy();

          expect(structuralMetadataLoader.structuralMetadata).not.toBeDefined();
          expect(structuralMetadataLoader.isDestroyed()).toBe(true);

          expect(destroyBufferView.calls.count()).toBe(6);
          expect(destroyTexture.calls.count()).toBe(2);
          expect(destroySchema.calls.count()).toBe(1);
        }
      );
    });

    function resolveAfterDestroy(rejectPromise) {
      spyOn(Resource.prototype, "fetchArrayBuffer").and.returnValue(
        Promise.resolve(buffer)
      );
      spyOn(Resource.prototype, "fetchImage").and.returnValue(
        Promise.resolve(image)
      );
      const deferredPromise = defer();
      spyOn(Resource.prototype, "fetchJson").and.returnValue(
        deferredPromise.promise
      );

      const destroyBufferView = spyOn(
        GltfBufferViewLoader.prototype,
        "destroy"
      ).and.callThrough();
      const destroyTexture = spyOn(
        GltfTextureLoader.prototype,
        "destroy"
      ).and.callThrough();
      const destroySchema = spyOn(
        MetadataSchemaLoader.prototype,
        "destroy"
      ).and.callThrough();

      // Load a copy of structural metadata into the cache so that the resource
      // promises resolve even if the structural metadata loader is destroyed
      const structuralMetadataLoaderCopy = new GltfStructuralMetadataLoader({
        gltf: gltf,
        extension: extension,
        gltfResource: gltfResource,
        baseResource: gltfResource,
        supportedImageFormats: new SupportedImageFormats(),
      });
      // Also load a copy of the schema into the cache
      const schemaResource = gltfResource.getDerivedResource({
        url: "schema.json",
      });
      const schemaCopy = ResourceCache.loadSchema({
        resource: schemaResource,
      });

      structuralMetadataLoaderCopy.load();

      return waitForLoaderProcess(structuralMetadataLoaderCopy, scene).then(
        function (structuralMetadataLoaderCopy) {
          // Ignore structuralMetadataLoaderCopy destroying its buffer views
          destroyBufferView.calls.reset();

          const structuralMetadataLoader = new GltfStructuralMetadataLoader({
            gltf: gltfSchemaUri,
            extension: extensionSchemaUri,
            gltfResource: gltfResource,
            baseResource: gltfResource,
            supportedImageFormats: new SupportedImageFormats(),
          });
          expect(structuralMetadataLoader.structuralMetadata).not.toBeDefined();
          structuralMetadataLoader.load();
          structuralMetadataLoader.destroy();

          if (rejectPromise) {
            deferredPromise.reject(new Error());
          } else {
            deferredPromise.resolve(schemaJson);
          }

          expect(structuralMetadataLoader.structuralMetadata).not.toBeDefined();
          expect(structuralMetadataLoader.isDestroyed()).toBe(true);

          structuralMetadataLoaderCopy.destroy();

          expect(destroyBufferView.calls.count()).toBe(6);
          expect(destroyTexture.calls.count()).toBe(2);
          expect(destroySchema.calls.count()).toBe(1);

          ResourceCache.unload(schemaCopy);
        }
      );
    }

    it("handles resolving resources after destroy", function () {
      return resolveAfterDestroy(false);
    });

    it("handles rejecting resources after destroy", function () {
      return resolveAfterDestroy(true);
    });
  },
  "WebGL"
);
<commit_msg>Avoid using defer<commit_after>import {
  clone,
  GltfBufferViewLoader,
  GltfStructuralMetadataLoader,
  GltfTextureLoader,
  MetadataSchemaLoader,
  Resource,
  ResourceCache,
  SupportedImageFormats,
} from "../../Source/Cesium.js";
import createScene from "../createScene.js";
import loaderProcess from "../loaderProcess.js";
import MetadataTester from "../MetadataTester.js";
import waitForLoaderProcess from "../waitForLoaderProcess.js";

describe(
  "Scene/GltfStructuralMetadataLoader",
  function () {
    if (!MetadataTester.isSupported()) {
      return;
    }

    const image = new Image();
    image.src =
      "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+ip1sAAAAASUVORK5CYII=";

    const gltfUri = "https://example.com/model.glb";
    const gltfResource = new Resource({
      url: gltfUri,
    });

    const schemaJson = {
      classes: {
        building: {
          properties: {
            name: {
              type: "STRING",
            },
            height: {
              type: "SCALAR",
              componentType: "FLOAT64",
            },
          },
        },
        tree: {
          properties: {
            species: {
              type: "STRING",
              array: true,
            },
          },
        },
        map: {
          properties: {
            color: {
              type: "SCALAR",
              componentType: "UINT8",
              array: true,
              count: 3,
            },
            intensity: {
              type: "SCALAR",
              componentType: "UINT8",
            },
          },
        },
        ortho: {
          properties: {
            vegetation: {
              type: "SCALAR",
              componentType: "UINT8",
              normalized: true,
            },
          },
        },
      },
    };

    const results = MetadataTester.createGltf({
      schema: schemaJson,
      propertyTables: [
        {
          name: "Buildings",
          class: "building",
          properties: {
            name: ["House", "Hospital"],
            height: [10.0, 20.0],
          },
        },
        {
          name: "Trees",
          class: "tree",
          properties: {
            species: [["Sparrow", "Squirrel"], ["Crow"]],
          },
        },
      ],
      images: [
        {
          uri: "map.png",
        },
        {
          uri: "ortho.png",
        },
      ],
      textures: [
        {
          source: 0,
        },
        {
          source: 1,
        },
      ],
      propertyTextures: [
        {
          name: "Map",
          class: "map",
          properties: {
            color: {
              channels: [0, 1, 2],
              index: 0,
              texCoord: 0,
            },
            intensity: {
              channels: [3],
              index: 0,
              texCoord: 0,
            },
          },
        },
        {
          name: "Ortho",
          class: "ortho",
          properties: {
            vegetation: {
              channels: [0],
              index: 1,
              texCoord: 1,
            },
          },
        },
      ],
    });

    const gltf = results.gltf;
    const extension = gltf.extensions.EXT_structural_metadata;
    const buffer = results.buffer.buffer;

    const gltfSchemaUri = clone(gltf, true);
    const extensionSchemaUri = gltfSchemaUri.extensions.EXT_structural_metadata;
    extensionSchemaUri.schemaUri = "schema.json";
    delete extensionSchemaUri.schema;

    let scene;

    beforeAll(function () {
      scene = createScene();
    });

    afterAll(function () {
      scene.destroyForSpecs();
    });

    afterEach(function () {
      ResourceCache.clearForSpecs();
    });

    it("throws if gltf is undefined", function () {
      expect(function () {
        return new GltfStructuralMetadataLoader({
          gltf: undefined,
          extension: extension,
          gltfResource: gltfResource,
          baseResource: gltfResource,
          supportedImageFormats: new SupportedImageFormats(),
        });
      }).toThrowDeveloperError();
    });

    it("throws if neither extension nor extensionLegacy is defined", function () {
      expect(function () {
        return new GltfStructuralMetadataLoader({
          gltf: gltf,
          extension: undefined,
          extensionLegacy: undefined,
          gltfResource: gltfResource,
          baseResource: gltfResource,
          supportedImageFormats: new SupportedImageFormats(),
        });
      }).toThrowDeveloperError();
    });

    it("throws if gltfResource is undefined", function () {
      expect(function () {
        return new GltfStructuralMetadataLoader({
          gltf: gltf,
          extension: extension,
          gltfResource: undefined,
          baseResource: gltfResource,
          supportedImageFormats: new SupportedImageFormats(),
        });
      }).toThrowDeveloperError();
    });

    it("throws if baseResource is undefined", function () {
      expect(function () {
        return new GltfStructuralMetadataLoader({
          gltf: gltf,
          extension: extension,
          gltfResource: gltfResource,
          baseResource: undefined,
          supportedImageFormats: new SupportedImageFormats(),
        });
      }).toThrowDeveloperError();
    });

    it("throws if gltf is undefined", function () {
      expect(function () {
        return new GltfStructuralMetadataLoader({
          gltf: gltf,
          extension: extension,
          gltfResource: gltfResource,
          baseResource: gltfResource,
          supportedImageFormats: undefined,
        });
      }).toThrowDeveloperError();
    });

    it("rejects promise if buffer view fails to load", function () {
      spyOn(Resource.prototype, "fetchArrayBuffer").and.callFake(function () {
        const error = new Error("404 Not Found");
        return Promise.reject(error);
      });

      spyOn(Resource.prototype, "fetchImage").and.returnValue(
        Promise.resolve(image)
      );

      const structuralMetadataLoader = new GltfStructuralMetadataLoader({
        gltf: gltf,
        extension: extension,
        gltfResource: gltfResource,
        baseResource: gltfResource,
        supportedImageFormats: new SupportedImageFormats(),
      });

      structuralMetadataLoader.load();

      return structuralMetadataLoader.promise
        .then(function (structuralMetadataLoader) {
          fail();
        })
        .catch(function (runtimeError) {
          expect(runtimeError.message).toBe(
            "Failed to load structural metadata\nFailed to load buffer view\nFailed to load external buffer: https://example.com/external.bin\n404 Not Found"
          );
        });
    });

    it("rejects promise if texture fails to load", function () {
      spyOn(Resource.prototype, "fetchArrayBuffer").and.returnValue(
        Promise.resolve(buffer)
      );

      spyOn(Resource.prototype, "fetchImage").and.callFake(function () {
        const error = new Error("404 Not Found");
        return Promise.reject(error);
      });

      const structuralMetadataLoader = new GltfStructuralMetadataLoader({
        gltf: gltf,
        extension: extension,
        gltfResource: gltfResource,
        baseResource: gltfResource,
        supportedImageFormats: new SupportedImageFormats(),
      });

      structuralMetadataLoader.load();

      return structuralMetadataLoader.promise
        .then(function (structuralMetadataLoader) {
          fail();
        })
        .catch(function (runtimeError) {
          expect(runtimeError.message).toBe(
            "Failed to load structural metadata\nFailed to load texture\nFailed to load image: map.png\n404 Not Found"
          );
        });
    });

    it("rejects promise if external schema fails to load", function () {
      spyOn(Resource.prototype, "fetchArrayBuffer").and.returnValue(
        Promise.resolve(buffer)
      );

      spyOn(Resource.prototype, "fetchImage").and.returnValue(
        Promise.resolve(image)
      );

      spyOn(Resource.prototype, "fetchJson").and.callFake(function () {
        const error = new Error("404 Not Found");
        return Promise.reject(error);
      });

      const structuralMetadataLoader = new GltfStructuralMetadataLoader({
        gltf: gltfSchemaUri,
        extension: extensionSchemaUri,
        gltfResource: gltfResource,
        baseResource: gltfResource,
        supportedImageFormats: new SupportedImageFormats(),
      });

      structuralMetadataLoader.load();

      return structuralMetadataLoader.promise
        .then(function () {
          fail();
        })
        .catch(function (runtimeError) {
          expect(runtimeError.message).toBe(
            "Failed to load structural metadata\nFailed to load schema: https://example.com/schema.json\n404 Not Found"
          );
        });
    });

    it("loads structural metadata", function () {
      spyOn(Resource.prototype, "fetchArrayBuffer").and.returnValue(
        Promise.resolve(buffer)
      );

      spyOn(Resource.prototype, "fetchImage").and.returnValue(
        Promise.resolve(image)
      );

      const structuralMetadataLoader = new GltfStructuralMetadataLoader({
        gltf: gltf,
        extension: extension,
        gltfResource: gltfResource,
        baseResource: gltfResource,
        supportedImageFormats: new SupportedImageFormats(),
      });

      structuralMetadataLoader.load();

      return waitForLoaderProcess(structuralMetadataLoader, scene).then(
        function (structuralMetadataLoader) {
          loaderProcess(structuralMetadataLoader, scene); // Check that calling process after load doesn't break anything
          const structuralMetadata =
            structuralMetadataLoader.structuralMetadata;
          const buildingsTable = structuralMetadata.getPropertyTable(0);
          expect(buildingsTable.id).toBe(0);
          const treesTable = structuralMetadata.getPropertyTable(1);
          expect(treesTable.id).toBe(1);
          const mapTexture = structuralMetadata.getPropertyTexture(0);
          expect(mapTexture.id).toBe(0);
          const orthoTexture = structuralMetadata.getPropertyTexture(1);
          expect(orthoTexture.id).toBe(1);

          expect(buildingsTable.getProperty(0, "name")).toBe("House");
          expect(buildingsTable.getProperty(1, "name")).toBe("Hospital");
          expect(treesTable.getProperty(0, "species")).toEqual([
            "Sparrow",
            "Squirrel",
          ]);
          expect(treesTable.getProperty(1, "species")).toEqual(["Crow"]);

          const colorProperty = mapTexture.getProperty("color");
          const intensityProperty = mapTexture.getProperty("intensity");
          const vegetationProperty = orthoTexture.getProperty("vegetation");

          expect(colorProperty.textureReader.texture.width).toBe(1);
          expect(colorProperty.textureReader.texture.height).toBe(1);
          expect(colorProperty.textureReader.texture).toBe(
            intensityProperty.textureReader.texture
          );

          expect(vegetationProperty.textureReader.texture.width).toBe(1);
          expect(vegetationProperty.textureReader.texture.height).toBe(1);
          expect(vegetationProperty.textureReader.texture).not.toBe(
            colorProperty.textureReader.texture
          );

          expect(
            Object.keys(structuralMetadata.schema.classes).sort()
          ).toEqual(["building", "map", "ortho", "tree"]);
        }
      );
    });

    it("loads structural metadata with external schema", function () {
      spyOn(Resource.prototype, "fetchArrayBuffer").and.returnValue(
        Promise.resolve(buffer)
      );

      spyOn(Resource.prototype, "fetchImage").and.returnValue(
        Promise.resolve(image)
      );

      spyOn(Resource.prototype, "fetchJson").and.returnValue(
        Promise.resolve(schemaJson)
      );

      const structuralMetadataLoader = new GltfStructuralMetadataLoader({
        gltf: gltfSchemaUri,
        extension: extensionSchemaUri,
        gltfResource: gltfResource,
        baseResource: gltfResource,
        supportedImageFormats: new SupportedImageFormats(),
      });

      structuralMetadataLoader.load();

      return waitForLoaderProcess(structuralMetadataLoader, scene).then(
        function (structuralMetadataLoader) {
          const structuralMetadata =
            structuralMetadataLoader.structuralMetadata;
          expect(
            Object.keys(structuralMetadata.schema.classes).sort()
          ).toEqual(["building", "map", "ortho", "tree"]);
        }
      );
    });

    it("destroys structural metadata", function () {
      spyOn(Resource.prototype, "fetchArrayBuffer").and.returnValue(
        Promise.resolve(buffer)
      );

      spyOn(Resource.prototype, "fetchImage").and.returnValue(
        Promise.resolve(image)
      );

      spyOn(Resource.prototype, "fetchJson").and.returnValue(
        Promise.resolve(schemaJson)
      );

      const destroyBufferView = spyOn(
        GltfBufferViewLoader.prototype,
        "destroy"
      ).and.callThrough();

      const destroyTexture = spyOn(
        GltfTextureLoader.prototype,
        "destroy"
      ).and.callThrough();

      const destroySchema = spyOn(
        MetadataSchemaLoader.prototype,
        "destroy"
      ).and.callThrough();

      const structuralMetadataLoader = new GltfStructuralMetadataLoader({
        gltf: gltf,
        extension: extension,
        gltfResource: gltfResource,
        baseResource: gltfResource,
        supportedImageFormats: new SupportedImageFormats(),
      });

      structuralMetadataLoader.load();

      return waitForLoaderProcess(structuralMetadataLoader, scene).then(
        function (structuralMetadataLoader) {
          expect(structuralMetadataLoader.structuralMetadata).toBeDefined();
          expect(structuralMetadataLoader.isDestroyed()).toBe(false);

          structuralMetadataLoader.destroy();

          expect(structuralMetadataLoader.structuralMetadata).not.toBeDefined();
          expect(structuralMetadataLoader.isDestroyed()).toBe(true);

          expect(destroyBufferView.calls.count()).toBe(6);
          expect(destroyTexture.calls.count()).toBe(2);
          expect(destroySchema.calls.count()).toBe(1);
        }
      );
    });

    function resolveAfterDestroy(rejectPromise) {
      spyOn(Resource.prototype, "fetchArrayBuffer").and.returnValue(
        Promise.resolve(buffer)
      );
      spyOn(Resource.prototype, "fetchImage").and.returnValue(
        Promise.resolve(image)
      );

      let promise = new Promise(function (resolve, reject) {
        if (rejectPromise) {
          const error = new Error("404 Not Found");
          reject(error);
          return;
        }
        resolve(schemaJson);
      });
      if (rejectPromise) {
        // handle the error so Jasmine doesn't fail the test
        promise = promise.catch(function () {});
      }
      spyOn(Resource.prototype, "fetchJson").and.returnValue(promise);

      const destroyBufferView = spyOn(
        GltfBufferViewLoader.prototype,
        "destroy"
      ).and.callThrough();
      const destroyTexture = spyOn(
        GltfTextureLoader.prototype,
        "destroy"
      ).and.callThrough();
      const destroySchema = spyOn(
        MetadataSchemaLoader.prototype,
        "destroy"
      ).and.callThrough();

      // Load a copy of structural metadata into the cache so that the resource
      // promises resolve even if the structural metadata loader is destroyed
      const structuralMetadataLoaderCopy = new GltfStructuralMetadataLoader({
        gltf: gltf,
        extension: extension,
        gltfResource: gltfResource,
        baseResource: gltfResource,
        supportedImageFormats: new SupportedImageFormats(),
      });
      // Also load a copy of the schema into the cache
      const schemaResource = gltfResource.getDerivedResource({
        url: "schema.json",
      });
      const schemaCopy = ResourceCache.loadSchema({
        resource: schemaResource,
      });

      if (rejectPromise) {
        // handle the error so Jasmine doesn't fail the test
        schemaCopy.promise.catch(function () {});
      }

      structuralMetadataLoaderCopy.load();

      return waitForLoaderProcess(structuralMetadataLoaderCopy, scene).then(
        function (structuralMetadataLoaderCopy) {
          // Ignore structuralMetadataLoaderCopy destroying its buffer views
          destroyBufferView.calls.reset();

          const structuralMetadataLoader = new GltfStructuralMetadataLoader({
            gltf: gltfSchemaUri,
            extension: extensionSchemaUri,
            gltfResource: gltfResource,
            baseResource: gltfResource,
            supportedImageFormats: new SupportedImageFormats(),
          });
          expect(structuralMetadataLoader.structuralMetadata).not.toBeDefined();
          structuralMetadataLoader.load();
          structuralMetadataLoader.destroy();

          expect(structuralMetadataLoader.structuralMetadata).not.toBeDefined();
          expect(structuralMetadataLoader.isDestroyed()).toBe(true);

          structuralMetadataLoaderCopy.destroy();

          expect(destroyBufferView.calls.count()).toBe(6);
          expect(destroyTexture.calls.count()).toBe(2);
          expect(destroySchema.calls.count()).toBe(1);

          ResourceCache.unload(schemaCopy);
        }
      );
    }

    it("handles resolving resources after destroy", function () {
      return resolveAfterDestroy(false);
    });

    it("handles rejecting resources after destroy", function () {
      return resolveAfterDestroy(true);
    });
  },
  "WebGL"
);
<|endoftext|>


<commit_before>(function() {
  var element = $('.Buildings');

  var scene = Snap('#Page-1');
  var background = Snap('#background');

  var buildings = new Snap.set();
  var buildingWindows = new Snap.set();

  var windowsShowMatrix = 's1 1';
  var windowsHideMatrix = 's0 0';

  var buildingsShowMatrix = 's1 1 0 563';
  var buildingsHideMatrix = 's1 0 0 563';

  var windowAnimationTime = 0.170;

  var timeBeteweenBuldings = 0.175;
  var timeBeforeBackgorund = 1.5;
  var backgroundAnimationTime = 1;

  var buildingAnimationMaxTime = 400;
  var buildingAnimationMinTime = 300;

  var windowsEasing = mina.backout;
  var buildingEasing = mina.backout;
  var backgroundEasing = mina.backout;

  var buildingsNames = [{
    name: '#building1',
    windows: '#windows1'
  }, {
    name: '#building2',
    windows: '#windows2'
  }, {
    name: '#building3',
    windows: '#windows3'
  }, {
    name: '#building4',
    windows: '#windows4'
  }, {
    name: '#building5',
    windows: '#windows5'
  }, {
    name: '#building6',
    windows: '#windows6'
  }, {
    name: '#building7',
    windows: '#windows7'
  }];

  init();

  function init() {
    initBuildings();
    hideAllElements();
    startAnimation();
  }

  function initBuildings() {
    shuffle(buildingsNames);
    buildingsNames.forEach(findBuildings);
  }

  function findBuildings(building, index) {
    var object = scene.select(building.name);
    populateBuildings(object, index);
  }

  function populateBuildings(building, index) {
    buildings.push(building);
    populateWindows(building, index);
  }

  function populateWindows(building, index) {
    var buildingWindow = building.select(buildingsNames[index].windows);
    buildingWindow = buildingWindow.selectAll('g');
    buildingWindow.items.reverse();
    buildingWindows.push(buildingWindow);
  }

  function hideAllElements() {
    buildings.forEach(hideBuilding);
    buildingWindows.forEach(hideWindows);
    hideBackground();
  }

  function hideBuilding(building) {
    TweenLite.set($(building.node), { scaleY: 0, transformOrigin: '100% 100%' });
  }

  function hideWindows(windowsGroup) {
    windowsGroup.forEach(hideWindow);
  }

  function hideWindow(buildingWindow) {
      TweenLite.set($(buildingWindow.node), { scaleY: 0, scaleX: 0, transformOrigin: '50% 50%' });
  }

  function hideBackground() {
    TweenLite.set($(background.node), { scaleY: 0, transformOrigin: '100% 100%' });
  }

  function startAnimation() {
    element.css('visibility', 'visible');
    buildings.forEach(showBuildings);
    setTimeout(showBackground, timeBeforeBackgorund);
  }

  function showBuildings(building, index) {
    TweenLite.to(
      $(building.node),
      getRandomInt(buildingAnimationMinTime, buildingAnimationMaxTime) / 1000,
      {
        delay: buildingDelay(index),
        scaleY: 1,
        onComplete: windowsAnimation(index),
        ease: Back.easeOut.config(1.7)
      }
    );
  }

  function buildingDelay(index) {
    return timeBeteweenBuldings + timeBeteweenBuldings * index;
  }

  function windowsAnimation(windowsIndex) {
    var windowGroup = buildingWindows.items[windowsIndex];
    return function showWindows() {
      animateWindow(windowGroup[0], 0, windowGroup);
    };
  }

  function animateWindow(buildingWindow, index, windowGroup) {
    TweenLite.to(
      $(buildingWindow.node),
      windowAnimationTime,
      {
        scaleX: 1,
        scaleY: 1,
        onComplete: nextWindowAnimation(index, windowGroup),
        ease: Back.easeOut.config(1.7)
      }
    );
  }

  function nextWindowAnimation(index, windowGroup) {
    var nextWindow = getNextWindow(index, windowGroup);

    if (!nextWindow) return;

    return function() {
      animateWindow(nextWindow, index + 1, windowGroup);
    };
  }

  function getNextWindow(currentIndex, windowGroup) {
    if (currentIndex + 1 >= windowGroup.length) return;

    return windowGroup[currentIndex + 1];
  }

  function showBackground()  {
    TweenLite.to(
      $(background.node),
      backgroundAnimationTime,
      {
        delay: timeBeforeBackgorund,
        scaleY: 1,
        ease: Back.easeOut.config(1.7)
      }
    );
  }

  function getRandomInt(min, max) {
    return Math.floor(Math.random() * (max - min)) + min;
  }

  function shuffle(o) {
    for (var j, x, i = o.length; i; j = Math.floor(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
    return 0;
  }
})();
<commit_msg>Start the animation after page load<commit_after>(function() {
  var element = $('.Buildings');

  var scene = Snap('#Page-1');
  var background = Snap('#background');

  var buildings = new Snap.set();
  var buildingWindows = new Snap.set();

  var windowsShowMatrix = 's1 1';
  var windowsHideMatrix = 's0 0';

  var buildingsShowMatrix = 's1 1 0 563';
  var buildingsHideMatrix = 's1 0 0 563';

  var windowAnimationTime = 0.170;

  var timeBeteweenBuldings = 0.175;
  var timeBeforeBackgorund = 1.5;
  var backgroundAnimationTime = 1;

  var buildingAnimationMaxTime = 400;
  var buildingAnimationMinTime = 300;

  var windowsEasing = mina.backout;
  var buildingEasing = mina.backout;
  var backgroundEasing = mina.backout;

  var buildingsNames = [{
    name: '#building1',
    windows: '#windows1'
  }, {
    name: '#building2',
    windows: '#windows2'
  }, {
    name: '#building3',
    windows: '#windows3'
  }, {
    name: '#building4',
    windows: '#windows4'
  }, {
    name: '#building5',
    windows: '#windows5'
  }, {
    name: '#building6',
    windows: '#windows6'
  }, {
    name: '#building7',
    windows: '#windows7'
  }];

  $(document).ready(init);
  $(window).load(startAnimation);

  function init() {
    initBuildings();
    hideAllElements();
  }

  function initBuildings() {
    shuffle(buildingsNames);
    buildingsNames.forEach(findBuildings);
  }

  function findBuildings(building, index) {
    var object = scene.select(building.name);
    populateBuildings(object, index);
  }

  function populateBuildings(building, index) {
    buildings.push(building);
    populateWindows(building, index);
  }

  function populateWindows(building, index) {
    var buildingWindow = building.select(buildingsNames[index].windows);
    buildingWindow = buildingWindow.selectAll('g');
    buildingWindow.items.reverse();
    buildingWindows.push(buildingWindow);
  }

  function hideAllElements() {
    buildings.forEach(hideBuilding);
    buildingWindows.forEach(hideWindows);
    hideBackground();
  }

  function hideBuilding(building) {
    TweenLite.set($(building.node), { scaleY: 0, transformOrigin: '100% 100%' });
  }

  function hideWindows(windowsGroup) {
    windowsGroup.forEach(hideWindow);
  }

  function hideWindow(buildingWindow) {
      TweenLite.set($(buildingWindow.node), { scaleY: 0, scaleX: 0, transformOrigin: '50% 50%' });
  }

  function hideBackground() {
    TweenLite.set($(background.node), { scaleY: 0, transformOrigin: '100% 100%' });
  }

  function startAnimation() {
    element.css('visibility', 'visible');
    buildings.forEach(showBuildings);
    setTimeout(showBackground, timeBeforeBackgorund);
  }

  function showBuildings(building, index) {
    TweenLite.to(
      $(building.node),
      getRandomInt(buildingAnimationMinTime, buildingAnimationMaxTime) / 1000,
      {
        delay: buildingDelay(index),
        scaleY: 1,
        onComplete: windowsAnimation(index),
        ease: Back.easeOut.config(1.7)
      }
    );
  }

  function buildingDelay(index) {
    return timeBeteweenBuldings + timeBeteweenBuldings * index;
  }

  function windowsAnimation(windowsIndex) {
    var windowGroup = buildingWindows.items[windowsIndex];
    return function showWindows() {
      animateWindow(windowGroup[0], 0, windowGroup);
    };
  }

  function animateWindow(buildingWindow, index, windowGroup) {
    TweenLite.to(
      $(buildingWindow.node),
      windowAnimationTime,
      {
        scaleX: 1,
        scaleY: 1,
        onComplete: nextWindowAnimation(index, windowGroup),
        ease: Back.easeOut.config(1.7)
      }
    );
  }

  function nextWindowAnimation(index, windowGroup) {
    var nextWindow = getNextWindow(index, windowGroup);

    if (!nextWindow) return;

    return function() {
      animateWindow(nextWindow, index + 1, windowGroup);
    };
  }

  function getNextWindow(currentIndex, windowGroup) {
    if (currentIndex + 1 >= windowGroup.length) return;

    return windowGroup[currentIndex + 1];
  }

  function showBackground()  {
    TweenLite.to(
      $(background.node),
      backgroundAnimationTime,
      {
        delay: timeBeforeBackgorund,
        scaleY: 1,
        ease: Back.easeOut.config(1.7)
      }
    );
  }

  function getRandomInt(min, max) {
    return Math.floor(Math.random() * (max - min)) + min;
  }

  function shuffle(o) {
    for (var j, x, i = o.length; i; j = Math.floor(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
    return 0;
  }
})();
<|endoftext|>


<commit_before>/**
 * Created by admin on 2.12.2016 г..
 */
/* globals module require */

'use strict';

const passport = require('passport');

module.exports = (data) => {
    return {

        getCreateEventPage(req, res) {
            let categoryName = req.params.categoryName;

            if(!categoryName) {
                categoryName = 'ski';
            }
            console.log(categoryName);

            if (req.isAuthenticated()) {
                res.render('./events/add-event-page', {
                    user: req.user,
                    category: categoryName
                });

                return;
            }

            res.render('auth-not-authorised-page');
        },

        getEventsPage(req, res){
            let categoryName=req.params.category;
            let eventId=req.params.id;

            console.log('categoryName ' + categoryName);
            console.log('eventId ' + eventId);

            if(!categoryName && !eventId){
                data.getAllEvents(req, res)
                    .then((events) => {
                        // TODO remove before production :)
                        console.log(events);
                        res.render('./events/all-categories-page', {
                            user: req.user,
                            events: events
                        });
                    })
                    .catch((error) => {
                        throw error;
                    });

            } else if (categoryName && !eventId) {
                data.getEventByCategoryName(categoryName)
                    .then((events) => {
                        // TODO remove before production :)
                        //console.log(events);
                        res.render('./events/single-category-page', {
                            user: req.user,
                            category: categoryName,
                            events: events
                        });
                    })
                    .catch((error) => {
                        throw error;
                    });

            } else if (categoryName && eventId) {
                data.getEventByCategoryAndId(categoryName, eventId)
                    .then((event) => {
                        // TODO remove before production :)
                        //console.log(event);
                        res.render('./events/single-event-page', {
                            user: req.user,
                            category: categoryName,
                            event: event
                        });
                    })
                    .catch((error) => {
                        throw error;
                    });
            }
        },

        createEvent(req, res) {
            let body = req.body,
                nowDt = new Date(),
                eventIsHidden = false,
                categoryName = req.params.categoryName,
                pictureUrl = req.body.eventPicture;

            var _category = 'ski';
            if(!(categoryName == undefined) && !(categoryName==='')){
                _category = categoryName;
            }

            var _picture = { src: '/res/images/default-picture.png' };
            if(!(pictureUrl == undefined) || !(pictureUrl==='')){
                _picture.src = pictureUrl;
            }

            // TODO remove before production :)
            console.log('picture-'+_picture);
            console.log('category-'+_category);

            data.eventCreateAndSave(body.title, categoryName, _picture, req.user, body.body, nowDt, eventIsHidden)
                .then((dbEvent) => {
                    res.render('error-page', {
                        user: req.user,
                        error: dbEvent
                    });
                })
                .catch((error) => {
                    res.render('error-page', {
                        user: req.user,
                        error: error
                    });

                });

            // if (req.isAuthenticated()) {
            //     data.eventCreateAndSave(body.title, categoryName, _picture, req.user, body.body, nowDt, eventIsHidden)
            //         .then(() => {
            //             //res.status(201).redirect('http://localhost:3001/categories/201', { user: req.user }).end();
            //             //res.status(201).send('The request has been fulfilled and resulted in a new resource being created. OK!');
            //             // res.redirect('/categories/201', {
            //             //     user: req.user
            //             // });
            //             //console.log('thenthen');
            //             res.render('all-categories-page', {
            //                 user: req.user,
            //                 category: categoryName,
            //                 event: event
            //             });
            //         })
            //         .catch((err) => {
            //             // The request could not be completed due to a conflict with the current state of the resource.
            //             // {"code":11000,"index":0,"errmsg":"E11000 duplicate key error collection...
            //             if(err.code == 11000){
            //                 // TODO delete duplicated key and try again
            //
            //                 var EventModel = data.EventModel;
            //                 // mongoose.connection.db.executeDbCommand({ dropIndexes: EventModel, index: 'a*' },
            //                 // var db = mongoose.connection;
            //                 mongoose.connection.executeDbCommand({ dropIndexes: EventModel, index: 'a*' },
            //                     function(err, result) {
            //                         console.log('index is dropped');
            //                         res.status(201).location('/categories/2011', { user: req.user }).end();
            //                     });
            //
            //                 console.log('index error');
            //                 res.status(409).location('/categories/409', { user: req.user }).end();
            //             }
            //
            //             res.status(400).location('/categories/400', { user: req.user }).end();
            //
            //             //res.status(409).send(err);
            //             //res.redirect('/', { user: req.user });
            //
            //             //return;
            //         });
            // }

            //res.render('auth-not-authorised-page');

        }
    }
};
<commit_msg>add new event changes<commit_after>/**
 * Created by admin on 2.12.2016 г..
 */
/* globals module require */

'use strict';

const passport = require('passport');

module.exports = (data) => {
    return {

        getCreateEventPage(req, res) {
            let categoryName = req.params.categoryName;

            if(!categoryName) {
                categoryName = 'ski';
            }
            console.log(categoryName);

            if (req.isAuthenticated()) {
                res.render('./events/add-event-page', {
                    user: req.user,
                    category: categoryName
                });

                return;
            }

            res.render('auth-not-authorised-page');
        },

        getEventsPage(req, res){
            let categoryName=req.params.category;
            let eventId=req.params.id;

            console.log('categoryName ' + categoryName);
            console.log('eventId ' + eventId);

            if(!categoryName && !eventId){
                data.getAllEvents(req, res)
                    .then((events) => {
                        // TODO remove before production :)
                        console.log(events);
                        res.render('./events/all-categories-page', {
                            user: req.user,
                            events: events
                        });
                    })
                    .catch((error) => {
                        throw error;
                    });

            } else if (categoryName && !eventId) {
                data.getEventByCategoryName(categoryName)
                    .then((events) => {
                        // TODO remove before production :)
                        //console.log(events);
                        res.render('./events/single-category-page', {
                            user: req.user,
                            category: categoryName,
                            events: events
                        });
                    })
                    .catch((error) => {
                        throw error;
                    });

            } else if (categoryName && eventId) {
                data.getEventByCategoryAndId(categoryName, eventId)
                    .then((event) => {
                        // TODO remove before production :)
                        //console.log(event);
                        res.render('./events/single-event-page', {
                            user: req.user,
                            category: categoryName,
                            event: event
                        });
                    })
                    .catch((error) => {
                        throw error;
                    });
            }
        },

        createEvent(req, res) {
            let body = req.body,
                nowDt = new Date(),
                eventIsHidden = false,
                categoryName = req.params.categoryName,
                pictureUrl = req.body.eventPicture;

            data.createAndSaveEvent(body.title, categoryName, pictureUrl, req.user, body.body, nowDt, eventIsHidden)
                .then((dbEvent) => {
                    res.render('error-page', {
                        user: req.user,
                        error: dbEvent
                    });
                })
                .catch((error) => {
                    res.render('error-page', {
                        user: req.user,
                        error: error
                    });

                });

            // if (req.isAuthenticated()) {
            //     data.eventCreateAndSave(body.title, categoryName, _picture, req.user, body.body, nowDt, eventIsHidden)
            //         .then(() => {
            //             //res.status(201).redirect('http://localhost:3001/categories/201', { user: req.user }).end();
            //             //res.status(201).send('The request has been fulfilled and resulted in a new resource being created. OK!');
            //             // res.redirect('/categories/201', {
            //             //     user: req.user
            //             // });
            //             //console.log('thenthen');
            //             res.render('all-categories-page', {
            //                 user: req.user,
            //                 category: categoryName,
            //                 event: event
            //             });
            //         })
            //         .catch((err) => {
            //             // The request could not be completed due to a conflict with the current state of the resource.
            //             // {"code":11000,"index":0,"errmsg":"E11000 duplicate key error collection...
            //             if(err.code == 11000){
            //                 // TODO delete duplicated key and try again
            //
            //                 var EventModel = data.EventModel;
            //                 // mongoose.connection.db.executeDbCommand({ dropIndexes: EventModel, index: 'a*' },
            //                 // var db = mongoose.connection;
            //                 mongoose.connection.executeDbCommand({ dropIndexes: EventModel, index: 'a*' },
            //                     function(err, result) {
            //                         console.log('index is dropped');
            //                         res.status(201).location('/categories/2011', { user: req.user }).end();
            //                     });
            //
            //                 console.log('index error');
            //                 res.status(409).location('/categories/409', { user: req.user }).end();
            //             }
            //
            //             res.status(400).location('/categories/400', { user: req.user }).end();
            //
            //             //res.status(409).send(err);
            //             //res.redirect('/', { user: req.user });
            //
            //             //return;
            //         });
            // }

            //res.render('auth-not-authorised-page');

        }
    }
};
<|endoftext|>


<commit_before>const parseCommandLine = require('../cli/parse-command-line')
const autobind = require('autobind-decorator')
const {
  clone,
  merge,
  when,
  equals,
  always,
  join,
  split,
  trim,
  pipe,
  replace,
  find,
  append,
  forEach,
  isNil,
  map
} = require('ramda')
const { findByProp, startsWith, isNilOrEmpty } = require('ramdasauce')
const { isBlank } = require('../utils/string-utils')
const { subdirectories, isDirectory } = require('../utils/filesystem-utils')
const RunContext = require('./run-context')
const loadPluginFromDirectory = require('../loaders/toml-plugin-loader')

// core extensions
const addTemplateExtension = require('../core-extensions/template-extension')
const addPrintExtension = require('../core-extensions/print-extension')
const addFilesystemExtension = require('../core-extensions/filesystem-extension')
const addSystemExtension = require('../core-extensions/system-extension')
const addPromptExtension = require('../core-extensions/prompt-extension')
const addHttpExtension = require('../core-extensions/http-extension')
const addStringsExtension = require('../core-extensions/strings-extension')

const COMMAND_DELIMITER = ' '

/**
 * Strips the command out of the args returns an array of the rest.
 *
 * @param {string} args        The full arguments including command.
 * @param {string} commandName The name of the command to strip.
 */
const extractSubArguments = (args, commandName) =>
  pipe(
    replace(commandName, ''),
    trim,
    split(COMMAND_DELIMITER),
    when(equals(['']), always([]))
  )(args)

/**
* Runs a command.
*
* @param  {{}} options Additional options use to execute a command.
*                      If nothing is passed, it will read from the command line.
* @return {RunContext} The RunContext object indicating what happened.
*/
async function run (options) {
  // prepare the run context
  const context = new RunContext()

  // attach the runtime
  context.runtime = this

  // prepare the context parameters
  if (isNilOrEmpty(options)) {
    // grab the params from the command line
    const { first, rest, options } = parseCommandLine(process.argv)
    context.parameters.pluginName = first
    context.parameters.rawCommand = rest
    context.parameters.options = options
  } else {
    // grab the options that were passed in
    context.parameters.pluginName = options.pluginName
    context.parameters.rawCommand = options.rawCommand
    context.parameters.options = options.options
  }

  // bring them back out for convenience
  const { pluginName, rawCommand } = context.parameters

  // try finding the command in the default plugin first
  const defaultPlugin = this.findCommand(this.defaultPlugin, rawCommand) && this.defaultPlugin

  // find the plugin
  const plugin = defaultPlugin || this.findPlugin(pluginName)

  if (!plugin) {
    return context
  }
  context.plugin = plugin

  // setup the config
  context.config = clone(this.defaults)
  context.config[plugin.name] = merge(plugin.defaults, this.defaults[plugin.pluginName] || {})

  // find the command
  const command = this.findCommand(plugin, rawCommand)

  if (command) {
    context.command = command
    // setup the rest of the parameters
    const subArgs = extractSubArguments(rawCommand, trim(command.name))
    context.parameters.array = subArgs
    context.parameters.first = subArgs[0]
    context.parameters.second = subArgs[1]
    context.parameters.third = subArgs[2]
    context.parameters.string = join(COMMAND_DELIMITER, subArgs)

    // kick it off
    if (command.run) {
      // attach extensions
      forEach(
        extension => {
          context[extension.name] = extension.setup(plugin, command, context)
        },
        this.extensions
      )

      try {
        context.result = await command.run(context)
      } catch (e) {
        console.dir(e)
        context.error = e
      }
    }
  }

  return context
}

/**
 * Loads plugins an action through the gauntlet.
 */
class Runtime {

  /**
   * Create and initialize an empty Runtime.
   */
  constructor (brand) {
    this.brand = brand
    this.run = run // awkward because node.js doesn't support async-based class functions yet.
    this.plugins = []
    this.extensions = []
    this.defaults = {}
    this.defaultPlugin = null
    this.events = {}

    this.addCoreExtensions()
  }

  /**
   * Adds the core extensions.  These provide the basic features
   * available in gluegun, but follow the exact same method
   * for extending the core as 3rd party extensions do.
   */
  addCoreExtensions () {
    this.addExtension('strings', addStringsExtension)
    this.addExtension('print', addPrintExtension)
    this.addExtension('template', addTemplateExtension)
    this.addExtension('filesystem', addFilesystemExtension)
    this.addExtension('system', addSystemExtension)
    this.addExtension('http', addHttpExtension)
    this.addExtension('prompt', addPromptExtension)
  }

  /**
   * Adds an extension so it is available when commands run. They live
   * as the given name on the context object passed to commands. The
   * 2nd parameter is a function that, when called, creates that object.
   *
   * @param {string} name   The context property name.
   * @param {object} setup  The setup function.
   */
  addExtension (name, setup) {
    this.extensions.push({ name, setup })
    return this
  }

  /**
   * Loads a plugin from a directory.
   *
   * @param  {string} directory The directory to load from.
   * @return {Plugin}           A plugin.
   */
  load (directory) {
    const { extensionNameToken, commandNameToken, commandDescriptionToken } = this

    const plugin = loadPluginFromDirectory(directory, {
      extensionNameToken,
      commandNameToken,
      commandDescriptionToken
    })

    this.plugins = append(plugin, this.plugins)
    return plugin
  }

  /**
   * Loads a plugin from a directory and sets it as the default.
   *
   * @param  {string} directory The directory to load from.
   * @return {Plugin}           A plugin.
   */
  loadDefault (directory) {
    const plugin = this.load(directory)
    this.defaultPlugin = plugin
    return plugin
  }

  /**
   * Loads a bunch of plugins from the immediate sub-directories of a directory.
   *
   * @param {string} directory The directory to grab from.
   * @return {Plugin[]}        A bunch of plugins
   */
  loadAll (directory) {
    if (isBlank(directory) || !isDirectory(directory)) return []
    return pipe(
      subdirectories,
      map(this.load)
    )(directory)
  }

  /**
   * The list of commands registered.
   *
   * @return {[]} A list of [{plugin, command}]
   */
  listCommands () {
    const commands = []
    const eachPlugin = plugin => {
      const eachCommand = command => {
        commands.push({ plugin, command })
      }
      forEach(eachCommand, plugin.commands)
    }
    forEach(eachPlugin, this.plugins)

    return commands
  }

  /**
   * Find the plugin for this name.
   *
   * @param {string} name The name to search through.
   * @returns {*}         A Plugin otherwise null.
   */
  findPlugin (name) {
    return findByProp('name', name || '', this.plugins)
  }

  /**
   * Find the command for this pluginName & command.
   *
   * @param {Plugin} plugin      The plugin in which the command lives.
   * @param {string} rawCommand  The command arguments to parse.
   * @returns {*}                A Command otherwise null.
   */
  findCommand (plugin, rawCommand) {
    if (isNil(plugin) || isBlank(rawCommand)) return null
    if (isNilOrEmpty(plugin.commands)) return null

    return find(
      (command) => startsWith(command.name, rawCommand)
      , plugin.commands
      )
  }

}

module.exports = autobind(Runtime)
<commit_msg>Fix for missing descriptions and defaults.<commit_after>const parseCommandLine = require('../cli/parse-command-line')
const autobind = require('autobind-decorator')
const {
  clone,
  merge,
  when,
  equals,
  always,
  join,
  split,
  trim,
  pipe,
  replace,
  find,
  append,
  forEach,
  isNil,
  map
} = require('ramda')
const { findByProp, startsWith, isNilOrEmpty } = require('ramdasauce')
const { isBlank } = require('../utils/string-utils')
const { subdirectories, isDirectory } = require('../utils/filesystem-utils')
const RunContext = require('./run-context')
const loadPluginFromDirectory = require('../loaders/toml-plugin-loader')

// core extensions
const addTemplateExtension = require('../core-extensions/template-extension')
const addPrintExtension = require('../core-extensions/print-extension')
const addFilesystemExtension = require('../core-extensions/filesystem-extension')
const addSystemExtension = require('../core-extensions/system-extension')
const addPromptExtension = require('../core-extensions/prompt-extension')
const addHttpExtension = require('../core-extensions/http-extension')
const addStringsExtension = require('../core-extensions/strings-extension')

const COMMAND_DELIMITER = ' '

/**
 * Strips the command out of the args returns an array of the rest.
 *
 * @param {string} args        The full arguments including command.
 * @param {string} commandName The name of the command to strip.
 */
const extractSubArguments = (args, commandName) =>
  pipe(
    replace(commandName, ''),
    trim,
    split(COMMAND_DELIMITER),
    when(equals(['']), always([]))
  )(args)

/**
* Runs a command.
*
* @param  {{}} options Additional options use to execute a command.
*                      If nothing is passed, it will read from the command line.
* @return {RunContext} The RunContext object indicating what happened.
*/
async function run (options) {
  // prepare the run context
  const context = new RunContext()

  // attach the runtime
  context.runtime = this

  // prepare the context parameters
  if (isNilOrEmpty(options)) {
    // grab the params from the command line
    const { first, rest, options } = parseCommandLine(process.argv)
    context.parameters.pluginName = first
    context.parameters.rawCommand = rest
    context.parameters.options = options
  } else {
    // grab the options that were passed in
    context.parameters.pluginName = options.pluginName
    context.parameters.rawCommand = options.rawCommand
    context.parameters.options = options.options
  }

  // bring them back out for convenience
  const { pluginName, rawCommand } = context.parameters

  // try finding the command in the default plugin first
  const defaultPlugin = this.findCommand(this.defaultPlugin, rawCommand) && this.defaultPlugin

  // find the plugin
  const plugin = defaultPlugin || this.findPlugin(pluginName)

  if (!plugin) {
    return context
  }
  context.plugin = plugin

  // setup the config
  context.config = clone(this.defaults)
  context.config[plugin.name] = merge(plugin.defaults, this.defaults[plugin.pluginName] || {})

  // find the command
  const command = this.findCommand(plugin, rawCommand)

  if (command) {
    context.command = command
    // setup the rest of the parameters
    const subArgs = extractSubArguments(rawCommand, trim(command.name))
    context.parameters.array = subArgs
    context.parameters.first = subArgs[0]
    context.parameters.second = subArgs[1]
    context.parameters.third = subArgs[2]
    context.parameters.string = join(COMMAND_DELIMITER, subArgs)

    // kick it off
    if (command.run) {
      // attach extensions
      forEach(
        extension => {
          context[extension.name] = extension.setup(plugin, command, context)
        },
        this.extensions
      )

      try {
        context.result = await command.run(context)
      } catch (e) {
        console.dir(e)
        context.error = e
      }
    }
  }

  return context
}

/**
 * Loads plugins an action through the gauntlet.
 */
class Runtime {

  /**
   * Create and initialize an empty Runtime.
   */
  constructor (brand) {
    this.brand = brand
    this.run = run // awkward because node.js doesn't support async-based class functions yet.
    this.plugins = []
    this.extensions = []
    this.defaults = {}
    this.defaultPlugin = null
    this.events = {}

    this.addCoreExtensions()
  }

  /**
   * Adds the core extensions.  These provide the basic features
   * available in gluegun, but follow the exact same method
   * for extending the core as 3rd party extensions do.
   */
  addCoreExtensions () {
    this.addExtension('strings', addStringsExtension)
    this.addExtension('print', addPrintExtension)
    this.addExtension('template', addTemplateExtension)
    this.addExtension('filesystem', addFilesystemExtension)
    this.addExtension('system', addSystemExtension)
    this.addExtension('http', addHttpExtension)
    this.addExtension('prompt', addPromptExtension)
  }

  /**
   * Adds an extension so it is available when commands run. They live
   * as the given name on the context object passed to commands. The
   * 2nd parameter is a function that, when called, creates that object.
   *
   * @param {string} name   The context property name.
   * @param {object} setup  The setup function.
   */
  addExtension (name, setup) {
    this.extensions.push({ name, setup })
    return this
  }

  /**
   * Loads a plugin from a directory.
   *
   * @param  {string} directory The directory to load from.
   * @return {Plugin}           A plugin.
   */
  load (directory) {
    const { brand, extensionNameToken, commandNameToken, commandDescriptionToken } = this

    const plugin = loadPluginFromDirectory(directory, {
      extensionNameToken,
      commandNameToken,
      commandDescriptionToken,
      brand
    })

    this.plugins = append(plugin, this.plugins)
    return plugin
  }

  /**
   * Loads a plugin from a directory and sets it as the default.
   *
   * @param  {string} directory The directory to load from.
   * @return {Plugin}           A plugin.
   */
  loadDefault (directory) {
    const plugin = this.load(directory)
    this.defaultPlugin = plugin
    return plugin
  }

  /**
   * Loads a bunch of plugins from the immediate sub-directories of a directory.
   *
   * @param {string} directory The directory to grab from.
   * @return {Plugin[]}        A bunch of plugins
   */
  loadAll (directory) {
    if (isBlank(directory) || !isDirectory(directory)) return []
    return pipe(
      subdirectories,
      map(this.load)
    )(directory)
  }

  /**
   * The list of commands registered.
   *
   * @return {[]} A list of [{plugin, command}]
   */
  listCommands () {
    const commands = []
    const eachPlugin = plugin => {
      const eachCommand = command => {
        commands.push({ plugin, command })
      }
      forEach(eachCommand, plugin.commands)
    }
    forEach(eachPlugin, this.plugins)

    return commands
  }

  /**
   * Find the plugin for this name.
   *
   * @param {string} name The name to search through.
   * @returns {*}         A Plugin otherwise null.
   */
  findPlugin (name) {
    return findByProp('name', name || '', this.plugins)
  }

  /**
   * Find the command for this pluginName & command.
   *
   * @param {Plugin} plugin      The plugin in which the command lives.
   * @param {string} rawCommand  The command arguments to parse.
   * @returns {*}                A Command otherwise null.
   */
  findCommand (plugin, rawCommand) {
    if (isNil(plugin) || isBlank(rawCommand)) return null
    if (isNilOrEmpty(plugin.commands)) return null

    return find(
      (command) => startsWith(command.name, rawCommand)
      , plugin.commands
      )
  }

}

module.exports = autobind(Runtime)
<|endoftext|>


<commit_before>var startDate;
var find = require('find');
var fs = require('fs-extra');
var jsonDir = 'reports/cucumber_reports/';
var screenshotDir = 'screenshots/';
exports.config = {
  seleniumAddress: 'http://127.0.0.1:4444/wd/hub',
  framework: 'custom',
  params: {
    testDomainURL: 'http://localhost:8080/solutions/sampleGallery/index.html?f=galleryMain'
  },

  // path relative to the current config file
  frameworkPath: require.resolve('protractor-cucumber-framework'),

  multiCapabilities: [{
    'browserName': 'chrome'
  }],

  // resultJsonOutputFile: 'reports//cucumber_reports//report.json',

  // Spec patterns are relative to this directory.
  specs: [
    'features/sample_application/*.feature'
  ],

  cucumberOpts: {
    require: ['features/step_definitions/servoy_step_definitions.js',
      'env.js',
      'features/step_definitions/hooks.js'],
    tags: false,
    // format: 'pretty',
    format: ['json:reports/cucumber_reports/report.json', 'pretty'],
    profile: false,
    keepAlive: false,
    'no-source': true
  },

  beforeLaunch: () => {
    console.log('beforeLaunch');
    removeJsonReports(); //removes reports from previous tests
    removeScreenshots(); //remove screenshots from previous tests
    startDate = new Date();
  },

  onPrepare: () => {
    console.log('onPrepare');
    browser.driver.executeScript(function () {
      return {
        width: window.screen.availWidth,
        height: window.screen.availHeight
      };
    }).then(function (result) {
      browser.driver.manage().window().setSize(result.width, result.height);
    });
  },

  onComplete: () => {
    console.log('onComplete');
  },

  onCleanUp: () => {
    console.log('onCleanUp');
  },

  afterLaunch: () => {
    console.log('afterLaunch');
  }
};

function removeJsonReports() {
  var files = find.fileSync(/\.json/, jsonDir);
  files.map(function (file) {
    fs.unlinkSync(file);
  });
}

function removeScreenshots() {
  var files = find.fileSync(/\.png/, jsonDir);
  files.map(function (file) {
    fs.unlinkSync(file);
  });
}<commit_msg>Create report folder if it does not exist<commit_after>var startDate;
var find = require('find');
var fs = require('fs-extra');
var jsonDir = 'reports/cucumber_reports/';
var screenshotDir = 'screenshots/';
exports.config = {
  seleniumAddress: 'http://127.0.0.1:4444/wd/hub',
  framework: 'custom',
  params: {
    testDomainURL: 'http://localhost:8080/solutions/sampleGallery/index.html?f=galleryMain'
  },

  // path relative to the current config file
  frameworkPath: require.resolve('protractor-cucumber-framework'),

  multiCapabilities: [{
    'browserName': 'chrome'
  }],

  // resultJsonOutputFile: 'reports//cucumber_reports//report.json',

  // Spec patterns are relative to this directory.
  specs: [
    'features/sample_application/*.feature'
  ],

  cucumberOpts: {
    require: ['features/step_definitions/servoy_step_definitions.js',
      'env.js',
      'features/step_definitions/hooks.js'],
    tags: false,
    // format: 'pretty',
    format: ['json:reports/cucumber_reports/report.json', 'pretty'],
    profile: false,
    keepAlive: false,
    'no-source': true
  },

  beforeLaunch: () => {
    console.log('beforeLaunch');
    createReportFolder();
    removeJsonReports(); //removes reports from previous tests
    removeScreenshots(); //remove screenshots from previous tests
    startDate = new Date();
  },

  onPrepare: () => {
    console.log('onPrepare');
    browser.driver.executeScript(function () {
      return {
        width: window.screen.availWidth,
        height: window.screen.availHeight
      };
    }).then(function (result) {
      browser.driver.manage().window().setSize(result.width, result.height);
    });
  },

  onComplete: () => {
    console.log('onComplete');
  },

  onCleanUp: () => {
    console.log('onCleanUp');
  },

  afterLaunch: () => {
    console.log('afterLaunch');
  }
};

function removeJsonReports() {
  var files = find.fileSync(/\.json/, jsonDir);
  files.map(function (file) {
    fs.unlinkSync(file);
  });
}

function removeScreenshots() {
  var files = find.fileSync(/\.png/, jsonDir);
  files.map(function (file) {
    fs.unlinkSync(file);
  });
}

function createReportFolder() {
  var path = require('path');
  var proc = require('process');
  var fs = require('fs');
  var pathToCreate = proc.cwd() + '/reports/cucumber_reports';
  pathToCreate.split(path.sep).reduce(function(currentPath, folder){
    currentPath += folder + path.sep;
    if(!fs.existsSync(currentPath)) {
      fs.mkdirSync(currentPath);
    }
    return currentPath;
  },'');
}<|endoftext|>


<commit_before>exports.commands = {
	bigtalk: function (target, room, user) {
		if (!this.can('hotpatch')) return false;
		msg = target;
		if (!msg || msg === ' ') return this.sendReply('/bigtalk [target] - Makes u talk like a big boi.  Requires ~.');
		if (user.userid === 'panpawn') {
			if (user.hiding) return room.add('|raw|
<button class="astext" name="parseCommand" value="/user '+user.name+'" target="_blank">' + user.name + ':</button> ' + msg + '
');
			room.add('|raw|
<button class="astext" name="parseCommand" value="/user '+user.name+'" target="_blank">' + user.group + '' + user.name + ':</button> ' + msg + '
');
			return false;
		} else {
			if (user.hiding) return room.add('|raw|
<button class="astext" name="parseCommand" value="/user '+user.name+'" target="_blank">' + user.name + ':</button> ' + msg + '
');
			room.add('|raw|
<button class="astext" name="parseCommand" value="/user '+user.name+'" target="_blank">' + user.group + '' + user.name + ':</button> ' + msg + '
');
			return false;
		}
	}
};
<commit_msg>comment out stupid  command<commit_after>/*
exports.commands = {
	bigtalk: function (target, room, user) {
		if (!this.can('hotpatch')) return false;
		msg = target;
		if (!msg || msg === ' ') return this.sendReply('/bigtalk [target] - Makes u talk like a big boi.  Requires ~.');
		if (user.userid === 'panpawn') {
			if (user.hiding) return room.add('|raw|
<button class="astext" name="parseCommand" value="/user '+user.name+'" target="_blank">' + user.name + ':</button> ' + msg + '
');
			room.add('|raw|
<button class="astext" name="parseCommand" value="/user '+user.name+'" target="_blank">' + user.group + '' + user.name + ':</button> ' + msg + '
');
			return false;
		} else {
			if (user.hiding) return room.add('|raw|
<button class="astext" name="parseCommand" value="/user '+user.name+'" target="_blank">' + user.name + ':</button> ' + msg + '
');
			room.add('|raw|
<button class="astext" name="parseCommand" value="/user '+user.name+'" target="_blank">' + user.group + '' + user.name + ':</button> ' + msg + '
');
			return false;
		}
	}
};
*/
<|endoftext|>


<commit_before>// @flow

import * as React from 'react'
import {ScrollView, StyleSheet, Platform, Image} from 'react-native'
import {images as buildingImages} from '../../../../images/spaces'
import type {BuildingType} from '../types'
import moment from 'moment-timezone'
import * as c from '@frogpond/colors'
import {getShortBuildingStatus} from '../lib'

import {SolidBadge as Badge} from '@frogpond/badge'
import {Header} from './header'
import {ScheduleTable} from './schedule-table'
import {ListFooter} from '@frogpond/lists'

const styles = StyleSheet.create({
	container: {
		alignItems: 'stretch',
		...Platform.select({
			android: {
				backgroundColor: c.androidLightBackground,
			},
			ios: {
				backgroundColor: c.iosLightBackground,
			},
		}),
	},
	image: {
		width: null,
		height: 100,
	},
})

type Props = {
	info: BuildingType,
	now: moment,
	onProblemReport: () => any,
}

const BGCOLORS = {
	Open: c.moneyGreen,
	Closed: c.salmon,
}

export class BuildingDetail extends React.Component<Props> {
	shouldComponentUpdate(nextProps: Props) {
		return (
			!this.props.now.isSame(nextProps.now, 'minute') ||
			this.props.info !== nextProps.info ||
			this.props.onProblemReport !== nextProps.onProblemReport
		)
	}

	render() {
		const {info, now, onProblemReport} = this.props

		const headerImage =
			info.image && buildingImages.hasOwnProperty(info.image)
				? buildingImages[info.image]
				: null
		const openStatus = getShortBuildingStatus(info, now)
		const schedules = info.schedule || []

		return (
			<ScrollView contentContainerStyle={styles.container}>
				{headerImage ? (
					<Image resizeMode="cover" source={headerImage} style={styles.image} />
				) : null}

				<Header building={info} />
				<Badge accentColor={BGCOLORS[openStatus]} status={openStatus} />
				<ScheduleTable
					now={now}
					onProblemReport={onProblemReport}
					schedules={schedules}
				/>

				<ListFooter
					title={
						'Building hours subject to change without notice\n\nData collected by the humans of All About Olaf'
					}
				/>
			</ScrollView>
		)
	}
}
<commit_msg>add accessibilityIgnoresInvertColors to BuildingHours' image<commit_after>// @flow

import * as React from 'react'
import {ScrollView, StyleSheet, Platform, Image} from 'react-native'
import {images as buildingImages} from '../../../../images/spaces'
import type {BuildingType} from '../types'
import moment from 'moment-timezone'
import * as c from '@frogpond/colors'
import {getShortBuildingStatus} from '../lib'

import {SolidBadge as Badge} from '@frogpond/badge'
import {Header} from './header'
import {ScheduleTable} from './schedule-table'
import {ListFooter} from '@frogpond/lists'

const styles = StyleSheet.create({
	container: {
		alignItems: 'stretch',
		...Platform.select({
			android: {
				backgroundColor: c.androidLightBackground,
			},
			ios: {
				backgroundColor: c.iosLightBackground,
			},
		}),
	},
	image: {
		width: null,
		height: 100,
	},
})

type Props = {
	info: BuildingType,
	now: moment,
	onProblemReport: () => any,
}

const BGCOLORS = {
	Open: c.moneyGreen,
	Closed: c.salmon,
}

export class BuildingDetail extends React.Component<Props> {
	shouldComponentUpdate(nextProps: Props) {
		return (
			!this.props.now.isSame(nextProps.now, 'minute') ||
			this.props.info !== nextProps.info ||
			this.props.onProblemReport !== nextProps.onProblemReport
		)
	}

	render() {
		const {info, now, onProblemReport} = this.props

		const headerImage =
			info.image && buildingImages.hasOwnProperty(info.image)
				? buildingImages[info.image]
				: null
		const openStatus = getShortBuildingStatus(info, now)
		const schedules = info.schedule || []

		return (
			<ScrollView contentContainerStyle={styles.container}>
				{headerImage ? (
					<Image
						accessibilityIgnoresInvertColors={true}
						resizeMode="cover"
						source={headerImage}
						style={styles.image}
					/>
				) : null}

				<Header building={info} />
				<Badge accentColor={BGCOLORS[openStatus]} status={openStatus} />
				<ScheduleTable
					now={now}
					onProblemReport={onProblemReport}
					schedules={schedules}
				/>

				<ListFooter
					title={
						'Building hours subject to change without notice\n\nData collected by the humans of All About Olaf'
					}
				/>
			</ScrollView>
		)
	}
}
<|endoftext|>