<commit_before>var path = require('path');
var fileSystem = require('fs');
var readdir = require('recursive-readdir');
var assign = Object.assign || require('object.assign');
var Promise = require('bluebird');
var cheerio = require('cheerio');

/**
 * this global var is reset every run in case of circular dependencies between files
 */
let filesProcessed = [];
let modulesProcessed = [];
/**
 * this is used for displaying logs only
 */
let optionsGlobal = {};

function getFilesRecursively(targetDir, extension) {
  return new Promise((resolve, reject) =>
    readdir(targetDir, [(file, stats) =>
      path.extname(file) !== extension && !stats.isDirectory()
    ], (error, files) => error ? reject(error) : resolve(files)));
}

/**
 * Recursively adds dependencies declared in the package and in all of its .html templates.
 * @param {{ root: string, src: string }} options
 */
async function processAll(options) {
  filesProcessed = [];
  modulesProcessed = [];
  optionsGlobal = options;
  const dependencies = {};
  const nodeModules = path.join(options.root, 'node_modules');
  const packageJson = path.join(options.root, 'package.json');
  let vendorPkg;
  try { vendorPkg = JSON.parse(fileSystem.readFileSync(packageJson, 'utf8')) } catch (_) {}

  // try to load any resources explicitly defined in package.json:
  if (vendorPkg && vendorPkg.aurelia && vendorPkg.aurelia.resources) {
    for (let resource of vendorPkg.aurelia.resources) {
      let fromPath = resource instanceof Object ? resource.path : resource;
      assign(dependencies, await getDependency(fromPath, options.root, options.root, [nodeModules], null, packageJson, options.async || resource.async, options.bundle || resource.bundle));
    }
  }

  // resolve all requirements of .html templates
  assign(dependencies, await autoresolveTemplates(options.src, nodeModules, options.async, options.bundle));
  return dependencies;
}

async function autoresolveTemplates(srcPath, nodeModules, isAsync, bundleName) {
  const dependencies = {};
  const templates = await getFilesRecursively(srcPath, '.html');

  for (let htmlFilePath of templates) {
    assign(dependencies, await resolveTemplate(htmlFilePath, srcPath, [nodeModules], undefined, isAsync, bundleName));
  }
  return dependencies;
}

/**
 * Generates key-value dependency pairs of:
 * - <require from="paths">
 * - view-model="file"
 * - view="file.html"
 * - files requested by dependencies of those above
 *
 * @param  {string} htmlFilePath
 * @param  {string} srcPath
 * @param  {Array<string>} nodeModulesList
 * @param  {string} fromWithinModule
 */
async function resolveTemplate(htmlFilePath, srcPath, nodeModulesList, fromWithinModule, isParentAsync, bundleName, rootAlias) {
  const dependencies = {};
  const html = fileSystem.readFileSync(htmlFilePath);
  const $ = cheerio.load(html);
  const relativeParent = path.dirname(htmlFilePath);
  const resources = [];

  // e.g. <require from="./file">
  // e.g. <require from="bootstrap" async bundle="vendor">
  const requireTags = $('require');
  requireTags.each(index => {
    const fromPath = requireTags[index].attribs.from;
    const isAsync = requireTags[index].attribs.hasOwnProperty('async');
    const bundle = requireTags[index].attribs.bundle;
    if (fromPath)
      resources.push({ path: fromPath, async: isAsync, bundle });
  });

  // e.g. <compose view-model="file">
  const viewModelRequests = $('[view-model]');
  viewModelRequests.each(index => {
    const fromPath = viewModelRequests[index].attribs['view-model'];
    const isAsync = viewModelRequests[index].attribs.hasOwnProperty('async');
    const bundle = viewModelRequests[index].attribs.bundle;
    if (fromPath)
      resources.push({ path: fromPath, async: isAsync, bundle });
  });

  // e.g. <compose view="file.html">
  const viewRequests = $('[view]');
  viewRequests.each(index => {
    const fromPath = viewRequests[index].attribs.view;
    const isAsync = viewRequests[index].attribs.hasOwnProperty('async');
    const bundle = viewRequests[index].attribs.bundle;
    if (fromPath)
      resources.push({ path: fromPath, async: isAsync, bundle });
  });

  // for (let fromPath of fromPaths) {
  for (let resource of resources) {
    assign(dependencies, await getDependency(resource.path, relativeParent, srcPath, nodeModulesList, fromWithinModule, htmlFilePath, isParentAsync || resource.async, bundleName || resource.bundle, rootAlias));
  }
  return dependencies;
}

function getPath(input, async, bundle) {
  const extension = path.extname(input);
  let output = '';
  // for .css files force the request to the raw loader (https://github.com/aurelia/webpack-plugin/issues/11#issuecomment-212578861)
  if (extension == ".css")
    output += `!!raw!`;
  if (async || bundle)
    output += `bundle?`;
  if (async)
    output += `lazy`;
  if (async && bundle)
    output += `&`;
  if (bundle)
    output += `name=${bundle}`;
  if (async || bundle)
    output += `!`;
  return `${output}${input}`
}

function getPathWithoutExtension(input) {
  return path.join(path.parse(input).dir, path.parse(input).name);
}

/**
 * @param  {string} fromPath
 * @param  {string} relativeParent
 * @param  {string} srcPath
 * @param  {Array<string>} nodeModulesList
 * @param  {string|void} fromWithinModule
 * @param  {string|void} requestedBy (used for debugging only)
 * @param  {boolean|void} isAsync
 * @param  {string|void} bundleName
 */
async function getDependency(fromPath, relativeParent, srcPath, nodeModulesList, fromWithinModule, requestedBy, isAsync, bundleName, rootAlias) {
  const dependencies = {};
  const relativeToSrc = path.relative(srcPath, requestedBy);

  let split = relativeToSrc.split(path.sep);
  if (split[0] == 'node_modules') {
    // handle edge case when adding htmlCounterpart
    nodeModulesList = nodeModulesList.concat([path.join(srcPath, 'node_modules')]);
    srcPath = path.join(srcPath, split[0], split[1]);
    fromWithinModule = split[1];
  }

  async function addDependency(webpackRequireString, webpackPath, htmlCounterpart, rootAlias, moduleName, modulePath) {
    if (filesProcessed.indexOf(webpackRequireString) == -1 && webpackRequireString.indexOf('..') == -1) {
      dependencies[webpackRequireString] = webpackPath;
      console.log((fromWithinModule ? '<' + fromWithinModule + '> ' + '[' + path.basename(requestedBy) : '[' + relativeToSrc) + '] required "' + webpackRequireString + '" from "' + webpackPath.replace(optionsGlobal.root + path.sep, '') + '".')
      filesProcessed.push(webpackRequireString);
    }

    if (htmlCounterpart) {
      let htmlWebpackRequireString = './' + getPathWithoutExtension(webpackRequireString) + '.html';
      if (filesProcessed.indexOf(htmlWebpackRequireString) >= 0) return;

      dependencies[htmlWebpackRequireString] = htmlCounterpart;
      console.log((fromWithinModule ? '<' + fromWithinModule + '> ' + '[' + path.basename(requestedBy) : '[' + relativeToSrc) + '] required "' + htmlWebpackRequireString + '" from "' + htmlCounterpart.replace(optionsGlobal.root + path.sep, '') + '".');
      filesProcessed.push(htmlWebpackRequireString);

      assign(dependencies, await resolveTemplate(htmlCounterpart, modulePath || srcPath, nodeModulesList, moduleName || fromWithinModule, isAsync, bundleName, rootAlias));
    }
  }

  let webpackPath;
  let webpackRequireString;
  let fullPathOrig = path.join(relativeParent, fromPath);
  let fullPath = fullPathOrig;
  let fullPathNoExt = getPathWithoutExtension(fullPath);
  let extOrig = path.extname(fullPath)
  const pathIsLocal = fromPath.startsWith('./');
  let htmlCounterpart;
  let stats;
  let extension;
  let moduleName;
  let modulePath;
  let packagesOwnNodeModules;

  try { stats = fileSystem.statSync(fullPath) } catch (_) {}

  if (!stats)
    try { stats = fileSystem.statSync(fullPath = fullPathOrig + '.js') } catch (_) {}

  if (!stats)
    try { stats = fileSystem.statSync(fullPath = fullPathOrig + '.ts') } catch (_) {}

  if (extOrig != '.html') {
    try {
      fileSystem.statSync(fullPathNoExt + '.html');
      htmlCounterpart = fullPathNoExt + '.html';
    } catch (_) {}
  }

  if (stats && stats.isFile()) {
    extension = path.extname(fullPath);

    // load relative file
    webpackPath = getPath(fullPath, isAsync, bundleName);

    if (!fromWithinModule) {
      // if user used './somepath' then traverse from local directory; else 'somepath', he meant /src/somepath

      webpackRequireString = './' + (pathIsLocal ? path.relative(srcPath, path.join(relativeParent, fromPath)) : fromPath);
      await addDependency(webpackRequireString, webpackPath, htmlCounterpart, rootAlias);

      if (rootAlias) {
        webpackRequireString = './' + (pathIsLocal ? path.relative(rootAlias, path.join(relativeParent, fromPath)) : fromPath);
        await addDependency(webpackRequireString, webpackPath, htmlCounterpart, rootAlias);
      }

    } else {
      // resolves to: 'some-module/some-file'

      webpackRequireString = './' + fromWithinModule + '/' + path.relative(srcPath, path.join(relativeParent, fromPath));
      await addDependency(webpackRequireString, webpackPath, htmlCounterpart, rootAlias);

      if (rootAlias) {
        webpackRequireString = './' + fromWithinModule + '/' + path.relative(rootAlias, path.join(relativeParent, fromPath));
        await addDependency(webpackRequireString, webpackPath, htmlCounterpart, rootAlias);
      }
    }
  }
  else if (!pathIsLocal) {
    // at this point the local file doesn't exist and we know that it doesn't start with './'
    // it's a file under a package or a package itself,
    // e.g.: <require from="bootstrap/file.css">

    stats = undefined;
    let nodeModulesIndex = nodeModulesList.length;
    while (!stats && nodeModulesIndex--) {
      let nodeModules = nodeModulesList[nodeModulesIndex]
      fullPathOrig = path.join(nodeModules, fromPath);
      fullPath = fullPathOrig;
      fullPathNoExt = getPathWithoutExtension(fullPath);

      try { stats = fileSystem.statSync(fullPath) } catch (_) {}

      if (!stats)
        try { stats = fileSystem.statSync(fullPath = fullPathOrig + '.js') } catch (_) {}

      if (extOrig != '.html') {
        try {
          fileSystem.statSync(fullPathNoExt + '.html');
          htmlCounterpart = fullPathNoExt + '.html';
        } catch (_) {}
      }
    }

    if (stats) {
      extension = path.extname(fullPath);

      // check if parent is a node_module directory
      if (stats.isDirectory() && path.basename(path.dirname(fullPath)) === 'node_modules') {
        // we're requiring a package (webpack will handle resolving main)
        moduleName = path.basename(fullPath);
        modulePath = fullPath;

        webpackPath = getPath(moduleName, isAsync, bundleName);
        webpackRequireString = './' + fromPath;

      } else if (stats.isFile()) {
        // require the file directly
        moduleName = fromPath.split('/')[0];
        modulePath = path.resolve(nodeModulesList[nodeModulesIndex], moduleName);

        webpackPath = getPath(fullPath, isAsync, bundleName);
        webpackRequireString = './' + fromPath;
      }

      await addDependency(webpackRequireString, webpackPath, htmlCounterpart, rootAlias, moduleName, modulePath);

      if (moduleName && modulePath) {
        const packageJson = path.resolve(modulePath, 'package.json');
        let vendorPkg;
        try { vendorPkg = JSON.parse(fileSystem.readFileSync(packageJson, 'utf8')) } catch (_) {}
        if (vendorPkg) {
          const mainDir = vendorPkg.main ? path.resolve(modulePath, path.dirname(vendorPkg.main)) : null;
          rootAlias = (vendorPkg.aurelia && vendorPkg.aurelia.root && path.resolve(modulePath, vendorPkg.aurelia.root)) || mainDir;

          if (rootAlias === modulePath)
            rootAlias = null;

          if (rootAlias && stats.isFile()) {
            webpackRequireString = './' + moduleName + '/' + path.relative(rootAlias, fullPath);
            await addDependency(webpackRequireString, webpackPath, htmlCounterpart, rootAlias);

            if (extension == '.js' || extension == '.ts') {
              webpackRequireString = './' + moduleName + '/' + path.relative(rootAlias, getPathWithoutExtension(fullPath));
              await addDependency(webpackRequireString, webpackPath, htmlCounterpart, rootAlias);
            }
          }

          if (modulesProcessed.indexOf(modulePath) == -1) {
            // try to also load any files and templates defined in package.json:
            try {
              // check if there are nested 'node_modules' under the package's directory
              const ownPath = path.resolve(modulePath, 'node_modules');
              fileSystem.statSync(ownPath);
              packagesOwnNodeModules = ownPath;
            } catch (_) {}

            if (vendorPkg.aurelia && vendorPkg.aurelia.resources) {
              for (let resource of vendorPkg.aurelia.resources) {
                let resourcePath = resource instanceof Object ? resource.path : resource;
                let useRootAlias = rootAlias;
                if (resource.root) {
                  let useRootAlias = path.resolve(modulePath, resource.root);
                  if (useRootAlias === modulePath)
                    useRootAlias = null;
                }
                assign(dependencies, await getDependency(resourcePath, modulePath, modulePath, packagesOwnNodeModules ? nodeModulesList.concat(packagesOwnNodeModules) : nodeModulesList, moduleName, packageJson, isAsync || resource.async, bundleName || resource.bundle, useRootAlias));
              }
            }
            modulesProcessed.push(modulePath);
          }
        }
      }
    }
  }

  if (!webpackPath) {
    console.error('[' + (fromWithinModule ? '<' + fromWithinModule + '>' : path.relative(srcPath, requestedBy)) + '] wants to require "' + fromPath + '", which does not exist.')
  } else {
    if (extension == ".html") {
      // if we were referred to .html, get dependencies recursively:
      assign(dependencies, await resolveTemplate(fullPath, modulePath || srcPath, packagesOwnNodeModules ? nodeModulesList.concat(packagesOwnNodeModules) : nodeModulesList, moduleName || fromWithinModule, isAsync, bundleName, rootAlias));
    }
  }

  return dependencies;
}

