<commit_before>import { pipeline as SHADERS, chunks as SHADERS_CHUNKS } from "pex-shaderlib";
import { es300Fragment, es300Vertex, getFileExtension } from "../utils.js";
import { mat3, mat4 } from "pex-math";

export default function createRenderSystem(opts) {
  const ctx = opts.ctx;

  let clearCmd = {
    pass: ctx.pass({
      clearColor: [0, 0.75, 0.5, 1],
    }),
  };

  const pipelineCache = {};
  const programCacheMap = {
    values: [],
    getValue(flags, vert, frag) {
      for (let i = 0; i < this.values.length; i++) {
        const v = this.values[i];
        if (v.frag === frag && v.vert === vert) {
          if (v.flags.length === flags.length) {
            let found = true;
            for (let j = 0; j < flags.length; j++) {
              if (v.flags[j] !== flags[j]) {
                found = false;
                break;
              }
            }
            if (found) {
              return v.program;
            }
          }
        }
      }
      return false;
    },
    setValue(flags, vert, frag, program) {
      this.values.push({ flags, vert, frag, program });
    },
  };

  const renderSystem = {
    cache: {},
    debug: true,
    shadowQuality: 1,
  };

  const getFlagsFromObject = (obj, flags, fn = (flag) => flag) =>
    Object.values(
      Object.fromEntries(
        Object.entries(flags)
          .filter(([key]) => obj[key])
          .map(([key, flag]) => [key, fn(flag, obj[key])])
      )
    );

  const parseMapFlags = (flag, map) => [
    `USE_${flag}`,
    `${flag}_TEX_COORD_INDEX ${map.texCoord || 0}`,
    map.texCoordTransformMatrix && `USE_${flag}_TEX_COORD_TRANSFORM`,
  ];

  const ATTRIBUTES_FLAGS = {
    aNormal: "USE_NORMALS",
    aTangent: "USE_TANGENTS",
    aTexCoord0: "USE_TEXCOORD_0",
    aTexCoord1: "USE_TEXCOORD_1",
    aOffset: "USE_INSTANCED_OFFSET",
    aScale: "USE_INSTANCED_SCALE",
    aRotation: "USE_INSTANCED_ROTATION",
    aColor: "USE_INSTANCED_COLOR",
    aVertexColor: "USE_VERTEX_COLORS",
  };

  const SHARED_MATERIALS_FLAGS = {
    displacementMap: "USE_DISPLACEMENT_MAP", // Not a glTF material property
    alphaTest: "USE_ALPHA_TEST",
  };

  const SHARED_MATERIAL_MAPS_FLAGS = {
    baseColorMap: "BASE_COLOR_MAP",
    alphaMap: "ALPHA_MAP",
  };

  const MATERIALS_FLAGS = {
    useSpecularGlossinessWorkflow: "USE_SPECULAR_GLOSSINESS_WORKFLOW",
    blend: "USE_BLEND",
  };
  const MATERIAL_MAPS_FLAGS = {
    diffuseMap: "DIFFUSE_MAP",
    specularGlossinessMap: "SPECULAR_GLOSSINESS_MAP",
    metallicMap: "METALLIC_MAP",
    roughnessMap: "ROUGHNESS_MAP",
    metallicRoughnessMap: "METALLIC_ROUGHNESS_MAP",
    occlusionMap: "OCCLUSION_MAP",
    normalMap: "NORMAL_MAP",
    emissiveColorMap: "EMISSIVE_COLOR_MAP",
    clearCoatMap: "CLEAR_COAT_MAP",
    clearCoatRoughnessMap: "CLEAR_COAT_ROUGHNESS_MAP",
    clearCoatNormalMap: "CLEAR_COAT_NORMAL_MAP",
    sheenColorMap: "SHEEN_COLOR_MAP",
  };

  const DEPTH_PASS_FLAGS = [
    "SHADOW_QUALITY 0",
    "NUM_AMBIENT_LIGHTS 0",
    "NUM_DIRECTIONAL_LIGHTS 0",
    "NUM_POINT_LIGHTS 0",
    "NUM_SPOT_LIGHTS 0",
    "NUM_AREA_LIGHTS 0",
  ];

  function buildProgram(vertSrc, fragSrc) {
    let program = null;
    try {
      program = ctx.program({ vert: vertSrc, frag: fragSrc });
    } catch (e) {
      console.error("pex-renderer glsl error", e, fragSrc);
      program = ctx.program({
        vert: SHADERS.error.vert,
        frag: SHADERS.error.frag,
      });
      throw e;
    }
    return program;
  }

  function getMaterialProgramAndFlags(
    ctx,
    entity,
    options = {},
    // TODO: pass shadowQuality as option
    State
  ) {
    const { _geometry: geometry, material, skin } = entity;
    const flags = [
      ctx.capabilities.maxColorAttachments > 1 && "USE_DRAW_BUFFERS", // TODO: move into getExtensions
      (!geometry.attributes.aNormal || material.unlit) && "USE_UNLIT_WORKFLOW",
      ...getFlagsFromObject(geometry.attributes, ATTRIBUTES_FLAGS),
      ...getFlagsFromObject(material, SHARED_MATERIALS_FLAGS),
      ...getFlagsFromObject(
        material,
        SHARED_MATERIAL_MAPS_FLAGS,
        parseMapFlags
      ),
      skin && ["USE_SKIN", `NUM_JOINTS ${skin.joints.length}`],
    ];

    let { vert, frag } = material;

    if (options.depthPrePassOnly) {
      vert ||= SHADERS.depthPass.vert;
      frag ||= SHADERS.depthPrePass.frag;

      flags.push("DEPTH_PRE_PASS_ONLY", ...DEPTH_PASS_FLAGS);
    } else if (options.depthPassOnly) {
      vert ||= SHADERS.depthPass.vert;
      frag ||= SHADERS.depthPass.frag;

      flags.push("DEPTH_PASS_ONLY", ...DEPTH_PASS_FLAGS);
    } else {
      vert ||= SHADERS.material.vert;
      frag ||= SHADERS.material.frag;

      flags.push(
        `SHADOW_QUALITY ${
          material.receiveShadows ? renderSystem.shadowQuality : 0
        }`,
        ...getFlagsFromObject(material, MATERIALS_FLAGS),
        ...getFlagsFromObject(material, MATERIAL_MAPS_FLAGS, parseMapFlags),
        !material.useSpecularGlossinessWorkflow &&
          "USE_METALLIC_ROUGHNESS_WORKFLOW",
        // TODO: is checking for "null" required here
        material.emissiveColor !== null && "USE_EMISSIVE_COLOR",
        material.clearCoat !== null && "USE_CLEAR_COAT",
        material.sheenColor !== null && "USE_SHEEN",
        `NUM_AMBIENT_LIGHTS ${options.numAmbientLights || 0}`,
        `NUM_DIRECTIONAL_LIGHTS ${options.numDirectionalLights || 0}`,
        `NUM_POINT_LIGHTS ${options.numPointLights || 0}`,
        `NUM_SPOT_LIGHTS ${options.numSpotLights || 0}`,
        `NUM_AREA_LIGHTS ${options.numAreaLights || 0}`,
        options.useSSAO && "USE_AO",
        options.useReflectionProbes && "USE_REFLECTION_PROBES",
        options.useTonemapping && "USE_TONEMAPPING"
      );
    }

    return {
      flags: flags
        .flat()
        .filter(Boolean)
        .map((flag) => `#define ${flag}`),
      vert,
      frag,
    };
  }

  function getExtensions() {
    return ctx.capabilities.isWebGL2
      ? ""
      : /* glsl */ `
#extension GL_OES_standard_derivatives : require
${
  ctx.capabilities.maxColorAttachments > 1
    ? `#extension GL_EXT_draw_buffers : enable`
    : ""
}
`;
  }

  function parseShader(string, options) {
    // Unroll loop
    const unrollLoopPattern =
      /#pragma unroll_loop[\s]+?for \(int i = (\d+); i < (\d+|\D+); i\+\+\) \{([\s\S]+?)(?=\})\}/g;

    string = string.replace(unrollLoopPattern, (match, start, end, snippet) => {
      let unroll = "";

      // Replace lights number
      end = end
        .replace(/NUM_AMBIENT_LIGHTS/g, options.numAmbientLights || 0)
        .replace(/NUM_DIRECTIONAL_LIGHTS/g, options.numDirectionalLights || 0)
        .replace(/NUM_POINT_LIGHTS/g, options.numPointLights || 0)
        .replace(/NUM_SPOT_LIGHTS/g, options.numSpotLights || 0)
        .replace(/NUM_AREA_LIGHTS/g, options.numAreaLights || 0);

      for (let i = Number.parseInt(start); i < Number.parseInt(end); i++) {
        unroll += snippet.replace(/\[i\]/g, `[${i}]`);
      }

      return unroll;
    });

    return string;
  }

  function getMaterialProgram(ctx, entity, options) {
    const { flags, vert, frag } = getMaterialProgramAndFlags(
      ctx,
      entity,
      options
    );
    const extensions = getExtensions();
    const flagsStr = `${flags.join("\n")}\n`;
    const vertSrc = flagsStr + vert;
    const fragSrc = extensions + flagsStr + frag;
    let program = programCacheMap.getValue(flags, vert, frag);
    if (!program) {
      program = buildProgram(
        parseShader(
          ctx.capabilities.isWebGL2 ? es300Vertex(vertSrc) : vertSrc,
          options
        ),
        parseShader(
          ctx.capabilities.isWebGL2 ? es300Fragment(fragSrc, 3) : fragSrc,
          options
        )
      );
      programCacheMap.setValue(flags, vert, frag, program);
    }
    return program;
  }

  function getGeometryPipeline(ctx, entity, opts) {
    const { material, _geometry: geometry } = entity;
    const program = getMaterialProgram(ctx, entity, opts);
    if (!pipelineCache) {
      pipelineCache = {};
    }
    const hash = `${material.id}_${program.id}_${geometry.primitive}`;
    let pipeline = pipelineCache[hash];
    if (!pipeline || material.needsPipelineUpdate) {
      material.needsPipelineUpdate = false;
      pipeline = ctx.pipeline({
        program,
        depthTest: material.depthTest,
        depthWrite: material.depthWrite,
        depthFunc: material.depthFunc,
        // blend: material.blend,
        // blendSrcRGBFactor: material.blendSrcRGBFactor,
        // blendSrcAlphaFactor: material.blendSrcAlphaFactor,
        // blendDstRGBFactor: material.blendDstRGBFactor,
        // blendDstAlphaFactor: material.blendDstAlphaFactor,
        cullFace: material.cullFace,
        cullFaceMode: material.cullFaceMode,
        primitive: geometry.primitive || ctx.Primitive.Triangles,
      });
      pipelineCache[hash] = pipeline;
    }

    return pipeline;
  }

  function drawMeshes(
    cameraEntity,
    shadowMapping,
    shadowMappingLight,
    entities,
    renderableEntities,
    skybox,
    forward
  ) {
    const camera = cameraEntity.camera;

    const sharedUniforms = {};
    const ambientLights = entities.filter((e) => e.ambientLight);
    const directionalLights = entities.filter((e) => e.directionalLight);
    console.log(
      "directionalLights",
      directionalLights.length,
      directionalLights
    );
    const pointLights = entities.filter((e) => e.pointLight);
    const spotLights = entities.filter((e) => e.spotLight);
    const areaLights = entities.filter((e) => e.areaLight);
    const reflectionProbes = entities.filter((e) => e.reflectionProbe);
    // const areaLights = entities.filter((e) => e.areaLight);

    // sharedUniforms.uCameraPosition = camera.entity.transform.worldPosition;
    sharedUniforms.uProjectionMatrix = camera.projectionMatrix;
    sharedUniforms.uViewMatrix = camera.viewMatrix;
    sharedUniforms.uInverseViewMatrix = camera.inverseViewMatrix; //TODO

    for (let i = 0; i < renderableEntities.length; i++) {
      const renderableEntity = renderableEntities[i];
      const {
        _geometry: geometry,
        _transform: transform,
        material,
        skin,
      } = renderableEntity;
      // const cachedUniforms = material._uniforms;
      const cachedUniforms = {};
      cachedUniforms.uModelMatrix = transform.modelMatrix; //FIXME: bypasses need for transformSystem access
      cachedUniforms.uBaseColor = material.baseColor;
      cachedUniforms.uBaseColorMap = material.baseColorMap;

      const pipeline = getGeometryPipeline(ctx, renderableEntity, {
        numAmbientLights: ambientLights.length,
        numDirectionalLights: directionalLights.length,
        numPointLights: pointLights.length,
        numSpotLights: spotLights.length,
        numAreaLights: areaLights.length,
        useReflectionProbes: reflectionProbes.length, // TODO: reflection probes true
        useSSAO: false,
        // postProcessingCmp &&
        // postProcessingCmp.enabled &&
        // postProcessingCmp.ssao,
        useTonemapping: true, //!(postProcessingCmp && postProcessingCmp.enabled),
      });

      Object.assign(cachedUniforms, sharedUniforms);

      const normalMat = mat4.copy(camera.viewMatrix);
      mat4.mult(normalMat, transform.modelMatrix);
      mat4.invert(normalMat);
      mat4.transpose(normalMat);
      cachedUniforms.uNormalMatrix = mat3.fromMat4(mat3.create(), normalMat);

      ctx.submit({
        name: "drawGeometry",
        attributes: geometry.attributes,
        indices: geometry.indices,
        count: geometry.count,
        pipeline,
        uniforms: cachedUniforms,
        instances: geometry.instances,
      });
    }
  }

  renderSystem.update = (entities) => {
    ctx.submit(clearCmd);

    const rendererableEntities = entities.filter(
      (e) => e.geometry && e.material
    );

    const cameraEntities = entities.filter((e) => e.camera);

    drawMeshes(
      cameraEntities[0],
      false,
      null,
      entities,
      rendererableEntities,
      null,
      true
    );
  };

  return renderSystem;
}
<commit_msg>fix: use != in render system checks to handle undefines<commit_after>import { pipeline as SHADERS, chunks as SHADERS_CHUNKS } from "pex-shaderlib";
import { es300Fragment, es300Vertex, getFileExtension } from "../utils.js";
import { mat3, mat4 } from "pex-math";

export default function createRenderSystem(opts) {
  const ctx = opts.ctx;

  let clearCmd = {
    pass: ctx.pass({
      clearColor: [0, 0.75, 0.5, 1],
    }),
  };

  const pipelineCache = {};
  const programCacheMap = {
    values: [],
    getValue(flags, vert, frag) {
      for (let i = 0; i < this.values.length; i++) {
        const v = this.values[i];
        if (v.frag === frag && v.vert === vert) {
          if (v.flags.length === flags.length) {
            let found = true;
            for (let j = 0; j < flags.length; j++) {
              if (v.flags[j] !== flags[j]) {
                found = false;
                break;
              }
            }
            if (found) {
              return v.program;
            }
          }
        }
      }
      return false;
    },
    setValue(flags, vert, frag, program) {
      this.values.push({ flags, vert, frag, program });
    },
  };

  const renderSystem = {
    cache: {},
    debug: true,
    shadowQuality: 1,
  };

  const getFlagsFromObject = (obj, flags, fn = (flag) => flag) =>
    Object.values(
      Object.fromEntries(
        Object.entries(flags)
          .filter(([key]) => obj[key])
          .map(([key, flag]) => [key, fn(flag, obj[key])])
      )
    );

  const parseMapFlags = (flag, map) => [
    `USE_${flag}`,
    `${flag}_TEX_COORD_INDEX ${map.texCoord || 0}`,
    map.texCoordTransformMatrix && `USE_${flag}_TEX_COORD_TRANSFORM`,
  ];

  const ATTRIBUTES_FLAGS = {
    aNormal: "USE_NORMALS",
    aTangent: "USE_TANGENTS",
    aTexCoord0: "USE_TEXCOORD_0",
    aTexCoord1: "USE_TEXCOORD_1",
    aOffset: "USE_INSTANCED_OFFSET",
    aScale: "USE_INSTANCED_SCALE",
    aRotation: "USE_INSTANCED_ROTATION",
    aColor: "USE_INSTANCED_COLOR",
    aVertexColor: "USE_VERTEX_COLORS",
  };

  const SHARED_MATERIALS_FLAGS = {
    displacementMap: "USE_DISPLACEMENT_MAP", // Not a glTF material property
    alphaTest: "USE_ALPHA_TEST",
  };

  const SHARED_MATERIAL_MAPS_FLAGS = {
    baseColorMap: "BASE_COLOR_MAP",
    alphaMap: "ALPHA_MAP",
  };

  const MATERIALS_FLAGS = {
    useSpecularGlossinessWorkflow: "USE_SPECULAR_GLOSSINESS_WORKFLOW",
    blend: "USE_BLEND",
  };
  const MATERIAL_MAPS_FLAGS = {
    diffuseMap: "DIFFUSE_MAP",
    specularGlossinessMap: "SPECULAR_GLOSSINESS_MAP",
    metallicMap: "METALLIC_MAP",
    roughnessMap: "ROUGHNESS_MAP",
    metallicRoughnessMap: "METALLIC_ROUGHNESS_MAP",
    occlusionMap: "OCCLUSION_MAP",
    normalMap: "NORMAL_MAP",
    emissiveColorMap: "EMISSIVE_COLOR_MAP",
    clearCoatMap: "CLEAR_COAT_MAP",
    clearCoatRoughnessMap: "CLEAR_COAT_ROUGHNESS_MAP",
    clearCoatNormalMap: "CLEAR_COAT_NORMAL_MAP",
    sheenColorMap: "SHEEN_COLOR_MAP",
  };

  const DEPTH_PASS_FLAGS = [
    "SHADOW_QUALITY 0",
    "NUM_AMBIENT_LIGHTS 0",
    "NUM_DIRECTIONAL_LIGHTS 0",
    "NUM_POINT_LIGHTS 0",
    "NUM_SPOT_LIGHTS 0",
    "NUM_AREA_LIGHTS 0",
  ];

  function buildProgram(vertSrc, fragSrc) {
    let program = null;
    try {
      program = ctx.program({ vert: vertSrc, frag: fragSrc });
    } catch (e) {
      console.error("pex-renderer glsl error", e, fragSrc);
      program = ctx.program({
        vert: SHADERS.error.vert,
        frag: SHADERS.error.frag,
      });
      throw e;
    }
    return program;
  }

  function getMaterialProgramAndFlags(
    ctx,
    entity,
    options = {},
    // TODO: pass shadowQuality as option
    State
  ) {
    const { _geometry: geometry, material, skin } = entity;
    const flags = [
      ctx.capabilities.maxColorAttachments > 1 && "USE_DRAW_BUFFERS", // TODO: move into getExtensions
      (!geometry.attributes.aNormal || material.unlit) && "USE_UNLIT_WORKFLOW",
      ...getFlagsFromObject(geometry.attributes, ATTRIBUTES_FLAGS),
      ...getFlagsFromObject(material, SHARED_MATERIALS_FLAGS),
      ...getFlagsFromObject(
        material,
        SHARED_MATERIAL_MAPS_FLAGS,
        parseMapFlags
      ),
      skin && ["USE_SKIN", `NUM_JOINTS ${skin.joints.length}`],
    ];

    let { vert, frag } = material;

    if (options.depthPrePassOnly) {
      vert ||= SHADERS.depthPass.vert;
      frag ||= SHADERS.depthPrePass.frag;

      flags.push("DEPTH_PRE_PASS_ONLY", ...DEPTH_PASS_FLAGS);
    } else if (options.depthPassOnly) {
      vert ||= SHADERS.depthPass.vert;
      frag ||= SHADERS.depthPass.frag;

      flags.push("DEPTH_PASS_ONLY", ...DEPTH_PASS_FLAGS);
    } else {
      vert ||= SHADERS.material.vert;
      frag ||= SHADERS.material.frag;

      flags.push(
        `SHADOW_QUALITY ${
          material.receiveShadows ? renderSystem.shadowQuality : 0
        }`,
        ...getFlagsFromObject(material, MATERIALS_FLAGS),
        ...getFlagsFromObject(material, MATERIAL_MAPS_FLAGS, parseMapFlags),
        !material.useSpecularGlossinessWorkflow &&
          "USE_METALLIC_ROUGHNESS_WORKFLOW",
        // TODO: is checking for "null" required here
        material.emissiveColor != null && "USE_EMISSIVE_COLOR",
        material.clearCoat != null && "USE_CLEAR_COAT",
        material.sheenColor != null && "USE_SHEEN",
        `NUM_AMBIENT_LIGHTS ${options.numAmbientLights || 0}`,
        `NUM_DIRECTIONAL_LIGHTS ${options.numDirectionalLights || 0}`,
        `NUM_POINT_LIGHTS ${options.numPointLights || 0}`,
        `NUM_SPOT_LIGHTS ${options.numSpotLights || 0}`,
        `NUM_AREA_LIGHTS ${options.numAreaLights || 0}`,
        options.useSSAO && "USE_AO",
        options.useReflectionProbes && "USE_REFLECTION_PROBES",
        options.useTonemapping && "USE_TONEMAPPING"
      );
    }

    return {
      flags: flags
        .flat()
        .filter(Boolean)
        .map((flag) => `#define ${flag}`),
      vert,
      frag,
    };
  }

  function getExtensions() {
    return ctx.capabilities.isWebGL2
      ? ""
      : /* glsl */ `
#extension GL_OES_standard_derivatives : require
${
  ctx.capabilities.maxColorAttachments > 1
    ? `#extension GL_EXT_draw_buffers : enable`
    : ""
}
`;
  }

  function parseShader(string, options) {
    // Unroll loop
    const unrollLoopPattern =
      /#pragma unroll_loop[\s]+?for \(int i = (\d+); i < (\d+|\D+); i\+\+\) \{([\s\S]+?)(?=\})\}/g;

    string = string.replace(unrollLoopPattern, (match, start, end, snippet) => {
      let unroll = "";

      // Replace lights number
      end = end
        .replace(/NUM_AMBIENT_LIGHTS/g, options.numAmbientLights || 0)
        .replace(/NUM_DIRECTIONAL_LIGHTS/g, options.numDirectionalLights || 0)
        .replace(/NUM_POINT_LIGHTS/g, options.numPointLights || 0)
        .replace(/NUM_SPOT_LIGHTS/g, options.numSpotLights || 0)
        .replace(/NUM_AREA_LIGHTS/g, options.numAreaLights || 0);

      for (let i = Number.parseInt(start); i < Number.parseInt(end); i++) {
        unroll += snippet.replace(/\[i\]/g, `[${i}]`);
      }

      return unroll;
    });

    return string;
  }

  function getMaterialProgram(ctx, entity, options) {
    const { flags, vert, frag } = getMaterialProgramAndFlags(
      ctx,
      entity,
      options
    );
    const extensions = getExtensions();
    const flagsStr = `${flags.join("\n")}\n`;
    const vertSrc = flagsStr + vert;
    const fragSrc = extensions + flagsStr + frag;
    let program = programCacheMap.getValue(flags, vert, frag);
    if (!program) {
      program = buildProgram(
        parseShader(
          ctx.capabilities.isWebGL2 ? es300Vertex(vertSrc) : vertSrc,
          options
        ),
        parseShader(
          ctx.capabilities.isWebGL2 ? es300Fragment(fragSrc, 3) : fragSrc,
          options
        )
      );
      programCacheMap.setValue(flags, vert, frag, program);
    }
    return program;
  }

  function getGeometryPipeline(ctx, entity, opts) {
    const { material, _geometry: geometry } = entity;
    const program = getMaterialProgram(ctx, entity, opts);
    if (!pipelineCache) {
      pipelineCache = {};
    }
    const hash = `${material.id}_${program.id}_${geometry.primitive}`;
    let pipeline = pipelineCache[hash];
    if (!pipeline || material.needsPipelineUpdate) {
      material.needsPipelineUpdate = false;
      pipeline = ctx.pipeline({
        program,
        depthTest: material.depthTest,
        depthWrite: material.depthWrite,
        depthFunc: material.depthFunc,
        // blend: material.blend,
        // blendSrcRGBFactor: material.blendSrcRGBFactor,
        // blendSrcAlphaFactor: material.blendSrcAlphaFactor,
        // blendDstRGBFactor: material.blendDstRGBFactor,
        // blendDstAlphaFactor: material.blendDstAlphaFactor,
        cullFace: material.cullFace,
        cullFaceMode: material.cullFaceMode,
        primitive: geometry.primitive || ctx.Primitive.Triangles,
      });
      pipelineCache[hash] = pipeline;
    }

    return pipeline;
  }

  function drawMeshes(
    cameraEntity,
    shadowMapping,
    shadowMappingLight,
    entities,
    renderableEntities,
    skybox,
    forward
  ) {
    const camera = cameraEntity.camera;

    const sharedUniforms = {};
    const ambientLights = entities.filter((e) => e.ambientLight);
    const directionalLights = entities.filter((e) => e.directionalLight);
    console.log(
      "directionalLights",
      directionalLights.length,
      directionalLights
    );
    const pointLights = entities.filter((e) => e.pointLight);
    const spotLights = entities.filter((e) => e.spotLight);
    const areaLights = entities.filter((e) => e.areaLight);
    const reflectionProbes = entities.filter((e) => e.reflectionProbe);
    // const areaLights = entities.filter((e) => e.areaLight);

    // sharedUniforms.uCameraPosition = camera.entity.transform.worldPosition;
    sharedUniforms.uProjectionMatrix = camera.projectionMatrix;
    sharedUniforms.uViewMatrix = camera.viewMatrix;
    sharedUniforms.uInverseViewMatrix = camera.inverseViewMatrix; //TODO

    for (let i = 0; i < renderableEntities.length; i++) {
      const renderableEntity = renderableEntities[i];
      const {
        _geometry: geometry,
        _transform: transform,
        material,
        skin,
      } = renderableEntity;
      // const cachedUniforms = material._uniforms;
      const cachedUniforms = {};
      cachedUniforms.uModelMatrix = transform.modelMatrix; //FIXME: bypasses need for transformSystem access
      cachedUniforms.uBaseColor = material.baseColor;
      cachedUniforms.uBaseColorMap = material.baseColorMap;

      const pipeline = getGeometryPipeline(ctx, renderableEntity, {
        numAmbientLights: ambientLights.length,
        numDirectionalLights: directionalLights.length,
        numPointLights: pointLights.length,
        numSpotLights: spotLights.length,
        numAreaLights: areaLights.length,
        useReflectionProbes: reflectionProbes.length, // TODO: reflection probes true
        useSSAO: false,
        // postProcessingCmp &&
        // postProcessingCmp.enabled &&
        // postProcessingCmp.ssao,
        useTonemapping: true, //!(postProcessingCmp && postProcessingCmp.enabled),
      });

      Object.assign(cachedUniforms, sharedUniforms);

      const normalMat = mat4.copy(camera.viewMatrix);
      mat4.mult(normalMat, transform.modelMatrix);
      mat4.invert(normalMat);
      mat4.transpose(normalMat);
      cachedUniforms.uNormalMatrix = mat3.fromMat4(mat3.create(), normalMat);

      ctx.submit({
        name: "drawGeometry",
        attributes: geometry.attributes,
        indices: geometry.indices,
        count: geometry.count,
        pipeline,
        uniforms: cachedUniforms,
        instances: geometry.instances,
      });
    }
  }

  renderSystem.update = (entities) => {
    ctx.submit(clearCmd);

    const rendererableEntities = entities.filter(
      (e) => e.geometry && e.material
    );

    const cameraEntities = entities.filter((e) => e.camera);

    drawMeshes(
      cameraEntities[0],
      false,
      null,
      entities,
      rendererableEntities,
      null,
      true
    );
  };

  return renderSystem;
}
<|endoftext|>


<commit_before>//
// Implementations of primitive methods
// This is loaded at the very end, everything is already available
// If this gets too long, split it in multiple files
//

ProtoObject._addClassMethods({
	basicNew: function() { return this._newInstance(); },
	_new: function() { return this.basicNew().initialize(); },
	name: function() { return this._classname; }
});
ProtoObject._addInstanceMethods({
	_equals_equals: function(anObject) { return bool(this === anObject); },
	identityHash: function() { return number(this.instanceNumber$); }
});

var _perform_ = function (aSTString){
		var theArguments = _toArray(arguments);
		theArguments.shift();
		var aJSString = _unboxObject(aSTString); //TODO: translated the selector to JS
		if(this[aJSString] !== undefined){
			return this[aJSString].apply(this, theArguments);
		} else {
			return this.doesNotUnderstand_(Message.selector_arguments_(aSTString, array(theArguments)));
		}
};

_Object._addInstanceMethods({
	doesNotUnderstand_: function(message) {
		// TODO implement this correctly
		var msg = this + "(instance of " + this._class().name() +
				") did not understand " + _unboxObject(message.selector());
		Exception.signal_(string(msg));
	},
	_class: function() { return this.__class; },
	halt: function() { debugger; },
	perform_: _perform_,
	perform_with_: _perform_,
	perform_with_with_: _perform_,
	perform_with_with_with_: _perform_,
	perform_withArguments_: function (aSTMessageSelector, anArgumentsCollection){
		return _perform_.apply(this, _unboxObject(anArgumentsCollection));
	}
});

Exception._addInstanceMethods({
	signal: function() {
		alert(_unboxObject(this.messageText()));
		throw this;
	}
});

_String._addInstanceMethods({
	// This is not actually a primitive function, but behaves in the same way the #, method does
	_comma: function(anotherString) { return string(this.original$ + anotherString.original$); },
	_equals: function(anotherString) { return bool(this.original$ == anotherString.original$); },
	_equals_equals: function(anotherString) { return this._equals(anotherString); },
	isEmpty: function() { return bool(this.original$.length === 0); },
	size: function() { return number(this.original$.length); },
	at_: function(num) { return character(this.original$[num.original$ - 1]); }
});

ByteString._addInstanceMethods({
	at_: function(num) { return character(this.original$[num.original$ - 1]); }
});

/*
at_ should also be defined in:
ByteSymbol
WideString
WideSymbol
*/

_Number._addInstanceMethods({
	printString: function() { return string(this.original$.toString()); }
});

Float._addInstanceMethods({
	_plus: function(other) { return number(this.original$ + other.original$); },
	_minus: function(other) { return number(this.original$ - other.original$); },
	_times: function(other) { return number(this.original$ * other.original$); },
	_slash: function(other) { return number(this.original$ / other.original$); },
	floor: function() { return number(Math.floor(this.original$)); },
	rounded: function() { return number(Math.round(this.original$)); },
	_less: function(other) {
		return bool(this.original$ < other.original$);
	},
	_greater: function(other) {
		return bool(this.original$ > other.original$);
	},
	_less_equals: function(other) {
		return bool(this.original$ <= other.original$);
	},
	_greater_equals: function(other) {
		return bool(this.original$ >= other.original$);
	},
	_equals: function(other) {
		return bool(this.original$ == other.original$);
	},
	_tilde_equals: function(other) {
	  return bool(this.original$ != other.original$);
	},
	timesRepeat_: function(aBlock){
		for (var i = 0; i < this.original$; i++) {
			aBlock.value();
		}
		return this;
	},
	truncated: function() {
		return number(Math.floor(this.original$));
	},
	roundTo_: function(quantum) {
		var result = _unboxObject((this._slash(quantum)).rounded()._times(quantum));
        var decimalCount = 0;
        while (decimalCount <= 21 && _unboxObject(quantum).toFixed(decimalCount) != _unboxObject(quantum)) {
            decimalCount++;
        }
        return number(result.toFixed(decimalCount));
	}
});

Point._addInstanceMethods({
	_times: function(aNumber){
		return (this.x()._times(aNumber))._at(this.y()._times(aNumber));
	}
});

// We distinct an evaluated$-function and an original$-function in case of blocks.
// evaluated$ is the version, that is evaluated in the squeak-world, original$ is the function inside javascript
var _blockValueFunction_ = function() {
	return this.evaluated$.apply(this, arguments); };

// Helpers to implement Block-Closure-primitives
var _toArray = function(iterable) {
	if (!iterable) return [];
	if (iterable.toArray) return iterable.toArray();
	var length = iterable.length, results = new Array(length);
	while (length--) results[length] = iterable[length];
	return results;
};
var _curried = function(func, boundArgs) {
	// Return a function with the first parameters bound to boundArgs. Function itself will be bound to this.
	return function() { return func.apply(this, boundArgs.concat(_toArray(arguments))); };
};
var _createInstance_ = function() {
	// This is done to enable varargs-parameters for constructor-parameters
	// First bind all constructor-parameters, then call the curried function without arguments
	// Bind the function to the function itself. Seemed necessary.
	// Constructor-arguments are determined polymorphically (set in boxing.js -> boundBlock() and squeaky.js -> block())
	var args = this.constructorArguments$(arguments);
	return _boxObject(new (_curried(this.original$, args)) ());
};

BlockClosure._addInstanceMethods({
	value: _blockValueFunction_,
	value_: _blockValueFunction_,
	value_value_: _blockValueFunction_,
	value_value_value_: _blockValueFunction_,
	value_value_value_value_: _blockValueFunction_,

	whileTrue_: function(anotherBlock) {
		while (this.value() === _true) {
			anotherBlock.value();
		}
		return nil;
	},
	whileTrue: function(anotherBlock) {
		while (this.value() === _true)
			anotherBlock.value();
		return nil;
	},
	whileFalse_: function() {
		while (this.value() === _false) {
			anotherBlock.value();
		}
		return nil;
	},
	whileFalse: function() {
		while (this.value() === _false) ;
		return nil;
	},

	// Up to 9 parameters for direct constructor call
	jsNew: _createInstance_,
	jsNew_: _createInstance_,
	jsNew_with_: _createInstance_,
	jsNew_with_with_: _createInstance_,
	jsNew_with_with_with_: _createInstance_,
	jsNew_with_with_with_with_: _createInstance_,
	jsNew_with_with_with_with_with_: _createInstance_,
	jsNew_with_with_with_with_with_with_: _createInstance_,
	jsNew_with_with_with_with_with_with_with_: _createInstance_,
	jsNew_with_with_with_with_with_with_with_with_: _createInstance_,

	// Any arguments as array
	jsNewWithArgs: function(args) {
		_createInstance_.apply(this, args);
	}
});

_Array._addInstanceMethods({
	_privateGet: function(index) {
		return _boxObject(this.original$[index]);
	},
	_privateSet: function(index, obj) {
		this.original$[index] = _unboxObject(obj);
	},

	size: function(){
		return number(this.original$.length);
	},
	at_put_: function(idx, val){
		this._privateSet(idx.original$ - 1, val);
		return val;
	},
	at_: function(idx){
		return this._privateGet(idx.original$ - 1);
	},
	isEmpty: function(){
		return bool(this.original$.length == 0);
	},

	/* this is actually not a primitive and should be implemented by squeak.
	   it is only implemented here to work around a hardly tracable bug, probably in out blocks
	   and nonlocal return */
	includes_: function(anElement) {
		for (index in this.original$) {
			if (this._privateGet(index)._equals(anElement))
				return _true;
		}
		return _false;
	},
	asObject : function () {
		// creates Prototype from object literal
		// Can only be called on arrays containing only Associations (understanding key()/value())
		var newObject = {};
		for (index in this.original$) {
			var anAssociation = this._privateGet(index);
			newObject[_unboxObject(anAssociation.key())] = _unboxObject(anAssociation.value());
		}
		return newObject;
	}
});
_Array._addClassMethods({
	new_: function(size) {
		// Not filling the indices of the array with 'nil', because of autoboxing
		return array(new Array(size.original$));
	}
});
<commit_msg>primitive for temporary ids<commit_after>//
// Implementations of primitive methods
// This is loaded at the very end, everything is already available
// If this gets too long, split it in multiple files
//

ProtoObject._addClassMethods({
	basicNew: function() { return this._newInstance(); },
	_new: function() { return this.basicNew().initialize(); },
	name: function() { return this._classname; }
});
ProtoObject._addInstanceMethods({
	_equals_equals: function(anObject) { return bool(this === anObject); },
	identityHash: function() { return number(this.instanceNumber$); }
});

var _perform_ = function (aSTString){
		var theArguments = _toArray(arguments);
		theArguments.shift();
		var aJSString = _unboxObject(aSTString); //TODO: translated the selector to JS
		if(this[aJSString] !== undefined){
			return this[aJSString].apply(this, theArguments);
		} else {
			return this.doesNotUnderstand_(Message.selector_arguments_(aSTString, array(theArguments)));
		}
};

_Object._addInstanceMethods({
	doesNotUnderstand_: function(message) {
		// TODO implement this correctly
		var msg = this + "(instance of " + this._class().name() +
				") did not understand " + _unboxObject(message.selector());
		Exception.signal_(string(msg));
	},
	_class: function() { return this.__class; },
	halt: function() { debugger; },
	perform_: _perform_,
	perform_with_: _perform_,
	perform_with_with_: _perform_,
	perform_with_with_with_: _perform_,
	perform_withArguments_: function (aSTMessageSelector, anArgumentsCollection){
		return _perform_.apply(this, _unboxObject(anArgumentsCollection));
	}
});

Exception._addInstanceMethods({
	signal: function() {
		alert(_unboxObject(this.messageText()));
		throw this;
	}
});

_String._addInstanceMethods({
	// This is not actually a primitive function, but behaves in the same way the #, method does
	_comma: function(anotherString) { return string(this.original$ + anotherString.original$); },
	_equals: function(anotherString) { return bool(this.original$ == anotherString.original$); },
	_equals_equals: function(anotherString) { return this._equals(anotherString); },
	isEmpty: function() { return bool(this.original$.length === 0); },
	size: function() { return number(this.original$.length); },
	at_: function(num) { return character(this.original$[num.original$ - 1]); }
});

ByteString._addInstanceMethods({
	at_: function(num) { return character(this.original$[num.original$ - 1]); }
});

/*
at_ should also be defined in:
ByteSymbol
WideString
WideSymbol
*/

_Number._addInstanceMethods({
	printString: function() { return string(this.original$.toString()); }
});

Float._addInstanceMethods({
	_plus: function(other) { return number(this.original$ + other.original$); },
	_minus: function(other) { return number(this.original$ - other.original$); },
	_times: function(other) { return number(this.original$ * other.original$); },
	_slash: function(other) { return number(this.original$ / other.original$); },
	floor: function() { return number(Math.floor(this.original$)); },
	rounded: function() { return number(Math.round(this.original$)); },
	_less: function(other) {
		return bool(this.original$ < other.original$);
	},
	_greater: function(other) {
		return bool(this.original$ > other.original$);
	},
	_less_equals: function(other) {
		return bool(this.original$ <= other.original$);
	},
	_greater_equals: function(other) {
		return bool(this.original$ >= other.original$);
	},
	_equals: function(other) {
		return bool(this.original$ == other.original$);
	},
	_tilde_equals: function(other) {
	  return bool(this.original$ != other.original$);
	},
	timesRepeat_: function(aBlock){
		for (var i = 0; i < this.original$; i++) {
			aBlock.value();
		}
		return this;
	},
	truncated: function() {
		return number(Math.floor(this.original$));
	},
	roundTo_: function(quantum) {
		var result = _unboxObject((this._slash(quantum)).rounded()._times(quantum));
        var decimalCount = 0;
        while (decimalCount <= 21 && _unboxObject(quantum).toFixed(decimalCount) != _unboxObject(quantum)) {
            decimalCount++;
        }
        return number(result.toFixed(decimalCount));
	}
});

Point._addInstanceMethods({
	_times: function(aNumber){
		return (this.x()._times(aNumber))._at(this.y()._times(aNumber));
	}
});

// We distinct an evaluated$-function and an original$-function in case of blocks.
// evaluated$ is the version, that is evaluated in the squeak-world, original$ is the function inside javascript
var _blockValueFunction_ = function() {
	return this.evaluated$.apply(this, arguments); };

// Helpers to implement Block-Closure-primitives
var _toArray = function(iterable) {
	if (!iterable) return [];
	if (iterable.toArray) return iterable.toArray();
	var length = iterable.length, results = new Array(length);
	while (length--) results[length] = iterable[length];
	return results;
};
var _curried = function(func, boundArgs) {
	// Return a function with the first parameters bound to boundArgs. Function itself will be bound to this.
	return function() { return func.apply(this, boundArgs.concat(_toArray(arguments))); };
};
var _createInstance_ = function() {
	// This is done to enable varargs-parameters for constructor-parameters
	// First bind all constructor-parameters, then call the curried function without arguments
	// Bind the function to the function itself. Seemed necessary.
	// Constructor-arguments are determined polymorphically (set in boxing.js -> boundBlock() and squeaky.js -> block())
	var args = this.constructorArguments$(arguments);
	return _boxObject(new (_curried(this.original$, args)) ());
};

BlockClosure._addInstanceMethods({
	value: _blockValueFunction_,
	value_: _blockValueFunction_,
	value_value_: _blockValueFunction_,
	value_value_value_: _blockValueFunction_,
	value_value_value_value_: _blockValueFunction_,

	whileTrue_: function(anotherBlock) {
		while (this.value() === _true) {
			anotherBlock.value();
		}
		return nil;
	},
	whileTrue: function(anotherBlock) {
		while (this.value() === _true)
			anotherBlock.value();
		return nil;
	},
	whileFalse_: function() {
		while (this.value() === _false) {
			anotherBlock.value();
		}
		return nil;
	},
	whileFalse: function() {
		while (this.value() === _false) ;
		return nil;
	},

	// Up to 9 parameters for direct constructor call
	jsNew: _createInstance_,
	jsNew_: _createInstance_,
	jsNew_with_: _createInstance_,
	jsNew_with_with_: _createInstance_,
	jsNew_with_with_with_: _createInstance_,
	jsNew_with_with_with_with_: _createInstance_,
	jsNew_with_with_with_with_with_: _createInstance_,
	jsNew_with_with_with_with_with_with_: _createInstance_,
	jsNew_with_with_with_with_with_with_with_: _createInstance_,
	jsNew_with_with_with_with_with_with_with_with_: _createInstance_,

	// Any arguments as array
	jsNewWithArgs: function(args) {
		_createInstance_.apply(this, args);
	}
});

_Array._addInstanceMethods({
	_privateGet: function(index) {
		return _boxObject(this.original$[index]);
	},
	_privateSet: function(index, obj) {
		this.original$[index] = _unboxObject(obj);
	},

	size: function(){
		return number(this.original$.length);
	},
	at_put_: function(idx, val){
		this._privateSet(idx.original$ - 1, val);
		return val;
	},
	at_: function(idx){
		return this._privateGet(idx.original$ - 1);
	},
	isEmpty: function(){
		return bool(this.original$.length == 0);
	},

	/* this is actually not a primitive and should be implemented by squeak.
	   it is only implemented here to work around a hardly tracable bug, probably in out blocks
	   and nonlocal return */
	includes_: function(anElement) {
		for (index in this.original$) {
			if (this._privateGet(index)._equals(anElement))
				return _true;
		}
		return _false;
	},
	asObject : function () {
		// creates Prototype from object literal
		// Can only be called on arrays containing only Associations (understanding key()/value())
		var newObject = {};
		for (index in this.original$) {
			var anAssociation = this._privateGet(index);
			newObject[_unboxObject(anAssociation.key())] = _unboxObject(anAssociation.value());
		}
		return newObject;
	}
});
_Array._addClassMethods({
	new_: function(size) {
		// Not filling the indices of the array with 'nil', because of autoboxing
		return array(new Array(size.original$));
	}
});

S2JWidget._addInstanceMethods({
	generateCssId: function() {
		return string(Date.now());
	}
});<|endoftext|>


<commit_before>/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

// This will handle the data
var Board = {
    value:'',
    position:'',
    color:'',
    getValue : function() {
      return this.value;
    },
    setValue : function(value) {
      this.value = value;
    },
    getPosition : function() {
      return this.position;
    },
    setPosition : function(position) {
      this.position = position;
    },
    getColor : function() {
      return this.color;
    },
    setColor : function(color) {
      this.color = color;
    }

};

    <commit_msg>deleted board.js<commit_after><|endoftext|>


<commit_before>steal('can/util',
	  'can/view',
	  'can/view/scanner.js',
	  'can/observe/compute',
	  'can/view/render.js',
function( can ){

	can.view.ext = ".mustache";

	var extend = can.extend,
		Mustache = function( options ) {
			// Supports calling Mustache without the constructor
			// This returns a function that renders the template.
			if ( this.constructor != Mustache ) {
				var mustache = new Mustache(options);
				return function( data, helpers ) {
					 return mustache.render(data, helpers);
				};
			}

			// If we get a `function` directly, it probably is coming from
			// a `steal`-packaged view.
			if ( typeof options == "function" ) {
				this.template = {
					fn: options
				};
				return;
			}

			// Set options on self.
			extend(this, options);
			this.template = this.scanner.scan(this.text, this.name);
		};

	can.Mustache = Mustache;

	/**
	 * @Prototype
	 */
	Mustache.prototype.
	/**
	 * Renders an object with view helpers attached to the view.
	 *
	 *		 new Mustache({text: "<%= message %>"}).render({
	 *			 message: "foo"
	 *		 },{helper: function(){ ... }})
	 *
	 * @param {Object} object data to be rendered
	 * @param {Object} [extraHelpers] an object with view helpers
	 * @return {String} returns the result of the string
	 */
	render = function( object, extraHelpers ) {
		object = object || {};
		return this.template.fn.call(object, object, new Mustache.Helpers(object, extraHelpers || {}));
	};

	extend(Mustache.prototype, {
		/**
		 * Singleton scanner instance for parsing templates.
		 */
		scanner: new can.view.Scanner({
			/**
			 * An ordered token registry for the scanner.
			 * This needs to be ordered by priority to prevent token parsing errors.
			 * Each token is defined as: ["token-name", "string representation", "optional regexp override"]
			 */
			tokens: [
				["templateLeft", "{{$"], // Template	 ---- Not supported
				["templateRight", "$}}"], // Right Template	---- Not supported
				["returnLeft", "{{{", "{{[{&]"], // Return Unescaped
				// ["commentFull", "{{!}}", "[\\s\\t]*{{!.+?}}\\n?"], // Comment
				["commentLeft", "{{!", "(\\n[\\s\\t]*{{!|{{!)"], // Comment
				["left", "{{~"], // Run
				["escapeLeft", "{{"], // Return Escaped
				["returnRight", "}}}"],
				["right", "}}"] // Right -> All have same FOR Mustache ...
			],

			helpers:[
				/**
				 * {{#evalvariable}}
				 *
				 * ## Sections
				 * Sections render blocks of text one or more times, depending on the value
				 * of the key in the current context.
				 *
				 * A section begins with a pound and ends with a slash. That is, {{#person}}
				 * begins a "person" section while {{/person}} ends it.
				 *
				 * The behavior of the section is determined by the value of the key.
				 *
				 * ### False Values or Empty Lists
				 * If the person key exists and has a value of false or an empty list, the HTML
				 * between the pound and slash will not be displayed.
				 *
				 * ### Non-Empty Lists
				 * If the person key exists and has a non-false value, the HTML between the pound
				 * and slash will be rendered and displayed one or more times.
				 *
				 * When the value is a non-empty list, the text in the block will be displayed once for
				 * each item in the list. The context of the block will be set to the current item for each
				 * iteration. In this way we can loop over collections.
				 *
				 * ### Lambdas
				 * When the value is a callable object, such as a function or lambda, the object will be
				 * invoked and passed the block of text. The text passed is the literal block, unrendered.
				 * {{tags}} will not have been expanded - the lambda should do that on its own. In this way you
				 * can implement filters or caching.
				 *
				 * @param {String} content
				 */
				function(content){
					var match = content.match(/^#\w*$/);
					if(match){
						return 'if(' + content.substring(1, content.length) + ') {';
					}

					return content;
				},

				/**
				 * {{#person?}}
				 *
				 * ## Non-False Values
				 * When the value is non-false but not a list, it will be used as the context
				 * for a single rendering of the block.
				 *
				 * @param {String} content
				 */
				function(content){
					return content;
				},

				/**
				 * {{^ evalvariable }}
				 *
				 * ## Inverted Sections
				 * An inverted section begins with a caret (hat) and ends with a slash.
				 * That is {{^person}} begins a "person" inverted section while {{/person}} ends it.
				 *
				 * @param {String} content
				 */
				function(content){
					return content;
				},

				/**
				 * {{/ evalvariable }}
				 * Closes sections.
				 * @param {String} content
				 */
				function(content){
					var match = content.match(/^\/\w*$/);
					if(match){
						return '};';
					}

					return content;
				},

				/**
				 * Basic Context Miss Interpolation
				 *  {{cannot}}
				 * 	Failed context lookups should default to empty strings.
				 * Dotted Names - Broken Chains
				 *	{{a.b.c}}
    			 * 	Any falsey value prior to the last part of the name should yield ''.
				 */
				function(content) {
					// match only words and dots
					var match = content.match(/^\w*[\.|\w]*$/);
					if(match){

						var split = content.split('.'),
						// Handle context miss
						result = ['(typeof ' + split[0] + ' != "undefined" ? '];

						// Handle broken chains
						if (split.length > 1) {
							result.push('(');
							for (var i = 1; i < split.length; i++) {
								i > 1 && result.push(' && ');
								result.push(split.slice(0, i+1).join('.'));
							}
							result.push(') || ""');
						}
						else {
							result.push(split[0]);
						}

						return result.concat([' : "")']).join('');
					}

					return content;
				}
			]
			// Add helpers from scanner in now.
			.concat(can.view.Scanner.prototype.helpers)
		})
	});

	Mustache.Helpers = function( data, extras ) {
		this._data = data;
		this._extras = extras;
		extend(this, extras);
	};

	/**
	 * Register the view.
	 */
	can.view.register({
		suffix: "mustache",

		contentType: "x-mustache-template",

		// returns a `function` that renders the view.
		script: function( id, src ) {
			return "can.Mustache(function(_CONTEXT,_VIEW) { " + new Mustache({
				text: src,
				name: id
			}).template.out + " })";
		},

		renderer: function( id, text ) {
			return Mustache({
				text: text,
				name: id
			});
		}
	});

});<commit_msg>adding check for content length<commit_after>steal('can/util',
	  'can/view',
	  'can/view/scanner.js',
	  'can/observe/compute',
	  'can/view/render.js',
function( can ){

	can.view.ext = ".mustache";

	var extend = can.extend,
		Mustache = function( options ) {
			// Supports calling Mustache without the constructor
			// This returns a function that renders the template.
			if ( this.constructor != Mustache ) {
				var mustache = new Mustache(options);
				return function( data, helpers ) {
					 return mustache.render(data, helpers);
				};
			}

			// If we get a `function` directly, it probably is coming from
			// a `steal`-packaged view.
			if ( typeof options == "function" ) {
				this.template = {
					fn: options
				};
				return;
			}

			// Set options on self.
			extend(this, options);
			this.template = this.scanner.scan(this.text, this.name);
		};

	can.Mustache = Mustache;

	/**
	 * @Prototype
	 */
	Mustache.prototype.
	/**
	 * Renders an object with view helpers attached to the view.
	 *
	 *		 new Mustache({text: "<%= message %>"}).render({
	 *			 message: "foo"
	 *		 },{helper: function(){ ... }})
	 *
	 * @param {Object} object data to be rendered
	 * @param {Object} [extraHelpers] an object with view helpers
	 * @return {String} returns the result of the string
	 */
	render = function( object, extraHelpers ) {
		object = object || {};
		return this.template.fn.call(object, object, new Mustache.Helpers(object, extraHelpers || {}));
	};

	extend(Mustache.prototype, {
		/**
		 * Singleton scanner instance for parsing templates.
		 */
		scanner: new can.view.Scanner({
			/**
			 * An ordered token registry for the scanner.
			 * This needs to be ordered by priority to prevent token parsing errors.
			 * Each token is defined as: ["token-name", "string representation", "optional regexp override"]
			 */
			tokens: [
				["templateLeft", "{{$"], // Template	 ---- Not supported
				["templateRight", "$}}"], // Right Template	---- Not supported
				["returnLeft", "{{{", "{{[{&]"], // Return Unescaped
				// ["commentFull", "{{!}}", "[\\s\\t]*{{!.+?}}\\n?"], // Comment
				["commentLeft", "{{!", "(\\n[\\s\\t]*{{!|{{!)"], // Comment
				["left", "{{~"], // Run
				["escapeLeft", "{{"], // Return Escaped
				["returnRight", "}}}"],
				["right", "}}"] // Right -> All have same FOR Mustache ...
			],

			helpers:[
				/**
				 * {{#evalvariable}}
				 *
				 * ## Sections
				 * Sections render blocks of text one or more times, depending on the value
				 * of the key in the current context.
				 *
				 * A section begins with a pound and ends with a slash. That is, {{#person}}
				 * begins a "person" section while {{/person}} ends it.
				 *
				 * The behavior of the section is determined by the value of the key.
				 *
				 * ### False Values or Empty Lists
				 * If the person key exists and has a value of false or an empty list, the HTML
				 * between the pound and slash will not be displayed.
				 *
				 * ### Non-Empty Lists
				 * If the person key exists and has a non-false value, the HTML between the pound
				 * and slash will be rendered and displayed one or more times.
				 *
				 * When the value is a non-empty list, the text in the block will be displayed once for
				 * each item in the list. The context of the block will be set to the current item for each
				 * iteration. In this way we can loop over collections.
				 *
				 * ### Lambdas
				 * When the value is a callable object, such as a function or lambda, the object will be
				 * invoked and passed the block of text. The text passed is the literal block, unrendered.
				 * {{tags}} will not have been expanded - the lambda should do that on its own. In this way you
				 * can implement filters or caching.
				 *
				 * @param {String} content
				 */
				function(content){
					var match = content.match(/^#\w*$/);
					if(match){
						return 'if(' + content.substring(1, content.length) + ') {';
					}

					return content;
				},

				/**
				 * {{#person?}}
				 *
				 * ## Non-False Values
				 * When the value is non-false but not a list, it will be used as the context
				 * for a single rendering of the block.
				 *
				 * @param {String} content
				 */
				function(content){
					return content;
				},

				/**
				 * {{^ evalvariable }}
				 *
				 * ## Inverted Sections
				 * An inverted section begins with a caret (hat) and ends with a slash.
				 * That is {{^person}} begins a "person" inverted section while {{/person}} ends it.
				 *
				 * @param {String} content
				 */
				function(content){
					return content;
				},

				/**
				 * {{/ evalvariable }}
				 * Closes sections.
				 * @param {String} content
				 */
				function(content){
					var match = content.match(/^\/\w*$/);
					if(match){
						return '};';
					}

					return content;
				},

				/**
				 * Basic Context Miss Interpolation
				 *  {{cannot}}
				 * 	Failed context lookups should default to empty strings.
				 * Dotted Names - Broken Chains
				 *	{{a.b.c}}
    			 * 	Any falsey value prior to the last part of the name should yield ''.
				 */
				function(content) {
					// match only words and dots
					var match = content.match(/^\w*[\.|\w]*$/);
					if(match && content.length){

						var split = content.split('.'),
						// Handle context miss
						result = ['(typeof ' + split[0] + ' != "undefined" ? '];

						// Handle broken chains
						if (split.length > 1) {
							result.push('(');
							for (var i = 1; i < split.length; i++) {
								i > 1 && result.push(' && ');
								result.push(split.slice(0, i+1).join('.'));
							}
							result.push(') || ""');
						}
						else {
							result.push(split[0]);
						}

						return result.concat([' : "")']).join('');
					}

					return content;
				}
			]
			// Add helpers from scanner in now.
			.concat(can.view.Scanner.prototype.helpers)
		})
	});

	Mustache.Helpers = function( data, extras ) {
		this._data = data;
		this._extras = extras;
		extend(this, extras);
	};

	/**
	 * Register the view.
	 */
	can.view.register({
		suffix: "mustache",

		contentType: "x-mustache-template",

		// returns a `function` that renders the view.
		script: function( id, src ) {
			return "can.Mustache(function(_CONTEXT,_VIEW) { " + new Mustache({
				text: src,
				name: id
			}).template.out + " })";
		},

		renderer: function( id, text ) {
			return Mustache({
				text: text,
				name: id
			});
		}
	});

});<|endoftext|>


<commit_before>exports['Should correctly timeout mongos socket operation and then correctly re-execute'] = {
  metadata: {
    requires: {
      generators: true,
      topology: "single"
    }
  },

  test: function(configuration, test) {
    var Mongos = configuration.require.Mongos,
      ObjectId = configuration.require.BSON.ObjectId,
      co = require('co'),
      mockupdb = require('../../../mock');

    // Contain mock server
    var server = null;
    var running = true;
    // Current index for the ismaster
    var currentStep = 0;
    // Primary stop responding
    var stopRespondingPrimary = false;

    // Extend the object
    var extend = function(template, fields) {
      for(var name in template) fields[name] = template[name];
      return fields;
    }

    // Default message fields
    var defaultFields = {
      "ismaster" : true,
      "msg" : "isdbgrid",
      "maxBsonObjectSize" : 16777216,
      "maxMessageSizeBytes" : 48000000,
      "maxWriteBatchSize" : 1000,
      "localTime" : new Date(),
      "maxWireVersion" : 3,
      "minWireVersion" : 0,
      "ok" : 1
    }

    // Primary server states
    var serverIsMaster = [extend(defaultFields, {})];
    var timeoutPromise = function(timeout) {
      return new Promise(function(resolve, reject) {
        setTimeout(function() {
          resolve();
        }, timeout);
      });
    }

    // Boot the mock
    co(function*() {
      server = yield mockupdb.createServer(52000, 'localhost');

      // Primary state machine
      co(function*() {
        while(running) {
          var request = yield server.receive();

          // Get the document
          var doc = request.document;
          if(doc.ismaster && currentStep == 0) {
            request.reply(serverIsMaster[0]);
            currentStep += 1;
          } else if(doc.insert && currentStep == 1) {
            // Stop responding to any calls (emulate dropping packets on the floor)
            if(stopRespondingPrimary) {
              yield timeoutPromise(1500);
              continue;
            }

            currentStep += 1;
          } else if(doc.ismaster && currentStep == 2) {
            request.reply(serverIsMaster[0]);
          } else if(doc.insert && currentStep == 2) {
            request.reply({ok:1, n:doc.documents, lastOp: new Date()});
          }
        }
      });

      // Start dropping the packets
      setTimeout(function() {
        stopRespondingPrimary = true;
        currentIsMasterState = 1;
      }, 5000);
    });

    // Attempt to connect
    var server = new Mongos([
        { host: 'localhost', port: 52000 },
      ], {
      connectionTimeout: 3000,
      socketTimeout: 1000,
      haInterval: 1000,
      size: 1
    });

    // Add event listeners
    server.once('connect', function(_server) {
      _server.insert('test.test', [{created:new Date()}], function(err, r) {
        test.ok(err != null);

        // Let auto-reconnect run and restablish connection
        setTimeout(function() {
          _server.insert('test.test', [{created:new Date()}], function(err, r) {
            test.equal(null, err);

            server.destroy();
            running = false;
            test.done();
          });
        }, 1200)
      });
    });

    server.on('error', function(){});
    server.connect();
  }
}
<commit_msg>minor test fix for single mongos<commit_after>exports['Should correctly timeout mongos socket operation and then correctly re-execute'] = {
  metadata: {
    requires: {
      generators: true,
      topology: "single"
    }
  },

  test: function(configuration, test) {
    var Mongos = configuration.require.Mongos,
      ObjectId = configuration.require.BSON.ObjectId,
      co = require('co'),
      mockupdb = require('../../../mock');

    // Contain mock server
    var server = null;
    var running = true;
    // Current index for the ismaster
    var currentStep = 0;
    // Primary stop responding
    var stopRespondingPrimary = false;

    // Extend the object
    var extend = function(template, fields) {
      for(var name in template) fields[name] = template[name];
      return fields;
    }

    // Default message fields
    var defaultFields = {
      "ismaster" : true,
      "msg" : "isdbgrid",
      "maxBsonObjectSize" : 16777216,
      "maxMessageSizeBytes" : 48000000,
      "maxWriteBatchSize" : 1000,
      "localTime" : new Date(),
      "maxWireVersion" : 3,
      "minWireVersion" : 0,
      "ok" : 1
    }

    // Primary server states
    var serverIsMaster = [extend(defaultFields, {})];
    var timeoutPromise = function(timeout) {
      return new Promise(function(resolve, reject) {
        setTimeout(function() {
          resolve();
        }, timeout);
      });
    }

    // Boot the mock
    co(function*() {
      server = yield mockupdb.createServer(52000, 'localhost');

      // Primary state machine
      co(function*() {
        while(running) {
          var request = yield server.receive();

          // Get the document
          var doc = request.document;
          if(doc.ismaster && currentStep == 0) {
            request.reply(serverIsMaster[0]);
            currentStep += 1;
          } else if(doc.insert && currentStep == 1) {
            // Stop responding to any calls (emulate dropping packets on the floor)
            if(stopRespondingPrimary) {
              yield timeoutPromise(1500);
              continue;
            }

            currentStep += 1;
            stopRespondingPrimary = false;
          } else if(doc.ismaster && currentStep == 2) {
            request.reply(serverIsMaster[0]);
          } else if(doc.insert && currentStep == 2) {
            request.reply({ok:1, n:doc.documents, lastOp: new Date()});
          }
        }
      });

      // Start dropping the packets
      setTimeout(function() {
        stopRespondingPrimary = true;
        currentIsMasterState = 1;
      }, 1000);
    });

    // Attempt to connect
    var server = new Mongos([
        { host: 'localhost', port: 52000 },
      ], {
      connectionTimeout: 3000,
      socketTimeout: 1000,
      haInterval: 1000,
      size: 1
    });

    // Add event listeners
    server.once('connect', function(_server) {
      _server.insert('test.test', [{created:new Date()}], function(err, r) {
        test.ok(err != null);

        // Let auto-reconnect run and restablish connection
        setTimeout(function() {
          _server.insert('test.test', [{created:new Date()}], function(err, r) {
            test.equal(null, err);

            server.destroy();
            running = false;
            test.done();
          });
        }, 1200)
      });
    });

    server.on('error', function(){});
    server.connect();
  }
}
<|endoftext|>


<commit_before>'use strict';

function Sb3Theme() {
  var self = this;
  var vertexCounts = {
    "true": {
      "117": "stack",
      "81": "cap",
      "-1": "boolean",
      "-2": "number",
      "-4": "string",
      "77": "hat",
      "233": "cblock",
      "197": "cend",
      "-3": "celse"
    },
    "false": {
      "120": "stack",
      "80": "cap",
      "22": "boolean",
      "26": "number",
      "42": "string",
      "81": "hat",
      "241": "cblock",
      "201": "cend",
      "360": "celse"
    }
  };
  var categoryColors = {
    "#4CBF56": "operators",
    "#9966FF": "looks",
    "#4C97FF": "motion",
    "#FFAB19": "control",
    "#FFD500": "events"
  };

  // hang out until the SVG exists, then run the init function
  var initObserver = new MutationObserver(function(mutations) {
    if(document.querySelector('svg.blocklySvg')) {
      initObserver.disconnect();
      self._initSVG();
    }
  });

  if(document.querySelector('svg.blocklySvg')) {
    this._initSVG();
  } else {
    initObserver.observe(document.getElementsByTagName('html')[0], {childList: true, subtree: true}); // <body> doesn't always exist at runtime
  }

  this._initSVG = function() {
    this.svg = document.querySelector('svg.blocklySvg');
    this.dragsvg = document.querySelector('svg.blocklyDragSurface');

    this.NS = Blockly.SVG_NS;
    this.defs = this.svg.getElementsByTagName('defs')[0];
    for(let i = 0; i < onLoads.length; i++) {
      onLoads[i]();
    }

    //set up an observer for future changes to the document
    var observer = new MutationObserver(function(mutations) {
      self.newBlocks = [];
      self.horizontal = Blockly.mainWorkspace.horizontalLayout;

      for(let m = 0; m < mutations.length; m++) {
        for(let n = 0; n < mutations[m].addedNodes.length; n++) {
          let node = mutations[m].addedNodes[n]
          if(node.nodeType == 1 && node.classList.contains("blocklyDraggable")) {
            onChange(mutations[m].addedNodes[n]);
          }
        }
      }
      if(self.newBlocks.length) {
        for(let i in onChanges) {
          onChanges[i]();
        }
      }
    });

    observer.observe(this.svg, {childList: true, subtree: true});
  }

  var onChange = function(block) {
    self.newBlocks.push(block);

    let path = block.querySelector(":scope > path");

    let vertexCount = path.getAttribute("d").match(/,| /g).length;
    let shapeName = vertexCounts[self.horizontal.toString()][vertexCount];
    if(shapeName) {
      block.classList.add(shapeName);
    }

    let colorName = categoryColors[path.getAttribute("fill")];
    if(colorName) {
      block.classList.add(colorName);
    }

    let inputs = block.querySelectorAll(':scope > g > g.blocklyEditableText');
    for(let j = 0; j < inputs.length; j++) {
      let input = inputs[j].parentNode;
      input.classList.add("input");

      /*let inputVertexCount = input.getElementsByTagName("path")[0].getAttribute("d").match(/,| /g).length;
      let inputShapeName = vertexCounts[self.horizontal.toString()][inputVertexCount];
      if(inputShapeName) {
        input.classList.add(shapeName);
      }*/
    }
  }

  var onLoads = [];
  this.addInit = function(func) {
    onLoads.push(func);
  }
  var onChanges = [];
  this.addOnChange = function(func) {
    onChanges.push(func);
  }

  this.addFilter = function(filter) {
    var doc = new DOMParser().parseFromString(`<svg xmlns="` + this.NS + `">` + filter + `</svg>`, 'image/svg+xml');
    this.defs.appendChild( this.defs.ownerDocument.importNode(doc.documentElement.firstElementChild, true) );
  }

  this.getBlocksWithText = function(query) {
    var result = [];
    for(let i = 0; i < this.newBlocks.length; i++) {
      let text = "";
      let children = this.newBlocks[i].children;
      for(let j = 0; j < children.length; j++) {
        if(children[j].tagName.match(/text/i)) {
          text += " " + children[j].textContent;
        }
      }
      text = text.replace(/( |  +)/g, " ")
      if(text.match(query)) {
        result.push( this.newBlocks[i] );
      }
    }
    return result;
  }

  this.getBlocksWithIcon = function(query) {
    var result = [];
    for(let i = 0; i < this.newBlocks.length; i++) {
      let images = this.newBlocks[i].querySelectorAll(':scope > g > image');
      for(let j = 0; j < images.length; j++) {
        if(images[j].getAttribute('xlink:href').match(query)) {
          result.push( this.newBlocks[i] );
        }
      }
    }
    return result;
  }

}

window.sb3theme = new Sb3Theme();
<commit_msg>add sketchy input type detection<commit_after>'use strict';

function Sb3Theme() {
  var self = this;
  var vertexCounts = {
    "true": {
      "117": "stack",
      "81": "cap",
      "-1": "boolean",
      "-2": "number",
      "-4": "string",
      "77": "hat",
      "233": "cblock",
      "197": "cend",
      "-3": "celse"
    },
    "false": {
      "120": "stack",
      "80": "cap",
      "22": "boolean",
      "26": "number",
      "42": "string",
      "81": "hat",
      "241": "cblock",
      "201": "cend",
      "360": "celse"
    }
  };
  var inputVertexCounts = {
    "26": "number",
    "42": "string"
  };
  var categoryColors = {
    "#4CBF56": "operators",
    "#9966FF": "looks",
    "#4C97FF": "motion",
    "#FFAB19": "control",
    "#FFD500": "events"
  };

  // hang out until the SVG exists, then run the init function
  var initObserver = new MutationObserver(function(mutations) {
    if(document.querySelector('svg.blocklySvg')) {
      initObserver.disconnect();
      self._initSVG();
    }
  });

  if(document.querySelector('svg.blocklySvg')) {
    this._initSVG();
  } else {
    initObserver.observe(document.getElementsByTagName('html')[0], {childList: true, subtree: true}); // <body> doesn't always exist at runtime
  }

  this._initSVG = function() {
    this.svg = document.querySelector('svg.blocklySvg');
    this.dragsvg = document.querySelector('svg.blocklyDragSurface');

    this.NS = Blockly.SVG_NS;
    this.defs = this.svg.getElementsByTagName('defs')[0];
    for(let i = 0; i < onLoads.length; i++) {
      onLoads[i]();
    }

    //set up an observer for future changes to the document
    var observer = new MutationObserver(function(mutations) {
      self.newBlocks = [];
      self.horizontal = Blockly.mainWorkspace.horizontalLayout;

      for(let m = 0; m < mutations.length; m++) {
        for(let n = 0; n < mutations[m].addedNodes.length; n++) {
          let node = mutations[m].addedNodes[n]
          if(node.nodeType == 1 && node.classList.contains("blocklyDraggable")) {
            onChange(mutations[m].addedNodes[n]);
          }
        }
      }
      if(self.newBlocks.length) {
        for(let i in onChanges) {
          onChanges[i]();
        }
      }
    });

    observer.observe(this.svg, {childList: true, subtree: true});
  }

  var onChange = function(block) {
    self.newBlocks.push(block);

    let path = block.querySelector(":scope > path");

    let vertexCount = path.getAttribute("d").match(/,| /g).length;
    let shapeName = vertexCounts[self.horizontal.toString()][vertexCount];
    if(shapeName) {
      block.classList.add(shapeName);
    }

    let colorName = categoryColors[path.getAttribute("fill")];
    if(colorName) {
      block.classList.add(colorName);
    }

    //empty bool inputs should all have the same d attribute
    let bools = block.querySelectorAll(':scope > path[d="M 16,0  h 16 l 16,16 l -16,16 h -16 l -16,-16 l 16,-16 z"]');
    for(let j = 0; j < bools.length; j++) {
      bools[j].classList.add("input", "boolean");
    }

    let inputs = block.querySelectorAll(':scope > g > g.blocklyEditableText');
    for(let j = 0; j < inputs.length; j++) {
      let input = inputs[j].parentNode;
      input.classList.add("input");

      let inputVertexCount = input.getElementsByTagName("path")[0].getAttribute("d").match(/,| /g).length;
      let inputShapeName = inputVertexCounts[inputVertexCount];
      if(inputShapeName) {
        if(inputShapeName == "string") {
          if(inputs[j].querySelector("text tspan")) {
            input.classList.add("dropdown");
          } else {
            input.classList.add("string");
          }
        } else {
          input.classList.add(inputShapeName);
        }
      }
    }
  }

  var onLoads = [];
  this.addInit = function(func) {
    onLoads.push(func);
  }
  var onChanges = [];
  this.addOnChange = function(func) {
    onChanges.push(func);
  }

  this.addFilter = function(filter) {
    var doc = new DOMParser().parseFromString(`<svg xmlns="` + this.NS + `">` + filter + `</svg>`, 'image/svg+xml');
    this.defs.appendChild( this.defs.ownerDocument.importNode(doc.documentElement.firstElementChild, true) );
  }

  this.getBlocksWithText = function(query) {
    var result = [];
    for(let i = 0; i < this.newBlocks.length; i++) {
      let text = "";
      let children = this.newBlocks[i].children;
      for(let j = 0; j < children.length; j++) {
        if(children[j].tagName.match(/text/i)) {
          text += " " + children[j].textContent;
        }
      }
      text = text.replace(/( |  +)/g, " ")
      if(text.match(query)) {
        result.push( this.newBlocks[i] );
      }
    }
    return result;
  }

  this.getBlocksWithIcon = function(query) {
    var result = [];
    for(let i = 0; i < this.newBlocks.length; i++) {
      let images = this.newBlocks[i].querySelectorAll(':scope > g > image');
      for(let j = 0; j < images.length; j++) {
        if(images[j].getAttribute('xlink:href').match(query)) {
          result.push( this.newBlocks[i] );
        }
      }
    }
    return result;
  }

}

window.sb3theme = new Sb3Theme();
<|endoftext|>


<commit_before>import Fs            from 'original-fs';
import Path          from 'path';
import Crypto        from 'crypto';
import MusicMetadata from 'musicmetadata';
import Util          from '../../common/Util.js';
import FileUtil      from './FileUtil.js';

/**
 * Read the metadata from music file.
 */
export default class MusicMetadataReader {
  /**
   * Initialize instance.
   *
   * @param {String} saveImageDirPath Path of the folder in which to save the image.
   */
  constructor( saveImageDirPath ) {
    /**
     * Path of the folder in which to save the image
     * @type {String}
     */
    this._saveImageDirPath = saveImageDirPath;

    if( this._saveImageDirPath ) {
      FileUtil.mkdir( this._saveImageDirPath, ( err ) => {
        if( err ) {
          if( DEBUG ) { Util.error( err ); }
        }
      } );
    }
  }

  /**
   * Get the save image directory path.
   *
   * @return {String} Directory path.
   */
  get saveImageDirPath() {
    return this._saveImageDirPath;
  }

  /**
   * Set the save image directory path.
   *
   * @param {String} path Directory path.
   */
  set saveImageDirPath( path ) {
    this._saveImageDirPath = path;
  }

  /**
   * Read the metadata from music file.
   *
   * @param {String}   filePath Music file path.
   * @param {Function} callback Callback function.
   */
  read( filePath, callback ) {
    Promise.resolve()
    .then( () => {
      return this._readMetadata( filePath );
    } )
    .then( ( params ) => {
      return this._readImage( params );
    } )
    .then( ( params ) => {
      const m     = params.metadata;
      const music = {
        path:     filePath,
        artist:   ( 0 < m.artist.length ? m.artist[ 0 ] : '' ),
        album:    m.album || '',
        title:    m.title || '',
        year:     m.year || '',
        track:    ( m.track ? m.track.no : 1 ),
        disk:     ( m.disk  ? m.disk.no : 1 ),
        genre:    ( 0 < m.genre.length ? m.genre[ 0 ] : '' ),
        duration: m.duration,
        image:    params.image
      };

      callback( null, music );
    } )
    .catch( ( err ) => {
      callback( err );
    } );
  }

  /**
   * Read the metadata from music file.
   *
   * @param {String} filePath Music file path.
   *
   * @return {Promise} Instance of Promise.
   */
  _readMetadata( filePath ) {
    return new Promise( ( resolve, reject ) => {
      const stream = Fs.createReadStream( filePath );
      MusicMetadata( stream, { duration: true }, ( err, metadata ) => {
        if( err ) {
          return reject( err );
        }

        resolve( { metadata } );
      } );
    } );
  }

  /**
   * Read and save the image from music metadata.
   *
   * @param {Object} metadata Music metadata.
   */
  _readImage( params ) {
    return new Promise( ( resolve ) => {
      const picture = params.metadata.picture;
      if( !( this._saveImageDirPath && picture && 0 < picture.length ) ) {
        return resolve( params );
      }

      const fileName = this._getHash( picture[ 0 ].data ) + '.' + picture[ 0 ].format;
      const filePath = Path.join( this._saveImageDirPath, fileName );

      // Save image file
      FileUtil.writeFile( filePath, picture[ 0 ].data, ( err ) => {
        const newParams = params;
        if( err ) {
          if( DEBUG ) { Util.error( err ); }
        } else {
          newParams.image = filePath;
        }

        resolve( newParams );
      } );
    } );
  }

  /**
   * Get the SHA-1 hash from binary data.
   *
   * @param {ArrayBuffer} data Ninary data.
   *
   * @return {String} Hash string.
   */
  _getHash( data ) {
    const sha = Crypto.createHash('sha1');

    sha.update( data );
    return sha.digest( 'hex' );
  }
}
<commit_msg>#30 Change the default value of a track and disk<commit_after>import Fs            from 'original-fs';
import Path          from 'path';
import Crypto        from 'crypto';
import MusicMetadata from 'musicmetadata';
import Util          from '../../common/Util.js';
import FileUtil      from './FileUtil.js';

/**
 * Read the metadata from music file.
 */
export default class MusicMetadataReader {
  /**
   * Initialize instance.
   *
   * @param {String} saveImageDirPath Path of the folder in which to save the image.
   */
  constructor( saveImageDirPath ) {
    /**
     * Path of the folder in which to save the image
     * @type {String}
     */
    this._saveImageDirPath = saveImageDirPath;

    if( this._saveImageDirPath ) {
      FileUtil.mkdir( this._saveImageDirPath, ( err ) => {
        if( err ) {
          if( DEBUG ) { Util.error( err ); }
        }
      } );
    }
  }

  /**
   * Get the save image directory path.
   *
   * @return {String} Directory path.
   */
  get saveImageDirPath() {
    return this._saveImageDirPath;
  }

  /**
   * Set the save image directory path.
   *
   * @param {String} path Directory path.
   */
  set saveImageDirPath( path ) {
    this._saveImageDirPath = path;
  }

  /**
   * Read the metadata from music file.
   *
   * @param {String}   filePath Music file path.
   * @param {Function} callback Callback function.
   */
  read( filePath, callback ) {
    Promise.resolve()
    .then( () => {
      return this._readMetadata( filePath );
    } )
    .then( ( params ) => {
      return this._readImage( params );
    } )
    .then( ( params ) => {
      const m     = params.metadata;
      const music = {
        path:     filePath,
        artist:   ( 0 < m.artist.length ? m.artist[ 0 ] : '' ),
        album:    m.album || '',
        title:    m.title || '',
        year:     m.year || '',
        track:    ( m.track && 0 < m.track.no ? m.track.no : 1 ),
        disk:     ( m.disk  && 0 < m.disk.no  ? m.disk.no  : 1 ),
        genre:    ( 0 < m.genre.length ? m.genre[ 0 ] : '' ),
        duration: m.duration,
        image:    params.image
      };

      callback( null, music );
    } )
    .catch( ( err ) => {
      callback( err );
    } );
  }

  /**
   * Read the metadata from music file.
   *
   * @param {String} filePath Music file path.
   *
   * @return {Promise} Instance of Promise.
   */
  _readMetadata( filePath ) {
    return new Promise( ( resolve, reject ) => {
      const stream = Fs.createReadStream( filePath );
      MusicMetadata( stream, { duration: true }, ( err, metadata ) => {
        if( err ) {
          return reject( err );
        }

        resolve( { metadata } );
      } );
    } );
  }

  /**
   * Read and save the image from music metadata.
   *
   * @param {Object} metadata Music metadata.
   */
  _readImage( params ) {
    return new Promise( ( resolve ) => {
      const picture = params.metadata.picture;
      if( !( this._saveImageDirPath && picture && 0 < picture.length ) ) {
        return resolve( params );
      }

      const fileName = this._getHash( picture[ 0 ].data ) + '.' + picture[ 0 ].format;
      const filePath = Path.join( this._saveImageDirPath, fileName );

      // Save image file
      FileUtil.writeFile( filePath, picture[ 0 ].data, ( err ) => {
        const newParams = params;
        if( err ) {
          if( DEBUG ) { Util.error( err ); }
        } else {
          newParams.image = filePath;
        }

        resolve( newParams );
      } );
    } );
  }

  /**
   * Get the SHA-1 hash from binary data.
   *
   * @param {ArrayBuffer} data Ninary data.
   *
   * @return {String} Hash string.
   */
  _getHash( data ) {
    const sha = Crypto.createHash('sha1');

    sha.update( data );
    return sha.digest( 'hex' );
  }
}
<|endoftext|>


<commit_before>import Ember from 'ember';
import D3Component from './d3-component';
import Translatable from '../mixins/translatable';

const { on, observer, computed } = Ember;

export default D3Component.extend(Translatable, {
  tagName: 'g',
  attributeBindings: ['transform'],

  init() {
    this.set('axis', d3.svg.axis());
    this._super.apply(this, arguments);
  },

  oritntationChanged: on('init', observer('orientation', function() {
    let orientation = this.get('orientation');

    if(orientation !== undefined) {
      this.get('axis').orient(orientation);
      Ember.run.once(this, this.updateAxis);
    }
  })),

  scaleChanged: on('init', observer('scale.scale', function() {
    let scale = this.get('scale.scale');

    if(scale !== undefined) {
      this.get('axis').scale(scale);
      Ember.run.once(this, this.updateAxis);
    }
  })),

  ticksChanged: on('init', observer('ticks', function() {
    let ticks = this.get('ticks');

    if(ticks !== undefined) {
      this.get('axis').ticks(ticks);
      Ember.run.once(this, this.updateAxis);
    }
  })),

  tickValuesChanged: on('init', observer('tickValues', function() {
    let tickValues = this.get('tickValues');

    if(tickValues !== undefined) {
      this.get('axis').tickValues(tickValues);
      Ember.run.once(this, this.updateAxis);
    }
  })),

  tickSizeChanged: on('init', observer('tickSize', function() {
    let tickSize = this.get('tickSize');

    if(tickSize !== undefined) {
      this.get('axis').tickSize(tickSize);
      Ember.run.once(this, this.updateAxis);
    }
  })),

  innerTickSizeChanged: on('init', observer('innerTickSize', function() {
    let innerTickSize = this.get('innerTickSize');

    if(innerTickSize !== undefined) {
      this.get('axis').innerTickSize(innerTickSize);
      Ember.run.once(this, this.updateAxis);
    }
  })),

  outerTickSizeChanged: on('init', observer('outerTickSize', function() {
    let outerTickSize = this.get('outerTickSize');

    if(outerTickSize !== undefined) {
      this.get('axis').outerTickSize(outerTickSize);
      Ember.run.once(this, this.updateAxis);
    }
  })),

  tickFormatChanged: on('init', observer('tickFormat', function() {
    let tickFormat = this.get('tickFormat');

    if(tickFormat !== undefined) {
      this.get('axis').tickFormat(tickFormat);
      Ember.run.once(this, this.updateAxis);
    }
  })),

  tickPaddingChanged: on('init', observer('tickPadding', function() {
    let tickPadding = this.get('tickPadding');

    if(tickPadding !== undefined) {
      this.get('axis').tickPadding(tickPadding);
      Ember.run.once(this, this.updateAxis);
    }
  })),

  translation: computed('translateX', 'translateY', function() {
    let translateX = this.get('translateX');
    let translateY = this.get('translateY');

    if(translateX !== undefined && translateY !== undefined) {
      return `translate(${translateX}, ${translateY})`;
    }
  }),

  transform: computed.alias('translation'),

  updateAxis() {
    this.get('d3Selection').transition().call(this.get('axis'));
  }
});
<commit_msg>Add support for axis tickSubdivide<commit_after>import Ember from 'ember';
import D3Component from './d3-component';
import Translatable from '../mixins/translatable';

const { on, observer, computed } = Ember;

export default D3Component.extend(Translatable, {
  tagName: 'g',
  attributeBindings: ['transform'],

  init() {
    this.set('axis', d3.svg.axis());
    this._super.apply(this, arguments);
  },

  oritntationChanged: on('init', observer('orientation', function() {
    let orientation = this.get('orientation');

    if(orientation !== undefined) {
      this.get('axis').orient(orientation);
      Ember.run.once(this, this.updateAxis);
    }
  })),

  scaleChanged: on('init', observer('scale.scale', function() {
    let scale = this.get('scale.scale');

    if(scale !== undefined) {
      this.get('axis').scale(scale);
      Ember.run.once(this, this.updateAxis);
    }
  })),

  ticksChanged: on('init', observer('ticks', function() {
    let ticks = this.get('ticks');

    if(ticks !== undefined) {
      this.get('axis').ticks(ticks);
      Ember.run.once(this, this.updateAxis);
    }
  })),

  tickValuesChanged: on('init', observer('tickValues', function() {
    let tickValues = this.get('tickValues');

    if(tickValues !== undefined) {
      this.get('axis').tickValues(tickValues);
      Ember.run.once(this, this.updateAxis);
    }
  })),

  tickSizeChanged: on('init', observer('tickSize', function() {
    let tickSize = this.get('tickSize');

    if(tickSize !== undefined) {
      this.get('axis').tickSize(tickSize);
      Ember.run.once(this, this.updateAxis);
    }
  })),

  innerTickSizeChanged: on('init', observer('innerTickSize', function() {
    let innerTickSize = this.get('innerTickSize');

    if(innerTickSize !== undefined) {
      this.get('axis').innerTickSize(innerTickSize);
      Ember.run.once(this, this.updateAxis);
    }
  })),

  outerTickSizeChanged: on('init', observer('outerTickSize', function() {
    let outerTickSize = this.get('outerTickSize');

    if(outerTickSize !== undefined) {
      this.get('axis').outerTickSize(outerTickSize);
      Ember.run.once(this, this.updateAxis);
    }
  })),

  tickFormatChanged: on('init', observer('tickFormat', function() {
    let tickFormat = this.get('tickFormat');

    if(tickFormat !== undefined) {
      this.get('axis').tickFormat(tickFormat);
      Ember.run.once(this, this.updateAxis);
    }
  })),

  tickPaddingChanged: on('init', observer('tickPadding', function() {
    let tickPadding = this.get('tickPadding');

    if(tickPadding !== undefined) {
      this.get('axis').tickPadding(tickPadding);
      Ember.run.once(this, this.updateAxis);
    }
  })),

  tickSubdivideChanged: on('init', observer('tickSubdivide', function() {
    let tickSubdivide = this.get('tickSubdivide');

    if(tickSubdivide !== undefined) {
      this.get('axis').tickSubdivide(tickSubdivide);
      Ember.run.once(this, this.updateAxis);
    }
  })),

  translation: computed('translateX', 'translateY', function() {
    let translateX = this.get('translateX');
    let translateY = this.get('translateY');

    if(translateX !== undefined && translateY !== undefined) {
      return `translate(${translateX}, ${translateY})`;
    }
  }),

  transform: computed.alias('translation'),

  updateAxis() {
    this.get('d3Selection').transition().call(this.get('axis'));
  }
});
<|endoftext|>


<commit_before>'use strict';

exports.up = function(knex, Promise) {
  return knex.schema.
    createTable('paths', function (t) {
      t.increments('id');
      t.text('curator').notNullable();
      t.text('curator_org');
      t.specificType('collaborators', 'text[]');
      t.text('name').notNullable();
      t.text('description');
      t.text('version').notNullable();
      t.text('license');
      t.text('image_url');
      t.text('asset_urls');
      t.text('keywords');
      t.timestamp('created').notNullable().defaultTo(knex.raw('CURRENT_TIMESTAMP'));
    });
};

exports.down = function(knex, Promise) {
  return knex.schema.dropTableIfExists('paths');
};
<commit_msg>Update keyword and asset_url fields to accept arrays.<commit_after>'use strict';

exports.up = function(knex, Promise) {
  return knex.schema.
    createTable('paths', function (t) {
      t.increments('id');
      t.text('curator').notNullable();
      t.text('curator_org');
      t.specificType('collaborators', 'text[]');
      t.text('name').notNullable();
      t.text('description');
      t.text('version').notNullable();
      t.text('license');
      t.text('image_url');
      t.specificType('asset_urls', 'text[]');
      t.specificType('keywords', 'text[]');
      t.timestamp('created').notNullable().defaultTo(knex.raw('CURRENT_TIMESTAMP'));
    });
};

exports.down = function(knex, Promise) {
  return knex.schema.dropTableIfExists('paths');
};
<|endoftext|>