<commit_before>// Copyright (c) 2012-2018, Matt Godbolt
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

var _ = require('underscore-node');
var logger = require('./logger').logger;
var utils = require('./utils');

var sourceTag = /^;\s*([0-9]+)\s*:/;
var ignoreAll = /^\s*include listing\.inc$/;
var fileFind = /^; File\s+(.*)$/;
var gccExplorerDir = /\\(compiler-explorer-compiler|tmp)/; // has to match part of the path in handlers/compile.js (ugly)
// Parse into:
// * optional leading whitespace
// * middle part
// * comment part
var parseRe = /^(\s*)([^;]*)(;.*)?$/;
var isProc = /.*PROC\s*$/;
var isEndp = /.*ENDP\s*$/;
var constDef = /^([a-zA-Z_$@][a-zA-Z_$@0-9.]*)\s*(=|DB|DD).*$/;
var labelFind = /[.a-zA-Z_@$][a-zA-Z_$@0-9.]*/g;
var labelDef = /^(.*):\s*$/;
// Anything identifier-looking with a "@@" in the middle, and a comment at the end
// is treated as a mangled name. The comment will be used to replace the identifier.
var mangledIdentifier = /\?[^ ]+@@[^ |]+/;
var commentedLine = /([^;]+);\s*(.*)/;
var numberRe = /^\s+(([0-9a-f]+\b\s*)(([0-9a-f][0-9a-f])+\b\s*)*)(.*)/;

function debug() {
    logger.debug.apply(logger, arguments);
}

function demangle(line) {
    var match, comment;
    if (!(match = line.match(mangledIdentifier))) return line;
    if (!(comment = line.match(commentedLine))) return line;
    return comment[1].trimRight().replace(match[0], comment[2]);
}

function AddrOpcoder() {
    var self = this;
    this.opcodes = [];
    this.offset = null;
    var prevOffset = -1;
    var prevOpcodes = [];
    this.hasOpcodes = function () {
        return self.offset !== null;
    };
    this.onLine = function (line) {
        var match = line.match(numberRe);
        self.opcodes = [];
        self.offset = null;
        if (!match) {
            prevOffset = -1;
            return line;
        }
        var restOfLine = match[5];
        var numbers = _.chain(match[1].split(/\s+/))
            .filter(function (x) {
                return x !== "";
            })
            .value();
        // If restOfLine is empty, we should accumulate offset opcodes...
        if (restOfLine === "") {
            if (prevOffset < 0) {
                // First in a batch of opcodes, so first is the offset
                prevOffset = parseInt(numbers[0], 16);
                prevOpcodes = numbers.splice(1);
            } else {
                prevOpcodes = prevOpcodes.concat(numbers);
            }
        } else {
            if (prevOffset >= 0) {
                // we had something from a prior line
                self.offset = prevOffset;
                self.opcodes = prevOpcodes.concat(numbers);
                prevOffset = -1;
            } else {
                self.offset = parseInt(numbers[0], 16);
                self.opcodes = numbers.splice(1);
            }
        }
        return "  " + restOfLine;
    };
}

function ClParser(filters) {
    this.filters = filters;
    this.opcoder = new AddrOpcoder();
    this.result = [];
    this.sourceFilename = null;
    this.source = null;
    this.labels = {};
    this.currentLabel = null;
    debug("Parser created");
}

ClParser.prototype._add = function (obj) {
    if (obj.text === "") return;
    if (this.currentLabel) obj.label = this.currentLabel;
    obj.text = utils.expandTabs(obj.text);
    if (this.filters.binary && this.opcoder.hasOpcodes()) {
        obj.opcodes = this.opcoder.opcodes;
        obj.address = this.opcoder.offset;
    }
    if (obj.keep) {
        this.markUsed(obj.lineLabels);
    }
    this.result.push(obj);
    debug(obj);
};

// TODO: unify this with asm.js.
// "moose" example, for example has extraneous labels (|$M7|)
ClParser.prototype.markUsed = function (lineLabels) {
    _.each(lineLabels, function (val, label) {
        if (!this.labels[label]) {
            debug("Marking " + label + " as used");
        }
        this.labels[label] = true;
    }, this);
};

ClParser.prototype.addLine = function (line) {
    if (!!line.match(ignoreAll)) return;
    line = this.opcoder.onLine(line);
    if (line.trim() === "") {
        this._add({keep: true, text: "", source: null});
        return;
    }

    var match;
    if (!!(match = line.match(fileFind))) {
        if (!!match[1].match(gccExplorerDir)) {
            this.sourceFilename = null;
        } else {
            this.sourceFilename = match[1];
        }
        return;
    }
    if (!!(match = line.match(sourceTag))) {
        this.source = {'file': this.sourceFilename, 'line': parseInt(match[1])};
        return;
    }

    line = demangle(line);

    match = line.match(parseRe);
    if (!match) {
        throw new Error("Unable to parse '" + line + "'");
    }

    var isIndented = match[1] !== "";
    var command = match[2];
    var comment = match[3] || "";
    var lineLabels = {};
    _.each(command.match(labelFind), function (label) {
        lineLabels[label] = true;
    }, this);
    if (isIndented && this.opcoder.hasOpcodes()) {
        this._add({keep: true, lineLabels: lineLabels, text: "\t" + command + comment, source: this.source});
    } else {
        var keep = !this.filters.directives;
        if (command.match(isProc))
            keep = true;
        if (command.match(isEndp)) {
            keep = true;
            this.source = null;
            this.currentLabel = null;
        }
        var tempDef = false;
        if (!!(match = command.match(labelDef))) {
            keep = !this.filters.labels;
            this.currentLabel = match[1];
            debug(match, this.currentLabel);
        }
        if (!!(match = command.match(constDef))) {
            keep = !this.filters.labels;
            this.currentLabel = match[1];
            debug(match, this.currentLabel);
            tempDef = true;
        }
        this._add({keep: keep, lineLabels: lineLabels, text: command + comment, source: null});
        if (tempDef) this.currentLabel = null;
    }
};

ClParser.prototype.findUsedInternal = function () {
    var changed = false;
    _.each(this.result, function (obj) {
        if (obj.keep || !this.labels[obj.label]) {
            return;
        }
        debug("Keeping", obj);
        obj.keep = true;
        this.markUsed(obj.lineLabels);
        changed = true;
    }, this);
    debug("changed", changed);
    return changed;
};

ClParser.prototype.findUsed = function () {
    // TODO write tests that cover dependent labels being used.
    var MaxIterations = 100;
    for (var i = 0; i < MaxIterations; ++i) {
        if (!this.findUsedInternal())
            return;
    }
};

ClParser.prototype.get = function () {
    this.findUsed();
    var lastWasEmpty = true;
    return _.chain(this.result)
        .filter(function (elem) {
            if (!elem.keep) return false;
            var thisIsEmpty = elem.text === "";
            if (thisIsEmpty && lastWasEmpty) return false;
            lastWasEmpty = thisIsEmpty;
            return true;
        })
        .map(function (elem) {
            return _.pick(elem, ['opcodes', 'address', 'source', 'text']);
        })
        .value();
};

function AsmParser(compilerProps) {
}

AsmParser.prototype.process = function (asm, filters) {
    var parser = new ClParser(filters);
    utils.eachLine(asm, function (line) {
        parser.addLine(line);
    });
    return parser.get();
};

module.exports = {
    ClParser: ClParser,
    AsmParser: AsmParser
};
<commit_msg>ES6ify asm-cl.js<commit_after>// Copyright (c) 2012-2018, Matt Godbolt
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

const _ = require('underscore-node');
const logger = require('./logger').logger;
const utils = require('./utils');

const sourceTag = /^;\s*([0-9]+)\s*:/;
const ignoreAll = /^\s*include listing\.inc$/;
const fileFind = /^; File\s+(.*)$/;
const gccExplorerDir = /\\(compiler-explorer-compiler|tmp)/; // has to match part of the path in handlers/compile.js (ugly)
// Parse into:
// * optional leading whitespace
// * middle part
// * comment part
const parseRe = /^(\s*)([^;]*)(;.*)?$/;
const isProc = /.*PROC\s*$/;
const isEndp = /.*ENDP\s*$/;
const constDef = /^([a-zA-Z_$@][a-zA-Z_$@0-9.]*)\s*(=|DB|DD).*$/;
const labelFind = /[.a-zA-Z_@$][a-zA-Z_$@0-9.]*/g;
const labelDef = /^(.*):\s*$/;
// Anything identifier-looking with a "@@" in the middle, and a comment at the end
// is treated as a mangled name. The comment will be used to replace the identifier.
const mangledIdentifier = /\?[^ ]+@@[^ |]+/;
const commentedLine = /([^;]+);\s*(.*)/;
const numberRe = /^\s+(([0-9a-f]+\b\s*)(([0-9a-f][0-9a-f])+\b\s*)*)(.*)/;

function debug() {
    logger.debug.apply(logger, arguments);
}

function demangle(line) {
    let match, comment;
    if (!(match = line.match(mangledIdentifier))) return line;
    if (!(comment = line.match(commentedLine))) return line;
    return comment[1].trimRight().replace(match[0], comment[2]);
}

class AddrOpcoder {
    constructor() {
        this.opcodes = [];
        this.offset = null;
        this.prevOffset = -1;
        this.prevOpcodes = [];
    }

    hasOpcodes() {
        return this.offset !== null;
    }

    onLine(line) {
        const match = line.match(numberRe);
        this.opcodes = [];
        this.offset = null;
        if (!match) {
            this.prevOffset = -1;
            return line;
        }
        const restOfLine = match[5];
        const numbers = _.chain(match[1].split(/\s+/))
            .filter(function (x) {
                return x !== "";
            })
            .value();
        // If restOfLine is empty, we should accumulate offset opcodes...
        if (restOfLine === "") {
            if (this.prevOffset < 0) {
                // First in a batch of opcodes, so first is the offset
                this.prevOffset = parseInt(numbers[0], 16);
                this.prevOpcodes = numbers.splice(1);
            } else {
                this.prevOpcodes = this.prevOpcodes.concat(numbers);
            }
        } else {
            if (this.prevOffset >= 0) {
                // we had something from a prior line
                this.offset = this.prevOffset;
                this.opcodes = this.prevOpcodes.concat(numbers);
                this.prevOffset = -1;
            } else {
                this.offset = parseInt(numbers[0], 16);
                this.opcodes = numbers.splice(1);
            }
        }
        return "  " + restOfLine;
    }
}

class ClParser {
    constructor(filters) {
        this.filters = filters;
        this.opcoder = new AddrOpcoder();
        this.result = [];
        this.sourceFilename = null;
        this.source = null;
        this.labels = {};
        this.currentLabel = null;
        debug("Parser created");
    }