module.exports = {
  getFilesRecursively,
  processAll,
  autoresolveTemplates,
  resolveTemplate,
  getDependency
}
<commit_msg>feat(resolve-template): allow non-root module paths and root overrides<commit_after>var path = require('path');
var fileSystem = require('fs');
var readdir = require('recursive-readdir');
var assign = Object.assign || require('object.assign');
var Promise = require('bluebird');
var cheerio = require('cheerio');

/**
 * this global var is reset every run in case of circular dependencies between files
 */
let filesProcessed = [];
let modulesProcessed = [];
/**
 * this is used for displaying logs only
 */
let optionsGlobal = {};
let baseVendorPkg;

function getFilesRecursively(targetDir, extension) {
  return new Promise((resolve, reject) =>
    readdir(targetDir, [(file, stats) =>
      path.extname(file) !== extension && !stats.isDirectory()
    ], (error, files) => error ? reject(error) : resolve(files)));
}

/**
 * Recursively adds dependencies declared in the package and in all of its .html templates.
 * @param {{ root: string, src: string }} options
 */
async function processAll(options) {
  filesProcessed = [];
  modulesProcessed = [];
  optionsGlobal = options;
  const dependencies = {};
  const nodeModules = path.join(options.root, 'node_modules');
  const packageJson = path.join(options.root, 'package.json');

  if (!baseVendorPkg) {
    try { baseVendorPkg = JSON.parse(fileSystem.readFileSync(packageJson, 'utf8')) } catch (_) {}
  }

  if (baseVendorPkg) {
    // try to load any resources explicitly defined in package.json:
    if (baseVendorPkg.aurelia && baseVendorPkg.aurelia.resources) {
      for (let resource of baseVendorPkg.aurelia.resources) {
        let fromPath = resource instanceof Object ? resource.path : resource;
        let moduleName = fromPath.split(path.sep)[0];
        let rootAlias = resource.root ? path.resolve(options.root, 'node_modules', moduleName, resource.root) : undefined;
        if (!rootAlias && baseVendorPkg.aurelia.moduleRootOverride && baseVendorPkg.aurelia.moduleRootOverride[moduleName]) {
          rootAlias = path.resolve(options.root, 'node_modules', moduleName, baseVendorPkg.aurelia.moduleRootOverride[moduleName]);
        }
        assign(dependencies, await getDependency(fromPath, options.root, options.root, [nodeModules], null, packageJson, options.async || resource.async, options.bundle || resource.bundle, rootAlias));
      }
    }

    // load all 'dependencies' defined in package.json:
    if (baseVendorPkg.dependencies) {
      for (let moduleName of Object.getOwnPropertyNames(baseVendorPkg.dependencies)) {
        const vendorPath = path.resolve(options.root, 'node_modules', moduleName);
        const vendorPkgPath = path.resolve(vendorPath, 'package.json');
        const vendorPkg = JSON.parse(fileSystem.readFileSync(vendorPkgPath, 'utf8'));
        if (vendorPkg.browser || vendorPkg.main) {
          // only load the dependencies that have either main or browser fields defined
          assign(dependencies, await getDependency(moduleName, options.root, options.root, [nodeModules], null, packageJson, options.async, options.bundle));
        }
      }
    }
    // TASK: first try-load without root-alias, then with
  }

  // resolve all requirements of .html templates
  assign(dependencies, await autoresolveTemplates(options.src, nodeModules, options.async, options.bundle));
  return dependencies;
}

async function autoresolveTemplates(srcPath, nodeModules, isAsync, bundleName) {
  const dependencies = {};
  const templates = await getFilesRecursively(srcPath, '.html');

  for (let htmlFilePath of templates) {
    assign(dependencies, await resolveTemplate(htmlFilePath, srcPath, [nodeModules], undefined, isAsync, bundleName));
  }
  return dependencies;
}

/**
 * Generates key-value dependency pairs of:
 * - <require from="paths">
 * - view-model="file"
 * - view="file.html"
 * - files requested by dependencies of those above
 *
 * @param  {string} htmlFilePath
 * @param  {string} srcPath
 * @param  {Array<string>} nodeModulesList
 * @param  {string} fromWithinModule
 */
async function resolveTemplate(htmlFilePath, srcPath, nodeModulesList, fromWithinModule, isParentAsync, bundleName, rootAlias) {
  const dependencies = {};
  const html = fileSystem.readFileSync(htmlFilePath);
  const $ = cheerio.load(html);
  const relativeParent = path.dirname(htmlFilePath);
  const resources = [];

  // e.g. <require from="./file">
  // e.g. <require from="bootstrap" async bundle="vendor">
  const requireTags = $('require');
  requireTags.each(index => {
    const fromPath = requireTags[index].attribs.from;
    const isAsync = requireTags[index].attribs.hasOwnProperty('async');
    const bundle = requireTags[index].attribs.bundle;
    if (fromPath)
      resources.push({ path: fromPath, async: isAsync, bundle });
  });

  // e.g. <compose view-model="file">
  const viewModelRequests = $('[view-model]');
  viewModelRequests.each(index => {
    const fromPath = viewModelRequests[index].attribs['view-model'];
    const isAsync = viewModelRequests[index].attribs.hasOwnProperty('async');
    const bundle = viewModelRequests[index].attribs.bundle;
    if (fromPath)
      resources.push({ path: fromPath, async: isAsync, bundle });
  });

  // e.g. <compose view="file.html">
  const viewRequests = $('[view]');
  viewRequests.each(index => {
    const fromPath = viewRequests[index].attribs.view;
    const isAsync = viewRequests[index].attribs.hasOwnProperty('async');
    const bundle = viewRequests[index].attribs.bundle;
    if (fromPath)
      resources.push({ path: fromPath, async: isAsync, bundle });
  });

  // for (let fromPath of fromPaths) {
  for (let resource of resources) {
    assign(dependencies, await getDependency(resource.path, relativeParent, srcPath, nodeModulesList, fromWithinModule, htmlFilePath, isParentAsync || resource.async, bundleName || resource.bundle, rootAlias));
  }
  return dependencies;
}

function getPath(input, async, bundle) {
  const extension = path.extname(input);
  let output = '';
  // for .css files force the request to the raw loader (https://github.com/aurelia/webpack-plugin/issues/11#issuecomment-212578861)
  if (extension == ".css")
    output += `!!raw!`;
  if (async || bundle)
    output += `bundle?`;
  if (async)
    output += `lazy`;
  if (async && bundle)
    output += `&`;
  if (bundle)
    output += `name=${bundle}`;
  if (async || bundle)
    output += `!`;
  return `${output}${input}`
}

function getPathWithoutExtension(input) {
  return path.join(path.parse(input).dir, path.parse(input).name);
}

/**
 * @param  {string} fromPath
 * @param  {string} relativeParent
 * @param  {string} srcPath
 * @param  {Array<string>} nodeModulesList
 * @param  {string|void} fromWithinModule
 * @param  {string|void} requestedBy (used for debugging only)
 * @param  {boolean|void} isAsync
 * @param  {string|void} bundleName
 */
