<commit_before>import { Readable } from "stream";
import fs from "fs";
import path from "path";
import crypto from "crypto";
import SVGIcons2SVGFontStream from "svgicons2svgfont";
import cosmiconfig from "cosmiconfig";
import pLimit from "p-limit";
import defaultMetadataProvider from "svgicons2svgfont/src/metadata";
import fileSorter from "svgicons2svgfont/src/filesorter";
import globby from "globby";
import deepmerge from "deepmerge";
import nunjucks from "nunjucks";
import svg2ttf from "svg2ttf";
import ttf2eot from "ttf2eot";
import ttf2woff from "ttf2woff";
import wawoff2 from "wawoff2";
import xml2js from "xml2js";

async function buildConfig(options) {
  let searchPath = process.cwd();
  let configPath = null;

  if (options.configFile) {
    searchPath = null;
    configPath = path.resolve(process.cwd(), options.configFile);
  }

  const configExplorer = cosmiconfig("webfont");
  const config = await (configPath
    ? configExplorer.load(configPath)
    : configExplorer.search(searchPath));

  if (!config) {
    return {};
  }

  return config;
}

function getGlyphsData(files, options) {
  const metadataProvider =
    options.metadataProvider ||
    defaultMetadataProvider({
      prependUnicode: options.prependUnicode,
      startUnicode: options.startUnicode,
    });

  const xmlParser = new xml2js.Parser();
  const throttle = pLimit(options.maxConcurrency);

  return Promise.all(
    files.map((srcPath) =>
      throttle(
        () =>
          new Promise((resolve, reject) => {
            const glyph = fs.createReadStream(srcPath);
            let glyphContents = "";

            // eslint-disable-next-line no-promise-executor-return
            return glyph
              .on("error", (glyphError) => reject(glyphError))
              .on("data", (data) => {
                glyphContents += data.toString();
              })
              .on("end", () => {
                // Maybe bug in xml2js
                if (glyphContents.length === 0) {
                  return reject(new Error(`Empty file ${srcPath}`));
                }

                return xmlParser.parseString(glyphContents, (error) => {
                  if (error) {
                    return reject(error);
                  }

                  const glyphData = {
                    contents: glyphContents,
                    srcPath,
                  };

                  return resolve(glyphData);
                });
              });
          })
      )
    )
  ).then((glyphsData) => {
    const sortedGlyphsData = options.sort
      ? glyphsData.sort((fileA, fileB) =>
          fileSorter(fileA.srcPath, fileB.srcPath)
        )
      : glyphsData;

    return Promise.all(
      sortedGlyphsData.map(
        (glyphData) =>
          new Promise((resolve, reject) => {
            metadataProvider(glyphData.srcPath, (error, metadata) => {
              if (error) {
                return reject(error);
              }

              glyphData.metadata = metadata;

              return resolve(glyphData);
            });
          })
      )
    );
  });
}

function toSvg(glyphsData, options) {
  let result = "";

  return new Promise((resolve, reject) => {
    const fontStream = new SVGIcons2SVGFontStream({
      ascent: options.ascent,
      centerHorizontally: options.centerHorizontally,
      descent: options.descent,
      fixedWidth: options.fixedWidth,
      fontHeight: options.fontHeight,
      fontId: options.fontId,
      fontName: options.fontName,
      fontStyle: options.fontStyle,
      fontWeight: options.fontWeight,
      // eslint-disable-next-line no-console, no-empty-function
      log: options.verbose ? console.log.bind(console) : () => {},
      metadata: options.metadata,
      normalize: options.normalize,
      round: options.round,
    })
      .on("finish", () => resolve(result))
      .on("data", (data) => {
        result += data;
      })
      .on("error", (error) => reject(error));

    glyphsData.forEach((glyphData) => {
      const glyphStream = new Readable();

      glyphStream.push(glyphData.contents);
      glyphStream.push(null);

      glyphStream.metadata = glyphData.metadata;

      fontStream.write(glyphStream);
    });

    fontStream.end();
  });
}

function toTtf(buffer, options) {
  return Buffer.from(svg2ttf(buffer, options).buffer);
}

function toEot(buffer) {
  return Buffer.from(ttf2eot(buffer).buffer);
}

function toWoff(buffer, options) {
  return Buffer.from(ttf2woff(buffer, options).buffer);
}

function toWoff2(buffer) {
  return wawoff2.compress(buffer);
}

export default async function (initialOptions) {
  if (!initialOptions || !initialOptions.files) {
    throw new Error("You must pass webfont a `files` glob");
  }

  let options = Object.assign(
    {},
    {
      ascent: undefined, // eslint-disable-line no-undefined
      centerHorizontally: false,
      descent: 0,
      fixedWidth: false,
      fontHeight: null,
      fontId: null,
      fontName: "webfont",
      fontStyle: "",
      fontWeight: "",
      formats: ["svg", "ttf", "eot", "woff", "woff2"],
      formatsOptions: {
        ttf: {
          copyright: null,
          ts: null,
          version: null,
        },
      },
      glyphTransformFn: null,
      // Maybe allow setup from CLI
      // This is usually less than file read maximums while staying performance
      maxConcurrency: 100,
      metadata: null,
      metadataProvider: null,
      normalize: false,
      prependUnicode: false,
      round: 10e12,
      sort: true,
      startUnicode: 0xea01,
      template: null,
      templateClassName: null,
      templateFontName: null,
      templateFontPath: "./",
      verbose: false,
    },
    initialOptions
  );

  const config = await buildConfig({
    configFile: options.configFile,
  });

  if (Object.keys(config).length > 0) {
    // eslint-disable-next-line require-atomic-updates
    options = deepmerge(options, config.config, {
      arrayMerge: (_destinationArray, sourceArray, _opts) => sourceArray,
    });
    // eslint-disable-next-line require-atomic-updates
    options.filePath = config.filepath;
  }

  const foundFiles = await globby([].concat(options.files));
  const filteredFiles = foundFiles.filter(
    (foundFile) => path.extname(foundFile) === ".svg"
  );

  if (filteredFiles.length === 0) {
    throw new Error("Files glob patterns specified did not match any files");
  }

  const result = {};

  result.glyphsData = await getGlyphsData(filteredFiles, options);
  result.svg = await toSvg(result.glyphsData, options);
  result.ttf = toTtf(
    result.svg,
    options.formatsOptions && options.formatsOptions.ttf
      ? options.formatsOptions.ttf
      : {}
  );

  result.hash = crypto.createHash("md5").update(result.svg).digest("hex");

  if (options.formats.includes("eot")) {
    result.eot = toEot(result.ttf);
  }

  if (options.formats.includes("woff")) {
    result.woff = toWoff(result.ttf, { metadata: options.metadata });
  }

  if (options.formats.includes("woff2")) {
    result.woff2 = await toWoff2(result.ttf);
  }

  if (options.template) {
    const templateDirectory = path.resolve(__dirname, "../templates");
    const buildInTemplates = {
      css: { path: path.join(templateDirectory, "template.css.njk") },
      html: { path: path.join(templateDirectory, "template.html.njk") },
      scss: { path: path.join(templateDirectory, "template.scss.njk") },
      styl: { path: path.join(templateDirectory, "template.styl.njk") },
    };

    let templateFilePath;

    if (Object.keys(buildInTemplates).includes(options.template)) {
      result.usedBuildInTemplate = true;

      nunjucks.configure(path.resolve(__dirname, "../"));

      templateFilePath = `${templateDirectory}/template.${options.template}.njk`;
    } else {
      const resolvedTemplateFilePath = path.resolve(options.template);

      nunjucks.configure(path.dirname(resolvedTemplateFilePath));

      templateFilePath = path.resolve(resolvedTemplateFilePath);
    }

    const hashOption = options.addHashInFontUrl ? { hash: result.hash } : {};
    const nunjucksOptions = deepmerge.all([
      {
        glyphs: result.glyphsData.map((glyphData) => {
          if (typeof options.glyphTransformFn === "function") {
            glyphData.metadata = options.glyphTransformFn(glyphData.metadata);
          }

          return glyphData.metadata;
        }),
      },
      options,
      {
        className: options.templateClassName || options.fontName,
        fontName: options.templateFontName || options.fontName,
        fontPath: options.templateFontPath.replace(/\/?$/, "/"),
      },
      hashOption,
    ]);

    result.template = nunjucks.render(templateFilePath, nunjucksOptions);
  }

  if (!options.formats.includes("svg")) {
    delete result.svg;
  }

  if (!options.formats.includes("ttf")) {
    delete result.ttf;
  }

  result.config = options;

  return result;
}
<commit_msg>feat(standalone): add support to JSON template and cacheString<commit_after>import { Readable } from "stream";
import fs from "fs";
import path from "path";
import crypto from "crypto";
import SVGIcons2SVGFontStream from "svgicons2svgfont";
import cosmiconfig from "cosmiconfig";
import pLimit from "p-limit";
import defaultMetadataProvider from "svgicons2svgfont/src/metadata";
import fileSorter from "svgicons2svgfont/src/filesorter";
import globby from "globby";
import deepmerge from "deepmerge";
import nunjucks from "nunjucks";
import svg2ttf from "svg2ttf";
import ttf2eot from "ttf2eot";
import ttf2woff from "ttf2woff";
import wawoff2 from "wawoff2";
import xml2js from "xml2js";

async function buildConfig(options) {
  let searchPath = process.cwd();
  let configPath = null;

  if (options.configFile) {
    searchPath = null;
    configPath = path.resolve(process.cwd(), options.configFile);
  }

  const configExplorer = cosmiconfig("webfont");
  const config = await (configPath
    ? configExplorer.load(configPath)
    : configExplorer.search(searchPath));

  if (!config) {
    return {};
  }

  return config;
}

function getGlyphsData(files, options) {
  const metadataProvider =
    options.metadataProvider ||
    defaultMetadataProvider({
      prependUnicode: options.prependUnicode,
      startUnicode: options.startUnicode,
    });

  const xmlParser = new xml2js.Parser();
  const throttle = pLimit(options.maxConcurrency);

  return Promise.all(
    files.map((srcPath) =>
      throttle(
        () =>
          new Promise((resolve, reject) => {
            const glyph = fs.createReadStream(srcPath);
            let glyphContents = "";

            // eslint-disable-next-line no-promise-executor-return
            return glyph
              .on("error", (glyphError) => reject(glyphError))
              .on("data", (data) => {
                glyphContents += data.toString();
              })
              .on("end", () => {
                // Maybe bug in xml2js
                if (glyphContents.length === 0) {
                  return reject(new Error(`Empty file ${srcPath}`));
                }

                return xmlParser.parseString(glyphContents, (error) => {
                  if (error) {
                    return reject(error);
                  }

                  const glyphData = {
                    contents: glyphContents,
                    srcPath,
                  };

                  return resolve(glyphData);
                });
              });
          })
      )
    )
  ).then((glyphsData) => {
    const sortedGlyphsData = options.sort
      ? glyphsData.sort((fileA, fileB) =>
          fileSorter(fileA.srcPath, fileB.srcPath)
        )
      : glyphsData;

    return Promise.all(
      sortedGlyphsData.map(
        (glyphData) =>
          new Promise((resolve, reject) => {
            metadataProvider(glyphData.srcPath, (error, metadata) => {
              if (error) {
                return reject(error);
              }

              metadata.unicode.push(metadata.name.replace(/-/g, "_"));
              glyphData.metadata = metadata;

              return resolve(glyphData);
            });
          })
      )
    );
  });
}