    _add(obj) {
        if (obj.text === "") return;
        if (this.currentLabel) obj.label = this.currentLabel;
        obj.text = utils.expandTabs(obj.text);
        if (this.filters.binary && this.opcoder.hasOpcodes()) {
            obj.opcodes = this.opcoder.opcodes;
            obj.address = this.opcoder.offset;
        }
        if (obj.keep) {
            this.markUsed(obj.lineLabels);
        }
        this.result.push(obj);
        debug(obj);
    }

    // TODO: unify this with asm.js.
    // "moose" example, for example has extraneous labels (|$M7|)
    markUsed(lineLabels) {
        _.each(lineLabels, function (val, label) {
            if (!this.labels[label]) {
                debug("Marking " + label + " as used");
            }
            this.labels[label] = true;
        }, this);
    }

    addLine(line) {
        if (!!line.match(ignoreAll)) return;
        line = this.opcoder.onLine(line);
        if (line.trim() === "") {
            this._add({keep: true, text: "", source: null});
            return;
        }

        let match;
        if (!!(match = line.match(fileFind))) {
            if (!!match[1].match(gccExplorerDir)) {
                this.sourceFilename = null;
            } else {
                this.sourceFilename = match[1];
            }
            return;
        }
        if (!!(match = line.match(sourceTag))) {
            this.source = {'file': this.sourceFilename, 'line': parseInt(match[1])};
            return;
        }

        line = demangle(line);

        match = line.match(parseRe);
        if (!match) {
            throw new Error("Unable to parse '" + line + "'");
        }

        const isIndented = match[1] !== "";
        const command = match[2];
        const comment = match[3] || "";
        const lineLabels = {};
        _.each(command.match(labelFind), function (label) {
            lineLabels[label] = true;
        }, this);
        if (isIndented && this.opcoder.hasOpcodes()) {
            this._add({keep: true, lineLabels: lineLabels, text: "\t" + command + comment, source: this.source});
        } else {
            let keep = !this.filters.directives;
            if (command.match(isProc))
                keep = true;
            if (command.match(isEndp)) {
                keep = true;
                this.source = null;
                this.currentLabel = null;
            }
            let tempDef = false;
            if (!!(match = command.match(labelDef))) {
                keep = !this.filters.labels;
                this.currentLabel = match[1];
                debug(match, this.currentLabel);
            }
            if (!!(match = command.match(constDef))) {
                keep = !this.filters.labels;
                this.currentLabel = match[1];
                debug(match, this.currentLabel);
                tempDef = true;
            }
            this._add({keep: keep, lineLabels: lineLabels, text: command + comment, source: null});
            if (tempDef) this.currentLabel = null;
        }
    }

    findUsedInternal() {
        let changed = false;
        _.each(this.result, function (obj) {
            if (obj.keep || !this.labels[obj.label]) {
                return;
            }
            debug("Keeping", obj);
            obj.keep = true;
            this.markUsed(obj.lineLabels);
            changed = true;
        }, this);
        debug("changed", changed);
        return changed;
    }

    findUsed() {
        // TODO write tests that cover dependent labels being used.
        const MaxIterations = 100;
        for (let i = 0; i < MaxIterations; ++i) {
            if (!this.findUsedInternal())
                return;
        }
    }

    get() {
        this.findUsed();
        let lastWasEmpty = true;
        return _.chain(this.result)
            .filter(function (elem) {
                if (!elem.keep) return false;
                const thisIsEmpty = elem.text === "";
                if (thisIsEmpty && lastWasEmpty) return false;
                lastWasEmpty = thisIsEmpty;
                return true;
            })
            .map(function (elem) {
                return _.pick(elem, ['opcodes', 'address', 'source', 'text']);
            })
            .value();
    }
}

class AsmParser {
    process(asm, filters) {
        const parser = new ClParser(filters);
        utils.eachLine(asm, function (line) {
            parser.addLine(line);
        });
        return parser.get();
    }
}

module.exports = {
    ClParser: ClParser,
    AsmParser: AsmParser
};
<|endoftext|>


<commit_before>const Promise = require('bluebird')
const asyncReplace = Promise.promisify(require('async-replace'))
const fs = require('fs')
const FS = Promise.promisifyAll(fs)
const path = require('path')
const nanoid = require('nanoid')
const minimatch = require('minimatch')
const url = require('url')
const jsStringEscape = require('js-string-escape')
const etag = require('etag')
const mime = require('mime')
const stripBom = require('strip-bom-buf')