async function getDependency(fromPath, relativeParent, srcPath, nodeModulesList, fromWithinModule, requestedBy, isAsync, bundleName, rootAlias, triedToCorrectPath) {
  const dependencies = {};
  const requestedByRelativeToSrc = path.relative(srcPath, requestedBy);

  let split = requestedByRelativeToSrc.split(path.sep);
  if (split[0] == 'node_modules') {
    // handle edge case when adding htmlCounterpart
    nodeModulesList = nodeModulesList.concat([path.join(srcPath, 'node_modules')]);
    srcPath = path.join(srcPath, split[0], split[1]);
    fromWithinModule = split[1];
  }

  async function addDependency(webpackRequireString, webpackPath, htmlCounterpart, rootAlias, moduleName, modulePath) {
    if (filesProcessed.indexOf(webpackRequireString) == -1 && webpackRequireString.indexOf('..') == -1) {
      dependencies[webpackRequireString] = webpackPath;
      console.log((fromWithinModule ? '<' + fromWithinModule + '> ' + '[' + path.basename(requestedBy) : '[' + requestedByRelativeToSrc) + '] required "' + webpackRequireString + '" from "' + webpackPath.replace(optionsGlobal.root + path.sep, '') + '".')
      filesProcessed.push(webpackRequireString);
    }

    if (htmlCounterpart) {
      let htmlWebpackRequireString = './' + getPathWithoutExtension(webpackRequireString) + '.html';
      if (filesProcessed.indexOf(htmlWebpackRequireString) >= 0) return;

      dependencies[htmlWebpackRequireString] = htmlCounterpart;
      console.log((fromWithinModule ? '<' + fromWithinModule + '> ' + '[' + path.basename(requestedBy) : '[' + requestedByRelativeToSrc) + '] required "' + htmlWebpackRequireString + '" from "' + htmlCounterpart.replace(optionsGlobal.root + path.sep, '') + '".');
      filesProcessed.push(htmlWebpackRequireString);

      assign(dependencies, await resolveTemplate(htmlCounterpart, modulePath || srcPath, nodeModulesList, moduleName || fromWithinModule, isAsync, bundleName, rootAlias));
    }
  }

  let webpackPath;
  let webpackRequireString;
  let fullPathOrig = path.join(relativeParent, fromPath);
  let fullPath = fullPathOrig;
  let fullPathNoExt = getPathWithoutExtension(fullPath);
  let extOrig = path.extname(fullPath)
  const pathIsLocal = fromPath.startsWith('./') || fromPath.startsWith('../');
  let htmlCounterpart;
  let stats;
  let extension;
  let moduleName;
  let modulePath;
  let packagesOwnNodeModules;

  try { stats = fileSystem.statSync(fullPath) } catch (_) {}

  if (!stats)
    try { stats = fileSystem.statSync(fullPath = fullPathOrig + '.js') } catch (_) {}

  if (!stats)
    try { stats = fileSystem.statSync(fullPath = fullPathOrig + '.ts') } catch (_) {}

  if (extOrig != '.html') {
    try {
      fileSystem.statSync(fullPathNoExt + '.html');
      htmlCounterpart = fullPathNoExt + '.html';
    } catch (_) {}
  }

  if (stats && stats.isFile()) {
    extension = path.extname(fullPath);

    // load relative file
    webpackPath = getPath(fullPath, isAsync, bundleName);

    if (!fromWithinModule) {
      // if user used './somepath' then traverse from local directory; else 'somepath', he meant /src/somepath

      webpackRequireString = './' + (pathIsLocal ? path.relative(srcPath, path.join(relativeParent, fromPath)) : fromPath);
      await addDependency(webpackRequireString, webpackPath, htmlCounterpart, rootAlias);

      if (rootAlias) {
        webpackRequireString = './' + (pathIsLocal ? path.relative(rootAlias, path.join(relativeParent, fromPath)) : fromPath);
        await addDependency(webpackRequireString, webpackPath, htmlCounterpart, rootAlias);
      }

    } else {
      // resolves to: 'some-module/some-file'

      webpackRequireString = './' + fromWithinModule + '/' + path.relative(srcPath, path.join(relativeParent, fromPath));
      await addDependency(webpackRequireString, webpackPath, htmlCounterpart, rootAlias);

      if (rootAlias) {
        webpackRequireString = './' + fromWithinModule + '/' + path.relative(rootAlias, path.join(relativeParent, fromPath));
        await addDependency(webpackRequireString, webpackPath, htmlCounterpart, rootAlias);
      }
    }
  }
  else if (!pathIsLocal) {
    // at this point the local file doesn't exist and we know that it doesn't start with './'
    // it's a file under a package or a package itself,
    // e.g.: <require from="bootstrap/file.css">

    stats = undefined;
    let nodeModulesIndex = nodeModulesList.length;
    while (!stats && nodeModulesIndex--) {
      let nodeModules = nodeModulesList[nodeModulesIndex]
      fullPathOrig = path.join(nodeModules, fromPath);
      fullPath = fullPathOrig;
      fullPathNoExt = getPathWithoutExtension(fullPath);

      try { stats = fileSystem.statSync(fullPath) } catch (_) {}

      if (!stats)
        try { stats = fileSystem.statSync(fullPath = fullPathOrig + '.js') } catch (_) {}

      if (extOrig != '.html') {
        try {
          fileSystem.statSync(fullPathNoExt + '.html');
          htmlCounterpart = fullPathNoExt + '.html';
        } catch (_) {}
      }
    }

    if (stats) {
      extension = path.extname(fullPath);

      // check if parent is a node_module directory
      if (stats.isDirectory() && path.basename(path.dirname(fullPath)) === 'node_modules') {
        // we're requiring a package (webpack will handle resolving main)
        moduleName = path.basename(fullPath);
        modulePath = fullPath;

        webpackPath = getPath(moduleName, isAsync, bundleName);
        webpackRequireString = './' + fromPath;

      } else if (stats.isFile()) {
        // require the file directly
        moduleName = fromPath.split('/')[0];
        modulePath = path.resolve(nodeModulesList[nodeModulesIndex], moduleName);

        webpackPath = getPath(fullPath, isAsync, bundleName);
        webpackRequireString = './' + fromPath;
      }

      await addDependency(webpackRequireString, webpackPath, htmlCounterpart, rootAlias, moduleName, modulePath);

      if (moduleName && modulePath) {
        const packageJson = path.resolve(modulePath, 'package.json');
        let vendorPkg;
        try { vendorPkg = JSON.parse(fileSystem.readFileSync(packageJson, 'utf8')) } catch (_) {}
        if (vendorPkg) {
          const mainDir = vendorPkg.main ? path.resolve(modulePath, path.dirname(vendorPkg.main)) : null;

          if (!rootAlias) {
            rootAlias = (vendorPkg.aurelia && vendorPkg.aurelia.root && path.resolve(modulePath, vendorPkg.aurelia.root)) || mainDir;

            if (rootAlias === modulePath)
              rootAlias = null;
          }

          if (rootAlias && stats.isFile()) {
            webpackRequireString = './' + moduleName + '/' + path.relative(rootAlias, fullPath);
            await addDependency(webpackRequireString, webpackPath, htmlCounterpart, rootAlias);

            if (extension == '.js' || extension == '.ts') {
              webpackRequireString = './' + moduleName + '/' + path.relative(rootAlias, getPathWithoutExtension(fullPath));
              await addDependency(webpackRequireString, webpackPath, htmlCounterpart, rootAlias);
            }
          }

          if (modulesProcessed.indexOf(modulePath) == -1) {
            // try to also load any files and templates defined in package.json:
            try {
              // check if there are nested 'node_modules' under the package's directory
              const ownPath = path.resolve(modulePath, 'node_modules');
              fileSystem.statSync(ownPath);
              packagesOwnNodeModules = ownPath;
            } catch (_) {}

            if (vendorPkg.aurelia && vendorPkg.aurelia.resources) {
              for (let resource of vendorPkg.aurelia.resources) {
                let resourcePath = resource instanceof Object ? resource.path : resource;
                let useRootAlias = rootAlias;
                // least important: package's global override
                if (vendorPkg.aurelia.moduleRootOverride && vendorPkg.aurelia.moduleRootOverride[moduleName]) {
                  useRootAlias = path.resolve(modulePath, vendorPkg.aurelia.moduleRootOverride[moduleName]);
                }
                // second least important: package resource's override
                if (resource.root) {
                  useRootAlias = path.resolve(modulePath, resource.root);
                }
                // most important: parent-most package's override
                if (baseVendorPkg && baseVendorPkg.aurelia && baseVendorPkg.aurelia.moduleRootOverride && baseVendorPkg.aurelia.moduleRootOverride[moduleName]) {
                  useRootAlias = path.resolve(modulePath, baseVendorPkg.aurelia.moduleRootOverride[moduleName]);
                }
                if (useRootAlias === modulePath) {
                  useRootAlias = null;
                }

                assign(dependencies, await getDependency(resourcePath, modulePath, modulePath, packagesOwnNodeModules ? nodeModulesList.concat(packagesOwnNodeModules) : nodeModulesList, moduleName, packageJson, isAsync || resource.async, bundleName || resource.bundle, useRootAlias));
              }
            }
            modulesProcessed.push(modulePath);
          }
        }
      }
    }
  }

  if (!webpackPath) {
    const pathParts = fromPath.split('/');
    if (!pathIsLocal && pathParts.length > 1 && rootAlias && rootAlias !== srcPath) {
      // the path provided was without root,
      // lets try to correct it and re-run

      const moduleName = pathParts.shift();
      let relativeRootAlias = path.relative(srcPath, rootAlias);
      let relativeRootSplit = relativeRootAlias.split(path.sep);
      while (relativeRootSplit[0] == 'node_modules') {
        relativeRootSplit.shift(); // 'node_modules'
        relativeRootSplit.shift(); // assert === moduleName
      }
      relativeRootAlias = relativeRootSplit.join('/');
      const rootedFromPath = path.join(moduleName, relativeRootAlias, pathParts.join(path.sep));
      if (rootedFromPath !== fromPath && !triedToCorrectPath) {
        return await getDependency(rootedFromPath, relativeParent, srcPath, nodeModulesList, fromWithinModule, requestedBy, isAsync, bundleName, rootAlias, true);
      }
    }
    console.error('[' + (fromWithinModule ? '<' + fromWithinModule + '>' : path.relative(srcPath, requestedBy)) + '] wants to require "' + fromPath + '", which does not exist.');
  } else {
    if (extension == ".html") {
      // if we were referred to .html, get dependencies recursively:
      assign(dependencies, await resolveTemplate(fullPath, modulePath || srcPath, packagesOwnNodeModules ? nodeModulesList.concat(packagesOwnNodeModules) : nodeModulesList, moduleName || fromWithinModule, isAsync, bundleName, rootAlias));
    }
  }

  return dependencies;
}

module.exports = {
  getFilesRecursively,
  processAll,
  autoresolveTemplates,
  resolveTemplate,
  getDependency
}
<|endoftext|>


<commit_before>document.addEventListener('DOMContentLoaded', function() {
	getSource();
});

function getSource() {
	document.getElementById('source').innerText = "Loading";
	chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
		chrome.tabs.sendMessage(tabs[0].id, {greeting: "GetEmailSource"}, function(response) {
			var textarea = document.getElementById('source');
			if(!response.source) {
				textarea.innerText = "Uh, oh! Something went wrong!";
				return;
			}

			var html_start = response.source.indexOf('<!DOCTYPE html');
			if(html_start == -1) {
				textarea.innerText = "Couldn't find message HTML. Please make sure you have opened a Gmail email. ";
				return;
			}

			//extract the source HTML
			var html_end = response.source.indexOf('--_=_swift', html_start);
			var source = response.source.substr(html_start, html_end - html_start);

			//decode it and display it
			textarea.innerText = quotedPrintable.decode(source);
		});
	});
}<commit_msg>Select textarea contents on click<commit_after>document.addEventListener('DOMContentLoaded', function() {
	document.getElementById('source').addEventListener('click', function() {
		this.focus();
		this.select();
	});

	getSource();
});

function getSource() {
	document.getElementById('source').innerText = "Loading";
	chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
		chrome.tabs.sendMessage(tabs[0].id, {greeting: "GetEmailSource"}, function(response) {
			var textarea = document.getElementById('source');
			if(!response.source) {
				textarea.innerText = "Uh, oh! Something went wrong!";
				return;
			}

			var html_start = response.source.indexOf('<!DOCTYPE html');
			if(html_start == -1) {
				textarea.innerText = "Couldn't find message HTML. Please make sure you have opened a Gmail email. ";
				return;
			}

			//extract the source HTML
			var html_end = response.source.indexOf('--_=_swift', html_start);
			var source = response.source.substr(html_start, html_end - html_start);

			//decode it and display it
			textarea.innerText = quotedPrintable.decode(source);
		});
	});
}<|endoftext|>


<commit_before>const Timer = require('../util/timer');
const Thread = require('./thread');
const execute = require('./execute.js');

/**
 * Profiler frame name for stepping a single thread.
 * @const {string}
 */
const stepThreadProfilerFrame = 'Sequencer.stepThread';

/**
 * Profiler frame name for the inner loop of stepThreads.
 * @const {string}
 */
const stepThreadsInnerProfilerFrame = 'Sequencer.stepThreads#inner';

/**
 * Profiler frame name for execute.
 * @const {string}
 */
const executeProfilerFrame = 'execute';

/**
 * Profiler frame ID for stepThreadProfilerFrame.
 * @type {number}
 */
let stepThreadProfilerId = -1;

/**
 * Profiler frame ID for stepThreadsInnerProfilerFrame.
 * @type {number}
 */
let stepThreadsInnerProfilerId = -1;

/**
 * Profiler frame ID for executeProfilerFrame.
 * @type {number}
 */
let executeProfilerId = -1;