function toSvg(glyphsData, options) {
  let result = "";

  return new Promise((resolve, reject) => {
    const fontStream = new SVGIcons2SVGFontStream({
      ascent: options.ascent,
      centerHorizontally: options.centerHorizontally,
      descent: options.descent,
      fixedWidth: options.fixedWidth,
      fontHeight: options.fontHeight,
      fontId: options.fontId,
      fontName: options.fontName,
      fontStyle: options.fontStyle,
      fontWeight: options.fontWeight,
      // eslint-disable-next-line no-console, no-empty-function
      log: options.verbose ? console.log.bind(console) : () => {},
      metadata: options.metadata,
      normalize: options.normalize,
      round: options.round,
    })
      .on("finish", () => resolve(result))
      .on("data", (data) => {
        result += data;
      })
      .on("error", (error) => reject(error));

    glyphsData.forEach((glyphData) => {
      const glyphStream = new Readable();

      glyphStream.push(glyphData.contents);
      glyphStream.push(null);

      glyphStream.metadata = glyphData.metadata;

      fontStream.write(glyphStream);
    });

    fontStream.end();
  });
}

function toTtf(buffer, options) {
  return Buffer.from(svg2ttf(buffer, options).buffer);
}

function toEot(buffer) {
  return Buffer.from(ttf2eot(buffer).buffer);
}

function toWoff(buffer, options) {
  return Buffer.from(ttf2woff(buffer, options).buffer);
}

function toWoff2(buffer) {
  return wawoff2.compress(buffer);
}

export default async function (initialOptions) {
  if (!initialOptions || !initialOptions.files) {
    throw new Error("You must pass webfont a `files` glob");
  }

  let options = Object.assign(
    {},
    {
      ascent: undefined, // eslint-disable-line no-undefined
      centerHorizontally: false,
      descent: 0,
      fixedWidth: false,
      fontHeight: null,
      fontId: null,
      fontName: "webfont",
      fontStyle: "",
      fontWeight: "",
      formats: ["svg", "ttf", "eot", "woff", "woff2"],
      formatsOptions: {
        ttf: {
          copyright: null,
          ts: null,
          version: null,
        },
      },
      glyphTransformFn: null,
      // Maybe allow setup from CLI
      // This is usually less than file read maximums while staying performance
      maxConcurrency: 100,
      metadata: null,
      metadataProvider: null,
      normalize: false,
      prependUnicode: false,
      round: 10e12,
      sort: true,
      startUnicode: 0xea01,
      template: null,
      templateClassName: null,
      templateFontName: null,
      templateFontPath: "./",
      templateCacheString: null,
      verbose: false,
    },
    initialOptions
  );

  const config = await buildConfig({
    configFile: options.configFile,
  });

  if (Object.keys(config).length > 0) {
    // eslint-disable-next-line require-atomic-updates
    options = deepmerge(options, config.config, {
      arrayMerge: (_destinationArray, sourceArray, _opts) => sourceArray,
    });
    // eslint-disable-next-line require-atomic-updates
    options.filePath = config.filepath;
  }

  const foundFiles = await globby([].concat(options.files));
  const filteredFiles = foundFiles.filter(
    (foundFile) => path.extname(foundFile) === ".svg"
  );

  if (filteredFiles.length === 0) {
    throw new Error("Files glob patterns specified did not match any files");
  }

  const result = {};

  result.glyphsData = await getGlyphsData(filteredFiles, options);
  result.svg = await toSvg(result.glyphsData, options);
  result.ttf = toTtf(
    result.svg,
    options.formatsOptions && options.formatsOptions.ttf
      ? options.formatsOptions.ttf
      : {}
  );

  result.hash = crypto.createHash("md5").update(result.svg).digest("hex");

  if (options.formats.includes("eot")) {
    result.eot = toEot(result.ttf);
  }

  if (options.formats.includes("woff")) {
    result.woff = toWoff(result.ttf, { metadata: options.metadata });
  }

  if (options.formats.includes("woff2")) {
    result.woff2 = await toWoff2(result.ttf);
  }

  if (options.template) {
    const templateDirectory = path.resolve(__dirname, "../templates");
    const buildInTemplates = {
      css: { path: path.join(templateDirectory, "template.css.njk") },
      html: { path: path.join(templateDirectory, "template.html.njk") },
      json: { path: path.join(templateDirectory, "template.json.njk") },
      scss: { path: path.join(templateDirectory, "template.scss.njk") },
      styl: { path: path.join(templateDirectory, "template.styl.njk") },
    };

    let templateFilePath;

    if (Object.keys(buildInTemplates).includes(options.template)) {
      result.usedBuildInTemplate = true;

      nunjucks.configure(path.resolve(__dirname, "../"));

      templateFilePath = `${templateDirectory}/template.${options.template}.njk`;
    } else {
      const resolvedTemplateFilePath = path.resolve(options.template);

      nunjucks.configure(path.dirname(resolvedTemplateFilePath));

      templateFilePath = path.resolve(resolvedTemplateFilePath);
    }

    const hashOption = options.addHashInFontUrl ? { hash: result.hash } : {};
    const nunjucksOptions = deepmerge.all([
      {
        glyphs: result.glyphsData.map((glyphData) => {
          if (typeof options.glyphTransformFn === "function") {
            glyphData.metadata = options.glyphTransformFn(glyphData.metadata);
          }

          return glyphData.metadata;
        }),
      },
      options,
      {
        cacheString: options.templateCacheString || Date.now(),
        className: options.templateClassName || options.fontName,
        fontName: options.templateFontName || options.fontName,
        fontPath: options.templateFontPath.replace(/\/?$/, "/"),
      },
      hashOption,
    ]);

    result.template = nunjucks.render(templateFilePath, nunjucksOptions);
  }

  if (!options.formats.includes("svg")) {
    delete result.svg;
  }

  if (!options.formats.includes("ttf")) {
    delete result.ttf;
  }

  result.config = options;

  return result;
}
<|endoftext|>


<commit_before>import m from 'mithril';
import _ from 'underscore';
import moment from 'moment';

const subscriptionsPerMonthTable = {
    controller() {
        return {
            emptyRow: {
                total_amount: 0,
                new_amount: 0,
                total_subscriptions: 0,
                new_subscriptions: 0
            }
        };
    },

    view(ctrl, args) {
        return m('div', [
            m(".fontsize-large.fontweight-semibold.u-text-center.u-marginbottom-30[id='origem']",
                'Crescimento mensal das assinaturas'
            ),
            m('.table-outer.u-marginbottom-60', [
                m('.table-row.fontweight-semibold.fontsize-smaller.header.lineheight-tighter.w-row', [
                    m('.table-col.w-col.w-col-4.w-col-small-4.w-col-tiny-4',
                        m('div',
                            'Mês'
                        )
                    ),
                    m('.table-col.w-hidden-small.w-hidden-tiny.w-col.w-col-2.w-col-small-2.w-col-tiny-2',
                        m('div', [
                            'Novos assinantes',
                            m.trust(' ')
                        ])
                    ),
                    m('.table-col.w-hidden-small.w-hidden-tiny.w-col.w-col-2.w-col-small-2.w-col-tiny-2',
                        m('div',
                            'Receita com novos assinantes'
                        )
                    ),
                    m('.w-col.w-col-2.w-col-small-2.w-col-tiny-2',
                        m('div',
                            'Assinantes totais'
                        )
                    ),
                    m('.w-col.w-col-2.w-col-small-2.w-col-tiny-2',
                        m('div',
                            'Receita total'
                        )
                    )
                ]),
                m('.table-inner.fontsize-small', [!args.data ? '' :
                    _.map(_.groupBy(args.data, 'month'), (subscription) => {
                        const slip = _.filter(subscription, sub => sub.payment_method === 'boleto')[0] || ctrl.emptyRow;
                        const credit_card = _.filter(subscription, sub => sub.payment_method === 'credit_card')[0] || ctrl.emptyRow;

                        return m('.table-row.w-row', [
                            m('.table-col.w-col.w-col-4.w-col-small-4.w-col-stack.w-col-tiny-4', [
                                m('.fontweight-semibold',
                                  moment(subscription[0].month).format('MMMM YYYY')
                                ),
                                m('.fontsize-smallest.fontcolor-secondary',
                                    'Cartão de crédito'
                                ),
                                m('.fontsize-smallest.fontcolor-secondary',
                                    'Boleto bancário'
                                )
                            ]),
                            m('.table-col.w-hidden-small.w-hidden-tiny.w-col.w-col-2.w-col-small-2.w-col-stack.w-col-tiny-2', [
                                m('.fontweight-semibold',
                                    slip.new_subscriptions + credit_card.new_subscriptions
                                ),
                                m('.fontsize-smallest.fontcolor-secondary',
                                    credit_card.new_subscriptions
                                ),
                                m('.fontsize-smallest.fontcolor-secondary',
                                    slip.new_subscriptions
                                )
                            ]),
                            m('.table-col.w-hidden-small.w-hidden-tiny.w-col.w-col-2.w-col-small-2.w-col-stack.w-col-tiny-2', [
                                m('.fontweight-semibold',
                                    `R$${(slip.new_amount + credit_card.new_amount) / 100}`
                                ),
                                m('.fontsize-smallest.fontcolor-secondary',
                                    `R$${(credit_card.new_amount) / 100}`
                                ),
                                m('.fontsize-smallest.fontcolor-secondary',
                                    `R$${(slip.new_amount) / 100}`
                                )
                            ]),
                            m('.w-col.w-col-2.w-col-small-2.w-col-stack.w-col-tiny-2', [
                                m('.fontweight-semibold',
                                    slip.total_subscriptions + credit_card.total_subscriptions
                                ),
                                m('.fontsize-smallest.fontcolor-secondary',
                                    credit_card.total_subscriptions
                                ),
                                m('.fontsize-smallest.fontcolor-secondary',
                                    slip.total_subscriptions
                                )
                            ]),
                            m('.w-col.w-col-2.w-col-small-2.w-col-stack.w-col-tiny-2', [
                                m('.fontweight-semibold.text-success',
                                    `R$${((slip.total_amount) + (credit_card.total_amount)) / 100}`
                                ),
                                m('.fontsize-smallest.fontcolor-secondary',
                                    `R$${(credit_card.total_amount) / 100}`
                                ),
                                m('.fontsize-smallest.fontcolor-secondary',
                                    `R$${(slip.total_amount) / 100}`
                                )
                            ])
                        ]);
                    })
                ])
            ])
        ]);
    }

};

export default subscriptionsPerMonthTable;
<commit_msg>fix number format<commit_after>import m from 'mithril';
import _ from 'underscore';
import moment from 'moment';
import h from '../h';