const test = /{#asset ([^{}]{0,150})}/g
const imageTest = /\.(jpeg|jpg|gif|png|svg)$/
const fontTest = /\.(woff|ttf|otf|eot|woff2)$/

function isImage (name) {
  return name.match(imageTest) != null
}
function isFont (name) {
  return name.match(fontTest) != null
}

async function evaluateAssets (reporter, stringToReplace, req) {
  req.context.evaluateAssetsCounter = req.context.evaluateAssetsCounter || 0
  req.context.evaluateAssetsCounter++

  const replacedAssets = []

  function convert (str, p1, offset, s, done) {
    const assetName = (p1.indexOf(' @') !== -1) ? p1.substring(0, p1.indexOf(' @')) : p1

    let encoding = 'utf8'
    if (p1.indexOf(' @') !== -1) {
      const paramRaw = p1.replace(assetName, '').replace(' @', '')

      if (paramRaw.split('=').length !== 2) {
        throw new Error('Wrong asset param specification, should be {#asset name @encoding=base64}')
      }

      const paramName = paramRaw.split('=')[0]
      const paramValue = paramRaw.split('=')[1]

      if (paramName !== 'encoding') {
        throw new Error('Unsupported param ' + paramName)
      }

      if (paramValue !== 'base64' && paramValue !== 'utf8' && paramValue !== 'string' && paramValue !== 'link' && paramValue !== 'dataURI') {
        throw new Error('Unsupported asset encoding param value ' + paramValue + ', supported values are base64, utf8, link, dataURI and string')
      }

      if (paramValue === 'dataURI' && !isImage(assetName) && !isFont(assetName)) {
        throw new Error('Asset encoded as dataURI needs to have file extension jpeg|jpg|gif|png|svg|woff|tff|otf|woff2|eot')
      }

      encoding = paramValue
    }

    readAsset(reporter, assetName, encoding, req).then(function (res) {
      replacedAssets.push(assetName)
      done(null, res.content)
    }).catch(done)
  }

  const result = await asyncReplace(stringToReplace, test, convert)
  if (replacedAssets.length) {
    reporter.logger.debug('Replaced assets ' + JSON.stringify(replacedAssets), req)
  }

  if (test.test(result) && req.context.evaluateAssetsCounter < 100) {
    return evaluateAssets(reporter, result, req)
  }

  return result
}

function isAssetPathValid (allowedFiles, link, absolutePath) {
  return (allowedFiles != null) && (minimatch(absolutePath, allowedFiles) || minimatch(absolutePath.replace('/', '\\'), allowedFiles) ||
    minimatch(link, allowedFiles) || minimatch(link.replace('/', '\\'), allowedFiles))
}

function linkPath (reporter, link) {
  const result = path.isAbsolute(link) ? link : path.join(reporter.options.rootDirectory, link)

  if (!isAssetPathValid(reporter.options.assets.allowedFiles, link, result)) {
    const err = new Error('Request to file ' + result + ' denied. Please allow it by setting config { "assets": { "allowedFiles": "**/foo.js" } }')
    err.weak = true
    throw err
  }

  return result
}

async function readFile (reporter, link) {
  const pathToLinkedFile = linkPath(reporter, link)
  try {
    const content = await FS.readFileAsync(pathToLinkedFile)
    const stat = await FS.statAsync(pathToLinkedFile)
    return {
      content: stripBom(content),
      filename: path.basename(pathToLinkedFile),
      modified: stat.mtime
    }
  } catch (e) {
    const err = new Error('Unable to find file ' + pathToLinkedFile)
    err.weak = true
    throw err
  }
}

function resolveAssetLink (reporter, req, assetName) {
  if (reporter.options.assets.rootUrlForLinks) {
    return url.resolve(reporter.options.assets.rootUrlForLinks, 'assets/content/' + assetName)
  }

  if (!reporter.express) {
    return 'assets/content/' + assetName
  }

  const baseUrl = req.context.http ? req.context.http.baseUrl : reporter.express.localhostUrl

  return baseUrl + '/assets/content/' + assetName
}

async function readAsset (reporter, name, encoding, req) {
  let escape = function (val) { return val }

  if (encoding === 'string') {
    escape = jsStringEscape
    encoding = 'utf8'
  }

  if (encoding === 'dataURI') {
    escape = function (val, name) {
      const type = mime.getType(name)
      const charset = type.startsWith('text') ? 'UTF-8' : null
      return 'data:' + type + (charset ? '; charset=' + charset : '') + ';base64,' + val
    }
    encoding = 'base64'
  }

  const assets = await reporter.documentStore.collection('assets').find({$or: [{ name: name }, { link: name }]}, reporter.options.assets.publicAccessEnabled ? null : req)
  if (assets.length < 1) {
    if (reporter.options.assets.searchOnDiskIfNotFoundInStore !== true) {
      throw new Error('Asset ' + name + ' not found')
    }

    if (encoding === 'link') {
      return {
        content: resolveAssetLink(reporter, req, name),
        filename: name
      }
    }

    const file = await readFile(reporter, name)
    return {
      content: escape(Buffer.from(file.content || '').toString(encoding), file.filename),
      filename: file.filename,
      modified: file.modified
    }
  }

  if (encoding === 'link') {
    return assets[0].link ? {
      content: resolveAssetLink(reporter, req, assets[0].link),
      filename: name
    } : {
      content: resolveAssetLink(reporter, req, name),
      filename: name
    }
  }

  if (assets[0].link) {
    const file = await readFile(reporter, assets[0].link)
    return {
      content: escape(Buffer.from(file.content).toString(encoding), file.filename),
      filename: file.filename,
      modified: file.modified
    }
  }

  return {
    content: escape(Buffer.from(assets[0].content || '').toString(encoding), assets[0].name),
    filename: assets[0].name,
    modified: assets[0].modificationDate || new Date()
  }
}

module.exports = function (reporter, definition) {
  reporter.documentStore.registerEntityType('AssetType', {
    _id: { type: 'Edm.String', key: true },
    name: { type: 'Edm.String', key: true, publicKey: true },
    shortid: { type: 'Edm.String' },
    modificationDate: { type: 'Edm.DateTimeOffset' },
    content: { type: 'Edm.Binary', document: { extension: 'html', content: true } },
    forceUpdate: { type: 'Edm.Boolean' },
    isSharedHelper: { type: 'Edm.Boolean' },
    link: { type: 'Edm.String' }
  })

  reporter.documentStore.registerEntitySet('assets', {
    entityType: 'jsreport.AssetType',
    splitIntoDirectories: true
  })

  reporter.options.assets = reporter.options.assets || definition.options

  reporter.on('express-configure', (app) => {
    app.get('/assets/content/:path*', (req, res) => {
      const assetLink = req.params.path + req.params['0']

      readAsset(reporter, assetLink, 'binary', req).then((asset) => {
        if (req.query.download === 'true') {
          res.setHeader('Content-Disposition', 'attachment;filename=' + asset.filename)
        }
        res.setHeader('ETag', etag(asset.content))
        res.setHeader('Cache-Control', 'public, max-age=0')
        res.setHeader('Last-Modified', asset.modified.toUTCString())

        const type = mime.getType(asset.filename)
        if (type) {
          const charset = type.startsWith('text') ? 'UTF-8' : null
          res.setHeader('Content-Type', type + (charset ? '; charset=' + charset : ''))
        }
        res.end(asset.content, 'binary')
      }).catch(function (e) {
        reporter.logger.warn('Unable to get asset content ' + assetLink, e)
        res.status(500).end(e.message)
      })
    })

    app.get('/assets/link/:path*', (req, res) => {
      const assetLink = req.params.path + req.params['0']
      try {
        res.send(linkPath(reporter, assetLink))
      } catch (e) {
        reporter.logger.warn('Unable to get asset link ' + assetLink, e)
        res.status(500).end(e.message)
      }
    })
  })

  reporter.beforeRenderListeners.insert({ after: 'scripts' }, definition.name, this, async (req, res) => {
    const sharedHelpersAssets = await reporter.documentStore.collection('assets').find({ isSharedHelper: true }, req)
    if (sharedHelpersAssets.length > 0 && typeof req.template.helpers === 'object') {
      reporter.logger.warn('Cannot add shared helpers when passing helpers as object', req)
    } else {
      const assetContents = await Promise.map(sharedHelpersAssets, (a) => readAsset(reporter, a.name, 'utf8', req))
      req.template.helpers = req.template.helpers || ''
      assetContents.forEach((ac) => {
        if (req.template.helpers.indexOf(ac.content) === -1) {
          req.template.helpers += '\n' + ac.content
        }
      })
    }

    req.template.content = await evaluateAssets(reporter, req.template.content, req)

    if (req.template.helpers && typeof req.template.helpers === 'string') {
      req.template.helpers = await evaluateAssets(reporter, req.template.helpers, req)
    }
  })

  reporter.afterTemplatingEnginesExecutedListeners.add('assets', async (req, res) => {
    const result = await evaluateAssets(reporter, res.content.toString(), req)
    res.content = Buffer.from(result)
  })

  reporter.initializeListeners.add('assets', () => {
    if (reporter.options.assets.publicAccessEnabled) {
      reporter.emit('export-public-route', '/assets')
    }

    reporter.documentStore.addFileExtensionResolver(function (doc, entitySetName, entityType, propertyType) {
      if (entitySetName === 'assets' && propertyType.document.content) {
        const extensions = path.extname(doc.name).split('.')
        return extensions[extensions.length - 1]
      }
    })

    if (reporter.beforeScriptListeners) {
      reporter.beforeScriptListeners.add('assets', function (scriptDef, req) {
        return evaluateAssets(reporter, scriptDef.script, req).then(function (result) {
          scriptDef.script = result
        })
      })
    }

    reporter.documentStore.collection('assets').beforeInsertListeners.add('assets', async (entity) => {
      delete entity.forceUpdate
      entity.modificationDate = new Date()

      if (!entity.shortid) {
        entity.shortid = nanoid(7)
      }

      if (entity.link) {
        entity.name = path.basename(entity.link)
        await readFile(reporter, entity.link)
        return entity
      }
    })

    reporter.documentStore.collection('assets').beforeUpdateListeners.add('assets', async (query, update) => {
      update.$set.modificationDate = new Date()

      if (query._id && update.$set && update.$set.forceUpdate && update.$set.link) {
        try {
          await FS.writeFileAsync(linkPath(reporter, update.$set.link), update.$set.content)
          delete update.$set.forceUpdate
          delete update.$set.content
        } catch (e) {
          const error = new Error('Unable to access file ' + linkPath(reporter, update.$set.link))
          error.weak = true
          throw error
        }
      } else {
        delete update.$set.forceUpdate
      }
    })
  })
}

module.exports.isAssetPathValid = isAssetPathValid
<commit_msg>don't crash on empty scripts<commit_after>const Promise = require('bluebird')
const asyncReplace = Promise.promisify(require('async-replace'))
const fs = require('fs')
const FS = Promise.promisifyAll(fs)
const path = require('path')
const nanoid = require('nanoid')
const minimatch = require('minimatch')
const url = require('url')
const jsStringEscape = require('js-string-escape')
const etag = require('etag')
const mime = require('mime')
const stripBom = require('strip-bom-buf')

const test = /{#asset ([^{}]{0,150})}/g
const imageTest = /\.(jpeg|jpg|gif|png|svg)$/
const fontTest = /\.(woff|ttf|otf|eot|woff2)$/

function isImage (name) {
  return name.match(imageTest) != null
}
function isFont (name) {
  return name.match(fontTest) != null
}

async function evaluateAssets (reporter, stringToReplace, req) {
  stringToReplace = stringToReplace || ''
  req.context.evaluateAssetsCounter = req.context.evaluateAssetsCounter || 0
  req.context.evaluateAssetsCounter++

  const replacedAssets = []

  function convert (str, p1, offset, s, done) {
    const assetName = (p1.indexOf(' @') !== -1) ? p1.substring(0, p1.indexOf(' @')) : p1

    let encoding = 'utf8'
    if (p1.indexOf(' @') !== -1) {
      const paramRaw = p1.replace(assetName, '').replace(' @', '')

      if (paramRaw.split('=').length !== 2) {
        throw new Error('Wrong asset param specification, should be {#asset name @encoding=base64}')
      }

      const paramName = paramRaw.split('=')[0]
      const paramValue = paramRaw.split('=')[1]

      if (paramName !== 'encoding') {
        throw new Error('Unsupported param ' + paramName)
      }

      if (paramValue !== 'base64' && paramValue !== 'utf8' && paramValue !== 'string' && paramValue !== 'link' && paramValue !== 'dataURI') {
        throw new Error('Unsupported asset encoding param value ' + paramValue + ', supported values are base64, utf8, link, dataURI and string')
      }

      if (paramValue === 'dataURI' && !isImage(assetName) && !isFont(assetName)) {
        throw new Error('Asset encoded as dataURI needs to have file extension jpeg|jpg|gif|png|svg|woff|tff|otf|woff2|eot')
      }

      encoding = paramValue
    }

    readAsset(reporter, assetName, encoding, req).then(function (res) {
      replacedAssets.push(assetName)
      done(null, res.content)
    }).catch(done)
  }

  const result = await asyncReplace(stringToReplace, test, convert)
  if (replacedAssets.length) {
    reporter.logger.debug('Replaced assets ' + JSON.stringify(replacedAssets), req)
  }

  if (test.test(result) && req.context.evaluateAssetsCounter < 100) {
    return evaluateAssets(reporter, result, req)
  }

  return result
}

function isAssetPathValid (allowedFiles, link, absolutePath) {
  return (allowedFiles != null) && (minimatch(absolutePath, allowedFiles) || minimatch(absolutePath.replace('/', '\\'), allowedFiles) ||
    minimatch(link, allowedFiles) || minimatch(link.replace('/', '\\'), allowedFiles))
}

function linkPath (reporter, link) {
  const result = path.isAbsolute(link) ? link : path.join(reporter.options.rootDirectory, link)

  if (!isAssetPathValid(reporter.options.assets.allowedFiles, link, result)) {
    const err = new Error('Request to file ' + result + ' denied. Please allow it by setting config { "assets": { "allowedFiles": "**/foo.js" } }')
    err.weak = true
    throw err
  }

  return result
}

async function readFile (reporter, link) {
  const pathToLinkedFile = linkPath(reporter, link)
  try {
    const content = await FS.readFileAsync(pathToLinkedFile)
    const stat = await FS.statAsync(pathToLinkedFile)
    return {
      content: stripBom(content),
      filename: path.basename(pathToLinkedFile),
      modified: stat.mtime
    }
  } catch (e) {
    const err = new Error('Unable to find file ' + pathToLinkedFile)
    err.weak = true
    throw err
  }
}

function resolveAssetLink (reporter, req, assetName) {
  if (reporter.options.assets.rootUrlForLinks) {
    return url.resolve(reporter.options.assets.rootUrlForLinks, 'assets/content/' + assetName)
  }

  if (!reporter.express) {
    return 'assets/content/' + assetName
  }

  const baseUrl = req.context.http ? req.context.http.baseUrl : reporter.express.localhostUrl

  return baseUrl + '/assets/content/' + assetName
}

async function readAsset (reporter, name, encoding, req) {
  let escape = function (val) { return val }

  if (encoding === 'string') {
    escape = jsStringEscape
    encoding = 'utf8'
  }

  if (encoding === 'dataURI') {
    escape = function (val, name) {
      const type = mime.getType(name)
      const charset = type.startsWith('text') ? 'UTF-8' : null
      return 'data:' + type + (charset ? '; charset=' + charset : '') + ';base64,' + val
    }
    encoding = 'base64'
  }

  const assets = await reporter.documentStore.collection('assets').find({$or: [{ name: name }, { link: name }]}, reporter.options.assets.publicAccessEnabled ? null : req)
  if (assets.length < 1) {
    if (reporter.options.assets.searchOnDiskIfNotFoundInStore !== true) {
      throw new Error('Asset ' + name + ' not found')
    }

    if (encoding === 'link') {
      return {
        content: resolveAssetLink(reporter, req, name),
        filename: name
      }
    }

    const file = await readFile(reporter, name)
    return {
      content: escape(Buffer.from(file.content || '').toString(encoding), file.filename),
      filename: file.filename,
      modified: file.modified
    }
  }

  if (encoding === 'link') {
    return assets[0].link ? {
      content: resolveAssetLink(reporter, req, assets[0].link),
      filename: name
    } : {
      content: resolveAssetLink(reporter, req, name),
      filename: name
    }
  }

  if (assets[0].link) {
    const file = await readFile(reporter, assets[0].link)
    return {
      content: escape(Buffer.from(file.content).toString(encoding), file.filename),
      filename: file.filename,
      modified: file.modified
    }
  }

  return {
    content: escape(Buffer.from(assets[0].content || '').toString(encoding), assets[0].name),
    filename: assets[0].name,
    modified: assets[0].modificationDate || new Date()
  }
}

module.exports = function (reporter, definition) {
  reporter.documentStore.registerEntityType('AssetType', {
    _id: { type: 'Edm.String', key: true },
    name: { type: 'Edm.String', key: true, publicKey: true },
    shortid: { type: 'Edm.String' },
    modificationDate: { type: 'Edm.DateTimeOffset' },
    content: { type: 'Edm.Binary', document: { extension: 'html', content: true } },
    forceUpdate: { type: 'Edm.Boolean' },
    isSharedHelper: { type: 'Edm.Boolean' },
    link: { type: 'Edm.String' }
  })

  reporter.documentStore.registerEntitySet('assets', {
    entityType: 'jsreport.AssetType',
    splitIntoDirectories: true
  })

  reporter.options.assets = reporter.options.assets || definition.options

  reporter.on('express-configure', (app) => {
    app.get('/assets/content/:path*', (req, res) => {
      const assetLink = req.params.path + req.params['0']

      readAsset(reporter, assetLink, 'binary', req).then((asset) => {
        if (req.query.download === 'true') {
          res.setHeader('Content-Disposition', 'attachment;filename=' + asset.filename)
        }
        res.setHeader('ETag', etag(asset.content))
        res.setHeader('Cache-Control', 'public, max-age=0')
        res.setHeader('Last-Modified', asset.modified.toUTCString())

        const type = mime.getType(asset.filename)
        if (type) {
          const charset = type.startsWith('text') ? 'UTF-8' : null
          res.setHeader('Content-Type', type + (charset ? '; charset=' + charset : ''))
        }
        res.end(asset.content, 'binary')
      }).catch(function (e) {
        reporter.logger.warn('Unable to get asset content ' + assetLink, e)
        res.status(500).end(e.message)
      })
    })

    app.get('/assets/link/:path*', (req, res) => {
      const assetLink = req.params.path + req.params['0']
      try {
        res.send(linkPath(reporter, assetLink))
      } catch (e) {
        reporter.logger.warn('Unable to get asset link ' + assetLink, e)
        res.status(500).end(e.message)
      }
    })
  })

  reporter.beforeRenderListeners.insert({ after: 'scripts' }, definition.name, this, async (req, res) => {
    const sharedHelpersAssets = await reporter.documentStore.collection('assets').find({ isSharedHelper: true }, req)
    if (sharedHelpersAssets.length > 0 && typeof req.template.helpers === 'object') {
      reporter.logger.warn('Cannot add shared helpers when passing helpers as object', req)
    } else {
      const assetContents = await Promise.map(sharedHelpersAssets, (a) => readAsset(reporter, a.name, 'utf8', req))
      req.template.helpers = req.template.helpers || ''
      assetContents.forEach((ac) => {
        if (req.template.helpers.indexOf(ac.content) === -1) {
          req.template.helpers += '\n' + ac.content
        }
      })
    }

    req.template.content = await evaluateAssets(reporter, req.template.content, req)

    if (req.template.helpers && typeof req.template.helpers === 'string') {
      req.template.helpers = await evaluateAssets(reporter, req.template.helpers, req)
    }
  })

  reporter.afterTemplatingEnginesExecutedListeners.add('assets', async (req, res) => {
    const result = await evaluateAssets(reporter, res.content.toString(), req)
    res.content = Buffer.from(result)
  })

  reporter.initializeListeners.add('assets', () => {
    if (reporter.options.assets.publicAccessEnabled) {
      reporter.emit('export-public-route', '/assets')
    }

    reporter.documentStore.addFileExtensionResolver(function (doc, entitySetName, entityType, propertyType) {
      if (entitySetName === 'assets' && propertyType.document.content) {
        const extensions = path.extname(doc.name).split('.')
        return extensions[extensions.length - 1]
      }
    })

    if (reporter.beforeScriptListeners) {
      reporter.beforeScriptListeners.add('assets', function (scriptDef, req) {
        return evaluateAssets(reporter, scriptDef.script, req).then(function (result) {
          scriptDef.script = result
        })
      })
    }

    reporter.documentStore.collection('assets').beforeInsertListeners.add('assets', async (entity) => {
      delete entity.forceUpdate
      entity.modificationDate = new Date()

      if (!entity.shortid) {
        entity.shortid = nanoid(7)
      }

      if (entity.link) {
        entity.name = path.basename(entity.link)
        await readFile(reporter, entity.link)
        return entity
      }
    })

    reporter.documentStore.collection('assets').beforeUpdateListeners.add('assets', async (query, update) => {
      update.$set.modificationDate = new Date()

      if (query._id && update.$set && update.$set.forceUpdate && update.$set.link) {
        try {
          await FS.writeFileAsync(linkPath(reporter, update.$set.link), update.$set.content)
          delete update.$set.forceUpdate
          delete update.$set.content
        } catch (e) {
          const error = new Error('Unable to access file ' + linkPath(reporter, update.$set.link))
          error.weak = true
          throw error
        }
      } else {
        delete update.$set.forceUpdate
      }
    })
  })
}

module.exports.isAssetPathValid = isAssetPathValid
<|endoftext|>


<commit_before>// assets.js
// --------------------
//
// Manage bundling/inclusion/compilation of assets
// Includes support for CSS, LESS, js, & CoffeeScript

var _ = require('underscore');
_.str = require('underscore.string');
var async = require('async');
var rack = require('asset-rack');
var pathutil = require('path');
var wrench = require('wrench');
var fs = require('fs');
var cleancss = require('clean-css');
var async = require('async');
var uglify = require('uglify-js');
var less = require('less');
var isProduction = sails.config.environment === 'production';

var JavascriptAsset = rack.Asset.extend({
    mimetype: 'text/javascript',
    create: function() {
        var self = this;
        self.paths = search(sails.config.assets.sequence, /(\.js|\.coffee)$/);
        self.assets = [];
        async.forEachSeries(self.paths, function(path, next) {
            var asset = new rack.Asset({
                mimetype: 'text/javascript',
                url: '/' + pathutil.relative(sails.config.appPath, path),
                contents: fs.readFileSync(path, 'utf8')
            });
            asset.isDev = true;
            self.assets.push(asset);
            asset.on('complete', next);
        }, function(error) {
            if(error) self.emit('error', error);
            self.contents = '';
            if (isProduction) {
                _.each(self.assets, function(asset) {
                    self.contents += asset.contents + '\n';
                });
                self.contents = uglify.minify(self.contents, {
                    fromString: true
                }).code;
            }
            self.isDev = false;
            self.emit('created');
        });
    }
});

var CssAsset = rack.Asset.extend({
    create: function() {
        var self = this;
        self.regex = /(\.css|\.less)$/;
        self.paths = search(sails.config.assets.sequence, self.regex);
        self.assets = [];
        self.lessContents = '';
        async.forEachSeries(self.paths, function(path, next) {
            var asset;
            if(pathutil.extname(path) == '.less') {
                self.lessContents += fs.readFileSync(path);
                return next();
            } else {
                asset = new rack.Asset({
                    url: '/' + pathutil.relative(sails.config.appPath, path),
                    contents: fs.readFileSync(path, 'utf8')
                });
            }
            asset.isDev = true;
            self.assets.push(asset);
            asset.on('complete', next);
        }, function(error) {
            if(error) self.emit('error', error);
            var parser = new less.Parser();
            parser.parse(self.lessContents, function(error, tree) {
                if (error) return next(error);
                var lessAsset = new rack.Asset({
                    url: '/assets/styles/style.less.css',
                    mimetype: 'text/css',
                    contents: tree.toCSS()
                });
                lessAsset.isDev = true;
                self.assets.push(lessAsset);
                lessAsset.on('complete', function() {
                    self.contents = '';
                    if (isProduction) {
                        _.each(self.assets, function(asset) {
                            self.contents += asset.contents += '\n';
                        });
                        self.contents = cleancss.process(self.contents);
                    }
                    self.isDev = false;
                    self.emit('created');
                });
            });
        });
    }
});

var TemplateAsset = rack.Asset.extend({
    create: function() {
        var self = this;
        self.regex = /\.ejs|\.html|\.tmpl$/;
        self.paths = search(sails.config.assets.sequence, self.regex);
        self.contents = '
\n';
        _.each(self.paths, function(path) {
            var fileContents = fs.readFileSync(path, 'utf8');
            self.contents += fileContents;
        });
        self.contents += '
';
        self.emit('created');
    }
});

var Rack = rack.Rack.extend({
    js: function() {
        var out = '';
        _.each(this.assets, function(asset) {
            isJs = asset.mimetype == 'text/javascript';
            if(sails.config.environment == 'production') {
                if(isJs && !asset.isDev) out += asset.tag() + '\n';
            } else {
                if(isJs && asset.isDev) out += asset.tag() + '\n';
            }
        });
        return out;
    },
    css: function() {
        var out = '';
        _.each(this.assets, function(asset) {
            isCss = asset.mimetype == 'text/css';
            if(sails.config.environment == 'production') {
                if(isCss && !asset.isDev) out += asset.tag() + '\n';
            } else {
                if(isCss && asset.isDev) out += asset.tag() + '\n';
            }
        });
        return out;
    },
    templateLibrary: function() {
        var out = '';
        _.each(this.assets, function(asset) {
            if(asset.mimetype == 'text/html') {
                out += asset.contents;
            }
        });
        return out;
    }
});

var search = function(dirnames, regex) {
    paths = [];
    _.each(dirnames, function(dirname) {
        abspath = pathutil.join(sails.config.appPath, dirname);
        filenames = wrench.readdirSyncRecursive(abspath);
        _.each(filenames, function(filename) {
            path = pathutil.join(abspath, filename);
            if(!fs.statSync(path).isFile() || !regex.test(path)) return;
            paths.push(path);
        });
    });
    return paths;
};

exports.createAssets = function() {
    return new Rack([
        new JavascriptAsset({
            url: '/app.js'
        }), new CssAsset({
            url: '/style.css'
        }), new TemplateAsset({
            url: '/templates.html'
        })
    ]);
};

<commit_msg>Added coffee-script support to assets.<commit_after>// assets.js
// --------------------
//
// Manage bundling/inclusion/compilation of assets
// Includes support for CSS, LESS, js, & CoffeeScript

var _ = require('underscore');
_.str = require('underscore.string');
var async = require('async');
var rack = require('asset-rack');
var pathutil = require('path');
var wrench = require('wrench');
var fs = require('fs');
var cleancss = require('clean-css');
var async = require('async');
var uglify = require('uglify-js');
var less = require('less');
var isProduction = sails.config.environment === 'production';

var JavascriptAsset = rack.Asset.extend({
    mimetype: 'text/javascript',
    create: function() {
        var self = this;
        self.paths = search(sails.config.assets.sequence, /(\.js|\.coffee)$/);
        self.assets = [];
        async.forEachSeries(self.paths, function(path, next) {
            var asset;
            if (path.indexOf('.coffee') != -1) {
                coffee = require('coffee-script');
                asset = new rack.Asset({
                    mimetype: 'text/javascript',
                    url: '/' + pathutil.relative(sails.config.appPath, path).replace('.coffee', '.js'),
                    contents: coffee.compile(fs.readFileSync(path, 'utf8'))
                });
            } else {
                asset = new rack.Asset({
                    mimetype: 'text/javascript',
                    url: '/' + pathutil.relative(sails.config.appPath, path),
                    contents: fs.readFileSync(path, 'utf8')
                });
            }
            asset.isDev = true;
            self.assets.push(asset);
            asset.on('complete', next);
        }, function(error) {
            if(error) self.emit('error', error);
            self.contents = '';
            if (isProduction) {
                _.each(self.assets, function(asset) {
                    self.contents += asset.contents + '\n';
                });
                self.contents = uglify.minify(self.contents, {
                    fromString: true
                }).code;
            }
            self.isDev = false;
            self.emit('created');
        });
    }
});

var CssAsset = rack.Asset.extend({
    create: function() {
        var self = this;
        self.regex = /(\.css|\.less)$/;
        self.paths = search(sails.config.assets.sequence, self.regex);
        self.assets = [];
        self.lessContents = '';
        async.forEachSeries(self.paths, function(path, next) {
            var asset;
            if(pathutil.extname(path) == '.less') {
                self.lessContents += fs.readFileSync(path);
                return next();
            } else {
                asset = new rack.Asset({
                    url: '/' + pathutil.relative(sails.config.appPath, path),
                    contents: fs.readFileSync(path, 'utf8')
                });
            }
            asset.isDev = true;
            self.assets.push(asset);
            asset.on('complete', next);
        }, function(error) {
            if(error) self.emit('error', error);
            var parser = new less.Parser();
            parser.parse(self.lessContents, function(error, tree) {
                if (error) return next(error);
                var lessAsset = new rack.Asset({
                    url: '/assets/styles/style.less.css',
                    mimetype: 'text/css',
                    contents: tree.toCSS()
                });
                lessAsset.isDev = true;
                self.assets.push(lessAsset);
                lessAsset.on('complete', function() {
                    self.contents = '';
                    if (isProduction) {
                        _.each(self.assets, function(asset) {
                            self.contents += asset.contents += '\n';
                        });
                        self.contents = cleancss.process(self.contents);
                    }
                    self.isDev = false;
                    self.emit('created');
                });
            });
        });
    }
});

var TemplateAsset = rack.Asset.extend({
    create: function() {
        var self = this;
        self.regex = /\.ejs|\.html|\.tmpl$/;
        self.paths = search(sails.config.assets.sequence, self.regex);
        self.contents = '
\n';
        _.each(self.paths, function(path) {
            var fileContents = fs.readFileSync(path, 'utf8');
            self.contents += fileContents;
        });
        self.contents += '
';
        self.emit('created');
    }
});

var Rack = rack.Rack.extend({
    js: function() {
        var out = '';
        _.each(this.assets, function(asset) {
            isJs = asset.mimetype == 'text/javascript';
            if(sails.config.environment == 'production') {
                if(isJs && !asset.isDev) out += asset.tag() + '\n';
            } else {
                if(isJs && asset.isDev) out += asset.tag() + '\n';
            }
        });
        return out;
    },
    css: function() {
        var out = '';
        _.each(this.assets, function(asset) {
            isCss = asset.mimetype == 'text/css';
            if(sails.config.environment == 'production') {
                if(isCss && !asset.isDev) out += asset.tag() + '\n';
            } else {
                if(isCss && asset.isDev) out += asset.tag() + '\n';
            }
        });
        return out;
    },
    templateLibrary: function() {
        var out = '';
        _.each(this.assets, function(asset) {
            if(asset.mimetype == 'text/html') {
                out += asset.contents;
            }
        });
        return out;
    }
});

var search = function(dirnames, regex) {
    paths = [];
    _.each(dirnames, function(dirname) {
        abspath = pathutil.join(sails.config.appPath, dirname);
        filenames = wrench.readdirSyncRecursive(abspath);
        _.each(filenames, function(filename) {
            path = pathutil.join(abspath, filename);
            if(!fs.statSync(path).isFile() || !regex.test(path)) return;
            paths.push(path);
        });
    });
    return paths;
};

exports.createAssets = function() {
    return new Rack([
        new JavascriptAsset({
            url: '/app.js'
        }), new CssAsset({
            url: '/style.css'
        }), new TemplateAsset({
            url: '/templates.html'
        })
    ]);
};

<|endoftext|>


<commit_before>function sendEnquire(fromModal){
    var message = "Requisicao do Site \n\n" +
                "Nome Contato: " + $("#nomeContato").val() + "\n\n" +
                "Empresa: " + $("#empresaContato").val() + "\n\n" +
                "Telefone Contato: " + $("#telefoneContato").val() + "\n\n" +
                "Email Contato: " + $("#emailContato").val() + "\n\n" +
                "Origem do contato: " + $.urlParam("s") + "\n\n" +
                "Mensagem: " + $("#mensagem").val();

    var postData = {
      nome: $("#nomeContato").val(),
      empresa: $("#empresaContato").val(),
      telefone: $("#telefoneContato").val(),
      email: $("#emailContato").val(),
      message: message
    };

    var cleanForm = function(){
        $("form input").val('');
        $("form textarea").val('');
        $("#sendMessage").prop('disabled', false);
    };

    $("#sendMessage").prop('disabled', true);

    $.ajax({
        url: "http://purewaterefluentes.tempsite.ws/sendEmail.php",
        method: "POST",
        data: postData,
        dataType: "json",
        success: function(){
            console.log('success');
             console.log(arguments);
            $("#confirmationModal").modal("show");
            $("#confirmationModal").on('hidden.bs.modal', cleanForm);
        },
        error: function(){
            console.log('fail' + arguments);
            console.log(arguments);
          $(".alert-danger").show();
        }
    });
}

function sendEnquireFormspree(fromModal){
    var message = "Requisicao do Site \n\n" +
                "Nome Contato: " + $("#nomeContato").val() + "\n\n" +
                "Empresa: " + $("#empresaContato").val() + "\n\n" +
                "Telefone Contato: " + $("#telefoneContato").val() + "\n\n" +
                "Email Contato: " + $("#emailContato").val() + "\n\n" +
                "Origem do contato: " + $.urlParam("s") + "\n\n" +
                "Mensagem: " + $("#mensagem").val();

    var mail = {
      _replyto: $("#emailContato").val(),
      _subject: "Requisicao do Site - " + $("#nomeContato").val(),
      message: message
    };

    var cleanForm = function(){
        $("form input").val('');
        $("form textarea").val('');
        $("#sendMessage").prop('disabled', false);
    };

    $("#sendMessage").prop('disabled', true);

    $.ajax({
        url: "https://formspree.io/vendas@purewaterefluentes.com.br",
        method: "POST",
        data: mail,
        dataType: "json",
        success: function(){
            $("#confirmationModal").modal("show");
            $("#confirmationModal").on('hidden.bs.modal', cleanForm);
        },
        error: function(){
          $(".alert-danger").show()
        }
    });
}

$.urlParam = function(name){
    var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(window.location.href);
    return results && decodeURIComponent(results[1]) || null;
}
<commit_msg>change datatype<commit_after>function sendEnquire(fromModal){
    var message = "Requisicao do Site \n\n" +
                "Nome Contato: " + $("#nomeContato").val() + "\n\n" +
                "Empresa: " + $("#empresaContato").val() + "\n\n" +
                "Telefone Contato: " + $("#telefoneContato").val() + "\n\n" +
                "Email Contato: " + $("#emailContato").val() + "\n\n" +
                "Origem do contato: " + $.urlParam("s") + "\n\n" +
                "Mensagem: " + $("#mensagem").val();

    var postData = {
      nome: $("#nomeContato").val(),
      empresa: $("#empresaContato").val(),
      telefone: $("#telefoneContato").val(),
      email: $("#emailContato").val(),
      message: message
    };

    var cleanForm = function(){
        $("form input").val('');
        $("form textarea").val('');
        $("#sendMessage").prop('disabled', false);
    };

    $("#sendMessage").prop('disabled', true);

    $.ajax({
        url: "http://purewaterefluentes.tempsite.ws/sendEmail.php",
        method: "POST",
        data: postData,
        dataType: "text",
        success: function(){
            console.log('success');
             console.log(arguments);
            $("#confirmationModal").modal("show");
            $("#confirmationModal").on('hidden.bs.modal', cleanForm);
        },
        error: function(){
            console.log('fail' + arguments);
            console.log(arguments);
          $(".alert-danger").show();
        }
    });
}

function sendEnquireFormspree(fromModal){
    var message = "Requisicao do Site \n\n" +
                "Nome Contato: " + $("#nomeContato").val() + "\n\n" +
                "Empresa: " + $("#empresaContato").val() + "\n\n" +
                "Telefone Contato: " + $("#telefoneContato").val() + "\n\n" +
                "Email Contato: " + $("#emailContato").val() + "\n\n" +
                "Origem do contato: " + $.urlParam("s") + "\n\n" +
                "Mensagem: " + $("#mensagem").val();

    var mail = {
      _replyto: $("#emailContato").val(),
      _subject: "Requisicao do Site - " + $("#nomeContato").val(),
      message: message
    };

    var cleanForm = function(){
        $("form input").val('');
        $("form textarea").val('');
        $("#sendMessage").prop('disabled', false);
    };

    $("#sendMessage").prop('disabled', true);

    $.ajax({
        url: "https://formspree.io/vendas@purewaterefluentes.com.br",
        method: "POST",
        data: mail,
        dataType: "json",
        success: function(){
            $("#confirmationModal").modal("show");
            $("#confirmationModal").on('hidden.bs.modal', cleanForm);
        },
        error: function(){
          $(".alert-danger").show()
        }
    });
}

$.urlParam = function(name){
    var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(window.location.href);
    return results && decodeURIComponent(results[1]) || null;
}
<|endoftext|>


<commit_before>'use strict';

/**
 * Module dependencies
 */

const path = require('path');
const _ = require('lodash');
const fs = require('fs-extra');

/**
 * This `before` function is run before generating targets.
 * Validate, configure defaults, get extra dependencies, etc.
 *
 * @param {Object} scope
 * @param {Function} cb
 */

module.exports = function (scope, cb) {

	const rootPath = path.resolve(scope.rootPath);
	const config = fs.readJsonSync(rootPath+'/config/general.json');

	const specFile = rootPath+'/config/swagger.json';
	if (!fs.existsSync(specFile)){
		fs.copySync(path.resolve(__dirname, '..', 'files'), path.resolve(scope.rootPath));
	}

	const swaggerConfig = fs.readJsonSync(specFile);

	let specification = swaggerConfig.template;
	specification.paths = {};

	if(specification.definitions == undefined) {
		specification.definitions = {};
	}

	let usedModels = ['RequestError'];

	findFiles(rootPath+'/api', 'config', 'routes.json', function(path, name, stat) {

		const json = fs.readJsonSync(path);
		const customPath = path.replace('routes', 'swagger');

		let custom = null;
		let found = false;

		try {
		    fs.accessSync(customPath, fs.F_OK);
		    custom = fs.readJsonSync(customPath);
		} catch(e) {
			custom = null;
		}

		_.forIn(json.routes, function(value, key) {

		  	const route = key.split(' ', 2);
		 	const method = route[0].toLowerCase();
		 	let path = route[1];

		 	let queryParams = [];
	 		path = path.replace(/:([a-z0-9-_]*)/ig, function(i,match) {
				queryParams.push(match);
				return "{"+match+"}";
			});

			if(specification.paths[path] == undefined) {
				specification.paths[path] = {};
			}

			const name = value.controller.toLowerCase();
			const multi = (path.indexOf('{id}') == -1);

			let data = { "$ref": "#/definitions/"+value.controller };
			let parameters = [];

			queryParams.forEach(function(param) {
				parameters.push({
					in: 'path',
					name: param,
					description: param,
					required: true,
					type: 'string'
				});
			});

			if(method == 'get' && multi) {
				data = {
					type: 'array',
					items: { "$ref": "#/definitions/"+value.controller }
				};
				parameters.push({
                    name: 'where',
                    in: 'query',
                    description: 'Waterline filter param',
                    required: false,
                    type: 'string'
                });
                parameters.push({
                	name: 'sort',
                    in: 'query',
                    description: 'Waterline sort param',
                    required: false,
                    type: 'string'
                });

                if(!_.includes(usedModels, value.controller)) {
                	usedModels.push(value.controller);
            	}
			} else if(method == 'post' || method == 'put') {
				parameters.push({
                    name: 'body',
                    in: 'body',
                    description: _.capitalize(name)+' object in JSON',
                    required: true,
                    schema: data
                });

                if(!_.includes(usedModels, value.controller)) {
                	usedModels.push(value.controller);
            	}
			}

			let summary = '';
			switch(method) {
				case 'get': summary = multi ? 'Get all '+name+'s with filter and sorting' : 'Get '+name+' by id'; break;
				case 'post': summary = 'Create new '+name; break;
				case 'delete': summary = 'Delete '+name; break;
				case 'put': summary = 'Update '+name; break;
			}

			let responseSchema = data;
			if(swaggerConfig.response != undefined) {
				let dataProperty = {};
				dataProperty[swaggerConfig.response.dataKey] = data;
				responseSchema = _.defaultsDeep({
					properties: dataProperty
				}, swaggerConfig.response.schema);
			}

			let spec = {
				summary: summary,
				tags: [_.capitalize(name)],
				parameters: parameters,
				responses: {
					'200': {
						description: "Repsonse",
						schema: responseSchema
					},
					default: {
						description: "Error",
						schema: {
							"$ref": "#/definitions/RequestError"
						}
					}
				},
				security: [{
                    token: []
                }]
			};

			// merge custom config
			if(custom != null && custom[key] != undefined) {
				if(custom[key].hide != true) {
					if(custom[key].responses != undefined) {
						spec.responses = _.defaults(custom[key].responses, spec.responses);
					}
					spec = _.defaults(custom[key], spec);
					specification.paths[path][method] = spec;
				}
			} else {
				specification.paths[path][method] = spec;
			}

		});
	});

	let definitions = [];

	findFiles(rootPath+'/api', 'models', 'settings.json', function(path, name, stat) {
		const json = fs.readJsonSync(path);
		const model = name.replace('.settings.json','');
		const availableTypes = ['integer','number','string','boolean'];

		let properties = {}, property;

		if(json.schema) {
			properties['id'] = {
				type: 'integer'
			};
		}

		_.forIn(json.attributes, function(value, key) {

			if(value.model != undefined) {
				const model = _.capitalize(value.model);
				if(config.blueprints.populate == true || value.populated == true) {
					property = { "$ref": "#/definitions/"+model };
				} else if(value.type == 'array') {
					property = { type: 'array', items: { "$ref": "#/definitions/"+model } };
                	if(!_.includes(usedModels, model)) {
	                	usedModels.push(model);
	            	}
				} else {
					property = { type: 'integer' };
				}
			} else if(config.blueprints.populate == true && value.collection != undefined) {
				const model = _.capitalize(value.collection);
				property = { type: 'array', items: { "$ref": "#/definitions/"+model } };
                if(!_.includes(usedModels, model)) {
                	usedModels.push(model);
            	}
			} else {
				if(_.includes(availableTypes, value.type)) {
					property = { type: value.type };
				} else {
					property = { type: 'string' };
				}
			}

			properties[key] = property;

		});

		if(json.autoCreatedAt) {
			properties['createdAt'] = {
				type: 'string'
			};
		}

		if(json.autoUpdatedAt) {
			properties['updatedAt'] = {
				type: 'string'
			};
		}

		if(definitions[model] == undefined) {
			definitions[model] = {
				type: 'object',
				properties: properties
			};
		}

	});

	_.forIn(definitions, function(value, key) {
		if(_.includes(usedModels, key)) {
			specification.definitions[key] = value;
		}
	});

	const dir = rootPath+'/public/docs';
	const file = 'swagger-generated.json';
	if (!fs.existsSync(dir)){
		fs.mkdirSync(dir);
	}
	fs.writeJsonSync(dir+'/'+file, specification);

	_.defaults(scope, {
	    humanizeId: file,
	    humanizedPath: '`'+dir+'`'
	  });

	// Trigger callback with no error to proceed.
 	return cb.success();
};

function findFiles(currentDirPath, dirPattern, pattern, callback) {
    const fs = require('fs');
    const path = require('path');
    const _ = require('lodash');
    const excludes = ['admin'];

    fs.readdirSync(currentDirPath).forEach(function (name) {
    	if(!_.includes(excludes, name)) {
	        let filePath = path.join(currentDirPath, name);
	        let stat = fs.statSync(filePath);
	        if (path.basename(currentDirPath) == dirPattern && stat.isFile() && name.indexOf(pattern) != -1) {
	            callback(filePath, name, stat);
	        } else if (stat.isDirectory()) {
	            findFiles(filePath, dirPattern, pattern, callback);
	        }
    	}
    });
}<commit_msg>cleanup<commit_after>'use strict';

/**
 * Module dependencies
 */

const path = require('path');
const _ = require('lodash');
const fs = require('fs-extra');

/**
 * This `before` function is run before generating targets.
 * Validate, configure defaults, get extra dependencies, etc.
 *
 * @param {Object} scope
 * @param {Function} cb
 */

module.exports = function (scope, cb) {

	const rootPath = path.resolve(scope.rootPath);
	const config = fs.readJsonSync(rootPath+'/config/general.json');

	const specFile = rootPath+'/config/swagger.json';
	if (!fs.existsSync(specFile)){
		fs.copySync(path.resolve(__dirname, '..', 'files'), path.resolve(scope.rootPath));
	}

	const swaggerConfig = fs.readJsonSync(specFile);

	let specification = swaggerConfig.template;
	specification.paths = {};

	if(specification.definitions == undefined) {
		specification.definitions = {};
	}

	let usedModels = ['RequestError'];

	findFiles(rootPath+'/api', 'config', 'routes.json', function(path, name, stat) {

		const json = fs.readJsonSync(path);
		const customPath = path.replace('routes', 'swagger');

		let custom = null;
		let found = false;

		try {
		    fs.accessSync(customPath, fs.F_OK);
		    custom = fs.readJsonSync(customPath);
		} catch(e) {
			custom = null;
		}

		_.forIn(json.routes, function(value, key) {

		  	const route = key.split(' ', 2);
		 	const method = route[0].toLowerCase();
		 	let path = route[1];

		 	let queryParams = [];
	 		path = path.replace(/:([a-z0-9-_]*)/ig, function(i,match) {
				queryParams.push(match);
				return "{"+match+"}";
			});

			if(specification.paths[path] == undefined) {
				specification.paths[path] = {};
			}

			const name = value.controller.toLowerCase();
			const multi = (path.indexOf('{id}') == -1);

			let data = { "$ref": "#/definitions/"+value.controller };
			let parameters = [];

			queryParams.forEach(function(param) {
				parameters.push({
					in: 'path',
					name: param,
					description: param,
					required: true,
					type: 'string'
				});
			});

			if(method == 'get' && multi) {
				data = {
					type: 'array',
					items: { "$ref": "#/definitions/"+value.controller }
				};
				parameters.push({
                    name: 'where',
                    in: 'query',
                    description: 'Waterline filter param',
                    required: false,
                    type: 'string'
                });
                parameters.push({
                	name: 'sort',
                    in: 'query',
                    description: 'Waterline sort param',
                    required: false,
                    type: 'string'
                });

                if(!_.includes(usedModels, value.controller)) {
                	usedModels.push(value.controller);
            	}
			} else if(method == 'post' || method == 'put') {
				parameters.push({
                    name: 'body',
                    in: 'body',
                    description: _.capitalize(name)+' object in JSON',
                    required: true,
                    schema: data
                });

                if(!_.includes(usedModels, value.controller)) {
                	usedModels.push(value.controller);
            	}
			}

			let summary = '';
			switch(method) {
				case 'get': summary = multi ? 'Get all '+name+'s with filter and sorting' : 'Get '+name+' by id'; break;
				case 'post': summary = 'Create new '+name; break;
				case 'delete': summary = 'Delete '+name; break;
				case 'put': summary = 'Update '+name; break;
			}

			let responseSchema = data;
			if(swaggerConfig.response != undefined) {
				let dataProperty = {};
				dataProperty[swaggerConfig.response.dataKey] = data;
				responseSchema = _.defaultsDeep({
					properties: dataProperty
				}, swaggerConfig.response.schema);
			}

			let spec = {
				summary: summary,
				tags: [_.capitalize(name)],
				parameters: parameters,
				responses: {
					'200': {
						description: "Repsonse",
						schema: responseSchema
					},
					default: {
						description: "Error",
						schema: {
							"$ref": "#/definitions/RequestError"
						}
					}
				},
				security: [{
                    token: []
                }]
			};

			// merge custom config
			if(custom != null && custom[key] != undefined) {
				if(custom[key].hide != true) {
					if(custom[key].responses != undefined) {
						spec.responses = _.defaults(custom[key].responses, spec.responses);
					}
					spec = _.defaults(custom[key], spec);
					specification.paths[path][method] = spec;
				}
			} else {
				specification.paths[path][method] = spec;
			}

		});
	});

	let definitions = [];

	findFiles(rootPath+'/api', 'models', 'settings.json', function(path, name, stat) {
		const json = fs.readJsonSync(path);
		const model = name.replace('.settings.json','');
		const availableTypes = ['integer','number','string','boolean'];

		let properties = {}, property;

		if(json.schema) {
			properties['id'] = {
				type: 'integer'
			};
		}

		_.forIn(json.attributes, function(value, key) {

			if(value.model != undefined) {
				const model = _.capitalize(value.model);
				if(config.blueprints.populate == true || value.populated == true) {
					property = { "$ref": "#/definitions/"+model };
				} else if(value.type == 'array') {
					property = { type: 'array', items: { "$ref": "#/definitions/"+model } };
                	if(!_.includes(usedModels, model)) {
	                	usedModels.push(model);
	            	}
				} else {
					property = { type: 'integer' };
				}
			} else if(config.blueprints.populate == true && value.collection != undefined) {
				const model = _.capitalize(value.collection);
				property = { type: 'array', items: { "$ref": "#/definitions/"+model } };
                if(!_.includes(usedModels, model)) {
                	usedModels.push(model);
            	}
			} else {
				if(_.includes(availableTypes, value.type)) {
					property = { type: value.type };
				} else {
					property = { type: 'string' };
				}
			}

			properties[key] = property;

		});

		if(json.autoCreatedAt) {
			properties['createdAt'] = {
				type: 'string'
			};
		}

		if(json.autoUpdatedAt) {
			properties['updatedAt'] = {
				type: 'string'
			};
		}

		if(definitions[model] == undefined) {
			definitions[model] = {
				type: 'object',
				properties: properties
			};
		}

	});

	_.forIn(definitions, function(value, key) {
		if(_.includes(usedModels, key)) {
			specification.definitions[key] = value;
		}
	});

	const dir = rootPath+'/public/docs';
	const file = 'swagger-generated.json';
	if (!fs.existsSync(dir)){
		fs.mkdirSync(dir);
	}
	fs.writeJsonSync(dir+'/'+file, specification);

	_.defaults(scope, {
	    humanizeId: file,
	    humanizedPath: '`'+dir+'`'
	  });

	// Trigger callback with no error to proceed.
 	return cb.success();
};

function findFiles(currentDirPath, dirPattern, pattern, callback) {
    const excludes = ['admin'];

    fs.readdirSync(currentDirPath).forEach(function (name) {
    	if(!_.includes(excludes, name)) {
	        let filePath = path.join(currentDirPath, name);
	        let stat = fs.statSync(filePath);
	        if (path.basename(currentDirPath) == dirPattern && stat.isFile() && name.indexOf(pattern) != -1) {
	            callback(filePath, name, stat);
	        } else if (stat.isDirectory()) {
	            findFiles(filePath, dirPattern, pattern, callback);
	        }
    	}
    });
}<|endoftext|>


<commit_before>/*
 * Copyright 2017 Pascal Mainini
 * Licensed under MIT license, see included file LICENSE or
 * http://opensource.org/licenses/MIT
 */

/**
 * This module wraps Leemon BigInt and provides additional, more performant
 * conversion functions.
 *
 * Kudos to Rolf Haenni for the initial implementation of the algorithms.
 *
 * @module famodulus-client/bigint
 */

'use strict';

const leemon = require('BigInt');
const randomBytes = require('randombytes');

/**
 * A BigInt representing the value 0.
 * @type BigInt
 */
const ZERO = [0];

/**
 * A BigInt representing the value 1.
 * @type BigInt
 */
const ONE = [1, 0];

// bit size of mantissa of JS numbers
const maxIntBits = 52;

// max bit size to perform integer multiplications
const maxMulBits = (() => {
  let bpi = 0;
  while ((1 << (bpi + 1)) > (1 << bpi)) {
    bpi++;
  }
  return bpi >> 1;
})();

const zeros = (() => {
  let retval = new Array(maxIntBits);
  let zeros = '';

  for (let i = 0; i < maxIntBits; i++) {
    retval[i] = zeros;
    zeros += '0';
  }

  return retval;
})();

function hex2Bin (hexstr) {
  let binstr = '';
  let maxBinBits = maxIntBits / 4;
  let r = hexstr.length % maxBinBits;
  r = r > 0 ? r - maxBinBits : r;

  for (let i = r; i < hexstr.length; i += maxBinBits) {
    let binpart = Number.parseInt(hexstr.substring(i, i + maxBinBits), 16).toString(2);
    if (i > r) {
      binstr += zeros[maxIntBits - binpart.length];
    }
    binstr += binpart;
  }

  return binstr;
}

function bin2Hex (binstr) {
  let hexstr = '';
  let r = binstr.length % maxIntBits;
  r = r > 0 ? r - maxIntBits : r;

  for (let i = r; i < binstr.length; i = i + maxIntBits) {
    let g = Number.parseInt(binstr.substring(i, i + maxIntBits), 2).toString(16);
    if (i > r) {
      hexstr += zeros[(maxIntBits / 4) - g.length];
    }
    hexstr += g;
  }

  return hexstr;
}

function hex2BigInt (hexstr) {
  if (hexstr === '0' || hexstr === '00') {
    return [0];
  } else {
    return bin2BigInt(hex2Bin(hexstr));
  }
}

function bin2BigInt (binstr) {
  let x = [];

  for (let i = binstr.length; i > 0; i -= maxMulBits) {
    x.push(Number.parseInt(binstr.substring(i - maxMulBits, i), 2));
  }

  x.push(0);
  return x;
}

function bigInt2Hex (bigint) {
  if (bigint.length === 1 && bigint[0] === 0) {
    return '0';
  } else {
    return bin2Hex(bigInt2Bin(bigint));
  }
}

function bigInt2Bin (bigint) {
  let s = '';

  for (let i = bigint.length - 2; i >= 0; i--) {
    let b = bigint[i].toString(2);
    if (i < bigint.length - 2) {
      s += zeros[maxMulBits - b.length];
    }
    s += b;
  }

  return s;
}

/**
 * Generate a random BigInt smaller than bound.
 * This uses either window.crypto (Web Crypto API) or crypto.randomBytes (nodejs).
 *
 * @param {BigInt} bound        Upper bound for the random number
 * @returns {BigInt}            A random BigInt smaller than bound.
 */
function random (bound) {
  let bytesize = Math.floor(leemon.bitSize(bound) / 8);
  bytesize = bytesize === 0 ? 1 : bytesize;

  let randhex = new Array(bytesize);
  let rand = ZERO;

  while (leemon.equals(rand, ZERO)) {
    randhex = randomBytes(bytesize).map(byte => byte.toString(16));
    rand = leemon.mod(hex2BigInt(randhex.join('')), bound);
  }

  return rand;
}

module.exports = leemon;
module.exports.ZERO = ZERO;
module.exports.ONE = ONE;
module.exports.hex2BigInt = hex2BigInt;
module.exports.bigInt2Hex = bigInt2Hex;
module.exports.random = random;
<commit_msg>documented, clearer variable names<commit_after>/*
 * Copyright 2017 Pascal Mainini
 * Licensed under MIT license, see included file LICENSE or
 * http://opensource.org/licenses/MIT
 */

/**
 * This module wraps Leemon BigInt and provides additional, more performant
 * conversion functions as well as support for CSPRNG random.
 *
 * Kudos to Rolf Haenni for the initial implementation of the algorithms.
 *
 * @module famodulus-client/bigint
 */

'use strict';

const leemon = require('BigInt');
const randomBytes = require('randombytes');

/**
 * A BigInt representing the value 0.
 * @type BigInt
 */
const ZERO = [0];

/**
 * A BigInt representing the value 1.
 * @type BigInt
 */
const ONE = [1, 0];

/**
 * Bit size of mantissa of JS numbers.
 * @type Number
 */
const maxIntBits = 52;

/**
 * Maximum bit size to perform integer multiplications.
 * @type Number
 */
const maxMulBits = (() => {
  let bpi = 0;
  while ((1 << (bpi + 1)) > (1 << bpi)) {
    bpi++;
  }
  return bpi >> 1;
})();

/**
 * Precomputed array containing strings with '0' up to maxIntBits.
 * @type String[]
 */
const zeros = (() => {
  let retval = new Array(maxIntBits);
  let zeros = '';

  for (let i = 0; i < maxIntBits; i++) {
    retval[i] = zeros;
    zeros += '0';
  }

  return retval;
})();

/**
 * Convert a string with a number in hexadecimal format to a binary string.
 *
 * @param {String} hexstr       String with number in hexadecimal
 * @returns {String}            String with number in binary
 */
function hex2Bin (hexstr) {
  let binstr = '';
  const maxBinBits = maxIntBits / 4;

  let r = hexstr.length % maxBinBits;
  r = r > 0 ? r - maxBinBits : r;

  for (let i = r; i < hexstr.length; i += maxBinBits) {
    let bin = Number.parseInt(hexstr.substring(i, i + maxBinBits), 16).toString(2);
    if (i > r) {
      binstr += zeros[maxIntBits - bin.length];
    }
    binstr += bin;
  }

  return binstr;
}

/**
 * Convert a string with a number in binary format to a hexadecimal string.
 *
 * @param {String} binstr       String with number in binary
 * @returns {String}            String with number in hexadecimal
 */
function bin2Hex (binstr) {
  let hexstr = '';

  let r = binstr.length % maxIntBits;
  r = r > 0 ? r - maxIntBits : r;

  for (let i = r; i < binstr.length; i += maxIntBits) {
    let hex = Number.parseInt(binstr.substring(i, i + maxIntBits), 2).toString(16);
    if (i > r) {
      hexstr += zeros[(maxIntBits / 4) - hex.length];
    }
    hexstr += hex;
  }

  return hexstr;
}

/**
 * Converts a string with a hexadecimal number to BigInt.
 *
 * @param {String} hexstr       The hexadecimal number to convert.
 * @returns {BigInt}            BigInt representation of the number
 */
function hex2BigInt (hexstr) {
  if (hexstr === '0' || hexstr === '00') {
    return [0];
  } else {
    return bin2BigInt(hex2Bin(hexstr));
  }
}

/**
 * Converts a string with a binary number to BigInt.
 *
 * @param {String} binstr       The binary number to convert.
 * @returns {BigInt}            BigInt representation of the number
 */
function bin2BigInt (binstr) {
  let bigint = [];

  for (let i = binstr.length; i > 0; i -= maxMulBits) {
    bigint.push(Number.parseInt(binstr.substring(i - maxMulBits, i), 2));
  }

  bigint.push(0);
  return bigint;
}

/**
 * Converts a BigInt to a hexadecimal string
 *
 * @param {BigInt} bigint       The BigInt to convert
 * @returns {String}            Hexadecimal representation of the number
 */
function bigInt2Hex (bigint) {
  if (bigint.length === 1 && bigint[0] === 0) {
    return '0';
  } else {
    return bin2Hex(bigInt2Bin(bigint));
  }
}

/**
 * Converts a BigInt to a binary string
 *
 * @param {BigInt} bigint       The BigInt to convert
 * @returns {String}            Binary representation of the number
 */
function bigInt2Bin (bigint) {
  let retval = '';

  for (let i = bigint.length - 2; i >= 0; i--) {
    let bin = bigint[i].toString(2);
    if (i < bigint.length - 2) {
      retval += zeros[maxMulBits - bin.length];
    }
    retval += bin;
  }

  return retval;
}

/**
 * Generate a random BigInt smaller than bound.
 * This uses either window.crypto (Web Crypto API) or crypto.randomBytes (nodejs).
 *
 * @param {BigInt} bound        Upper bound for the random number
 * @returns {BigInt}            A random BigInt smaller than bound.
 */
function random (bound) {
  let bytesize = Math.floor(leemon.bitSize(bound) / 8);
  bytesize = bytesize === 0 ? 1 : bytesize;

  let randhex = new Array(bytesize);
  let rand = ZERO;

  while (leemon.equals(rand, ZERO)) {
    randhex = randomBytes(bytesize).map(byte => byte.toString(16));
    rand = leemon.mod(hex2BigInt(randhex.join('')), bound);
  }

  return rand;
}

module.exports = leemon;
module.exports.ZERO = ZERO;
module.exports.ONE = ONE;
module.exports.hex2BigInt = hex2BigInt;
module.exports.bin2BigInt = bin2BigInt;
module.exports.bigInt2Hex = bigInt2Hex;
module.exports.bigInt2Bin = bigInt2Bin;
module.exports.random = random;
<|endoftext|>


<commit_before>// Separate Numbers with Commas in JavaScript **Pairing Challenge**

// I worked on this challenge with: .
// separateComma(1569743) == "1,569,743"
// Pseudocode
// create function that adds commas into integer Numbers
// determine where commas should go

var separateComma = function(num) {
  var newString = '';
  num = String(num);
  if (num.length === 4)
    for(var i = 0; i < num.length; i++)
      if( i === 1)
        newString +=  ',' + num[i];
      else
        newString +=  num[i];
  else if (num.length === 5)
    for(var i = 0; i < num.length; i++)
      if( i === 2)
        newString +=  ',' + num[i];
      else
        newString +=  num[i];
  else if (num.length === 6)
    for(var i = 0; i < num.length; i++)
      if( i === 3)
        newString +=  ',' + num[i];
      else
        newString +=  num[i];
  else if (num.length === 7)
    for(var i = 0; i < num.length; i++)
      if( i === 1 || i === 4)
        newString +=  ',' + num[i];
      else
        newString +=  num[i];
  else if (num.length === 8)
    for(var i = 0; i < num.length; i++)
      if( i === 2 || i === 5)
        newString +=  ',' + num[i];
      else
        newString +=  num[i];
  else
    for(var i = 0; i < num.length; i++)
      newString +=  num[i];
    return newString;
};
console.log(separateComma(10000))

// Refactored Solution
var separateComma = function(num) {
  var newString = '';
  num = String(num);
  for(var i = 0; i < num.length; i++)
    if ( num.length - i === 4 || num.length - i === 7 || num.length - i === 10)
      newString += num[i] + ',';
    else
      newString += num[i];
    return newString;
};
console.log(separateComma(1532433));

// Your Own Tests (OPTIONAL)

// Reflection<commit_msg>Added reflection to nums_commas.js<commit_after>// Separate Numbers with Commas in JavaScript **Pairing Challenge**

// I worked on this challenge with: .
// separateComma(1569743) == "1,569,743"
// Pseudocode
// create function that adds commas into integer Numbers
// determine where commas should go

var separateComma = function(num) {
  var newString = '';
  num = String(num);
  if (num.length === 4)
    for(var i = 0; i < num.length; i++)
      if( i === 1)
        newString +=  ',' + num[i];
      else
        newString +=  num[i];
  else if (num.length === 5)
    for(var i = 0; i < num.length; i++)
      if( i === 2)
        newString +=  ',' + num[i];
      else
        newString +=  num[i];
  else if (num.length === 6)
    for(var i = 0; i < num.length; i++)
      if( i === 3)
        newString +=  ',' + num[i];
      else
        newString +=  num[i];
  else if (num.length === 7)
    for(var i = 0; i < num.length; i++)
      if( i === 1 || i === 4)
        newString +=  ',' + num[i];
      else
        newString +=  num[i];
  else if (num.length === 8)
    for(var i = 0; i < num.length; i++)
      if( i === 2 || i === 5)
        newString +=  ',' + num[i];
      else
        newString +=  num[i];
  else
    for(var i = 0; i < num.length; i++)
      newString +=  num[i];
    return newString;
};
console.log(separateComma(10000))

// Refactored Solution
var separateComma = function(num) {
  var newString = '';
  num = String(num);
  for(var i = 0; i < num.length; i++)
    if ( num.length - i === 4 || num.length - i === 7 || num.length - i === 10)
      newString += num[i] + ',';
    else
      newString += num[i];
    return newString;
};
console.log(separateComma(1532433));

// Your Own Tests (OPTIONAL)

// Reflection

What was it like to approach the problem from the perspective of JavaScript? Did you approach the problem differently?
It's hard!  I feel like I just KINDA wrapped my head around ruby and now i'm trying to understand JS... it's scrambbling my brain a little
My pair took a stronger initiative because I didn't really know how to go about it and he felt a little more confortable, but i'd say
yea we generally approached this a little differently.

What did you learn about iterating over arrays in JavaScript?
for loops are super useful during these, and we can write them in the ( ; ; )
  The part before the first semicolon initializes the loop, usually defining the variable.
  the second parrt is the expression that checks whether the loop must continue.
  the final part updates the state of the loop fter every iteration.

What was different about solving this problem in JavaScript?
I feel like this is just because we're still learning about JS but, since there isn't really a clear range function... to create something similar,
we can opt to something like OR, and that'll be the case for several different methods that we think COULD work like ruby but actually have no JS equivalent :(

What built-in methods did you find to incorporate in your refactored solution?
Our refactored version just used OR and .length, nothing else.

<|endoftext|>


<commit_before>'use strict';

var dgram = require('dgram');

/**
 *  noop
 */

function noop() {};

/**
 *  Expose `Artnet`
 */

module.exports = Artnet;

function Artnet(opts) {
  var opts = opts || {};

  this.host = opts.host || '255.255.255.255';
  this.port = opts.port || 6454;
  //this.refresh = opts.refresh || 0;

  this.header =  [65, 114, 116, 45, 78, 101, 116, 0, 0, 80, 0, 14, 0, 0, 1, 0, 2, 0];

  this.socket = null;
  //this.interval = null;
  //this.timer = null;

  this.data = [];
};

/**
 *  Connect to host
 */

Artnet.prototype.connect = function connect() {
  this.socket = dgram.createSocket('udp4');

  //this._startInterval();

  return this;
};

/**
 *  Close a connection
 *
 *  @param {Function} callback - optional
 */

Artnet.prototype.disconnect = function disconnect(callback) {
  callback = callback || noop;

  var self = this;

  this.socket && this.socket.close(function() {
    self.socket = null;
    callback();
  });

  return this;
};

/**
 *  Start the interval
 *
 *  @param {Number} interval
 */

/**
Artnet.prototype._startInterval = function _startInterval(interval) {

  if (interval) this.interval = interval;

  if (!this.interval || this.interval < 50) return null;

  var self = this;

  this.timer = setInterval(function() {
    self._send();
  }, this.interval);

  return this.timer;
}
*/

/**
 *  Stop the interval
 */

/**
Artnet.prototype._stopInterval = function _startInterval() {
  if (!this.timer) return null;

  clearInterval(this.timer);
};
*/

/**
 *  Set a value
 *
 *  @param {Number} universe
 *  @param {Number} channel
 *  @param {Number} value
 */

Artnet.prototype.set = function set(universe, channel, value) {

  if (value < 0) value = 0;
  if (value > 255) value = 255;

  if (channel < 0) channel = 0;
  if (channel > 512) channel = 512;

  if (!this.data[universe]) this.data[universe] = _createUniverse();

  this.data[universe][channel] = value;
};

Artnet.prototype.setAll = function set(universe, data) {
  if (!this.data[universe]) this.data[universe] = _createUniverse();

  this.data[universe] = data;
};

/**
 *  Transmit the data
 */

Artnet.prototype.send = function send() {

  this.data.forEach(function(universe, uIndex){

    this.header[14] = uIndex;

    var data = this.header.concat(this.data[uIndex]);
    var buff = new Buffer(data);

    this.socket.send(buff, 0, buff.length, this.port, this.host);
  }, this);

};

/**
 *  Create a universe (a 512 member array of zeros)
 */

function _createUniverse() {
  return Array.apply(null, new Array(512)).map(Number.prototype.valueOf, 0);
};
<commit_msg>Added sequence<commit_after>'use strict';

var dgram = require('dgram');

/**
 *  noop
 */

function noop() {};

/**
 *  Expose `Artnet`
 */

module.exports = Artnet;

function Artnet(opts) {
  var opts = opts || {};

  this.host = opts.host || '255.255.255.255';
  this.port = opts.port || 6454;
  //this.refresh = opts.refresh || 0;

  this.header =  [65, 114, 116, 45, 78, 101, 116, 0, 0, 80, 0, 14, 0, 0, 1, 0, 2, 0];
  this.sequence = 0;

  this.socket = null;
  //this.interval = null;
  //this.timer = null;

  this.data = [];
};

/**
 *  Connect to host
 */

Artnet.prototype.connect = function connect() {
  this.socket = dgram.createSocket('udp4');

  //this._startInterval();

  return this;
};

/**
 *  Close a connection
 *
 *  @param {Function} callback - optional
 */

Artnet.prototype.disconnect = function disconnect(callback) {
  callback = callback || noop;

  var self = this;

  this.socket && this.socket.close(function() {
    self.socket = null;
    callback();
  });

  return this;
};

/**
 *  Start the interval
 *
 *  @param {Number} interval
 */

/**
Artnet.prototype._startInterval = function _startInterval(interval) {

  if (interval) this.interval = interval;

  if (!this.interval || this.interval < 50) return null;

  var self = this;

  this.timer = setInterval(function() {
    self._send();
  }, this.interval);

  return this.timer;
}
*/

/**
 *  Stop the interval
 */

/**
Artnet.prototype._stopInterval = function _startInterval() {
  if (!this.timer) return null;

  clearInterval(this.timer);
};
*/

/**
 *  Set a value
 *
 *  @param {Number} universe
 *  @param {Number} channel
 *  @param {Number} value
 */

Artnet.prototype.set = function set(universe, channel, value) {

  if (value < 0) value = 0;
  if (value > 255) value = 255;

  if (channel < 0) channel = 0;
  if (channel > 512) channel = 512;

  if (!this.data[universe]) this.data[universe] = _createUniverse();

  this.data[universe][channel] = value;
};

Artnet.prototype.setAll = function set(universe, data) {
  if (!this.data[universe]) this.data[universe] = _createUniverse();

  this.data[universe] = data;
};

/**
 *  Transmit the data
 */

Artnet.prototype.send = function send() {

  this.data.forEach(function(universe, uIndex){

    this.header[12] = this.sequence;
    this.header[14] = uIndex;

    var data = this.header.concat(this.data[uIndex]);
    var buff = new Buffer(data);

    this.socket.send(buff, 0, buff.length, this.port, this.host);

    this.sequence++;
    if (this.sequence > 255)
    {
      this.sequence = 0;
    }
  }, this);

};

/**
 *  Create a universe (a 512 member array of zeros)
 */

function _createUniverse() {
  return Array.apply(null, new Array(512)).map(Number.prototype.valueOf, 0);
};
<|endoftext|>