class Sequencer {
    constructor (runtime) {
        /**
         * A utility timer for timing thread sequencing.
         * @type {!Timer}
         */
        this.timer = new Timer();

        /**
         * Reference to the runtime owning this sequencer.
         * @type {!Runtime}
         */
        this.runtime = runtime;
    }

    /**
     * Time to run a warp-mode thread, in ms.
     * @type {number}
     */
    static get WARP_TIME () {
        return 500;
    }

    /**
     * Step through all threads in `this.runtime.threads`, running them in order.
     * @return {Array.<!Thread>} List of inactive threads after stepping.
     */
    stepThreads () {
        // Work time is 75% of the thread stepping interval.
        const WORK_TIME = 0.75 * this.runtime.currentStepTime;
        // For compatibility with Scatch 2, update the millisecond clock
        // on the Runtime once per step (see Interpreter.as in Scratch 2
        // for original use of `currentMSecs`)
        this.runtime.updateCurrentMSecs();
        // Start counting toward WORK_TIME.
        this.timer.start();
        // Count of active threads.
        let numActiveThreads = Infinity;
        // Whether `stepThreads` has run through a full single tick.
        let ranFirstTick = false;
        const doneThreads = [];
        // Conditions for continuing to stepping threads:
        // 1. We must have threads in the list, and some must be active.
        // 2. Time elapsed must be less than WORK_TIME.
        // 3. Either turbo mode, or no redraw has been requested by a primitive.
        while (this.runtime.threads.length > 0 &&
               numActiveThreads > 0 &&
               this.timer.timeElapsed() < WORK_TIME &&
               (this.runtime.turboMode || !this.runtime.redrawRequested)) {
            if (this.runtime.profiler !== null) {
                if (stepThreadsInnerProfilerId === -1) {
                    stepThreadsInnerProfilerId = this.runtime.profiler.idByName(stepThreadsInnerProfilerFrame);
                }
                this.runtime.profiler.start(stepThreadsInnerProfilerId);
            }

            numActiveThreads = 0;
            let stoppedThread = false;
            // Attempt to run each thread one time.
            for (let i = 0; i < this.runtime.threads.length; i++) {
                const activeThread = this.runtime.threads[i];
                // Check if the thread is done so it is not executed.
                if (activeThread.stack.length === 0 ||
                    activeThread.status === Thread.STATUS_DONE) {
                    // Finished with this thread.
                    stoppedThread = true;
                    continue;
                }
                if (activeThread.status === Thread.STATUS_YIELD_TICK &&
                    !ranFirstTick) {
                    // Clear single-tick yield from the last call of `stepThreads`.
                    activeThread.status = Thread.STATUS_RUNNING;
                }
                if (activeThread.status === Thread.STATUS_RUNNING ||
                    activeThread.status === Thread.STATUS_YIELD) {
                    // Normal-mode thread: step.
                    if (this.runtime.profiler !== null) {
                        if (stepThreadProfilerId === -1) {
                            stepThreadProfilerId = this.runtime.profiler.idByName(stepThreadProfilerFrame);
                        }
                        this.runtime.profiler.start(stepThreadProfilerId);
                    }
                    this.stepThread(activeThread);
                    if (this.runtime.profiler !== null) {
                        this.runtime.profiler.stop();
                    }
                    activeThread.warpTimer = null;
                    if (activeThread.isKilled) {
                        i--; // if the thread is removed from the list (killed), do not increase index
                    }
                }
                if (activeThread.status === Thread.STATUS_RUNNING) {
                    numActiveThreads++;
                }
                // Check if the thread completed while it just stepped to make
                // sure we remove it before the next iteration of all threads.
                if (activeThread.stack.length === 0 ||
                    activeThread.status === Thread.STATUS_DONE) {
                    // Finished with this thread.
                    stoppedThread = true;
                }
            }
            // We successfully ticked once. Prevents running STATUS_YIELD_TICK
            // threads on the next tick.
            ranFirstTick = true;

            if (this.runtime.profiler !== null) {
                this.runtime.profiler.stop();
            }

            // Filter inactive threads from `this.runtime.threads`.
            if (stoppedThread) {
                let nextActiveThread = 0;
                for (let i = 0; i < this.runtime.threads.length; i++) {
                    const thread = this.runtime.threads[i];
                    if (thread.stack.length !== 0 &&
                        thread.status !== Thread.STATUS_DONE) {
                        this.runtime.threads[nextActiveThread] = thread;
                        nextActiveThread++;
                    } else {
                        doneThreads.push(thread);
                    }
                }
                this.runtime.threads.length = nextActiveThread;
            }
        }

        return doneThreads;
    }

    /**
     * Step the requested thread for as long as necessary.
     * @param {!Thread} thread Thread object to step.
     */
    stepThread (thread) {
        let currentBlockId = thread.peekStack();
        if (!currentBlockId) {
            // A "null block" - empty branch.
            thread.popStack();
        }
        // Save the current block ID to notice if we did control flow.
        while ((currentBlockId = thread.peekStack())) {
            let isWarpMode = thread.peekStackFrame().warpMode;
            if (isWarpMode && !thread.warpTimer) {
                // Initialize warp-mode timer if it hasn't been already.
                // This will start counting the thread toward `Sequencer.WARP_TIME`.
                thread.warpTimer = new Timer();
                thread.warpTimer.start();
            }
            // Execute the current block.
            if (this.runtime.profiler !== null) {
                if (executeProfilerId === -1) {
                    executeProfilerId = this.runtime.profiler.idByName(executeProfilerFrame);
                }
                // The method commented below has its code inlined underneath to
                // reduce the bias recorded for the profiler's calls in this
                // time sensitive stepThread method.
                //
                // this.runtime.profiler.start(executeProfilerId, null);
                this.runtime.profiler.records.push(
                    this.runtime.profiler.START, executeProfilerId, null, 0);
            }
            if (thread.target === null) {
                this.retireThread(thread);
            } else {
                execute(this, thread);
            }
            if (this.runtime.profiler !== null) {
                // this.runtime.profiler.stop();
                this.runtime.profiler.records.push(this.runtime.profiler.STOP, 0);
            }
            thread.blockGlowInFrame = currentBlockId;
            // If the thread has yielded or is waiting, yield to other threads.
            if (thread.status === Thread.STATUS_YIELD) {
                // Mark as running for next iteration.
                thread.status = Thread.STATUS_RUNNING;
                // In warp mode, yielded blocks are re-executed immediately.
                if (isWarpMode &&
                    thread.warpTimer.timeElapsed() <= Sequencer.WARP_TIME) {
                    continue;
                }
                return;
            } else if (thread.status === Thread.STATUS_PROMISE_WAIT) {
                // A promise was returned by the primitive. Yield the thread
                // until the promise resolves. Promise resolution should reset
                // thread.status to Thread.STATUS_RUNNING.
                return;
            } else if (thread.status === Thread.STATUS_YIELD_TICK) {
                // stepThreads will reset the thread to Thread.STATUS_RUNNING
                return;
            }
            // If no control flow has happened, switch to next block.
            if (thread.peekStack() === currentBlockId) {
                thread.goToNextBlock();
            }
            // If no next block has been found at this point, look on the stack.
            while (!thread.peekStack()) {
                thread.popStack();

                if (thread.stack.length === 0) {
                    // No more stack to run!
                    thread.status = Thread.STATUS_DONE;
                    return;
                }

                const stackFrame = thread.peekStackFrame();
                isWarpMode = stackFrame.warpMode;

                if (stackFrame.isLoop) {
                    // The current level of the stack is marked as a loop.
                    // Return to yield for the frame/tick in general.
                    // Unless we're in warp mode - then only return if the
                    // warp timer is up.
                    if (!isWarpMode ||
                        thread.warpTimer.timeElapsed() > Sequencer.WARP_TIME) {
                        // Don't do anything to the stack, since loops need
                        // to be re-executed.
                        return;
                    }
                    // Don't go to the next block for this level of the stack,
                    // since loops need to be re-executed.
                    continue;

                } else if (stackFrame.waitingReporter) {
                    // This level of the stack was waiting for a value.
                    // This means a reporter has just returned - so don't go
                    // to the next block for this level of the stack.
                    return;
                }
                // Get next block of existing block on the stack.
                thread.goToNextBlock();
            }
        }
    }

    /**
     * Step a thread into a block's branch.
     * @param {!Thread} thread Thread object to step to branch.
     * @param {number} branchNum Which branch to step to (i.e., 1, 2).
     * @param {boolean} isLoop Whether this block is a loop.
     */
    stepToBranch (thread, branchNum, isLoop) {
        if (!branchNum) {
            branchNum = 1;
        }
        const currentBlockId = thread.peekStack();
        const branchId = thread.target.blocks.getBranch(
            currentBlockId,
            branchNum
        );
        thread.peekStackFrame().isLoop = isLoop;
        if (branchId) {
            // Push branch ID to the thread's stack.
            thread.pushStack(branchId);
        } else {
            thread.pushStack(null);
        }
    }

    /**
     * Step a procedure.
     * @param {!Thread} thread Thread object to step to procedure.
     * @param {!string} procedureCode Procedure code of procedure to step to.
     */
    stepToProcedure (thread, procedureCode) {
        const definition = thread.target.blocks.getProcedureDefinition(procedureCode);
        if (!definition) {
            return;
        }
        // Check if the call is recursive.
        // If so, set the thread to yield after pushing.
        const isRecursive = thread.isRecursiveCall(procedureCode);
        // To step to a procedure, we put its definition on the stack.
        // Execution for the thread will proceed through the definition hat
        // and on to the main definition of the procedure.
        // When that set of blocks finishes executing, it will be popped
        // from the stack by the sequencer, returning control to the caller.
        thread.pushStack(definition);
        // In known warp-mode threads, only yield when time is up.
        if (thread.peekStackFrame().warpMode &&
            thread.warpTimer.timeElapsed() > Sequencer.WARP_TIME) {
            thread.status = Thread.STATUS_YIELD;
        } else {
            // Look for warp-mode flag on definition, and set the thread
            // to warp-mode if needed.
            const definitionBlock = thread.target.blocks.getBlock(definition);
            const innerBlock = thread.target.blocks.getBlock(
                definitionBlock.inputs.custom_block.block);
            let doWarp = false;
            if (innerBlock && innerBlock.mutation) {
                const warp = innerBlock.mutation.warp;
                if (typeof warp === 'boolean') {
                    doWarp = warp;
                } else if (typeof warp === 'string') {
                    doWarp = JSON.parse(warp);
                }
            }
            if (doWarp) {
                thread.peekStackFrame().warpMode = true;
            } else if (isRecursive) {
                // In normal-mode threads, yield any time we have a recursive call.
                thread.status = Thread.STATUS_YIELD;
            }
        }
    }

    /**
     * Retire a thread in the middle, without considering further blocks.
     * @param {!Thread} thread Thread object to retire.
     */
    retireThread (thread) {
        thread.stack = [];
        thread.stackFrame = [];
        thread.requestScriptGlowInFrame = false;
        thread.status = Thread.STATUS_DONE;
    }
}

module.exports = Sequencer;
<commit_msg>retire new threads if they have no block after the hat<commit_after>const Timer = require('../util/timer');
const Thread = require('./thread');
const execute = require('./execute.js');

/**
 * Profiler frame name for stepping a single thread.
 * @const {string}
 */
const stepThreadProfilerFrame = 'Sequencer.stepThread';

/**
 * Profiler frame name for the inner loop of stepThreads.
 * @const {string}
 */
const stepThreadsInnerProfilerFrame = 'Sequencer.stepThreads#inner';

/**
 * Profiler frame name for execute.
 * @const {string}
 */
const executeProfilerFrame = 'execute';

/**
 * Profiler frame ID for stepThreadProfilerFrame.
 * @type {number}
 */
let stepThreadProfilerId = -1;

/**
 * Profiler frame ID for stepThreadsInnerProfilerFrame.
 * @type {number}
 */