const subscriptionsPerMonthTable = {
    controller() {
        return {
            emptyRow: {
                total_amount: 0,
                new_amount: 0,
                total_subscriptions: 0,
                new_subscriptions: 0
            }
        };
    },

    view(ctrl, args) {
        return m('div', [
            m(".fontsize-large.fontweight-semibold.u-text-center.u-marginbottom-30[id='origem']",
                'Crescimento mensal das assinaturas'
            ),
            m('.table-outer.u-marginbottom-60', [
                m('.table-row.fontweight-semibold.fontsize-smaller.header.lineheight-tighter.w-row', [
                    m('.table-col.w-col.w-col-4.w-col-small-4.w-col-tiny-4',
                        m('div',
                            'Mês'
                        )
                    ),
                    m('.table-col.w-hidden-small.w-hidden-tiny.w-col.w-col-2.w-col-small-2.w-col-tiny-2',
                        m('div', [
                            'Novos assinantes',
                            m.trust(' ')
                        ])
                    ),
                    m('.table-col.w-hidden-small.w-hidden-tiny.w-col.w-col-2.w-col-small-2.w-col-tiny-2',
                        m('div',
                            'Receita com novos assinantes'
                        )
                    ),
                    m('.w-col.w-col-2.w-col-small-2.w-col-tiny-2',
                        m('div',
                            'Assinantes totais'
                        )
                    ),
                    m('.w-col.w-col-2.w-col-small-2.w-col-tiny-2',
                        m('div',
                            'Receita total'
                        )
                    )
                ]),
                m('.table-inner.fontsize-small', [!args.data ? '' :
                    _.map(_.groupBy(args.data, 'month'), (subscription) => {
                        const slip = _.filter(subscription, sub => sub.payment_method === 'boleto')[0] || ctrl.emptyRow;
                        const credit_card = _.filter(subscription, sub => sub.payment_method === 'credit_card')[0] || ctrl.emptyRow;

                        return m('.table-row.w-row', [
                            m('.table-col.w-col.w-col-4.w-col-small-4.w-col-stack.w-col-tiny-4', [
                                m('.fontweight-semibold',
                                  moment(subscription[0].month).format('MMMM YYYY')
                                ),
                                m('.fontsize-smallest.fontcolor-secondary',
                                    'Cartão de crédito'
                                ),
                                m('.fontsize-smallest.fontcolor-secondary',
                                    'Boleto bancário'
                                )
                            ]),
                            m('.table-col.w-hidden-small.w-hidden-tiny.w-col.w-col-2.w-col-small-2.w-col-stack.w-col-tiny-2', [
                                m('.fontweight-semibold',
                                    slip.new_subscriptions + credit_card.new_subscriptions
                                ),
                                m('.fontsize-smallest.fontcolor-secondary',
                                    credit_card.new_subscriptions
                                ),
                                m('.fontsize-smallest.fontcolor-secondary',
                                    slip.new_subscriptions
                                )
                            ]),
                            m('.table-col.w-hidden-small.w-hidden-tiny.w-col.w-col-2.w-col-small-2.w-col-stack.w-col-tiny-2', [
                                m('.fontweight-semibold',
                                  `R$${h.formatNumber((slip.new_amount + credit_card.new_amount) / 100, 2, 3)}`
                                ),
                                m('.fontsize-smallest.fontcolor-secondary',
                                    `R$${h.formatNumber((credit_card.new_amount) / 100, 2, 3)}`
                                ),
                                m('.fontsize-smallest.fontcolor-secondary',
                                  `R$${h.formatNumber((slip.new_amount) / 100, 2, 3)}`
                                )
                            ]),
                            m('.w-col.w-col-2.w-col-small-2.w-col-stack.w-col-tiny-2', [
                                m('.fontweight-semibold',
                                    slip.total_subscriptions + credit_card.total_subscriptions
                                ),
                                m('.fontsize-smallest.fontcolor-secondary',
                                    credit_card.total_subscriptions
                                ),
                                m('.fontsize-smallest.fontcolor-secondary',
                                    slip.total_subscriptions
                                )
                            ]),
                            m('.w-col.w-col-2.w-col-small-2.w-col-stack.w-col-tiny-2', [
                                m('.fontweight-semibold.text-success',
                                    `R$${h.formatNumber(((slip.total_amount) + (credit_card.total_amount)) / 100, 2, 3)}`
                                ),
                                m('.fontsize-smallest.fontcolor-secondary',
                                    `R$${h.formatNumber((credit_card.total_amount) / 100, 2, 3)}`
                                ),
                                m('.fontsize-smallest.fontcolor-secondary',
                                    `R$${h.formatNumber((slip.total_amount) / 100, 2, 3)}`
                                )
                            ])
                        ]);
                    })
                ])
            ])
        ]);
    }

};

export default subscriptionsPerMonthTable;
<|endoftext|>


<commit_before>op.part.ImageGallery = function(options) {
  this.options = options;
  this.element = hui.get(options.element);
  this.images = options.images;
  if (options.variant=='masonry') {
    new op.part.ImageGallery.Masonry({
      element : options.element,
      height : options.height
    });
  }
  else if (options.variant=='changing') {
    op.part.ImageGallery.changing.init(this.element);
  }
  this._attach();
}

op.part.ImageGallery.prototype = {
  _attach : function() {
    hui.listen(this.element,'click',this._click.bind(this));
  },
  _click : function(e) {
    e = hui.event(e);
    var a = e.findByTag('a');
    if (a) {
      e.stop();
      this.showImage(a.getAttribute('data-id'));
    }
  },
  registerImage : function(node,image) {
    node = hui.get(node);
    if (this.options.editor) {
      return;
    }
    this.images.push(image);
    var self = this;
    node.onclick = function(e) {
      hui.stop(e);
      self.showImage(image.id);
      return false;
    }
  },
  showImage : function(id) {
    if (!this.imageViewer) {
      this.imageViewer = hui.ui.ImageViewer.create({maxWidth:2000,maxHeight:2000,perimeter:40,sizeSnap:10});
      this.imageViewer.listen(op.imageViewerDelegate);
    }
    this.imageViewer.clearImages();
    this.imageViewer.addImages(this.images);
    this.imageViewer.showById(id);
  }
}

op.part.ImageGallery.changing = {
  init : function(element) {
    var nodes = hui.get.byClass(element,'part_imagegallery_item');
    if (nodes.length==0) {
      return;
    }
        element.style.height = element.clientHeight+'px';
        for (var i = 0; i < nodes.length; i++) {
            nodes[i].style.position = 'absolute';
        }
    var timer;
    var index = -1;
    var zIndex = 1;
    var first = true;
    timer = function() {
      if (index>-1) {
        hui.animate(nodes[index],'opacity',0,200,{hideOnComplete:true,delay:800});
      }
      index++;
      if (index>nodes.length-1) {
        index = 0;
      }
      if (!first) {
                hui.style.set(nodes[index],{
                    opacity: 0,
                    zIndex : zIndex,
                    display : 'block'
                })
        hui.animate(nodes[index],'opacity',1,1000,{ease:hui.ease.slowFastSlow});
      }
      window.setTimeout(timer,3000);
      zIndex++;
      first = false;
    }
    timer();
  }
}

op.part.ImageGallery.Masonry = function(options) {
  this.options = options;
  this.element = hui.get(options.element);
  this.name = options.name;

  this.height = parseInt(options.height,10) || 200;
  this.items = [];
  this.latestWidth = 0;

  hui.ui.extend(this);
  this._attach();
}

op.part.ImageGallery.Masonry.prototype = {
  _attach : function() {
    var links = hui.get.byTag(this.element,'a');
    for (var i = 0; i < links.length; i++) {
      var data = hui.string.fromJSON(links[i].getAttribute('data'));
      data.href = links[i].href;
      this.items.push(data)
    }
    this.element.innerHTML = '';
    this._rebuild();
    hui.ui.listen({
      $$afterResize : this._rebuild.bind(this)
    })
    hui.listen(window,'scroll',this._reveal.bind(this));
    //hui.listen(this.element,'click',this._click.bind(this));
  },
  _rebuild : function() {
    var fullWidth = this.element.clientWidth;
    if (Math.abs(this.latestWidth-fullWidth)<100) {
      return;
    }
    //hui.log('_rebuild');
    this.latestWidth = fullWidth;
    var rows = [];
    var row = [];
    rows.push(row);
    var pixels = 0;
    for (var i = 0,l = this.items.length; i < l; i++) {
      var item = this.items[i];
      item.revealed = false;
      item.index = i;
      var width = item.width/item.height * this.height;
      pixels+=width;
      var info = {width:width,item:item,percent:percent};
      if (pixels/fullWidth>1.2) {
        pixels = width;
        row = [];
        rows.push(row);
      }
      info.place = pixels;
      item.row = rows.length;
      row.push(info);
    }
    for (var i = 0; i < rows.length; i++) {
      var row = rows[i];
      var total = 0;
      for (var j = 0; j < row.length; j++) {
        total+=row[j].width;
      }
      var adjustment = fullWidth/total;
      var pos = 0;

      var rowHeight = Number.MAX_VALUE;

      for (var j = 0; j < row.length; j++) {
        var last = j == row.length - 1;
        var info = row[j], item = info.item;
        var percent = info.width / fullWidth * 100;
        percent = Math.round(adjustment * percent);
        pos+=percent;
        if (last) {
          percent+=100-pos;
          if (hui.browser.msie7 || hui.browser.msie6) {
            percent -= 0.1;
          }
        }
        info.percent = percent;
        rowHeight = Math.min(rowHeight, (percent/100 * fullWidth) * item.height/item.width );
      }
      rowHeight = Math.floor(rowHeight);
      for (var j = 0; j < row.length; j++) {
        var last = j == row.length - 1;
        var info = row[j], item = info.item;
        var percent = info.percent;

        var cls = last ? 'part_imagegallery_masonry_item part_imagegallery_masonry_item_last' : 'part_imagegallery_masonry_item';
        if (item.element) {
          item.element.style.width = percent + '%';
          item.element.style.height = rowHeight + 'px';
          item.element.className = cls;
        } else {
          item.element = hui.build('a',{
            'class' : cls,
            style : {
              width : percent + '%',
              height : rowHeight + 'px'
            },
            'data-id' : item.id,
            'data' : item.index,
            parent : this.element
          });
        }
      }
      for (var j = 0; j < row.length; j++) {
        row[j].item.element.style.height = rowHeight + 'px'
      }
    }
    this._reveal();
  },
  _getUrl : function(item,info) {
    var ratio = window.devicePixelRatio || 1;
    return op.context+'services/images/?id=' + item.id + '&width=' + (info.width * ratio) + '&height=' + (info.height * ratio) + '&method=crop';
  },
  _reveal : function() {
    var min = hui.window.getScrollTop();
    var max = min + hui.window.getViewHeight();
    for (var i = 0,l = this.items.length; i < l; i++) {
      var item = this.items[i],
        element = item.element;
      if (item.revealed) {
        continue;
      }
      var top = hui.position.getTop(element),
        bottom = top + this.height;
      if (top > max || bottom < min) {
        continue;
      }
      var height = item.element.clientHeight;
      //var width = item.element.clientWidth;
      var width = Math.round(item.width/item.height * height);
      item.element.style.backgroundImage = 'url(' + this._getUrl(item,{width:width,height:height}) + ')';
      item.revealed = true;
    }
  },
  _click : function(e) {
    e = hui.event(e);
    var item = e.findByClass('part_imagegallery_masonry_item');
    if (item) {
      var index = parseInt(item.getAttribute('data'),10);
      if (hui.window.getViewWidth()<400) {
        document.location = this.items[index].href;
      } else {
        this._toggle(index);
      }
    }
  },
  _toggle : function(index) {
    var dur = 1000;
    if (this._toggled!==undefined) {
      var tog = this.items[this._toggled];
      hui.animate({node:tog.disclosed,css:{height:'0px'},duration:200,ease:hui.ease.fastSlow})
      hui.animate({node:tog.element,css:{marginBottom:'0px'},duration:200,ease:hui.ease.fastSlow,$complete : function() {
        tog.disclosed.style.display = 'none';
        var same = this._toggled === index;
        this._toggled = undefined;
        if (!same) {
          this._toggle(index);
        } else {
          this._reveal();
        }
      }.bind(this)})
      return;
    }
    this._toggled = index;
    var item = this.items[index],
      element = item.element;
    if (!item.disclosed) {
      item.disclosed = hui.build('div',{
        'class' : 'oo_masonry_disclosed',
        style : {top : (item.row*this.height+1)+'px'},
        parent: this.element
      })
    } else {
      item.disclosed.style.display='block';
    }
    var height = Math.round(this.latestWidth*.6);
    this._updateDiclosed(item,height);
    hui.animate({
      node : item.disclosed,
      css : {height: (height-1) + 'px'},
      duration : dur,
      ease : hui.ease.elastic
    })
    hui.animate({
      node : element,
      css : {marginBottom:height + 'px'},
      duration : dur,
      ease : hui.ease.elastic,
      $complete : function() {
        hui.window.scrollTo({
          element : item.disclosed,
          duration : 300
        });
      }
    })
  },
  _updateDiclosed : function(item,height) {
    item.disclosed.innerHTML = '

' +
    '
' +
      '
' + hui.string.escape(item.title) + '
' +
      '
'+
        //'Full screen'+
        'Info...'+
      '
'+
    '
';
  }
}

window.define && define('op.part.ImageGallery');<commit_msg>Set max pixel ratio to 2<commit_after>op.part.ImageGallery = function(options) {
  this.options = options;
  this.element = hui.get(options.element);
  this.images = options.images;
  if (options.variant=='masonry') {
    new op.part.ImageGallery.Masonry({
      element : options.element,
      height : options.height
    });
  }
  else if (options.variant=='changing') {
    op.part.ImageGallery.changing.init(this.element);
  }
  this._attach();
}

op.part.ImageGallery.prototype = {
  _attach : function() {
    hui.listen(this.element,'click',this._click.bind(this));
  },
  _click : function(e) {
    e = hui.event(e);
    var a = e.findByTag('a');
    if (a) {
      e.stop();
      this.showImage(a.getAttribute('data-id'));
    }
  },
  registerImage : function(node,image) {
    node = hui.get(node);
    if (this.options.editor) {
      return;
    }
    this.images.push(image);
    var self = this;
    node.onclick = function(e) {
      hui.stop(e);
      self.showImage(image.id);
      return false;
    }
  },
  showImage : function(id) {
    if (!this.imageViewer) {
      this.imageViewer = hui.ui.ImageViewer.create({maxWidth:2000,maxHeight:2000,perimeter:40,sizeSnap:10});
      this.imageViewer.listen(op.imageViewerDelegate);
    }
    this.imageViewer.clearImages();
    this.imageViewer.addImages(this.images);
    this.imageViewer.showById(id);
  }
}

op.part.ImageGallery.changing = {
  init : function(element) {
    var nodes = hui.get.byClass(element,'part_imagegallery_item');
    if (nodes.length==0) {
      return;
    }
        element.style.height = element.clientHeight+'px';
        for (var i = 0; i < nodes.length; i++) {
            nodes[i].style.position = 'absolute';
        }
    var timer;
    var index = -1;
    var zIndex = 1;
    var first = true;
    timer = function() {
      if (index>-1) {
        hui.animate(nodes[index],'opacity',0,200,{hideOnComplete:true,delay:800});
      }
      index++;
      if (index>nodes.length-1) {
        index = 0;
      }
      if (!first) {
                hui.style.set(nodes[index],{
                    opacity: 0,
                    zIndex : zIndex,
                    display : 'block'
                })
        hui.animate(nodes[index],'opacity',1,1000,{ease:hui.ease.slowFastSlow});
      }
      window.setTimeout(timer,3000);
      zIndex++;
      first = false;
    }
    timer();
  }
}

op.part.ImageGallery.Masonry = function(options) {
  this.options = options;
  this.element = hui.get(options.element);
  this.name = options.name;

  this.height = parseInt(options.height,10) || 200;
  this.items = [];
  this.latestWidth = 0;

  hui.ui.extend(this);
  this._attach();
}

op.part.ImageGallery.Masonry.prototype = {
  _attach : function() {
    var links = hui.get.byTag(this.element,'a');
    for (var i = 0; i < links.length; i++) {
      var data = hui.string.fromJSON(links[i].getAttribute('data'));
      data.href = links[i].href;
      this.items.push(data)
    }
    this.element.innerHTML = '';
    this._rebuild();
    hui.ui.listen({
      $$afterResize : this._rebuild.bind(this)
    })
    hui.listen(window,'scroll',this._reveal.bind(this));
    //hui.listen(this.element,'click',this._click.bind(this));
  },
  _rebuild : function() {
    var fullWidth = this.element.clientWidth;
    if (Math.abs(this.latestWidth-fullWidth)<100) {
      return;
    }
    //hui.log('_rebuild');
    this.latestWidth = fullWidth;
    var rows = [];
    var row = [];
    rows.push(row);
    var pixels = 0;
    for (var i = 0,l = this.items.length; i < l; i++) {
      var item = this.items[i];
      item.revealed = false;
      item.index = i;
      var width = item.width/item.height * this.height;
      pixels+=width;
      var info = {width:width,item:item,percent:percent};
      if (pixels/fullWidth>1.2) {
        pixels = width;
        row = [];
        rows.push(row);
      }
      info.place = pixels;
      item.row = rows.length;
      row.push(info);
    }
    for (var i = 0; i < rows.length; i++) {
      var row = rows[i];
      var total = 0;
      for (var j = 0; j < row.length; j++) {
        total+=row[j].width;
      }
      var adjustment = fullWidth/total;
      var pos = 0;

      var rowHeight = Number.MAX_VALUE;

      for (var j = 0; j < row.length; j++) {
        var last = j == row.length - 1;
        var info = row[j], item = info.item;
        var percent = info.width / fullWidth * 100;
        percent = Math.round(adjustment * percent);
        pos+=percent;
        if (last) {
          percent+=100-pos;
          if (hui.browser.msie7 || hui.browser.msie6) {
            percent -= 0.1;
          }
        }
        info.percent = percent;
        rowHeight = Math.min(rowHeight, (percent/100 * fullWidth) * item.height/item.width );
      }
      rowHeight = Math.floor(rowHeight);
      for (var j = 0; j < row.length; j++) {
        var last = j == row.length - 1;
        var info = row[j], item = info.item;
        var percent = info.percent;

        var cls = last ? 'part_imagegallery_masonry_item part_imagegallery_masonry_item_last' : 'part_imagegallery_masonry_item';
        if (item.element) {
          item.element.style.width = percent + '%';
          item.element.style.height = rowHeight + 'px';
          item.element.className = cls;
        } else {
          item.element = hui.build('a',{
            'class' : cls,
            style : {
              width : percent + '%',
              height : rowHeight + 'px'
            },
            'data-id' : item.id,
            'data' : item.index,
            parent : this.element
          });
        }
      }
      for (var j = 0; j < row.length; j++) {
        row[j].item.element.style.height = rowHeight + 'px'
      }
    }
    this._reveal();
  },
  _getUrl : function(item,info) {
    var ratio = window.devicePixelRatio > 1 ? 2 : 1;
    return op.context+'services/images/?id=' + item.id + '&width=' + (info.width * ratio) + '&height=' + (info.height * ratio) + '&method=crop';
  },
  _reveal : function() {
    var min = hui.window.getScrollTop();
    var max = min + hui.window.getViewHeight();
    for (var i = 0,l = this.items.length; i < l; i++) {
      var item = this.items[i],
        element = item.element;
      if (item.revealed) {
        continue;
      }
      var top = hui.position.getTop(element),
        bottom = top + this.height;
      if (top > max || bottom < min) {
        continue;
      }
      var height = item.element.clientHeight;
      //var width = item.element.clientWidth;
      var width = Math.round(item.width/item.height * height);
      item.element.style.backgroundImage = 'url(' + this._getUrl(item,{width:width,height:height}) + ')';
      item.revealed = true;
    }
  },
  _click : function(e) {
    e = hui.event(e);
    var item = e.findByClass('part_imagegallery_masonry_item');
    if (item) {
      var index = parseInt(item.getAttribute('data'),10);
      if (hui.window.getViewWidth()<400) {
        document.location = this.items[index].href;
      } else {
        this._toggle(index);
      }
    }
  },
  _toggle : function(index) {
    var dur = 1000;
    if (this._toggled!==undefined) {
      var tog = this.items[this._toggled];
      hui.animate({node:tog.disclosed,css:{height:'0px'},duration:200,ease:hui.ease.fastSlow})
      hui.animate({node:tog.element,css:{marginBottom:'0px'},duration:200,ease:hui.ease.fastSlow,$complete : function() {
        tog.disclosed.style.display = 'none';
        var same = this._toggled === index;
        this._toggled = undefined;
        if (!same) {
          this._toggle(index);
        } else {
          this._reveal();
        }
      }.bind(this)})
      return;
    }
    this._toggled = index;
    var item = this.items[index],
      element = item.element;
    if (!item.disclosed) {
      item.disclosed = hui.build('div',{
        'class' : 'oo_masonry_disclosed',
        style : {top : (item.row*this.height+1)+'px'},
        parent: this.element
      })
    } else {
      item.disclosed.style.display='block';
    }
    var height = Math.round(this.latestWidth*.6);
    this._updateDiclosed(item,height);
    hui.animate({
      node : item.disclosed,
      css : {height: (height-1) + 'px'},
      duration : dur,
      ease : hui.ease.elastic
    })
    hui.animate({
      node : element,
      css : {marginBottom:height + 'px'},
      duration : dur,
      ease : hui.ease.elastic,
      $complete : function() {
        hui.window.scrollTo({
          element : item.disclosed,
          duration : 300
        });
      }
    })
  },
  _updateDiclosed : function(item,height) {
    item.disclosed.innerHTML = '

' +
    '
' +
      '
' + hui.string.escape(item.title) + '
' +
      '
'+
        //'Full screen'+
        'Info...'+
      '
'+
    '
';
  }
}

window.define && define('op.part.ImageGallery');<|endoftext|>


<commit_before>import React, { createElement } from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import WeakMap from 'es6-weak-map';
import { memoize, compose, curry } from 'ramda';
import osom from 'osom';
import { camelize } from 'humps';

/**
 * @constant components
 * @type {Object}
 */
const components = {};

/**
 * @constant metaData
 * @type {WeakMap}
 */
const metaData = new WeakMap();

/**
 * @method removePrefix
 * @param {String} attr
 * @return {String}
 */
const removePrefix = memoize(attr => attr.replace('data-', ''));

/**
 * @method tagName
 * @param {String} nodeName
 * @return {String}
 */
const tagName = memoize(nodeName => nodeName.toLowerCase());