let stepThreadsInnerProfilerId = -1;

/**
 * Profiler frame ID for executeProfilerFrame.
 * @type {number}
 */
let executeProfilerId = -1;

class Sequencer {
    constructor (runtime) {
        /**
         * A utility timer for timing thread sequencing.
         * @type {!Timer}
         */
        this.timer = new Timer();

        /**
         * Reference to the runtime owning this sequencer.
         * @type {!Runtime}
         */
        this.runtime = runtime;
    }

    /**
     * Time to run a warp-mode thread, in ms.
     * @type {number}
     */
    static get WARP_TIME () {
        return 500;
    }

    /**
     * Step through all threads in `this.runtime.threads`, running them in order.
     * @return {Array.<!Thread>} List of inactive threads after stepping.
     */
    stepThreads () {
        // Work time is 75% of the thread stepping interval.
        const WORK_TIME = 0.75 * this.runtime.currentStepTime;
        // For compatibility with Scatch 2, update the millisecond clock
        // on the Runtime once per step (see Interpreter.as in Scratch 2
        // for original use of `currentMSecs`)
        this.runtime.updateCurrentMSecs();
        // Start counting toward WORK_TIME.
        this.timer.start();
        // Count of active threads.
        let numActiveThreads = Infinity;
        // Whether `stepThreads` has run through a full single tick.
        let ranFirstTick = false;
        const doneThreads = [];
        // Conditions for continuing to stepping threads:
        // 1. We must have threads in the list, and some must be active.
        // 2. Time elapsed must be less than WORK_TIME.
        // 3. Either turbo mode, or no redraw has been requested by a primitive.
        while (this.runtime.threads.length > 0 &&
               numActiveThreads > 0 &&
               this.timer.timeElapsed() < WORK_TIME &&
               (this.runtime.turboMode || !this.runtime.redrawRequested)) {
            if (this.runtime.profiler !== null) {
                if (stepThreadsInnerProfilerId === -1) {
                    stepThreadsInnerProfilerId = this.runtime.profiler.idByName(stepThreadsInnerProfilerFrame);
                }
                this.runtime.profiler.start(stepThreadsInnerProfilerId);
            }

            numActiveThreads = 0;
            let stoppedThread = false;
            // Attempt to run each thread one time.
            for (let i = 0; i < this.runtime.threads.length; i++) {
                const activeThread = this.runtime.threads[i];
                // Check if the thread is done so it is not executed.
                if (activeThread.stack.length === 0 ||
                    activeThread.status === Thread.STATUS_DONE) {
                    // Finished with this thread.
                    stoppedThread = true;
                    continue;
                }
                if (activeThread.status === Thread.STATUS_YIELD_TICK &&
                    !ranFirstTick) {
                    // Clear single-tick yield from the last call of `stepThreads`.
                    activeThread.status = Thread.STATUS_RUNNING;
                }
                if (activeThread.status === Thread.STATUS_RUNNING ||
                    activeThread.status === Thread.STATUS_YIELD) {
                    // Normal-mode thread: step.
                    if (this.runtime.profiler !== null) {
                        if (stepThreadProfilerId === -1) {
                            stepThreadProfilerId = this.runtime.profiler.idByName(stepThreadProfilerFrame);
                        }
                        this.runtime.profiler.start(stepThreadProfilerId);
                    }
                    this.stepThread(activeThread);
                    if (this.runtime.profiler !== null) {
                        this.runtime.profiler.stop();
                    }
                    activeThread.warpTimer = null;
                    if (activeThread.isKilled) {
                        i--; // if the thread is removed from the list (killed), do not increase index
                    }
                }
                if (activeThread.status === Thread.STATUS_RUNNING) {
                    numActiveThreads++;
                }
                // Check if the thread completed while it just stepped to make
                // sure we remove it before the next iteration of all threads.
                if (activeThread.stack.length === 0 ||
                    activeThread.status === Thread.STATUS_DONE) {
                    // Finished with this thread.
                    stoppedThread = true;
                }
            }
            // We successfully ticked once. Prevents running STATUS_YIELD_TICK
            // threads on the next tick.
            ranFirstTick = true;

            if (this.runtime.profiler !== null) {
                this.runtime.profiler.stop();
            }

            // Filter inactive threads from `this.runtime.threads`.
            if (stoppedThread) {
                let nextActiveThread = 0;
                for (let i = 0; i < this.runtime.threads.length; i++) {
                    const thread = this.runtime.threads[i];
                    if (thread.stack.length !== 0 &&
                        thread.status !== Thread.STATUS_DONE) {
                        this.runtime.threads[nextActiveThread] = thread;
                        nextActiveThread++;
                    } else {
                        doneThreads.push(thread);
                    }
                }
                this.runtime.threads.length = nextActiveThread;
            }
        }

        return doneThreads;
    }

    /**
     * Step the requested thread for as long as necessary.
     * @param {!Thread} thread Thread object to step.
     */
    stepThread (thread) {
        let currentBlockId = thread.peekStack();
        if (!currentBlockId) {
            // A "null block" - empty branch.
            thread.popStack();

            // Did the null follow a hat block?
            if (thread.stack.length === 0) {
                thread.status = Thread.STATUS_DONE;
                return;
            }
        }
        // Save the current block ID to notice if we did control flow.
        while ((currentBlockId = thread.peekStack())) {
            let isWarpMode = thread.peekStackFrame().warpMode;
            if (isWarpMode && !thread.warpTimer) {
                // Initialize warp-mode timer if it hasn't been already.
                // This will start counting the thread toward `Sequencer.WARP_TIME`.
                thread.warpTimer = new Timer();
                thread.warpTimer.start();
            }
            // Execute the current block.
            if (this.runtime.profiler !== null) {
                if (executeProfilerId === -1) {
                    executeProfilerId = this.runtime.profiler.idByName(executeProfilerFrame);
                }
                // The method commented below has its code inlined underneath to
                // reduce the bias recorded for the profiler's calls in this
                // time sensitive stepThread method.
                //
                // this.runtime.profiler.start(executeProfilerId, null);
                this.runtime.profiler.records.push(
                    this.runtime.profiler.START, executeProfilerId, null, 0);
            }
            if (thread.target === null) {
                this.retireThread(thread);
            } else {
                execute(this, thread);
            }
            if (this.runtime.profiler !== null) {
                // this.runtime.profiler.stop();
                this.runtime.profiler.records.push(this.runtime.profiler.STOP, 0);
            }
            thread.blockGlowInFrame = currentBlockId;
            // If the thread has yielded or is waiting, yield to other threads.
            if (thread.status === Thread.STATUS_YIELD) {
                // Mark as running for next iteration.
                thread.status = Thread.STATUS_RUNNING;
                // In warp mode, yielded blocks are re-executed immediately.
                if (isWarpMode &&
                    thread.warpTimer.timeElapsed() <= Sequencer.WARP_TIME) {
                    continue;
                }
                return;
            } else if (thread.status === Thread.STATUS_PROMISE_WAIT) {
                // A promise was returned by the primitive. Yield the thread
                // until the promise resolves. Promise resolution should reset
                // thread.status to Thread.STATUS_RUNNING.
                return;
            } else if (thread.status === Thread.STATUS_YIELD_TICK) {
                // stepThreads will reset the thread to Thread.STATUS_RUNNING
                return;
            }
            // If no control flow has happened, switch to next block.
            if (thread.peekStack() === currentBlockId) {
                thread.goToNextBlock();
            }
            // If no next block has been found at this point, look on the stack.
            while (!thread.peekStack()) {
                thread.popStack();

                if (thread.stack.length === 0) {
                    // No more stack to run!
                    thread.status = Thread.STATUS_DONE;
                    return;
                }

                const stackFrame = thread.peekStackFrame();
                isWarpMode = stackFrame.warpMode;

                if (stackFrame.isLoop) {
                    // The current level of the stack is marked as a loop.
                    // Return to yield for the frame/tick in general.
                    // Unless we're in warp mode - then only return if the
                    // warp timer is up.
                    if (!isWarpMode ||
                        thread.warpTimer.timeElapsed() > Sequencer.WARP_TIME) {
                        // Don't do anything to the stack, since loops need
                        // to be re-executed.
                        return;
                    }
                    // Don't go to the next block for this level of the stack,
                    // since loops need to be re-executed.
                    continue;

                } else if (stackFrame.waitingReporter) {
                    // This level of the stack was waiting for a value.
                    // This means a reporter has just returned - so don't go
                    // to the next block for this level of the stack.
                    return;
                }
                // Get next block of existing block on the stack.
                thread.goToNextBlock();
            }
        }
    }

    /**
     * Step a thread into a block's branch.
     * @param {!Thread} thread Thread object to step to branch.
     * @param {number} branchNum Which branch to step to (i.e., 1, 2).
     * @param {boolean} isLoop Whether this block is a loop.
     */
    stepToBranch (thread, branchNum, isLoop) {
        if (!branchNum) {
            branchNum = 1;
        }
        const currentBlockId = thread.peekStack();
        const branchId = thread.target.blocks.getBranch(
            currentBlockId,
            branchNum
        );
        thread.peekStackFrame().isLoop = isLoop;
        if (branchId) {
            // Push branch ID to the thread's stack.
            thread.pushStack(branchId);
        } else {
            thread.pushStack(null);
        }
    }

    /**
     * Step a procedure.
     * @param {!Thread} thread Thread object to step to procedure.
     * @param {!string} procedureCode Procedure code of procedure to step to.
     */
    stepToProcedure (thread, procedureCode) {
        const definition = thread.target.blocks.getProcedureDefinition(procedureCode);
        if (!definition) {
            return;
        }
        // Check if the call is recursive.
        // If so, set the thread to yield after pushing.
        const isRecursive = thread.isRecursiveCall(procedureCode);
        // To step to a procedure, we put its definition on the stack.
        // Execution for the thread will proceed through the definition hat
        // and on to the main definition of the procedure.
        // When that set of blocks finishes executing, it will be popped
        // from the stack by the sequencer, returning control to the caller.
        thread.pushStack(definition);
        // In known warp-mode threads, only yield when time is up.
        if (thread.peekStackFrame().warpMode &&
            thread.warpTimer.timeElapsed() > Sequencer.WARP_TIME) {
            thread.status = Thread.STATUS_YIELD;
        } else {
            // Look for warp-mode flag on definition, and set the thread
            // to warp-mode if needed.
            const definitionBlock = thread.target.blocks.getBlock(definition);
            const innerBlock = thread.target.blocks.getBlock(
                definitionBlock.inputs.custom_block.block);
            let doWarp = false;
            if (innerBlock && innerBlock.mutation) {
                const warp = innerBlock.mutation.warp;
                if (typeof warp === 'boolean') {
                    doWarp = warp;
                } else if (typeof warp === 'string') {
                    doWarp = JSON.parse(warp);
                }
            }
            if (doWarp) {
                thread.peekStackFrame().warpMode = true;
            } else if (isRecursive) {
                // In normal-mode threads, yield any time we have a recursive call.
                thread.status = Thread.STATUS_YIELD;
            }
        }
    }

    /**
     * Retire a thread in the middle, without considering further blocks.
     * @param {!Thread} thread Thread object to retire.
     */
    retireThread (thread) {
        thread.stack = [];
        thread.stackFrame = [];
        thread.requestScriptGlowInFrame = false;
        thread.status = Thread.STATUS_DONE;
    }
}

module.exports = Sequencer;
<|endoftext|>


<commit_before>/*
** Sonnarr Extention
** Shows upcoming and missed episodes.
*/