/**
 * @method throwError
 * @param {String} message
 * @return {void}
 */
const throwError = message => {
    throw `Standalone: ${message}.`;
};

/**
 * @method metaDataFor
 * @param {HTMLElement} element
 * @return {Object}
 */
const metaDataFor = element => metaData.get(element);

/**
 * @method renderComponent
 * @param {Function} Component
 * @param {HTMLElement} element
 * @param {Object} [schema]
 * @return {Object}
 */
const renderComponent = (Component, element, schema) => {

    const validator = schema ? osom(schema) : x => x;
    const parseNodeName = compose(camelize, removePrefix);
    const keys = Object.keys(element.attributes);

    /**
     * @method dataAttributes
     * @param {Object} attributes
     * @param {String} key
     * @return {Boolean}
     */
    const dataAttributes = curry((attributes, key) => {
        return /data-/.test(attributes[key].nodeName);
    });

    const attributes = validator(keys.filter(dataAttributes(element.attributes)).reduce((accumulator, key) => {

        // Reduce the NodeList into a standard object for passing into the React component.
        const attribute = element.attributes[key];
        return { ...accumulator, [parseNodeName(attribute.nodeName)]: attribute.nodeValue };

    }, {}));

    return render(<Component {...attributes} />, element);

};

/**
 * @method getPrototype
 * @param {Object} [methods]
 * @return {Object}
 */
const getPrototype = (methods = {}) => {

    /**
     * @constant prototype
     * @type {Object}
     */
    const prototype = Object.create(window.HTMLElement.prototype);

    /**
     * @method createdCallback
     * @return {void}
     */
    prototype.createdCallback = function createdCallback() {

        metaData.set(this, {
            isMounted: false
        });

        Object.getPrototypeOf(this).component = null;

    };

    /**
     * @method attributeChangedCallback
     * @return {void}
     */
    prototype.attributeChangedCallback = function attributeChangedCallback() {

        const meta = metaDataFor(this);

        if (meta.isMounted) {

            // Re-render element only if it's currently mounted.
            const tag = tagName(this.nodeName);
            const { component, schema } = components[tag];
            Object.getPrototypeOf(this).component = renderComponent(component, this, schema);

        }

    };

    /**
     * @method attachedCallback
     * @return {void}
     */
    prototype.attachedCallback = function attachedCallback() {

        const tag = tagName(this.nodeName);
        const meta = metaDataFor(this);

        // Element has been attached to the DOM, so we'll update the meta data, and
        // then render the element into the custom element container.
        metaData.set(this, { ...meta, isMounted: true });
        const { component, schema } = components[tag];
        Object.getPrototypeOf(this).component = renderComponent(component, this, schema);

    };

    /**
     * @method detachedCallback
     * @return {void}
     */
    prototype.detachedCallback = function detachedCallback() {

        metaData.set(this, {
            isMounted: false
        });

        // Instruct the component to unmount, which will invoke the `componentWillUnmount` lifecycle
        // function for handling any cleaning up of the component.
        unmountComponentAtNode(this);
        Object.getPrototypeOf(this).component = null;

    };

    Object.keys(methods).forEach(key => {

        // Apply the user-defined functions onto the prototype.
        prototype[key] = prototype[key] || methods[key];

    });

    return prototype;

};

/**
 * @method make
 * @param {String} tag
 * @param {Object} schema
 * @param {Object} methods
 * @param {Object} component
 * @return {Object|void}
 */
export const make = (tag, { schema, methods, component }) => {

    try {
        document.registerElement(tagName(tag), { prototype: getPrototype(methods) });
        components[tagName(tag)] = { component, schema };
    } catch (e) {
        return void throwError(e.message);
    }

    return component;

};
<commit_msg>Allowed the extension of existing elements. Fixes #4 #5<commit_after>import React from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import { memoize, compose, curry, pickBy, complement, isNil } from 'ramda';
import osom from 'osom';
import { camelize } from 'humps';

/**
 * @constant metaData
 * @type {Symbol}
 */
const metaData = Symbol('standalone/meta-data');

/**
 * @method removePrefix
 * @param {String} attr
 * @return {String}
 */
const removePrefix = memoize(attr => attr.replace('data-', ''));

/**
 * @method throwError
 * @param {String} message
 * @return {void}
 */
const throwError = message => {
    throw `Standalone: ${message}.`;
};

/**
 * @method renderComponent
 * @param {Function} Component
 * @param {HTMLElement} element
 * @param {Object} [schema]
 * @return {Object}
 */
const renderComponent = (Component, element, schema) => {

    const validator = schema ? osom(schema) : x => x;
    const parseNodeName = compose(camelize, removePrefix);
    const keys = Object.keys(element.attributes);

    /**
     * @method dataAttributes
     * @param {Object} attributes
     * @param {String} key
     * @return {Boolean}
     */
    const dataAttributes = curry((attributes, key) => {
        return /data-/.test(attributes[key].nodeName);
    });

    const attributes = validator(keys.filter(dataAttributes(element.attributes)).reduce((accumulator, key) => {

        // Reduce the NodeList into a standard object for passing into the React component.
        const attribute = element.attributes[key];
        return { ...accumulator, [parseNodeName(attribute.nodeName)]: attribute.nodeValue };

    }, {}));

    return render(<Component {...attributes} />, element);

};

/**
 * @method getPrototype
 * @param {String} inherits
 * @param {Object} schema
 * @param {Object} methods
 * @param {Object} component
 * @return {Object}
 */
const getPrototype = ({ inherits, schema, methods, component }) => {

    const prototypeFrom = Object.getPrototypeOf(document.createElement(inherits));
    const isUnknownElement = prototypeFrom === HTMLUnknownElement.prototype;
    const prototype = Object.create(isUnknownElement ? HTMLElement.prototype : prototypeFrom);

    /**
     * @method createdCallback
     * @return {void}
     */
    prototype.createdCallback = function createdCallback() {
        this.component = null;
    };

    /**
     * @method attributeChangedCallback
     * @return {void}
     */
    prototype.attributeChangedCallback = function attributeChangedCallback() {

        if (this[metaData].component) {

            // Re-render element only if it's currently mounted.
            const { component, schema } = this[metaData];
            this.component = renderComponent(component, this, schema);

        }

    };

    /**
     * @method attachedCallback
     * @return {void}
     */
    prototype.attachedCallback = function attachedCallback() {

        // Element has been attached to the DOM, so we'll update the meta data, and
        // then render the element into the custom element container.
        const { component, schema } = this[metaData];
        this.component = renderComponent(component, this, schema);

    };

    /**
     * @method detachedCallback
     * @return {void}
     */
    prototype.detachedCallback = function detachedCallback() {

        // Instruct the component to unmount, which will invoke the `componentWillUnmount` lifecycle
        // function for handling any cleaning up of the component.
        unmountComponentAtNode(this);
        this.component = null;

    };

    Object.keys(methods).forEach(key => {

        // Apply the user-defined functions onto the prototype.
        prototype[key] = prototype[key] || methods[key];

    });

    // Register the metadata used by Standalone.
    prototype[metaData] = { methods, schema, component };

    return prototype;

};

/**
 * @method make
 * @param {String} reference
 * @param {Object} schema
 * @param {Object} methods
 * @param {Object} component
 * @return {Object|void}
 */
export const make = (reference, { schema, methods, component }) => {

    const [name, inherits] = (() => {
        const regExp = /^(.+?)(?:\/(.+?))?$/i;
        const matches = reference.match(regExp);
        return [matches[2] || matches[1], matches[2] ? matches[1] : undefined];
    })();

    try {

        return document.registerElement(name, pickBy(complement(isNil), {
            prototype: getPrototype({ inherits, schema, methods, component }),
            extends: inherits
        }));

    } catch (e) {
        return void throwError(e.message);
    }

};
<|endoftext|>


<commit_before>game.EnemyCreep = me.Entity.extend({
	init: function (x, y, settings) {
		this._super(me.Entity, 'init', [x, y, {
			image: "creep1",
			width: 32,
			height: 64,
			spritewidth: "32",
			spriteheight: "64",
			getShape: function(){
				return (new me.Rect (0, 0, 32, 64)).toPolygon();
			}
			}]);
		this.health = game.data.enemyCreepHealth;
		this.alwaysUpdate = true;
		// this.attacking lets us know if the enemy is currently attacking
		this.attacking = false;
		// keeps track of when our creep last attacked anything
		this.lastAttacking = new Date().getTime();
		this.lastHit = new Date().getTime();
		this.now = new Date().getTime();
		this.body.setVelocity(3, 20);

		this.type = "EnemyCreep";

		this.renderable.addAnimation("walk", [3, 4, 5], 80);
		this.renderable.setCurrentAnimation("walk");
	},

	loseHealth: function(damage){
		this.health = this.health - damage;
	},

	update: function (delta) {
		console.log(this.health);
		if (this.health <= 0) {
			me.game.world.removeChild(this);
		}

		this.now = new Date().getTime();

		this.body.vel.x -= this.body.accel.x * me.timer.tick;

		me.collision.check(this, true, this.collideHandler.bind(this), true);

		this.body.update(delta);

		this._super(me.Entity, "update", [delta]);

		return true;
	},

	collideHandler: function(response) {
		if(response.b.type==='PlayerBase') {
			this.attacking=true;
			this.lastAttacking=this.now;
			// this.lastAttacking = this.now;
			this.body.vel.x = 0;
			// keeps moving the creep to the right to maintain its position
			this.pos.x = this.pos.x + 1;
			// checks that it has been at least 1 second since this creep hit a base
			if(this.now-this.lastHit >= 1000) {
				// updates the lastHit timer
				this.lastHit = this.now;
				// makes the player base call its loseHealth function and passes it
				// damage of 1
				response.b.loseHealth(1);
			}
		}else if(response.b.type==='PlayerEntity'){
			var xdif = this.pos.x - response.b.pos.x;

			this.attacking=true;
			// this.lastAttacking=this.now;

			// this.lastAttacking = this.now;
			// keeps moving the creep to the right to maintain its position
			if(xdif>0) {

				this.pos.x = this.pos.x + 1;
				this.body.vel.x = 0;
			}
			// checks that it has been at least 1 second since this creep hit a base
			if(this.now-this.lastHit >= 1000 && xdif>0){
				// updates the lastHit timer
				this.lastHit = this.now;
				// makes the player base call its loseHealth function and passes it
				// damage of 1
				response.b.loseHealth(gmae.data.enemyCreepAttack);
			}
		}

	}
});<commit_msg>fixing errors<commit_after>game.EnemyCreep = me.Entity.extend({
	init: function (x, y, settings) {
		this._super(me.Entity, 'init', [x, y, {
			image: "creep1",
			width: 32,
			height: 64,
			spritewidth: "32",
			spriteheight: "64",
			getShape: function(){
				return (new me.Rect (0, 0, 32, 64)).toPolygon();
			}
			}]);
		this.health = game.data.enemyCreepHealth;
		this.alwaysUpdate = true;
		// this.attacking lets us know if the enemy is currently attacking
		this.attacking = false;
		// keeps track of when our creep last attacked anything
		this.lastAttacking = new Date().getTime();
		this.lastHit = new Date().getTime();
		this.now = new Date().getTime();
		this.body.setVelocity(3, 20);

		this.type = "EnemyCreep";

		this.renderable.addAnimation("walk", [3, 4, 5], 80);
		this.renderable.setCurrentAnimation("walk");
	},

	loseHealth: function(damage){
		this.health = this.health - damage;
	},

	update: function (delta) {
		console.log(this.health);
		if (this.health <= 0) {
			me.game.world.removeChild(this);
		}

		this.now = new Date().getTime();

		this.body.vel.x -= this.body.accel.x * me.timer.tick;

		me.collision.check(this, true, this.collideHandler.bind(this), true);

		this.body.update(delta);

		this._super(me.Entity, "update", [delta]);

		return true;
	},

	collideHandler: function(response) {
		if(response.b.type==='PlayerBase') {
			this.attacking=true;
			this.lastAttacking=this.now;
			// this.lastAttacking = this.now;
			this.body.vel.x = 0;
			// keeps moving the creep to the right to maintain its position
			this.pos.x = this.pos.x + 1;
			// checks that it has been at least 1 second since this creep hit a base
			if(this.now-this.lastHit >= 1000) {
				// updates the lastHit timer
				this.lastHit = this.now;
				// makes the player base call its loseHealth function and passes it
				// damage of 1
				response.b.loseHealth(1);
			}
		}else if(response.b.type==='PlayerEntity'){
			var xdif = this.pos.x - response.b.pos.x;

			this.attacking=true;
			// this.lastAttacking=this.now;

			// this.lastAttacking = this.now;
			// keeps moving the creep to the right to maintain its position
			if(xdif>0) {

				this.pos.x = this.pos.x + 1;
				this.body.vel.x = 0;
			}
			// checks that it has been at least 1 second since this creep hit a base
			if(this.now-this.lastHit >= 1000 && xdif>0){
				// updates the lastHit timer
				this.lastHit = this.now;
				// makes the player base call its loseHealth function and passes it
				// damage of 1
				response.b.loseHealth(game.data.enemyCreepAttack);
			}
		}

	}
});<|endoftext|>


<commit_before>module.exports = ({dom, player, iframe}) =>
() => {
  if (!dom.refs.screen) return;
  const screenRect = dom.refs.screen.getBoundingClientRect();
  const iframeStyle = getComputedStyle(iframe);
  const offset = {
    x: iframeStyle.position === 'absolute' ? pageXOffset : 0,
    y: iframeStyle.position === 'absolute' ? pageYOffset : 0,
  };
  const contentSize = player.getContentSize();

  const ratio = (size) => Math.max(size.height, 1) / Math.max(size.width, 1);
  if (ratio(screenRect) > ratio(contentSize)) {
    iframe.width = screenRect.width;
    iframe.height = screenRect.width * ratio(contentSize);
    iframe.style.width = screenRect.width + 'px';
    iframe.style.height = screenRect.width * ratio(contentSize) + 'px';
    iframe.style.left = screenRect.left + offset.x + 'px';
    iframe.style.top = screenRect.top + (screenRect.height - iframe.height) + offset.y + 'px';
  } else {
    iframe.width = screenRect.height / ratio(contentSize);
    iframe.height = screenRect.height;
    iframe.style.width = screenRect.height / ratio(contentSize) + 'px';
    iframe.style.height = screenRect.height + 'px';
    iframe.style.left = screenRect.left + (screenRect.width - iframe.width) / 2 + offset.x + 'px';
    iframe.style.top = screenRect.top + offset.y + 'px';
  }
};
<commit_msg>default is prevent<commit_after>module.exports = (dom) => {

  var _frame, _width, _height;

  return ({frame, width, height}) => {
    _frame = typeof frame === 'undefined' ? _frame : frame;
    _width = typeof width === 'undefined' ? _width : width;
    _height = typeof height === 'undefined' ? _height : height;

    resizeHandler(dom, _frame, _width, _height);
  };

};

function resizeHandler(dom, frame, width, height) {
  if (!dom.refs.screen || !frame) return;
  const screenRect = dom.refs.screen.getBoundingClientRect();
  const frameStyle = getComputedStyle(frame);
  const offset = {
    x: frameStyle.position === 'absolute' ? pageXOffset : 0,
    y: frameStyle.position === 'absolute' ? pageYOffset : 0,
  };
  const contentSize = { width, height };

  const ratio = (size) => Math.max(size.height, 1) / Math.max(size.width, 1);
  if (ratio(screenRect) > ratio(contentSize)) {
    frame.width = screenRect.width;
    frame.height = screenRect.width * ratio(contentSize);
    frame.style.width = screenRect.width + 'px';
    frame.style.height = screenRect.width * ratio(contentSize) + 'px';
    frame.style.left = screenRect.left + offset.x + 'px';
    frame.style.top = screenRect.top + (screenRect.height - frame.height) + offset.y + 'px';
  } else {
    frame.width = screenRect.height / ratio(contentSize);
    frame.height = screenRect.height;
    frame.style.width = screenRect.height / ratio(contentSize) + 'px';
    frame.style.height = screenRect.height + 'px';
    frame.style.left = screenRect.left + (screenRect.width - frame.width) / 2 + offset.x + 'px';
    frame.style.top = screenRect.top + offset.y + 'px';
  }
};
<|endoftext|>


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