var sonarr = {
  settings: {
    wanted : "api/wanted/missing?page=1&pageSize={wantedItems}&sortKey=airDateUtc&sortDir=desc&apikey={apikey}",
    history : "api/history?page=1&pageSize={historyItems}&sortKey=date&sortDir=desc&apikey={apikey}"
  },
  getData : function (mode, callback) {
    var url = "";
    url = app.settings.url + sonarr.settings[mode];

    url = url.replace("{wantedItems}", app.settings.wantedItems);
    url = url.replace("{historyItems}", app.settings.historyItems);
    url = url.replace("{apikey}", app.settings.apiKey);

    $.getJSON( url , function( data ) {
      if (callback && typeof(callback) === "function")
      {
        callback(data);
        //store data
        if(mode === "wanted"){
          localStorage.setItem("wanted", JSON.stringify(data));
        }
        if(mode === "history"){
          localStorage.setItem("history", JSON.stringify(data));
        }
      }
    });
  }
}

var getupcomingEpisodes = {
  connect: function(){

  },
  generate: function(){

  }
}

var getHistory = {
  connect: function(){
    if(localStorage.getItem("history") !== 'undefined'){
      var historyData = $.parseJSON(localStorage.getItem("history"));
      getHistory.generate(historyData);
    }
    sonarr.getData("history", getHistory.generate);
  },
  generate: function(data){
    data = data.records;
    app.cleanList();
    $.each(data, function (index, value) {
      getHistory.add(value);
    });
  },
  add : function (episode) {
    var template = $('.templates #history');
    var event = {
      "downloadFolderImported" : 'Imported',
      "grabbed" : 'grabbed',
      "episodeFileDeleted" : 'deleted file',
    }
    var classe = {
      "downloadFolderImported" : 'label success',
      "grabbed" : 'label secondary',
      "episodeFileDeleted" : 'label',
    }
    template.find('#title').html(episode.series.title);
    template.find('#episodeNum').html(formatEpisodeNumer(episode.episode.seasonNumber,episode.episode.episodeNumber));

    template.find('#event').html(event[episode.eventType]).attr('class', classe[episode.eventType]);

    template.find('#date').html(jQuery.format.prettyDate(new Date(episode.date))).attr('class', 'label');

    template.attr("data-episodeId", episode.episode.id);
    template.clone().appendTo( ".list" );
  }
}