CLASS({
  package: 'foam.build',
  name: 'BuildApp',

  imports: [
    'log',
    'error'
  ],
  requires: [
    'foam.dao.NodeFileDAO as FileDAO',
    'foam.dao.File',
    'foam.core.dao.OrDAO',
    'node.dao.ModelFileDAO',
    'foam.build.WebApplication'
  ],
  properties: [
    {
      name: 'appDefinition'
    },
    {
      name: 'controller',
      help: 'Name of the main controller/model to create',
    },
    {
      model_: 'StringArrayProperty',
      name: 'coreFiles',
      adapt: function(_, s) { if ( typeof s === 'string' ) return s.split(','); return s; }
    },
    {
      name: 'defaultView',
      help: "Default view of the controller to use.  If not set, the controller will be used as view (if it is one), or DetailView will be used"
    },
    {
      name: 'targetPath',
      help: "Directory to write output files to.  Will be created if it doesn't exist.",
      required: true
    },
    {
      model_: 'BooleanProperty',
      name: 'precompileTemplates',
      help: 'True to precompile templates of models loaded from the ModelDAO.',
      defaultValue: false
    },
    {
      model_: 'BooleanProperty',
      name: 'includeFoamCSS',
      defaultValue: false
    },
    {
      model_: 'StringProperty',
      name: 'icon'
    },
    {
      model_: 'StringProperty',
      name: 'version'
    },
    {
      model_: 'StringArrayProperty',
      name: 'resources'
    },
    {
      model_: 'BooleanProperty',
      name: 'appcacheManifest',
      defaultValue: false
    },
    {
      model_: 'StringArrayProperty',
      name: 'extraFiles',
      help: 'Extra files to both load during the build process, and include in the built image.',
      adapt: function(_, s) { if ( typeof s === 'string' ) return s.split(','); return s; }
    },
    {
      model_: 'StringArrayProperty',
      name: 'extraBuildFiles',
      help: 'Extra files to load during the build process, but NOT include in the built image.',
      adapt: function(_, s) { if ( typeof s === 'string' ) return s.split(','); return s; }
    },
    {
      model_: 'StringArrayProperty',
      name: 'extraModels',
      help: 'Extra models to include in the image regardless of if they were arequired or not.',
      adapt: function(_, s) { if ( typeof s === 'string' ) return s.split(','); return s; },
      factory: function() { return ['foam.ui.FoamTagView']; }
    },
    {
      model_: 'StringArrayProperty',
      name: 'blacklistModels',
      help: 'Models to unconditionally exclude from the image, even if they are listed as required.',
      adapt: function(_, s) { if ( typeof s === 'string' ) return s.split(','); return s; }
    },
    {
      model_: 'BooleanProperty',
      name: 'outputManifest',
      defaultValue: false,
      help: 'Set to true to write out a MANIFEST file listing all included models.'
    },
    {
      name: 'htmlFileName',
      help: 'Name of the main html file to produce.',
      defaultValue: 'main.html'
    },
    {
      model_: 'StringArrayProperty',
      name: 'htmlHeaders'
    },
    {
      name: 'formatter',
      factory: function() {
        return {
          __proto__: JSONUtil.compact,
          formatFunction: function(f) {
            var s = f.code.toString();
            if ( s.startsWith('function ' + f.name + '(') ) return s;
            return s.replace(/function ([^\(]*)\(/, 'function ' + f.name + '(')
          },
          keys_: {},
          keyify: JSONUtil.prettyModel.keyify,
          outputObject_: function(out, obj, opt_defaultModel) {
            var first = true;

            out('{');
            if ( obj.model_.id !== opt_defaultModel ) {
              this.outputModel_(out, obj);
              first = false;
            }

            if ( Template.isInstance(obj) ) var isTemplate = true;

            var properties = obj.model_.getRuntimeProperties();
            for ( var key in properties ) {
              var prop = properties[key];

              if ( ! this.p(prop) && ( ! isTemplate || prop.name !== 'code' ) ) continue;

              if ( prop.name === 'documentation' ) continue;

              if ( prop.name in obj.instance_ ) {
                var val = obj[prop.name];
                if ( Array.isArray(val) && ! val.length ) continue;
                if ( ! first ) out(',');
                out(this.keyify(prop.name), ': ');

                if ( prop.name === 'methods' ) {
                  out('[');
                  var ff = true;
                  for ( var i = 0 ; i < val.length ; i++ ) {
                    if ( ! ff ) out(',');

                    out(this.formatFunction(val[i]));
                    ff = false;
                  }
                  out(']');
                } else {
                  if ( Array.isArray(val) && prop.subType ) {
                    this.outputArray_(out, val, prop.subType);
                  } else {
                    this.output(out, val);
                  }
                }
                first = false;
              }
            }

            out('}');
          }
        };
      }
    },
    {
      name: 'path',
      factory: function() { return require('path'); }
    },
    {
      name: 'fileDAO',
      factory: function() { return this.FileDAO.create(); }
    },
    {
      model_: 'StringArrayProperty',
      name: 'extraClassPaths',
      help: 'List of extra .js hierarchies to load models from.  Paths will be checked in the order given, finally falling back to the main FOAM js/ hierarchy.',
      adapt: function(_, s) { if ( typeof s === 'string' ) return s.split(','); return s; }
    },
    {
      model_: 'StringProperty',
      name: 'locale'
    },
    // TODO(markdittmer): Remove "i18nMessagesPath" when all build processes
    // no longer require it.
    {
      model_: 'StringProperty',
      name: 'i18nMessagesPath'
    },
    {
      model_: 'StringProperty',
      name: 'i18nTranslationsPath'
    },
    {
      model_: 'StringArrayProperty',
      name: 'i18nMessages',
      adapt: function(_, s) {
        if (typeof s === 'string') return s.split(',');
        return s;
      }
    },
    {
      model_: 'StringArrayProperty',
      name: 'i18nTranslations',
      adapt: function(_, s) {
        if (typeof s === 'string') return s.split(',');
        return s;
      }
    },
    {
      model_: 'StringProperty',
      name: 'jsFileName',
      getter: function() {
        return 'foam' + (this.locale ? '_' + this.locale : '') + '.js';
      }
    },
    {
      model_: 'StringProperty',
      name: 'manifestFileName',
      getter: function() {
        return 'app' + (this.locale ? '_' + this.locale : '') + '.manifest';
      }
    },
    {
      name: 'localizedHTMLFileName_',
      getter: function() {
        var match = this.htmlFileName.match(/[.][^.]*$/g);
        var ext = match ? match[0] : '';
        var baseName = this.htmlFileName.slice(0,
            this.htmlFileName.length - ext.length);
        return baseName + (this.locale ? '_' + this.locale : '') + ext;
      }
    },
    {
      model_: 'StringProperty',
      name: 'delegate'
    }
  ],
  methods: {
    execute: function() {
      for ( var i = 0; i < this.extraClassPaths.length ; i++ ) {
        this.X.ModelDAO = this.OrDAO.create({
          delegate: this.ModelFileDAO.create({
            classpath: this.extraClassPaths[i]
          }),
          primary: this.X.ModelDAO
        });
      }

      if ( this.appDefinition ) {
        this.X.ModelDAO.find(this.appDefinition, {
          put: function(d) {
            this.copyFrom(d);
            this.execute_();
          }.bind(this),
          error: function() {
            console.log("App definition failed");
            this.execute_();
          }.bind(this)
        });
      } else {
        this.execute_();
      }
    },
    execute_: function() {
      if ( this.delegate ) {
        this.X.arequire(this.delegate)(function(DelegateModel) {
          DelegateModel.create({ builder: this }).buildApp();
        }.bind(this));
      } else {
        this.buildApp();
      }
    },
    buildApp: function() {
      if ( ! this.targetPath ) {
        this.error("targetPath is required");
        process.exit(1);
      }
      if ( ! this.controller ) {
        this.error("controller is required");
        process.exit(1);
      }

      var extraBuildFiles = this.extraBuildFiles.concat(this.extraFiles);
      for ( var i = 0 ; i < extraBuildFiles.length ; i++ ) {
        var path = this.getFilePath(extraBuildFiles[i]);
        require(path);
      }

      var view = this.defaultView ? this.X.arequire(this.defaultView) : anop;

      var seq = [view];
      for ( var i = 0; i < this.extraModels.length ; i++ ) {
        seq.push(this.X.arequire(this.extraModels[i]));
      }

      aseq(
        aseq.apply(null, seq),
        this.X.arequire(this.controller))(this.buildModel.bind(this));
    },
    buildCoreJS_: function(ret) {
      var i = 0;
      var self = this;
      var corejs = '';
      var file;
      var myfiles = this.coreFiles.length ? this.coreFiles : files ;
      myfiles = myfiles.concat(this.extraFiles);
      awhile(
        function() { return i < myfiles.length; },
        aif(
          function() {
            file = myfiles[i++];
            if ( Array.isArray(file) ) {
              if ( file[1] == IN_NODEJS || file[1] == IN_CHROME_APP )
                return false;
              file = file[0];
            }
            return true;
          },
          aseq(
            function(ret) {
              var path = this.getFilePath(file);
              this.fileDAO.find(path, {
                put: ret,
                error: function() {
                  self.error.apply(["Error reading file: ", path].concat(arguments));
                }
              });
            }.bind(this),
            function(ret, file) {
              corejs += '\n';
              corejs += file.contents;
              ret();
            })))(function() { ret(corejs); });
    },
    buildAppJS_: function(ret) {
      var models = {};
      var visited = {};
      var error = this.error;
      var self = this;

      function add(require) {
        if ( visited[require] ) return;
        visited[require] = true;

        var model = X.lookup(require);
        if ( ! model ) {
          error("Could not load model: ", require);
        }
        if ( model.package &&
             self.blacklistModels.indexOf(model.id) == -1 ) {
          models[model.id] = model;
        }

        model.getAllRequires().forEach(add);
      };
      add(this.controller);
      if ( this.defaultView ) add(this.defaultView);

      for ( var i = 0; i < this.extraModels.length ; i++ ) {
        add(this.extraModels[i]);
      }

      var contents = '';

      var ids = Object.keys(models);
      if ( this.outputManifest ) {
        this.fileDAO.put(this.File.create({
          path: this.targetPath + this.path.sep + 'MANIFEST',
          contents: ids.join('\n')
        }));
      }

      for ( var i = 0; i < ids.length; i++ ) {
        var model = models[ids[i]];
        if ( this.precompileTemplates ) {

          function precompile(model) {
            for ( var j = 0 ; j < model.templates.length ; j++ ) {
              var t = model.templates[j];
              // It's safe to remove leading and trailing whitespace from CSS.
              if ( t.name === 'CSS' ) t.template = t.template.split('\n').map(function(s) { return s.trim(); }).join('\n');
              t.code = TemplateUtil.compile(t);
              t.clearProperty('template');
            }
            model.models.forEach(precompile)
          }

          precompile(model);
        }
        contents += 'CLASS(';
        var formatter = this.precompileTemplates ? this.formatter : JSONUtil.compact;
        contents += formatter.where(NOT_TRANSIENT).stringifyObject(models[ids[i]], 'Model');
        contents += ')\n';
      }

      ret(contents);
    },
    buildModel: function(model) {
      if ( ! model ) {
        this.error('Could not find model: ', this.controller);
      }
      this.log('Building   ', model.id);
      this.log('Target is: ', this.targetPath);
      this.log(this.precompileTemplates ? '' : 'NOT ', 'pre-compiling templates.');

      var self = this;
      aseq(
        function(ret) {
          var file = this.File.create({
            path: this.targetPath + this.path.sep + this.localizedHTMLFileName_,
            contents: this.HTML()
          });

          console.log('Writing: ', file.path);
          this.fileDAO.put(file, {
            put: ret,
            error: function() {
              self.error('ERROR writing file: ', file.path);
              process.exit(1);
            }
          });
        }.bind(this),
        aif(this.appcacheManifest,
            function(ret) {
              var file = this.File.create({
                path: this.targetPath + this.path.sep + this.manifestFileName,
                contents: this.MANIFEST()
              });
              console.log('Writing: ', file.path);
              this.fileDAO.put(file, {
                put: ret,
                error: function() {
                  this.error("ERROR writing file: ", file.path);
                  process.exit(1);
                }
              });
            }.bind(this)),
        apar(
          function(ret) { this.buildCoreJS_(ret); }.bind(this),
          function(ret) { this.buildAppJS_(ret); }.bind(this)),
          function(ret, corejs, appjs) {
            var file = this.File.create({
              path: this.targetPath + this.path.sep + this.jsFileName,
              contents: corejs + appjs
            });
            console.log('Writing: ', file.path);
            this.fileDAO.put(file, {
              put: ret,
              error: function() {
                self.error('ERROR writing file: ', file.path);
                process.exit(1);
              }
            });
          }.bind(this)
          )(
        function(){
          process.exit(0);
        });
    },
    getFilePath: function(file) {
      var path = file;
      if ( path.slice(-3) !== '.js' ) path += '.js';
      if ( path.charAt(0) !== this.path.sep )
        path = FOAM_BOOT_DIR + this.path.sep + path;
      return path;
    }
  },
  templates: [
    function HTML() {/*<html<% if ( this.appcacheManifest ) { %> manifest="%%getManifestFileName"<% } %>><meta charset="utf-8"><%= this.htmlHeaders.join('') %><% if ( this.includeFoamCSS ) { %><link rel="stylesheet" type="text/css" href="foam.css"/><% } %><% if ( this.icon ) { %><link rel="icon" sizes="128x128" href="<%= this.icon %>"/><% } %><foam model="<%= this.controller %>"<% if ( this.defaultView ) { %> view="<%= this.defaultView %>"<% } %>></foam>*/},
    function MANIFEST() {/*CACHE MANIFEST
# version <%= this.version %>
<% if ( this.appDefinition ) { %># hash: <%= this.appDefinition.hashCode() %><% } %>

CACHE:
%%jsFileName
%%localizedHTMLFileName_
<% if ( this.includeFoamCSS ) { %>foam.css<% } %>
<% for ( var i = 0 ; i < this.resources.length ; i++ ) { %><%= this.resources[i] %>
<% } %>

NETWORK:
*
*/}
  ]
});
<commit_msg>Fix BuildApp when dealing with the __EXTRA_PROPERTIES__ business.<commit_after>/**
 * @license
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

CLASS({
  package: 'foam.build',
  name: 'BuildApp',

  imports: [
    'log',
    'error'
  ],
  requires: [
    'foam.dao.NodeFileDAO as FileDAO',
    'foam.dao.File',
    'foam.core.dao.OrDAO',
    'node.dao.ModelFileDAO',
    'foam.build.WebApplication'
  ],
  properties: [
    {
      name: 'appDefinition'
    },
    {
      name: 'controller',
      help: 'Name of the main controller/model to create',
    },
    {
      model_: 'StringArrayProperty',
      name: 'coreFiles',
      adapt: function(_, s) { if ( typeof s === 'string' ) return s.split(','); return s; }
    },
    {
      name: 'defaultView',
      help: "Default view of the controller to use.  If not set, the controller will be used as view (if it is one), or DetailView will be used"
    },
    {
      name: 'targetPath',
      help: "Directory to write output files to.  Will be created if it doesn't exist.",
      required: true
    },
    {
      model_: 'BooleanProperty',
      name: 'precompileTemplates',
      help: 'True to precompile templates of models loaded from the ModelDAO.',
      defaultValue: false
    },
    {
      model_: 'BooleanProperty',
      name: 'includeFoamCSS',
      defaultValue: false
    },
    {
      model_: 'StringProperty',
      name: 'icon'
    },
    {
      model_: 'StringProperty',
      name: 'version'
    },
    {
      model_: 'StringArrayProperty',
      name: 'resources'
    },
    {
      model_: 'BooleanProperty',
      name: 'appcacheManifest',
      defaultValue: false
    },
    {
      model_: 'StringArrayProperty',
      name: 'extraFiles',
      help: 'Extra files to both load during the build process, and include in the built image.',
      adapt: function(_, s) { if ( typeof s === 'string' ) return s.split(','); return s; }
    },
    {
      model_: 'StringArrayProperty',
      name: 'extraBuildFiles',
      help: 'Extra files to load during the build process, but NOT include in the built image.',
      adapt: function(_, s) { if ( typeof s === 'string' ) return s.split(','); return s; }
    },
    {
      model_: 'StringArrayProperty',
      name: 'extraModels',
      help: 'Extra models to include in the image regardless of if they were arequired or not.',
      adapt: function(_, s) { if ( typeof s === 'string' ) return s.split(','); return s; },
      factory: function() { return ['foam.ui.FoamTagView']; }
    },
    {
      model_: 'StringArrayProperty',
      name: 'blacklistModels',
      help: 'Models to unconditionally exclude from the image, even if they are listed as required.',
      adapt: function(_, s) { if ( typeof s === 'string' ) return s.split(','); return s; }
    },
    {
      model_: 'BooleanProperty',
      name: 'outputManifest',
      defaultValue: false,
      help: 'Set to true to write out a MANIFEST file listing all included models.'
    },
    {
      name: 'htmlFileName',
      help: 'Name of the main html file to produce.',
      defaultValue: 'main.html'
    },
    {
      model_: 'StringArrayProperty',
      name: 'htmlHeaders'
    },
    {
      name: 'formatter',
      factory: function() {
        return {
          __proto__: JSONUtil.compact,
          formatFunction: function(f) {
            var s = f.code.toString();
            if ( s.startsWith('function ' + f.name + '(') ) return s;
            return s.replace(/function ([^\(]*)\(/, 'function ' + f.name + '(')
          },
          keys_: {},
          keyify: JSONUtil.prettyModel.keyify,
          outputObject_: function(out, obj, opt_defaultModel) {
            var first = true;

            out('{');
            if ( obj.model_.id !== opt_defaultModel ) {
              this.outputModel_(out, obj);
              first = false;
            }

            if ( Template.isInstance(obj) ) var isTemplate = true;

            var properties = obj.model_.getRuntimeProperties();
            for ( var key in properties ) {
              var prop = properties[key];

              if ( ! this.p(prop) && ( ! isTemplate || prop.name !== 'code' ) ) continue;

              if ( prop.name === 'documentation' ) continue;

              if ( prop.name in obj.instance_ ) {
                var val = obj[prop.name];
                if ( Array.isArray(val) && ! val.length ) continue;
                if ( ! first ) out(',');
                out(this.keyify(prop.name), ': ');

                if ( prop.name === 'methods' ) {
                  out('[');
                  var ff = true;
                  for ( var i = 0 ; i < val.length ; i++ ) {
                    if ( ! ff ) out(',');

                    out(this.formatFunction(val[i]));
                    ff = false;
                  }
                  out(']');
                } else {
                  if ( Array.isArray(val) && prop.subType ) {
                    this.outputArray_(out, val, prop.subType);
                  } else {
                    this.output(out, val);
                  }
                }
                first = false;
              }
            }

            out('}');
          }
        };
      }
    },
    {
      name: 'path',
      factory: function() { return require('path'); }
    },
    {
      name: 'fileDAO',
      factory: function() { return this.FileDAO.create(); }
    },
    {
      model_: 'StringArrayProperty',
      name: 'extraClassPaths',
      help: 'List of extra .js hierarchies to load models from.  Paths will be checked in the order given, finally falling back to the main FOAM js/ hierarchy.',
      adapt: function(_, s) { if ( typeof s === 'string' ) return s.split(','); return s; }
    },
    {
      model_: 'StringProperty',
      name: 'locale'
    },
    // TODO(markdittmer): Remove "i18nMessagesPath" when all build processes
    // no longer require it.
    {
      model_: 'StringProperty',
      name: 'i18nMessagesPath'
    },
    {
      model_: 'StringProperty',
      name: 'i18nTranslationsPath'
    },
    {
      model_: 'StringArrayProperty',
      name: 'i18nMessages',
      adapt: function(_, s) {
        if (typeof s === 'string') return s.split(',');
        return s;
      }
    },
    {
      model_: 'StringArrayProperty',
      name: 'i18nTranslations',
      adapt: function(_, s) {
        if (typeof s === 'string') return s.split(',');
        return s;
      }
    },
    {
      model_: 'StringProperty',
      name: 'jsFileName',
      getter: function() {
        return 'foam' + (this.locale ? '_' + this.locale : '') + '.js';
      }
    },
    {
      model_: 'StringProperty',
      name: 'manifestFileName',
      getter: function() {
        return 'app' + (this.locale ? '_' + this.locale : '') + '.manifest';
      }
    },
    {
      name: 'localizedHTMLFileName_',
      getter: function() {
        var match = this.htmlFileName.match(/[.][^.]*$/g);
        var ext = match ? match[0] : '';
        var baseName = this.htmlFileName.slice(0,
            this.htmlFileName.length - ext.length);
        return baseName + (this.locale ? '_' + this.locale : '') + ext;
      }
    },
    {
      model_: 'StringProperty',
      name: 'delegate'
    }
  ],
  methods: {
    execute: function() {
      for ( var i = 0; i < this.extraClassPaths.length ; i++ ) {
        this.X.ModelDAO = this.OrDAO.create({
          delegate: this.ModelFileDAO.create({
            classpath: this.extraClassPaths[i]
          }),
          primary: this.X.ModelDAO
        });
      }

      if ( this.appDefinition ) {
        this.X.ModelDAO.find(this.appDefinition, {
          put: function(d) {
            this.copyFrom(d);
            this.execute_();
          }.bind(this),
          error: function() {
            console.log("App definition failed");
            this.execute_();
          }.bind(this)
        });
      } else {
        this.execute_();
      }
    },
    execute_: function() {
      if ( this.delegate ) {
        this.X.arequire(this.delegate)(function(DelegateModel) {
          DelegateModel.create({ builder: this }).buildApp();
        }.bind(this));
      } else {
        this.buildApp();
      }
    },
    buildApp: function() {
      if ( ! this.targetPath ) {
        this.error("targetPath is required");
        process.exit(1);
      }
      if ( ! this.controller ) {
        this.error("controller is required");
        process.exit(1);
      }

      var extraBuildFiles = this.extraBuildFiles.concat(this.extraFiles);
      for ( var i = 0 ; i < extraBuildFiles.length ; i++ ) {
        var path = this.getFilePath(extraBuildFiles[i]);
        require(path);
      }

      var view = this.defaultView ? this.X.arequire(this.defaultView) : anop;

      var seq = [view];
      for ( var i = 0; i < this.extraModels.length ; i++ ) {
        seq.push(this.X.arequire(this.extraModels[i]));
      }

      aseq(
        aseq.apply(null, seq),
        this.X.arequire(this.controller))(this.buildModel.bind(this));
    },
    buildCoreJS_: function(ret) {
      var i = 0;
      var self = this;
      var corejs = '';
      var file;
      var myfiles = this.coreFiles.length ? this.coreFiles : files ;
      myfiles = myfiles.concat(this.extraFiles);
      awhile(
        function() { return i < myfiles.length; },
        aif(
          function() {
            file = myfiles[i++];
            if ( Array.isArray(file) ) {
              if ( ! file[0] || file[1] == IN_NODEJS || file[1] == IN_CHROME_APP )
                return false;
              file = file[0];
            }
            return true;
          },
          aseq(
            function(ret) {
              var path = this.getFilePath(file);
              this.fileDAO.find(path, {
                put: ret,
                error: function() {
                  self.error.apply(["Error reading file: ", path].concat(arguments));
                }
              });
            }.bind(this),
            function(ret, file) {
              corejs += '\n';
              corejs += file.contents;
              ret();
            })))(function() { ret(corejs); });
    },
    buildAppJS_: function(ret) {
      var models = {};
      var visited = {};
      var error = this.error;
      var self = this;

      function add(require) {
        if ( visited[require] ) return;
        visited[require] = true;

        var model = X.lookup(require);
        if ( ! model ) {
          error("Could not load model: ", require);
        }
        if ( model.package &&
             self.blacklistModels.indexOf(model.id) == -1 ) {
          models[model.id] = model;
        }

        model.getAllRequires().forEach(add);
      };
      add(this.controller);
      if ( this.defaultView ) add(this.defaultView);

      for ( var i = 0; i < this.extraModels.length ; i++ ) {
        add(this.extraModels[i]);
      }

      var contents = '';

      var ids = Object.keys(models);
      if ( this.outputManifest ) {
        this.fileDAO.put(this.File.create({
          path: this.targetPath + this.path.sep + 'MANIFEST',
          contents: ids.join('\n')
        }));
      }

      for ( var i = 0; i < ids.length; i++ ) {
        var model = models[ids[i]];
        if ( this.precompileTemplates ) {

          function precompile(model) {
            for ( var j = 0 ; j < model.templates.length ; j++ ) {
              var t = model.templates[j];
              // It's safe to remove leading and trailing whitespace from CSS.
              if ( t.name === 'CSS' ) t.template = t.template.split('\n').map(function(s) { return s.trim(); }).join('\n');
              t.code = TemplateUtil.compile(t);
              t.clearProperty('template');
            }
            model.models.forEach(precompile)
          }

          precompile(model);
        }
        contents += 'CLASS(';
        var formatter = this.precompileTemplates ? this.formatter : JSONUtil.compact;
        contents += formatter.where(NOT_TRANSIENT).stringifyObject(models[ids[i]], 'Model');
        contents += ')\n';
      }

      ret(contents);
    },
    buildModel: function(model) {
      if ( ! model ) {
        this.error('Could not find model: ', this.controller);
      }
      this.log('Building   ', model.id);
      this.log('Target is: ', this.targetPath);
      this.log(this.precompileTemplates ? '' : 'NOT ', 'pre-compiling templates.');

      var self = this;
      aseq(
        function(ret) {
          var file = this.File.create({
            path: this.targetPath + this.path.sep + this.localizedHTMLFileName_,
            contents: this.HTML()
          });

          console.log('Writing: ', file.path);
          this.fileDAO.put(file, {
            put: ret,
            error: function() {
              self.error('ERROR writing file: ', file.path);
              process.exit(1);
            }
          });
        }.bind(this),
        aif(this.appcacheManifest,
            function(ret) {
              var file = this.File.create({
                path: this.targetPath + this.path.sep + this.manifestFileName,
                contents: this.MANIFEST()
              });
              console.log('Writing: ', file.path);
              this.fileDAO.put(file, {
                put: ret,
                error: function() {
                  this.error("ERROR writing file: ", file.path);
                  process.exit(1);
                }
              });
            }.bind(this)),
        apar(
          function(ret) { this.buildCoreJS_(ret); }.bind(this),
          function(ret) { this.buildAppJS_(ret); }.bind(this)),
          function(ret, corejs, appjs) {
            var file = this.File.create({
              path: this.targetPath + this.path.sep + this.jsFileName,
              contents: corejs + appjs
            });
            console.log('Writing: ', file.path);
            this.fileDAO.put(file, {
              put: ret,
              error: function() {
                self.error('ERROR writing file: ', file.path);
                process.exit(1);
              }
            });
          }.bind(this)
          )(
        function(){
          process.exit(0);
        });
    },
    getFilePath: function(file) {
      var path = file;
      if ( path.slice(-3) !== '.js' ) path += '.js';
      if ( path.charAt(0) !== this.path.sep )
        path = FOAM_BOOT_DIR + this.path.sep + path;
      return path;
    }
  },
  templates: [
    function HTML() {/*<html<% if ( this.appcacheManifest ) { %> manifest="%%getManifestFileName"<% } %>><meta charset="utf-8"><%= this.htmlHeaders.join('') %><% if ( this.includeFoamCSS ) { %><link rel="stylesheet" type="text/css" href="foam.css"/><% } %><% if ( this.icon ) { %><link rel="icon" sizes="128x128" href="<%= this.icon %>"/><% } %><foam model="<%= this.controller %>"<% if ( this.defaultView ) { %> view="<%= this.defaultView %>"<% } %>></foam>*/},
    function MANIFEST() {/*CACHE MANIFEST
# version <%= this.version %>
<% if ( this.appDefinition ) { %># hash: <%= this.appDefinition.hashCode() %><% } %>

CACHE:
%%jsFileName
%%localizedHTMLFileName_
<% if ( this.includeFoamCSS ) { %>foam.css<% } %>
<% for ( var i = 0 ; i < this.resources.length ; i++ ) { %><%= this.resources[i] %>
<% } %>

NETWORK:
*
*/}
  ]
});
<|endoftext|>