var getWantedEpisodes = {
  connect: function(){
    if(localStorage.getItem("wanted") !== 'undefined'){
      var wantedData = $.parseJSON(localStorage.getItem('wanted'));
      getWantedEpisodes.generate(wantedData);
    }
    sonarr.getData("wanted", getWantedEpisodes.generate);
  },
  generate: function(data){
    var totalRecords = data.totalRecords;
    data = data.records;
    app.cleanList();
    $.each(data, function (index, value) {
      getWantedEpisodes.add(value);
    });
    //set num items in button
    $('.menu .wanted .num').html(totalRecords.toString());
    getWantedEpisodes.click();
  },
  add : function(episode){
    var template = $('.templates #wanted');
    var date = {
      day : episode.airDate.substring(8, 10),
      month : episode.airDate.substring(5, 7)
    }
    var month = {
      '01': 'jan',
      '02': 'feb',
      '03': 'mar',
      '04': 'apr',
      '05': 'may',
      '06': 'jun',
      '07': 'jul',
      '08': 'aug',
      '09': 'sep',
      '10': 'okt',
      '11': 'nov',
      '12': 'dec',
    }
    template.find('#title').html(episode.series.title);
    template.find('#episodeName').html(episode.title);
    template.find('#episodeNum').html("download
" + formatEpisodeNumer(episode.seasonNumber,episode.episodeNumber));

    template.attr("data-episodeId", episode.id);

    template.find('#day').html(date.day);
    template.find('#month').html(month[date.month]);
    template.clone().appendTo( ".list" );
  },
  searchEpisode : function (episodeId) {
    if(episodeId < 1){
      return false;
    }
    var url = app.settings.url + "api/Command?apikey=" + app.settings.apiKey;
    console.log(url);
    $.ajax({
      type: "get",
      url: url,
      data: {
        name: "episodesearch",
        episodeIds: episodeId
      }
    });
  },
  click : function() {
    $( ".wanted .button" ).click(function() {
      var episodeId = $(this).parentsUntil('.wanted').attr("data-episodeId");
      $(this).parentsUntil('.wanted').animate({'opacity': '0.5'});
      getWantedEpisodes.searchEpisode(episodeId);
    });
  }
}

//set variable from chrome storage option fields
// stored in chrome.storage.
function getOptions() {
  chrome.storage.sync.get({
    apiKey: app.settings.apiKey,
    url: app.settings.url,
    wantedItems : app.settings.wantedItems,
    historyItems: app.settings.historyItems,
  }, function(items){
    app.settings.apiKey = items.apiKey;
    app.settings.url = items.url;
    app.settings.mode = "wanted";
    app.settings.wantedItems = items.wantedItems;
    app.settings.historyItems = items.historyItems;
    app.run();
    console.log('get options from chrome storage');
  });
}

var bottomMenu = {
  bind : function(){
    $('.bottom-menu a').unbind( "click" );
    $('#sonarr-url-link').click(bottomMenu.openSonarrUrl);
    $('#options-link').click(bottomMenu.openOptions);
    $('#refresh-link').click(bottomMenu.refreshList);
  },
  openOptions : function() {
    chrome.tabs.create({url: "options.html"});
  },
  openSonarrUrl : function() {
    chrome.tabs.create({url: app.settings.url});
  },
  refreshList : function() {
    app.run();
  }
}

var menu =  {
  bind : function (){
    $('.menu .item').unbind( "click" ).click(function(){
      var mode = $(this).attr('data-mode');
      if(app.settings.mode !== mode){
        //change active item
        $('.menu .item').removeClass('active');
        $(this).addClass('active');
        //change mode
        app.settings.mode = mode;
        console.log(mode);
        //rerun app
        app.run();
      }
    });
  }
}

function setLocalStorage () {
  if (localStorage.getItem('wanted') === null) {
    localStorage.setItem('wanted', undefined);
  }
  if (localStorage.getItem('history') === null) {
    localStorage.setItem('history', undefined);
  }
}

var formatEpisodeNumer = function(seasonNumber, episodeNumber) {
  var episodeNum = "S"+ (seasonNumber.toString().length === 1 ? '0' : '') + seasonNumber + "E" + (episodeNumber.toString().length === 1 ? '0' : '') + episodeNumber;
  return episodeNum;
}

var app = {
  settings : {
    apiKey : '',
    url: '',
    mode : 'getOptions',
    wantedItems: 15,
    historyItems: 15
  },
  run : function(){
    //prepare local storage
    setLocalStorage();

    if(app.settings.mode == 'getOptions' || app.settings.apiKey === '' || app.settings.url === '')
    {
      getOptions();
      return false;
    }
    if (app.settings.mode === "upcoming")
    {
      getupcomingEpisodes.connect();
    }
    else if (app.settings.mode === "wanted")
    {
      getWantedEpisodes.connect();
    }
    else if (app.settings.mode === "history")
    {
      getHistory.connect();
    }
    //bind actions to the menu
    menu.bind();
    //bind bottom menu
    bottomMenu.bind();
  },
  cleanList : function() {
    //clean list
    $( ".list > div" ).remove();
  }
}
app.run();
<commit_msg>add quality to history tab<commit_after>/*
** Sonnarr Extention
** Shows upcoming and missed episodes.
*/

var sonarr = {
  settings: {
    wanted : "api/wanted/missing?page=1&pageSize={wantedItems}&sortKey=airDateUtc&sortDir=desc&apikey={apikey}",
    history : "api/history?page=1&pageSize={historyItems}&sortKey=date&sortDir=desc&apikey={apikey}"
  },
  getData : function (mode, callback) {
    var url = "";
    url = app.settings.url + sonarr.settings[mode];

    url = url.replace("{wantedItems}", app.settings.wantedItems);
    url = url.replace("{historyItems}", app.settings.historyItems);
    url = url.replace("{apikey}", app.settings.apiKey);

    $.getJSON( url , function( data ) {
      if (callback && typeof(callback) === "function")
      {
        callback(data);
        //store data
        if(mode === "wanted"){
          localStorage.setItem("wanted", JSON.stringify(data));
        }
        if(mode === "history"){
          localStorage.setItem("history", JSON.stringify(data));
        }
      }
    });
  }
}

var getupcomingEpisodes = {
  connect: function(){

  },
  generate: function(){

  }
}

var getHistory = {
  connect: function(){
    if(localStorage.getItem("history") !== 'undefined'){
      var historyData = $.parseJSON(localStorage.getItem("history"));
      getHistory.generate(historyData);
    }
    sonarr.getData("history", getHistory.generate);
  },
  generate: function(data){
    data = data.records;
    app.cleanList();
    $.each(data, function (index, value) {
      getHistory.add(value);
    });
  },
  add : function (episode) {
    var template = $('.templates #history');
    var event = {
      "downloadFolderImported" : 'Imported',
      "grabbed" : 'grabbed',
      "episodeFileDeleted" : 'deleted file',
    }
    var classe = {
      "downloadFolderImported" : 'label success',
      "grabbed" : 'label secondary',
      "episodeFileDeleted" : 'label',
    }
    template.find('#title').html(episode.series.title);
    template.find('#episodeNum').html(formatEpisodeNumer(episode.episode.seasonNumber,episode.episode.episodeNumber));

    template.find('#quality').html(episode.quality.quality.name).attr('class', 'label');

    template.find('#event').html(event[episode.eventType]).attr('class', classe[episode.eventType]);

    template.find('#date').html(jQuery.format.prettyDate(new Date(episode.date))).attr('class', 'label');

    template.attr("data-episodeId", episode.episode.id);
    template.clone().appendTo( ".list" );
  }
}

var getWantedEpisodes = {
  connect: function(){
    if(localStorage.getItem("wanted") !== 'undefined'){
      var wantedData = $.parseJSON(localStorage.getItem('wanted'));
      getWantedEpisodes.generate(wantedData);
    }
    sonarr.getData("wanted", getWantedEpisodes.generate);
  },
  generate: function(data){
    var totalRecords = data.totalRecords;
    data = data.records;
    app.cleanList();
    $.each(data, function (index, value) {
      getWantedEpisodes.add(value);
    });
    //set num items in button
    $('.menu .wanted .num').html(totalRecords.toString());
    getWantedEpisodes.click();
  },
  add : function(episode){
    var template = $('.templates #wanted');
    var date = {
      day : episode.airDate.substring(8, 10),
      month : episode.airDate.substring(5, 7)
    }
    var month = {
      '01': 'jan',
      '02': 'feb',
      '03': 'mar',
      '04': 'apr',
      '05': 'may',
      '06': 'jun',
      '07': 'jul',
      '08': 'aug',
      '09': 'sep',
      '10': 'okt',
      '11': 'nov',
      '12': 'dec',
    }
    template.find('#title').html(episode.series.title);
    template.find('#episodeName').html(episode.title);
    template.find('#episodeNum').html("download
" + formatEpisodeNumer(episode.seasonNumber,episode.episodeNumber));

    template.attr("data-episodeId", episode.id);

    template.find('#day').html(date.day);
    template.find('#month').html(month[date.month]);
    template.clone().appendTo( ".list" );
  },
  searchEpisode : function (episodeId) {
    if(episodeId < 1){
      return false;
    }
    var url = app.settings.url + "api/Command?apikey=" + app.settings.apiKey;
    console.log(url);
    $.ajax({
      type: "get",
      url: url,
      data: {
        name: "episodesearch",
        episodeIds: episodeId
      }
    });
  },
  click : function() {
    $( ".wanted .button" ).click(function() {
      var episodeId = $(this).parentsUntil('.wanted').attr("data-episodeId");
      $(this).parentsUntil('.wanted').animate({'opacity': '0.5'});
      getWantedEpisodes.searchEpisode(episodeId);
    });
  }
}

//set variable from chrome storage option fields
// stored in chrome.storage.
function getOptions() {
  chrome.storage.sync.get({
    apiKey: app.settings.apiKey,
    url: app.settings.url,
    wantedItems : app.settings.wantedItems,
    historyItems: app.settings.historyItems,
  }, function(items){
    app.settings.apiKey = items.apiKey;
    app.settings.url = items.url;
    app.settings.mode = "wanted";
    app.settings.wantedItems = items.wantedItems;
    app.settings.historyItems = items.historyItems;
    app.run();
    console.log('get options from chrome storage');
  });
}

var bottomMenu = {
  bind : function(){
    $('.bottom-menu a').unbind( "click" );
    $('#sonarr-url-link').click(bottomMenu.openSonarrUrl);
    $('#options-link').click(bottomMenu.openOptions);
    $('#refresh-link').click(bottomMenu.refreshList);
  },
  openOptions : function() {
    chrome.tabs.create({url: "options.html"});
  },
  openSonarrUrl : function() {
    chrome.tabs.create({url: app.settings.url});
  },
  refreshList : function() {
    app.run();
  }
}

var menu =  {
  bind : function (){
    $('.menu .item').unbind( "click" ).click(function(){
      var mode = $(this).attr('data-mode');
      if(app.settings.mode !== mode){
        //change active item
        $('.menu .item').removeClass('active');
        $(this).addClass('active');
        //change mode
        app.settings.mode = mode;
        console.log(mode);
        //rerun app
        app.run();
      }
    });
  }
}

function setLocalStorage () {
  if (localStorage.getItem('wanted') === null) {
    localStorage.setItem('wanted', undefined);
  }
  if (localStorage.getItem('history') === null) {
    localStorage.setItem('history', undefined);
  }
}

var formatEpisodeNumer = function(seasonNumber, episodeNumber) {
  var episodeNum = "S"+ (seasonNumber.toString().length === 1 ? '0' : '') + seasonNumber + "E" + (episodeNumber.toString().length === 1 ? '0' : '') + episodeNumber;
  return episodeNum;
}

var app = {
  settings : {
    apiKey : '',
    url: '',
    mode : 'getOptions',
    wantedItems: 15,
    historyItems: 15
  },
  run : function(){
    //prepare local storage
    setLocalStorage();

    if(app.settings.mode == 'getOptions' || app.settings.apiKey === '' || app.settings.url === '')
    {
      getOptions();
      return false;
    }
    if (app.settings.mode === "upcoming")
    {
      getupcomingEpisodes.connect();
    }
    else if (app.settings.mode === "wanted")
    {
      getWantedEpisodes.connect();
    }
    else if (app.settings.mode === "history")
    {
      getHistory.connect();
    }
    //bind actions to the menu
    menu.bind();
    //bind bottom menu
    bottomMenu.bind();
  },
  cleanList : function() {
    //clean list
    $( ".list > div" ).remove();
  }
}
app.run();
<|endoftext|>


<commit_before>const {SafeString} = require('../services/handlebars');
const {config, urlUtils, getFrontendKey, labs} = require('../services/proxy');

async function comments(options) {
    // todo: For now check on the comment id to exclude normal pages (we probably have a better way to do this)

    const commentId = this.comment_id;

    if (!commentId) {
        return;
    }

    let colorScheme = 'auto';
    if (options.hash.color_scheme === 'dark' || options.hash.color_scheme === 'light') {
        colorScheme = options.hash.color_scheme;
    }

    let avatarSaturation = parseInt(options.hash.avatar_saturation);
    if (isNaN(avatarSaturation)) {
        avatarSaturation = 50;
    }

    let accentColor = '';
    if (options.data.site.accent_color) {
        accentColor = options.data.site.accent_color;
    }

    const frontendKey = await getFrontendKey();

    const data = {
        'ghost-comments': urlUtils.getSiteUrl(),
        api: urlUtils.urlFor('api', {type: 'content'}, true),
        admin: urlUtils.urlFor('admin', true),
        key: frontendKey,
        'post-id': this.id,
        'sentry-dsn': '', /* todo: insert sentry dsn key here */
        'color-scheme': colorScheme,
        'avatar-saturation': avatarSaturation,
        'accent-color': accentColor,
        'app-version': config.get('comments:version')
    };

    let dataAttributes = '';

    Object.entries(data).forEach(([key, value]) => {
        dataAttributes += `data-${key}="${value}" `;
    });

    return new SafeString(`
        <script defer src="${config.get('comments:url')}" ${dataAttributes} crossorigin="anonymous"></script>
    `);
}

module.exports = async function commentsLabsWrapper() {
    const self = this;
    const args = arguments;

    return labs.enabledHelper({
        flagKey: 'comments',
        flagName: 'Comments',
        helperName: 'comments'
    }, () => {
        return comments.apply(self, args);
    });
};

module.exports.async = true;
<commit_msg>Added `data-comments-enabled` attribute to comments helper<commit_after>const {SafeString} = require('../services/handlebars');
const {config, urlUtils, getFrontendKey, labs, settingsCache} = require('../services/proxy');

async function comments(options) {
    // todo: For now check on the comment id to exclude normal pages (we probably have a better way to do this)

    const commentId = this.comment_id;

    if (!commentId) {
        return;
    }

    /**
     * We need to check if comments enabled, because the theme might not be using the other available helpers to check
     * if comments is enabled + the member has access
     * @type {'all'|'paid'|'off'}
     */
    const commentsEnabled = settingsCache.get('comments_enabled');
    const hasAccess = !!this.access;

    if (commentsEnabled === 'off' || !hasAccess) {
        return;
    }

    let colorScheme = 'auto';
    if (options.hash.color_scheme === 'dark' || options.hash.color_scheme === 'light') {
        colorScheme = options.hash.color_scheme;
    }

    let avatarSaturation = parseInt(options.hash.avatar_saturation);
    if (isNaN(avatarSaturation)) {
        avatarSaturation = 50;
    }

    let accentColor = '';
    if (options.data.site.accent_color) {
        accentColor = options.data.site.accent_color;
    }

    const frontendKey = await getFrontendKey();

    const data = {
        'ghost-comments': urlUtils.getSiteUrl(),
        api: urlUtils.urlFor('api', {type: 'content'}, true),
        admin: urlUtils.urlFor('admin', true),
        key: frontendKey,
        'post-id': this.id,
        'sentry-dsn': '', /* todo: insert sentry dsn key here */
        'color-scheme': colorScheme,
        'avatar-saturation': avatarSaturation,
        'accent-color': accentColor,
        'app-version': config.get('comments:version'),
        'comments-enabled': commentsEnabled
    };

    let dataAttributes = '';

    Object.entries(data).forEach(([key, value]) => {
        dataAttributes += `data-${key}="${value}" `;
    });

    return new SafeString(`
        <script defer src="${config.get('comments:url')}" ${dataAttributes} crossorigin="anonymous"></script>
    `);
}

module.exports = async function commentsLabsWrapper() {
    const self = this;
    const args = arguments;

    return labs.enabledHelper({
        flagKey: 'comments',
        flagName: 'Comments',
        helperName: 'comments'
    }, () => {
        return comments.apply(self, args);
    });
};

module.exports.async = true;
<|endoftext|>


<commit_before>
var tags = localStorage.getItem('dados');
var $ul = $('
');
var clientes = [];

      // console.log(tabUrl)

$ul.html(tags);
if (tags) {

  $.each($ul.children(), function(i, li) {
    //clientes[i] = $(li).html();
    var html = $(li).html();
    $('#clientes ul').append('
 '+html+'
');
  });
} else {
  $('#clientes ul').append('
Sem tags
');

}
// mailto:info@example.com?subject='Mi casa'&body=Telefono

/**
 *Tabs from Skeleton
 */
(function ($) {
  // hash change handler
  function hashchange () {
    var hash = window.location.hash,
        el = $('ul.tabs [href*="' + hash + '"]'),
        content = $(hash);

    if (el.length && !el.hasClass('active') && content.length) {
        el.closest('.tabs').find('.active').removeClass('active');
        el.addClass('active');
        content.show().addClass('active').siblings().hide().removeClass('active');
    }
  }

  // listen on event and fire right away
  $(window).on('hashchange.skeleton', hashchange);
  hashchange();
  $(hashchange);
})(jQuery);
<commit_msg>fix custom tags<commit_after>
var tags = localStorage.getItem('dados');
var $ul = $('
');
var clientes = [];

      // console.log(tabUrl)

$ul.html(tags);
if (tags) {

  $.each($ul.children(), function(i, li) {
    //clientes[i] = $(li).html();
    var html = $(li).html();
    $('#clientes ul').append('
 ['+html+']
');
  });
} else {
  $('#clientes ul').append('
Sem tags
');

}
// mailto:info@example.com?subject='Mi casa'&body=Telefono

/**
 *Tabs from Skeleton
 */
(function ($) {
  // hash change handler
  function hashchange () {
    var hash = window.location.hash,
        el = $('ul.tabs [href*="' + hash + '"]'),
        content = $(hash);

    if (el.length && !el.hasClass('active') && content.length) {
        el.closest('.tabs').find('.active').removeClass('active');
        el.addClass('active');
        content.show().addClass('active').siblings().hide().removeClass('active');
    }
  }

  // listen on event and fire right away
  $(window).on('hashchange.skeleton', hashchange);
  hashchange();
  $(hashchange);
})(jQuery);
<|endoftext|>


<commit_before>import xs from 'xstream';
import dropRepeats from 'xstream/extra/dropRepeats';
import isolate from '@cycle/isolate';

let _id = 0;

function id () {
  return _id++;
}

function handlerStreams (item, handlers = {}) {
  const sinkStreams = Object.keys(item).map(sink => {
    if (handlers[sink] === undefined) {
      return null;
    }

    const handler = handlers[sink];
    const sink$ = item[sink];

    return sink$.map(event => {
      event && event.stopPropagation && event.stopPropagation();

      const handlerReducer = (state) => handler(state, item, event);

      return handlerReducer;
    });
  });

  return xs.merge(...sinkStreams.filter(reducer => reducer !== null));
}

function makeItem (component, sources) {
  const newId = id();

  const newItem = isolate(component, newId.toString())(sources);

  newItem.id = newId;
  newItem.name = component.name;

  return newItem;
}

function collection(options, items = [], handler$Hash = {}) {
  const { component, sources, handlers, reducers, proxy } = options;

  return {
    add (additionalSources = {}) {
      const newItem = makeItem(component, {...sources, ...additionalSources});
      const handler$ = handlerStreams(newItem, handlers);
      handler$.addListener(proxy);

      return collection(
        options,
        [...items, newItem],
        {
          ...handler$Hash,
          [newItem.id]:handler$
        }
      );
    },

    remove (itemForRemoval) {
      const id = itemForRemoval && itemForRemoval.id;
      id && handler$Hash[id] && handler$Hash[id].removeListener(proxy);

      return collection(
        options,
        items.filter(item => item !== itemForRemoval),
        {
          ...handler$Hash,
          [id]: null
        }
      );
    },

    asArray () {
      return items.slice(); // returns a copy of items to avoid mutation
    },

    reducers
  }
}

function Collection (component, sources = {}, handlers = {}) {
  const reducers = xs.create();
  const proxy = {
    next(value) {
      reducers.shamefullySendNext(value);
    },
    error(err) {
      reducers.shamefullySendError(err);
    },
    complete() {
      // Maybe autoremove here?
    }
  };

  return collection({ component, sources, handlers, reducers, proxy });
}

Collection.pluck = function pluck (collection$, sinkProperty) {
  const sinks = {};

  function sink$ (item) {
    const key = `${item.name}.${item.id}.${sinkProperty}`;

    if (sinks[key] === undefined) {
      if (sinkProperty === 'DOM') {
        sinks[key] = item[sinkProperty].map(vtree => ({...vtree, key})).remember();
      } else {
        sinks[key] = item[sinkProperty].remember();
      }
    }

    return sinks[key];
  }

  return collection$
    .map(collection => collection.asArray().map(item => sink$(item)))
    .map(sinkStreams => xs.combine((...items) => items, ...sinkStreams))
    .flatten()
    .startWith([]);
};

// convert a stream of items' sources snapshots into a stream of collections
Collection.gather = function gather (itemsState$ , component, sources, handlers = {}, idAttribute = 'id') {
  const makeDestroyable = component => (sources) => {
    const sinks = component(sources);
    return {
      ...sinks,
      remove$: xs.merge(sinks.remove$ || xs.never(), sources.destroy$)
    };
  };
  const collection = Collection(makeDestroyable(component), sources, {
    remove$: (collection, item) => collection.remove(item),
    ...handlers
  });
  // each time a new item appears, it should be added to the collection
  const addReducers$ = itemsState$
    // get the added items at each step
    .fold(
      ({prevIds}, items) => ({
        prevIds: items.map(item => item[idAttribute]),
        addedItems: items.filter(item => prevIds.indexOf(item[idAttribute]) === -1)
      }),
      {
        prevIds: [],
        addedItems: []
      }
    )
    .map(({addedItems}) => addedItems)
    .filter(addedItems => addedItems.length)
    // turn each new item into a hash of source streams, tracking all the future updates
    .map(addedItems =>
      addedItems.map(addedItem => {
        const itemStateInfinite$ = itemsState$
          .map(items =>
            items.find(item => item[idAttribute] === addedItem[idAttribute])
          );
        // if an item isn't present if a new snapshot, it shall be destroyed
        const destroy$ = itemStateInfinite$.filter(item => !item).take(1);
        const itemState$ = itemStateInfinite$.endWhen(destroy$);

        return Object.keys(addedItem)
          .reduce((sources, key) => {
            if (key === idAttribute) {
              return sources;
            }

            return {
              ...sources,
              [key]: itemState$
                .map(state => state[key])
                .startWith(addedItem[key])
                // skip the snapshot if the value didn't change
                .compose(dropRepeats((value, nextValue) => {
                  if (value === nextValue) {
                    return true;
                  }
                  try {
                    if (JSON.stringify(value) === JSON.stringify(nextValue)) {
                      return true;
                    }
                  } catch(e) {}
                  // if not equal or not serializable
                  return false;
                }))
                .remember()
            };
          }, {
            destroy$
          })
      })
    )
    .map(itemsSources => collection =>
      itemsSources.reduce(
        (collection, sources) => collection.add(sources),
        collection
      )
    );
  return xs.merge(addReducers$, collection.reducers)
      .fold((collection, reducer) => reducer(collection), collection);
};

export default Collection;
<commit_msg>Collection.gather refactoring<commit_after>import xs from 'xstream';
import dropRepeats from 'xstream/extra/dropRepeats';
import isolate from '@cycle/isolate';

let _id = 0;

function id () {
  return _id++;
}

function handlerStreams (item, handlers = {}) {
  const sinkStreams = Object.keys(item).map(sink => {
    if (handlers[sink] === undefined) {
      return null;
    }

    const handler = handlers[sink];
    const sink$ = item[sink];

    return sink$.map(event => {
      event && event.stopPropagation && event.stopPropagation();

      const handlerReducer = (state) => handler(state, item, event);

      return handlerReducer;
    });
  });

  return xs.merge(...sinkStreams.filter(reducer => reducer !== null));
}

function makeItem (component, sources) {
  const newId = id();

  const newItem = isolate(component, newId.toString())(sources);

  newItem.id = newId;
  newItem.name = component.name;

  return newItem;
}

function collection(options, items = [], handler$Hash = {}) {
  const { component, sources, handlers, reducers, proxy } = options;

  return {
    add (additionalSources = {}) {
      const newItem = makeItem(component, {...sources, ...additionalSources});
      const handler$ = handlerStreams(newItem, handlers);
      handler$.addListener(proxy);

      return collection(
        options,
        [...items, newItem],
        {
          ...handler$Hash,
          [newItem.id]:handler$
        }
      );
    },

    remove (itemForRemoval) {
      const id = itemForRemoval && itemForRemoval.id;
      id && handler$Hash[id] && handler$Hash[id].removeListener(proxy);

      return collection(
        options,
        items.filter(item => item !== itemForRemoval),
        {
          ...handler$Hash,
          [id]: null
        }
      );
    },

    asArray () {
      return items.slice(); // returns a copy of items to avoid mutation
    },

    reducers
  }
}

function Collection (component, sources = {}, handlers = {}) {
  const reducers = xs.create();
  const proxy = {
    next(value) {
      reducers.shamefullySendNext(value);
    },
    error(err) {
      reducers.shamefullySendError(err);
    },
    complete() {
      // Maybe autoremove here?
    }
  };

  return collection({ component, sources, handlers, reducers, proxy });
}

Collection.pluck = function pluck (collection$, sinkProperty) {
  const sinks = {};

  function sink$ (item) {
    const key = `${item.name}.${item.id}.${sinkProperty}`;

    if (sinks[key] === undefined) {
      if (sinkProperty === 'DOM') {
        sinks[key] = item[sinkProperty].map(vtree => ({...vtree, key})).remember();
      } else {
        sinks[key] = item[sinkProperty].remember();
      }
    }

    return sinks[key];
  }

  return collection$
    .map(collection => collection.asArray().map(item => sink$(item)))
    .map(sinkStreams => xs.combine((...items) => items, ...sinkStreams))
    .flatten()
    .startWith([]);
};

// convert a stream of items' sources snapshots into a stream of collections
Collection.gather = function gather (itemsState$ , component, sources, handlers = {}, idAttribute = 'id') {
  function makeDestroyable (component) {
    return (sources) => {
      const sinks = component(sources);
      return {
        ...sinks,
        remove$: xs.merge(sinks.remove$ || xs.never(), sources.destroy$)
      };
    };
  }

  // finds items not present in previous snapshot
  function findNewItems({prevIds}, items) {
    return {
      prevIds: items.map(item => item[idAttribute]),
      addedItems: items.filter(item => prevIds.indexOf(item[idAttribute]) === -1)
    };
  }

  function compareJSON(value, nextValue) {
    if (value === nextValue) {
      return true;
    }
    try {
      if (JSON.stringify(value) === JSON.stringify(nextValue)) {
        return true;
      }
    } catch(e) {}
    // if not equal or not serializable
    return false;
  }

  // turn a new item into a hash of source streams, tracking all the future updates
  function itemToSourceStreams(addedItem) {
    const itemStateInfinite$ = itemsState$
      .map(items =>
        items.find(item => item[idAttribute] === addedItem[idAttribute])
      );
    // if an item isn't present if a new snapshot, it shall be destroyed
    const destroy$ = itemStateInfinite$.filter(item => !item).take(1);
    const itemState$ = itemStateInfinite$.endWhen(destroy$);

    return Object.keys(addedItem)
      .reduce((sources, key) => {
        // skip idAttribute
        if (key === idAttribute) {
          return sources;
        }

        return {
          ...sources,
          [key]: itemState$
            .map(state => state[key])
            .startWith(addedItem[key])
            // skip the snapshot if the value didn't change
            .compose(dropRepeats(compareJSON))
            .remember()
        };
      }, {
        destroy$
      });
  }

  function addAllItems(itemsSources) {
    return collection =>
      itemsSources.reduce(
        (collection, sources) => collection.add(sources),
        collection
      );
  }

  const collection = Collection(makeDestroyable(component), sources, {
    remove$: (collection, item) => collection.remove(item),
    ...handlers
  });

  const addReducers$ = itemsState$
    // get the added items at each step
    .fold(findNewItems, {prevIds: [], addedItems: []})
    .map(({addedItems}) => addedItems)
    .filter(addedItems => addedItems.length)
    .map(addedItems => addedItems.map(itemToSourceStreams))
    .map(addAllItems);

  return xs.merge(addReducers$, collection.reducers)
    .fold((collection, reducer) => reducer(collection), collection);
};

export default Collection;
<|endoftext|>


<commit_before>/**
 * Creates a set of animations
 * @param {Item} item
 * @constructor
 */
function Collection(item) {
  EventEmitter.call(this)

  this.start = null
  this.item = item
  this.delay = 0
  this.duration = 0
  this.easeName = 'linear'
  this.animations = []
}

Collection.prototype = new EventEmitter

/**
 * Add item to the collection
 * @param transform
 * @param duration
 * @param ease
 * @param delay
 */
Collection.prototype.add = function (transform, duration, ease, delay) {
  var animation

  if (transform instanceof Collection) {
    animation = transform
  } else if (Array.isArray(transform)) {
    animation = parallel(this.item, transform)
  } else {
    animation = new Animation(this.item, transform, duration, ease, delay)
  }

  this.animations.push(animation)

  duration = this.animations.map(function (a) {
    return a.duration + a.delay
  })

  if (this instanceof Parallel) {
    this.duration = Math.max.apply(null, duration)
  } else {
    this.duration = duration.reduce(function (a, b) {
      return a + b
    }, 0)
  }

  return this

  function sequence(item, transforms) {
    var sequence = new Sequence(item)
    transforms.forEach(function (t) {
      sequence.add(t, duration, ease, delay)
    })
    return sequence
  }

  function parallel(item, transforms) {
    var parallel = new Parallel(item)

    transforms.forEach(function (t) {
      if (Array.isArray(t)) {
	      parallel.add(sequence(item, t))
      } else {
	      parallel.add(t, duration, ease, delay)
      }
    })
    return parallel
  }
}

/**
 * Add animation to collection
 * chainable
 * @returns {Sequence}
 */
Collection.prototype.animate = function (transform, duration, ease, delay) {
  return this.runner.add(transform, duration, ease, delay)
}

/**
 * Apply styles
 * @returns {CSS}
 */
Collection.prototype.css = function () {
  return this.item.css()
}
<commit_msg>easier to read<commit_after>/**
 * Creates a set of animations
 * @param {Item} item
 * @constructor
 */
function Collection(item) {
  EventEmitter.call(this)

  this.start = null
  this.item = item
  this.delay = 0
  this.duration = 0
  this.easeName = 'linear'
  this.animations = []
}

Collection.prototype = new EventEmitter

/**
 * Add item to the collection
 * @param transform
 * @param duration
 * @param ease
 * @param delay
 */
Collection.prototype.add = function (transform, duration, ease, delay) {
  var animation

  if (transform instanceof Collection) {
    animation = transform
  } else if (Array.isArray(transform)) {
    animation = parallel(this.item, transform)
  } else {
    animation = new Animation(this.item, transform, duration, ease, delay)
  }

  this.animations.push(animation)

  duration = this.animations.map(function (a) {
    return a.duration + a.delay
  })

  if (this instanceof Parallel) {
    this.duration = Math.max.apply(null, duration)
  } else {
    this.duration = duration.reduce(function (a, b) {
      return a + b
    }, 0)
  }

  return this

  function sequence(item, transforms) {
    var sequence = new Sequence(item)

    transforms.forEach(function (t) {
      sequence.add(t, duration, ease, delay)
    })

    return sequence
  }

  function parallel(item, transforms) {
    var parallel = new Parallel(item)

    transforms.forEach(function (t) {
      if (Array.isArray(t)) {
	      parallel.add(sequence(item, t))
      } else {
	      parallel.add(t, duration, ease, delay)
      }
    })

    return parallel
  }
}

/**
 * Add animation to collection
 * chainable
 * @returns {Sequence}
 */
Collection.prototype.animate = function (transform, duration, ease, delay) {
  return this.runner.add(transform, duration, ease, delay)
}

/**
 * Apply styles
 * @returns {CSS}
 */
Collection.prototype.css = function () {
  return this.item.css()
}
<|endoftext|>