<commit_before>"use strict";

const os = require("os");
const fs = require("fs-extra");
const path = require("path");
const pFinally = require("p-finally");
const pMap = require("p-map");
const pReduce = require("p-reduce");
const semver = require("semver");

const Command = require("@lerna/command");
const childProcess = require("@lerna/child-process");
const output = require("@lerna/output");
const collectUpdates = require("@lerna/collect-updates");
const npmDistTag = require("@lerna/npm-dist-tag");
const npmPublish = require("@lerna/npm-publish");
const { createRunner } = require("@lerna/run-lifecycle");
const batchPackages = require("@lerna/batch-packages");
const runParallelBatches = require("@lerna/run-parallel-batches");
const versionCommand = require("@lerna/version");

const createTempLicenses = require("./lib/create-temp-licenses");
const getCurrentSHA = require("./lib/get-current-sha");
const getCurrentTags = require("./lib/get-current-tags");
const getTaggedPackages = require("./lib/get-tagged-packages");
const getPackagesWithoutLicense = require("./lib/get-packages-without-license");
const gitCheckout = require("./lib/git-checkout");
const removeTempLicenses = require("./lib/remove-temp-licenses");
const verifyNpmRegistry = require("./lib/verify-npm-registry");
const verifyNpmPackageAccess = require("./lib/verify-npm-package-access");

module.exports = factory;

function factory(argv) {
  return new PublishCommand(argv);
}

class PublishCommand extends Command {
  get otherCommandConfigs() {
    // back-compat
    return ["version"];
  }

  initialize() {
    if (this.options.skipNpm) {
      // TODO: remove in next major release
      this.logger.warn("deprecated", "Instead of --skip-npm, call `lerna version` directly");

      return versionCommand(this._argv).then(() => false);
    }

    if (this.options.canary) {
      this.logger.info("canary", "enabled");
    }

    if (this.options.requireScripts) {
      this.logger.info("require-scripts", "enabled");
    }

    // inverted boolean options
    this.verifyAccess = this.options.verifyAccess !== false;
    this.verifyRegistry = this.options.verifyRegistry !== false;

    // https://docs.npmjs.com/misc/config#save-prefix
    this.savePrefix = this.options.exact ? "" : "^";

    this.npmConfig = {
      npmClient: this.options.npmClient || "npm",
      registry: this.options.registry,
    };

    return Promise.resolve(this.findVersionedUpdates()).then(result => {
      if (!result) {
        // early return from nested VersionCommand
        return false;
      }

      if (!result.updates.length) {
        this.logger.success("No updated packages to publish");

        // still exits zero, aka "ok"
        return false;
      }

      this.updates = result.updates;
      this.updatesVersions = new Map(result.updatesVersions);

      this.runPackageLifecycle = createRunner(this.options);
      this.packagesToPublish = this.updates.map(({ pkg }) => pkg).filter(pkg => !pkg.private);
      this.batchedPackages = this.toposort
        ? batchPackages(
            this.packagesToPublish,
            this.options.rejectCycles,
            // Don't sort based on devDependencies because that would increase the chance of dependency cycles
            // causing less-than-ideal a publishing order.
            "dependencies"
          )
        : [this.packagesToPublish];

      return Promise.resolve()
        .then(() => this.prepareRegistryActions())
        .then(() => this.prepareLicenseActions());
    });
  }

  execute() {
    this.enableProgressBar();
    this.logger.info("publish", "Publishing packages to npm...");

    let chain = Promise.resolve();

    if (this.options.canary) {
      chain = chain.then(() => this.updateCanaryVersions());
    }

    chain = chain.then(() => this.resolveLocalDependencyLinks());
    chain = chain.then(() => this.annotateGitHead());
    chain = chain.then(() => this.packUpdated());
    chain = chain.then(() => this.publishPacked());
    chain = chain.then(() => this.resetChanges());

    if (this.options.tempTag) {
      chain = chain.then(() => this.npmUpdateAsLatest());
    }

    return chain.then(() => {
      const count = this.packagesToPublish.length;
      const message = this.packagesToPublish.map(pkg => ` - ${pkg.name}@${pkg.version}`);

      output("Successfully published:");
      output(message.join(os.EOL));

      this.logger.success("published", "%d %s", count, count === 1 ? "package" : "packages");
    });
  }

  findVersionedUpdates() {
    if (this.options.bump === "from-git") {
      return this.detectFromGit();
    }

    if (this.options.canary) {
      // TODO: throw useful error when canary attempted on tagged release?
      return this.detectCanaryVersions();
    }

    return versionCommand(this._argv);
  }

  detectFromGit() {
    let chain = Promise.resolve();

    chain = chain.then(() => getCurrentTags(this.execOpts));
    chain = chain.then(taggedPackageNames => {
      if (!taggedPackageNames.length) {
        this.logger.notice("from-git", "No tagged release found");

        return [];
      }

      if (this.project.isIndependent()) {
        return taggedPackageNames.map(name => this.packageGraph.get(name));
      }

      return getTaggedPackages(this.packageGraph, this.project.rootPath, this.execOpts);
    });

    return chain.then(updates => {
      const updatesVersions = updates.map(({ pkg }) => [pkg.name, pkg.version]);

      return { updates, updatesVersions };
    });
  }

  detectCanaryVersions() {
    const { bump = "prepatch", preid = "alpha" } = this.options;
    // "prerelease" and "prepatch" are identical, for our purposes
    const release = bump.startsWith("pre") ? bump.replace("release", "patch") : `pre${bump}`;

    let chain = Promise.resolve();

    // find changed packages since last release, if any
    chain = chain.then(() =>
      collectUpdates(this.filteredPackages, this.packageGraph, this.execOpts, {
        bump: "prerelease",
        canary: true,
        ignoreChanges: this.options.ignoreChanges,
      })
    );

    const makeVersion = described => {
      // FIXME: this will blow up when `described` doesn't match the regex (returns null)
      const [, baseVersion, refCount, buildMeta] = /^(?:.*@)?(.*)-(\d+)-g([0-9a-f]+)$/.exec(described);

      // the next version is bumped without concern for preid or current index
      const nextVersion = semver.inc(baseVersion, release.replace("pre", ""));

      // semver.inc() starts a new prerelease at .0, git describe starts at .1
      // and build metadata is always ignored when comparing dependency ranges
      return `${nextVersion}-${preid}.${refCount - 1}+${buildMeta}`;
    };

    const gitDescribeMatching = globFilter =>
      childProcess.execSync("git", ["describe", "--match", globFilter], this.execOpts);

    if (this.project.isIndependent()) {
      // each package is described against its tags only
      chain = chain.then(updates =>
        pMap(updates, ({ pkg }) => {
          const described = gitDescribeMatching(`${pkg.name}@*`);
          const version = makeVersion(described);

          return [pkg.name, version];
        }).then(updatesVersions => ({ updates, updatesVersions }))
      );
    } else {
      // all packages are described against the last tag
      chain = chain.then(updates => {
        const tagPrefix = this.options.tagVersionPrefix || "v";
        const described = gitDescribeMatching(`${tagPrefix}*.*.*`);
        const version = makeVersion(described);
        const updatesVersions = updates.map(({ pkg }) => [pkg.name, version]);

        return { updates, updatesVersions };
      });
    }

    return chain;
  }

  prepareLicenseActions() {
    return Promise.resolve()
      .then(() => getPackagesWithoutLicense(this.project, this.packagesToPublish))
      .then(packagesWithoutLicense => {
        if (packagesWithoutLicense.length && !this.project.licensePath) {
          this.packagesToBeLicensed = [];
          this.logger.warn(
            "ENOLICENSE",
            `Packages ${packagesWithoutLicense.map(pkg => pkg.name).join(", ")} are missing a license`
          );
        } else {
          this.packagesToBeLicensed = packagesWithoutLicense;
        }
      });
  }

  prepareRegistryActions() {
    let chain = Promise.resolve();

    if (this.verifyRegistry) {
      chain = chain.then(() => verifyNpmRegistry(this.project.rootPath, this.npmConfig));
    }

    /* istanbul ignore else */
    if (process.env.LERNA_INTEGRATION) {
      return chain;
    }

    if (this.verifyAccess) {
      chain = chain.then(() =>
        verifyNpmPackageAccess(this.packagesToPublish, this.project.rootPath, this.npmConfig)
      );
    }

    return chain;
  }

  updateCanaryVersions() {
    const publishableUpdates = this.updates.filter(node => !node.pkg.private);

    return pMap(publishableUpdates, ({ pkg, localDependencies }) => {
      pkg.version = this.updatesVersions.get(pkg.name);

      for (const [depName, resolved] of localDependencies) {
        // other canary versions need to be updated, non-canary is a no-op
        const depVersion = this.updatesVersions.get(depName) || this.packageGraph.get(depName).pkg.version;

        // it no longer matters if we mutate the shared Package instance
        pkg.updateLocalDependency(resolved, depVersion, this.savePrefix);
      }

      // writing changes to disk handled in annotateGitHead()
    });
  }

  resolveLocalDependencyLinks() {
    // resolve relative file: links to their actual version range
    const updatesWithLocalLinks = this.updates.filter(
      ({ pkg, localDependencies }) =>
        !pkg.private &&
        localDependencies.size &&
        Array.from(localDependencies.values()).some(({ type }) => type === "directory")
    );

    return pMap(updatesWithLocalLinks, ({ pkg, localDependencies }) => {
      for (const [depName, resolved] of localDependencies) {
        // regardless of where the version comes from, we can't publish "file:../sibling-pkg" specs
        const depVersion = this.updatesVersions.get(depName) || this.packageGraph.get(depName).pkg.version;

        // it no longer matters if we mutate the shared Package instance
        pkg.updateLocalDependency(resolved, depVersion, this.savePrefix);
      }

      // writing changes to disk handled in annotateGitHead()
    });
  }

  annotateGitHead() {
    const gitHead = getCurrentSHA(this.execOpts);

    return pMap(this.packagesToPublish, pkg => {
      // provide gitHead property that is normally added during npm publish
      pkg.set("gitHead", gitHead);

      return pkg.serialize();
    });
  }

  resetChanges() {
    // the package.json files are changed (by gitHead if not --canary)
    // and we should always leave the working tree clean
    return pReduce(this.project.packageConfigs, (_, pkgGlob) =>
      gitCheckout(`${pkgGlob}/package.json`, this.execOpts)
    ).then(() =>
      // --skip-git should not leave unstaged changes behind
      gitCheckout(this.project.manifest.location, this.execOpts)
    );
  }

  execScript(pkg, script) {
    const scriptLocation = path.join(pkg.location, "scripts", script);

    try {
      // eslint-disable-next-line import/no-dynamic-require, global-require
      require(scriptLocation);
    } catch (ex) {
      this.logger.silly("execScript", `No ${script} script found at ${scriptLocation}`);
    }
  }

  runPrepublishScripts(pkg) {
    return Promise.resolve()
      .then(() => this.runPackageLifecycle(pkg, "prepare"))
      .then(() => this.runPackageLifecycle(pkg, "prepublishOnly"));
  }

  removeTempLicensesOnError(error) {
    return Promise.resolve()
      .then(() =>
        removeTempLicenses(this.packagesToBeLicensed).catch(removeError => {
          this.logger.error(
            "licenses",
            "error removing temporary license files",
            removeError.stack || removeError
          );
        })
      )
      .then(() => {
        // restore original error into promise chain
        throw error;
      });
  }

  packUpdated() {
    const tracker = this.logger.newItem("npm pack");

    tracker.addWork(
      this.options.requireScripts
        ? // track completion of prepublish.js on updates as well
          this.packagesToPublish.length + this.updates.length
        : this.packagesToPublish.length
    );

    let chain = Promise.resolve();

    chain = chain.then(() => {
      this.npmPack = npmPublish.makePacker(this.project.manifest);
    });

    chain = chain.then(() => createTempLicenses(this.project.licensePath, this.packagesToBeLicensed));
    chain = chain.then(() => this.runPrepublishScripts(this.project.manifest));

    if (this.options.requireScripts) {
      chain = chain.then(() =>
        pMap(this.updates, ({ pkg }) => {
          this.execScript(pkg, "prepublish");
          tracker.completeWork(1);
        })
      );
    }

    chain = chain.then(() =>
      pReduce(this.batchedPackages, (_, batch) =>
        this.npmPack(batch).then(() => {
          tracker.completeWork(batch.length);
        })
      )
    );

    chain = chain.then(() => removeTempLicenses(this.packagesToBeLicensed));

    // remove temporary license files if _any_ error occurs _anywhere_ in the promise chain
    chain = chain.catch(error => this.removeTempLicensesOnError(error));

    return pFinally(chain, () => tracker.finish());
  }

  publishPacked() {
    // if we skip temp tags we should tag with the proper value immediately
    const distTag = this.options.tempTag ? "lerna-temp" : this.getDistTag();
    const tracker = this.logger.newItem(`${this.npmConfig.npmClient} publish`);

    tracker.addWork(this.packagesToPublish.length);

    let chain = Promise.resolve();

    chain = chain.then(() =>
      runParallelBatches(this.batchedPackages, this.concurrency, pkg =>
        npmPublish(pkg, distTag, this.npmConfig)
          // postpublish is _not_ run when publishing a tarball
          .then(() => this.runPackageLifecycle(pkg, "postpublish"))
          // don't leave the generated tarball hanging around
          .then(() => fs.remove(path.join(pkg.rootPath, pkg.tarball)))
          .then(() => {
            tracker.info("published", pkg.name);

            if (this.options.requireScripts) {
              this.execScript(pkg, "postpublish");
            }

            tracker.completeWork(1);
          })
      )
    );

    chain = chain.then(() => this.runPackageLifecycle(this.project.manifest, "postpublish"));

    return pFinally(chain, () => tracker.finish());
  }

  npmUpdateAsLatest() {
    const distTag = this.getDistTag() || "latest";
    const tracker = this.logger.newItem("npmUpdateAsLatest");

    tracker.addWork(this.packagesToPublish.length);

    let chain = Promise.resolve();

    chain = chain.then(() =>
      runParallelBatches(this.batchedPackages, this.concurrency, pkg =>
        this.updateTag(pkg, distTag).then(() => {
          tracker.info("dist-tag", "%s@%s => %j", pkg.name, pkg.version, distTag);
          tracker.completeWork(1);
        })
      )
    );

    return pFinally(chain, () => tracker.finish());
  }

  updateTag(pkg, distTag) {
    return Promise.resolve()
      .then(() => npmDistTag.check(pkg, "lerna-temp", this.npmConfig))
      .then(exists => {
        if (exists) {
          return npmDistTag.remove(pkg, "lerna-temp", this.npmConfig);
        }
      })
      .then(() => npmDistTag.add(pkg, distTag, this.npmConfig));
  }

  getDistTag() {
    if (this.options.npmTag) {
      return this.options.npmTag;
    }

    if (this.options.canary) {
      return "canary";
    }

    // undefined defaults to "latest" OR whatever is in pkg.publishConfig.tag
  }
}

module.exports.PublishCommand = PublishCommand;
<commit_msg>chore(publish): correct coverage ignore<commit_after>"use strict";

const os = require("os");
const fs = require("fs-extra");
const path = require("path");
const pFinally = require("p-finally");
const pMap = require("p-map");
const pReduce = require("p-reduce");
const semver = require("semver");

const Command = require("@lerna/command");
const childProcess = require("@lerna/child-process");
const output = require("@lerna/output");
const collectUpdates = require("@lerna/collect-updates");
const npmDistTag = require("@lerna/npm-dist-tag");
const npmPublish = require("@lerna/npm-publish");
const { createRunner } = require("@lerna/run-lifecycle");
const batchPackages = require("@lerna/batch-packages");
const runParallelBatches = require("@lerna/run-parallel-batches");
const versionCommand = require("@lerna/version");

const createTempLicenses = require("./lib/create-temp-licenses");
const getCurrentSHA = require("./lib/get-current-sha");
const getCurrentTags = require("./lib/get-current-tags");
const getTaggedPackages = require("./lib/get-tagged-packages");
const getPackagesWithoutLicense = require("./lib/get-packages-without-license");
const gitCheckout = require("./lib/git-checkout");
const removeTempLicenses = require("./lib/remove-temp-licenses");
const verifyNpmRegistry = require("./lib/verify-npm-registry");
const verifyNpmPackageAccess = require("./lib/verify-npm-package-access");

module.exports = factory;

function factory(argv) {
  return new PublishCommand(argv);
}

class PublishCommand extends Command {
  get otherCommandConfigs() {
    // back-compat
    return ["version"];
  }

  initialize() {
    if (this.options.skipNpm) {
      // TODO: remove in next major release
      this.logger.warn("deprecated", "Instead of --skip-npm, call `lerna version` directly");

      return versionCommand(this._argv).then(() => false);
    }

    if (this.options.canary) {
      this.logger.info("canary", "enabled");
    }

    if (this.options.requireScripts) {
      this.logger.info("require-scripts", "enabled");
    }

    // inverted boolean options
    this.verifyAccess = this.options.verifyAccess !== false;
    this.verifyRegistry = this.options.verifyRegistry !== false;

    // https://docs.npmjs.com/misc/config#save-prefix
    this.savePrefix = this.options.exact ? "" : "^";

    this.npmConfig = {
      npmClient: this.options.npmClient || "npm",
      registry: this.options.registry,
    };

    return Promise.resolve(this.findVersionedUpdates()).then(result => {
      if (!result) {
        // early return from nested VersionCommand
        return false;
      }

      if (!result.updates.length) {
        this.logger.success("No updated packages to publish");

        // still exits zero, aka "ok"
        return false;
      }

      this.updates = result.updates;
      this.updatesVersions = new Map(result.updatesVersions);

      this.runPackageLifecycle = createRunner(this.options);
      this.packagesToPublish = this.updates.map(({ pkg }) => pkg).filter(pkg => !pkg.private);
      this.batchedPackages = this.toposort
        ? batchPackages(
            this.packagesToPublish,
            this.options.rejectCycles,
            // Don't sort based on devDependencies because that would increase the chance of dependency cycles
            // causing less-than-ideal a publishing order.
            "dependencies"
          )
        : [this.packagesToPublish];

      return Promise.resolve()
        .then(() => this.prepareRegistryActions())
        .then(() => this.prepareLicenseActions());
    });
  }

  execute() {
    this.enableProgressBar();
    this.logger.info("publish", "Publishing packages to npm...");

    let chain = Promise.resolve();

    if (this.options.canary) {
      chain = chain.then(() => this.updateCanaryVersions());
    }

    chain = chain.then(() => this.resolveLocalDependencyLinks());
    chain = chain.then(() => this.annotateGitHead());
    chain = chain.then(() => this.packUpdated());
    chain = chain.then(() => this.publishPacked());
    chain = chain.then(() => this.resetChanges());

    if (this.options.tempTag) {
      chain = chain.then(() => this.npmUpdateAsLatest());
    }

    return chain.then(() => {
      const count = this.packagesToPublish.length;
      const message = this.packagesToPublish.map(pkg => ` - ${pkg.name}@${pkg.version}`);

      output("Successfully published:");
      output(message.join(os.EOL));

      this.logger.success("published", "%d %s", count, count === 1 ? "package" : "packages");
    });
  }

  findVersionedUpdates() {
    if (this.options.bump === "from-git") {
      return this.detectFromGit();
    }

    if (this.options.canary) {
      // TODO: throw useful error when canary attempted on tagged release?
      return this.detectCanaryVersions();
    }

    return versionCommand(this._argv);
  }

  detectFromGit() {
    let chain = Promise.resolve();

    chain = chain.then(() => getCurrentTags(this.execOpts));
    chain = chain.then(taggedPackageNames => {
      if (!taggedPackageNames.length) {
        this.logger.notice("from-git", "No tagged release found");

        return [];
      }

      if (this.project.isIndependent()) {
        return taggedPackageNames.map(name => this.packageGraph.get(name));
      }

      return getTaggedPackages(this.packageGraph, this.project.rootPath, this.execOpts);
    });

    return chain.then(updates => {
      const updatesVersions = updates.map(({ pkg }) => [pkg.name, pkg.version]);

      return { updates, updatesVersions };
    });
  }

  detectCanaryVersions() {
    const { bump = "prepatch", preid = "alpha" } = this.options;
    // "prerelease" and "prepatch" are identical, for our purposes
    const release = bump.startsWith("pre") ? bump.replace("release", "patch") : `pre${bump}`;

    let chain = Promise.resolve();

    // find changed packages since last release, if any
    chain = chain.then(() =>
      collectUpdates(this.filteredPackages, this.packageGraph, this.execOpts, {
        bump: "prerelease",
        canary: true,
        ignoreChanges: this.options.ignoreChanges,
      })
    );

    const makeVersion = described => {
      // FIXME: this will blow up when `described` doesn't match the regex (returns null)
      const [, baseVersion, refCount, buildMeta] = /^(?:.*@)?(.*)-(\d+)-g([0-9a-f]+)$/.exec(described);

      // the next version is bumped without concern for preid or current index
      const nextVersion = semver.inc(baseVersion, release.replace("pre", ""));

      // semver.inc() starts a new prerelease at .0, git describe starts at .1
      // and build metadata is always ignored when comparing dependency ranges
      return `${nextVersion}-${preid}.${refCount - 1}+${buildMeta}`;
    };

    const gitDescribeMatching = globFilter =>
      childProcess.execSync("git", ["describe", "--match", globFilter], this.execOpts);

    if (this.project.isIndependent()) {
      // each package is described against its tags only
      chain = chain.then(updates =>
        pMap(updates, ({ pkg }) => {
          const described = gitDescribeMatching(`${pkg.name}@*`);
          const version = makeVersion(described);

          return [pkg.name, version];
        }).then(updatesVersions => ({ updates, updatesVersions }))
      );
    } else {
      // all packages are described against the last tag
      chain = chain.then(updates => {
        const tagPrefix = this.options.tagVersionPrefix || "v";
        const described = gitDescribeMatching(`${tagPrefix}*.*.*`);
        const version = makeVersion(described);
        const updatesVersions = updates.map(({ pkg }) => [pkg.name, version]);

        return { updates, updatesVersions };
      });
    }

    return chain;
  }

  prepareLicenseActions() {
    return Promise.resolve()
      .then(() => getPackagesWithoutLicense(this.project, this.packagesToPublish))
      .then(packagesWithoutLicense => {
        if (packagesWithoutLicense.length && !this.project.licensePath) {
          this.packagesToBeLicensed = [];
          this.logger.warn(
            "ENOLICENSE",
            `Packages ${packagesWithoutLicense.map(pkg => pkg.name).join(", ")} are missing a license`
          );
        } else {
          this.packagesToBeLicensed = packagesWithoutLicense;
        }
      });
  }

  prepareRegistryActions() {
    let chain = Promise.resolve();

    if (this.verifyRegistry) {
      chain = chain.then(() => verifyNpmRegistry(this.project.rootPath, this.npmConfig));
    }

    /* istanbul ignore if */
    if (process.env.LERNA_INTEGRATION) {
      return chain;
    }

    if (this.verifyAccess) {
      chain = chain.then(() =>
        verifyNpmPackageAccess(this.packagesToPublish, this.project.rootPath, this.npmConfig)
      );
    }

    return chain;
  }

  updateCanaryVersions() {
    const publishableUpdates = this.updates.filter(node => !node.pkg.private);

    return pMap(publishableUpdates, ({ pkg, localDependencies }) => {
      pkg.version = this.updatesVersions.get(pkg.name);

      for (const [depName, resolved] of localDependencies) {
        // other canary versions need to be updated, non-canary is a no-op
        const depVersion = this.updatesVersions.get(depName) || this.packageGraph.get(depName).pkg.version;

        // it no longer matters if we mutate the shared Package instance
        pkg.updateLocalDependency(resolved, depVersion, this.savePrefix);
      }

      // writing changes to disk handled in annotateGitHead()
    });
  }

  resolveLocalDependencyLinks() {
    // resolve relative file: links to their actual version range
    const updatesWithLocalLinks = this.updates.filter(
      ({ pkg, localDependencies }) =>
        !pkg.private &&
        localDependencies.size &&
        Array.from(localDependencies.values()).some(({ type }) => type === "directory")
    );

    return pMap(updatesWithLocalLinks, ({ pkg, localDependencies }) => {
      for (const [depName, resolved] of localDependencies) {
        // regardless of where the version comes from, we can't publish "file:../sibling-pkg" specs
        const depVersion = this.updatesVersions.get(depName) || this.packageGraph.get(depName).pkg.version;

        // it no longer matters if we mutate the shared Package instance
        pkg.updateLocalDependency(resolved, depVersion, this.savePrefix);
      }

      // writing changes to disk handled in annotateGitHead()
    });
  }

  annotateGitHead() {
    const gitHead = getCurrentSHA(this.execOpts);

    return pMap(this.packagesToPublish, pkg => {
      // provide gitHead property that is normally added during npm publish
      pkg.set("gitHead", gitHead);

      return pkg.serialize();
    });
  }

  resetChanges() {
    // the package.json files are changed (by gitHead if not --canary)
    // and we should always leave the working tree clean
    return pReduce(this.project.packageConfigs, (_, pkgGlob) =>
      gitCheckout(`${pkgGlob}/package.json`, this.execOpts)
    ).then(() =>
      // --skip-git should not leave unstaged changes behind
      gitCheckout(this.project.manifest.location, this.execOpts)
    );
  }

  execScript(pkg, script) {
    const scriptLocation = path.join(pkg.location, "scripts", script);

    try {
      // eslint-disable-next-line import/no-dynamic-require, global-require
      require(scriptLocation);
    } catch (ex) {
      this.logger.silly("execScript", `No ${script} script found at ${scriptLocation}`);
    }
  }

  runPrepublishScripts(pkg) {
    return Promise.resolve()
      .then(() => this.runPackageLifecycle(pkg, "prepare"))
      .then(() => this.runPackageLifecycle(pkg, "prepublishOnly"));
  }

  removeTempLicensesOnError(error) {
    return Promise.resolve()
      .then(() =>
        removeTempLicenses(this.packagesToBeLicensed).catch(removeError => {
          this.logger.error(
            "licenses",
            "error removing temporary license files",
            removeError.stack || removeError
          );
        })
      )
      .then(() => {
        // restore original error into promise chain
        throw error;
      });
  }

  packUpdated() {
    const tracker = this.logger.newItem("npm pack");

    tracker.addWork(
      this.options.requireScripts
        ? // track completion of prepublish.js on updates as well
          this.packagesToPublish.length + this.updates.length
        : this.packagesToPublish.length
    );

    let chain = Promise.resolve();

    chain = chain.then(() => {
      this.npmPack = npmPublish.makePacker(this.project.manifest);
    });

    chain = chain.then(() => createTempLicenses(this.project.licensePath, this.packagesToBeLicensed));
    chain = chain.then(() => this.runPrepublishScripts(this.project.manifest));

    if (this.options.requireScripts) {
      chain = chain.then(() =>
        pMap(this.updates, ({ pkg }) => {
          this.execScript(pkg, "prepublish");
          tracker.completeWork(1);
        })
      );
    }

    chain = chain.then(() =>
      pReduce(this.batchedPackages, (_, batch) =>
        this.npmPack(batch).then(() => {
          tracker.completeWork(batch.length);
        })
      )
    );

    chain = chain.then(() => removeTempLicenses(this.packagesToBeLicensed));

    // remove temporary license files if _any_ error occurs _anywhere_ in the promise chain
    chain = chain.catch(error => this.removeTempLicensesOnError(error));

    return pFinally(chain, () => tracker.finish());
  }

  publishPacked() {
    // if we skip temp tags we should tag with the proper value immediately
    const distTag = this.options.tempTag ? "lerna-temp" : this.getDistTag();
    const tracker = this.logger.newItem(`${this.npmConfig.npmClient} publish`);

    tracker.addWork(this.packagesToPublish.length);

    let chain = Promise.resolve();

    chain = chain.then(() =>
      runParallelBatches(this.batchedPackages, this.concurrency, pkg =>
        npmPublish(pkg, distTag, this.npmConfig)
          // postpublish is _not_ run when publishing a tarball
          .then(() => this.runPackageLifecycle(pkg, "postpublish"))
          // don't leave the generated tarball hanging around
          .then(() => fs.remove(path.join(pkg.rootPath, pkg.tarball)))
          .then(() => {
            tracker.info("published", pkg.name);

            if (this.options.requireScripts) {
              this.execScript(pkg, "postpublish");
            }

            tracker.completeWork(1);
          })
      )
    );

    chain = chain.then(() => this.runPackageLifecycle(this.project.manifest, "postpublish"));

    return pFinally(chain, () => tracker.finish());
  }

  npmUpdateAsLatest() {
    const distTag = this.getDistTag() || "latest";
    const tracker = this.logger.newItem("npmUpdateAsLatest");

    tracker.addWork(this.packagesToPublish.length);

    let chain = Promise.resolve();

    chain = chain.then(() =>
      runParallelBatches(this.batchedPackages, this.concurrency, pkg =>
        this.updateTag(pkg, distTag).then(() => {
          tracker.info("dist-tag", "%s@%s => %j", pkg.name, pkg.version, distTag);
          tracker.completeWork(1);
        })
      )
    );

    return pFinally(chain, () => tracker.finish());
  }

  updateTag(pkg, distTag) {
    return Promise.resolve()
      .then(() => npmDistTag.check(pkg, "lerna-temp", this.npmConfig))
      .then(exists => {
        if (exists) {
          return npmDistTag.remove(pkg, "lerna-temp", this.npmConfig);
        }
      })
      .then(() => npmDistTag.add(pkg, distTag, this.npmConfig));
  }

  getDistTag() {
    if (this.options.npmTag) {
      return this.options.npmTag;
    }

    if (this.options.canary) {
      return "canary";
    }

    // undefined defaults to "latest" OR whatever is in pkg.publishConfig.tag
  }
}

module.exports.PublishCommand = PublishCommand;
<|endoftext|>


<commit_before>/**
 * @file anime command
 * @author Sankarsan Kampa (a.k.a k3rn31p4nic)
 * @license GPL-3.0
 */

const Kitsu = xrequire('kitsu/node');
const kitsu = new Kitsu();

exports.exec = async (Bastion, message, args) => {
  try {
    if (!args.name) {
      /**
      * The command was ran with invalid parameters.
      * @fires commandUsage
      */
      return Bastion.emit('commandUsage', message, this.help);
    }

    let { data: anime } = await kitsu.fetch('anime', {
      filter: {
        text: args.name
      },
      fields: {
        anime: 'titles,slug,synopsis,startDate,endDate,ageRating,ageRatingGuide,nsfw,posterImage'
      }
    });
    anime = anime[0];

    if (anime) {
      message.channel.send({
        embed: {
          color: Bastion.colors.BLUE,
          title: Object.values(anime.titles)[0],
          url: `https://kitsu.io/anime/${anime.slug}`,
          description: anime.synopsis,
          fields: [
            {
              name: 'Status',
              value: anime.endDate ? 'Finished' : 'Airing',
              inline: true
            },
            {
              name: 'Aired',
              value: anime.endDate ? `${anime.startDate} - ${anime.endDate}` : `${anime.startDate} - Present`,
              inline: true
            },
            {
              name: 'Rating',
              value: `${anime.ageRating} - ${anime.ageRatingGuide} ${anime.nsfw ? '[NSFW]' : ''}`,
              inline: true
            }
          ],
          image: {
            url: anime.posterImage.original
          },
          footer: {
            text: 'Powered by Kitsu'
          }
        }
      }).catch(e => {
        Bastion.log.error(e);
      });
    }
    else {
      return Bastion.emit('error', '', Bastion.i18n.error(message.guild.language, 'notFound', 'anime'), message.channel);
    }
  }
  catch (e) {
    Bastion.log.error(e);
  }
};

exports.config = {
  aliases: [],
  enabled: true,
  argsDefinitions: [
    { name: 'name', type: String, multiple: true, defaultOption: true }
  ]
};

exports.help = {
  name: 'anime',
  description: 'Searches for the details of an Anime.',
  botPermission: '',
  userTextPermission: '',
  userVoicePermission: '',
  usage: 'anime <Anime Name>',
  example: [ 'anime One Piece' ]
};
<commit_msg>commands: use BWAPI for anime command<commit_after>/**
 * @file anime command
 * @author Sankarsan Kampa (a.k.a k3rn31p4nic)
 * @license GPL-3.0
 */

exports.exec = async (Bastion, message, args) => {
  try {
    if (!args.name) {
      /**
      * The command was ran with invalid parameters.
      * @fires commandUsage
      */
      return Bastion.emit('commandUsage', message, this.help);
    }

    let anime = await Bastion.methods.makeBWAPIRequest(`/kitsu/anime?name=${args.name}`);
    anime = anime[0];

    if (anime) {
      message.channel.send({
        embed: {
          color: Bastion.colors.BLUE,
          title: Object.values(anime.titles)[0],
          url: `https://kitsu.io/anime/${anime.slug}`,
          description: anime.synopsis,
          fields: [
            {
              name: 'Status',
              value: anime.endDate ? 'Finished' : 'Airing',
              inline: true
            },
            {
              name: 'Aired',
              value: anime.endDate ? `${anime.startDate} - ${anime.endDate}` : `${anime.startDate} - Present`,
              inline: true
            },
            {
              name: 'Rating',
              value: `${anime.ageRating} - ${anime.ageRatingGuide} ${anime.nsfw ? '[NSFW]' : ''}`,
              inline: true
            }
          ],
          image: {
            url: anime.posterImage.original
          },
          footer: {
            text: 'Powered by Kitsu'
          }
        }
      }).catch(e => {
        Bastion.log.error(e);
      });
    }
    else {
      return Bastion.emit('error', '', Bastion.i18n.error(message.guild.language, 'notFound', 'anime'), message.channel);
    }
  }
  catch (e) {
    Bastion.log.error(e);
  }
};

exports.config = {
  aliases: [],
  enabled: true,
  argsDefinitions: [
    { name: 'name', type: String, multiple: true, defaultOption: true }
  ]
};

exports.help = {
  name: 'anime',
  description: 'Searches for the details of an Anime.',
  botPermission: '',
  userTextPermission: '',
  userVoicePermission: '',
  usage: 'anime <Anime Name>',
  example: [ 'anime One Piece' ]
};
<|endoftext|>


<commit_before>function Heap() {
	this.heap = [undefined];
}

Heap.prototype.add = function(data) {
	var lastPositionIndex = this.heap.push(data) - 1;
	this._bubbleUp(lastPositionIndex);
};

Heap.prototype.delete = function() {
	var lastPositionIndex = this.heap.length - 1;

	this.heap[1] = this.heap[lastPositionIndex];
	this.heap.splice(this.heap.length - 1);

	this._bubbleDown();
};

Heap.prototype.top = function() {
	return this.heap[1];
};

Heap.prototype._swap = function(lastPositionIndex, parentIndex) {
	var temp = this.heap[lastPositionIndex];
	this.heap[lastPositionIndex] = this.heap[parentIndex];
	this.heap[parentIndex] = temp;
};

Heap.prototype._bubbleUp = function(lastPositionIndex) {
	var lastData = this.heap[lastPositionIndex],
		parentIndex = Math.floor(lastPositionIndex / 2),
		temp;

	while (lastData > this.heap[parentIndex]) {
		this._swap(lastPositionIndex, parentIndex);
		lastPositionIndex = parentIndex;
		parentIndex = Math.floor(temp / 2);
	}
};

Heap.prototype._bubbleDown = function() {
	var index = 1,
		firstData = this.heap[index];

	while (this._hasChild(index) && this._lesserThanAnyChild(index)) {
		this._swapBiggerChild(index);
	}
};

Heap.prototype._hasChild = function(index) {
	return this.heap[index * 2] || this.heap[(index * 2) + 1];
};

Heap.prototype._lesserThanAnyChild = function(index) {
	var firstChild = this.heap[index * 2],
		parent = this.heap[index];
		secondChild = this.heap[(index * 2) + 1];

	return parent < firstChild || parent < secondChild;
};

Heap.prototype._swapBiggerChild = function(index) {
	var firstChildIndex = index * 2,
		firstChild = this.heap[firstChildIndex],
		secondChildIndex = (index * 2) + 1,
		secondChild = this.heap[secondChildIndex];

		firstChild > secondChild ?
			this._swap(firstChildIndex, index) :
			this._swap(secondChildIndex, index);
};

Heap.prototype.print = function() {
	console.log(this.heap.slice(1));
};

exports.Heap = Heap;
<commit_msg>Change Heap name to MaxHeap.<commit_after>function MaxHeap() {
	this.heap = [undefined];
}

MaxHeap.prototype.add = function(data) {
	var lastPositionIndex = this.heap.push(data) - 1;
	this._bubbleUp(lastPositionIndex);
};

MaxHeap.prototype.delete = function() {
	var lastPositionIndex = this.heap.length - 1;

	this.heap[1] = this.heap[lastPositionIndex];
	this.heap.splice(this.heap.length - 1);

	this._bubbleDown();
};

MaxHeap.prototype.top = function() {
	return this.heap[1];
};

MaxHeap.prototype._swap = function(lastPositionIndex, parentIndex) {
	var temp = this.heap[lastPositionIndex];
	this.heap[lastPositionIndex] = this.heap[parentIndex];
	this.heap[parentIndex] = temp;
};

MaxHeap.prototype._bubbleUp = function(lastPositionIndex) {
	var lastData = this.heap[lastPositionIndex],
		parentIndex = Math.floor(lastPositionIndex / 2),
		temp;

	while (lastData > this.heap[parentIndex]) {
		this._swap(lastPositionIndex, parentIndex);
		lastPositionIndex = parentIndex;
		parentIndex = Math.floor(temp / 2);
	}
};

MaxHeap.prototype._bubbleDown = function() {
	var index = 1,
		firstData = this.heap[index];

	while (this._hasChild(index) && this._lesserThanAnyChild(index)) {
		this._swapBiggerChild(index);
	}
};

MaxHeap.prototype._hasChild = function(index) {
	return this.heap[index * 2] || this.heap[(index * 2) + 1];
};

MaxHeap.prototype._lesserThanAnyChild = function(index) {
	var firstChild = this.heap[index * 2],
		parent = this.heap[index];
		secondChild = this.heap[(index * 2) + 1];

	return parent < firstChild || parent < secondChild;
};

MaxHeap.prototype._swapBiggerChild = function(index) {
	var firstChildIndex = index * 2,
		firstChild = this.heap[firstChildIndex],
		secondChildIndex = (index * 2) + 1,
		secondChild = this.heap[secondChildIndex];

		firstChild > secondChild ?
			this._swap(firstChildIndex, index) :
			this._swap(secondChildIndex, index);
};

MaxHeap.prototype.print = function() {
	console.log(this.heap.slice(1));
};

exports.MaxHeap = MaxHeap;
<|endoftext|>


<commit_before>import Manager from './Manager';

class TemplatesManager extends Manager {
  /**
  * Imports a template into a project.
  * @param {number} project_id
  * @param {???} file
  * @param {Object} params
  * @return {Promise}
  */
  import_into_project(project_id, file, params) {
    const args = Object.assign( {}, params, { project_id });
    const files = { file };
    return this.api.post('templates/import_into_project', args, files);
  }

  /**
  * Exports a template as a file.
  * @param {number} project_id
  * @param {Object} params
  * @return {Promise}
  */
  export_as_file(project_id, params) {
    const args = Object.assign( {}, params, { project_id });
    return this.api.post('templates/export_as_file', args);
  }

  /**
  * Exports a template as a URL.
  * @param {number} project_id
  * @param {Object} params
  * @return {Promise}
  */
  export_as_url(project_id, params) {
    const args = Object.assign( {}, params, { project_id });
    return this.api.post('templates/export_as_url', args);
  }
}

export default TemplatesManager;
<commit_msg>make template manager to set its own request Content-Type as needed<commit_after>import Manager from './Manager';

class TemplatesManager extends Manager {
  /**
  * Imports a template into a project.
  * @param {number} project_id
  * @param {???} file
  * @param {Object} params
  * @return {Promise}
  */
  import_into_project(project_id, file, params) {
    const args = Object.assign( {}, params, { project_id, file });
    const headers = { 'Content-Type': 'application/x-www-form-urlencoded' }
    return this.api.post('templates/import_into_project', args, files, headers);
  }

  /**
  * Exports a template as a file.
  * @param {number} project_id
  * @param {Object} params
  * @return {Promise}
  */
  export_as_file(project_id, params) {
    const args = Object.assign( {}, params, { project_id });
    return this.api.post('templates/export_as_file', args);
  }

  /**
  * Exports a template as a URL.
  * @param {number} project_id
  * @param {Object} params
  * @return {Promise}
  */
  export_as_url(project_id, params) {
    const args = Object.assign( {}, params, { project_id });
    return this.api.post('templates/export_as_url', args);
  }
}

export default TemplatesManager;
<|endoftext|>


<commit_before>import React from 'react';
import {NativeModules} from 'react-native';
import {NavigationContext} from 'navigation-react';

class Scene extends React.Component {
    constructor(props) {
        super(props);
        this.state = {navigationEvent: null};
    }
    static defaultProps = {
        crumb: 0
    }
    static getDerivedStateFromProps(props) {
        var {crumb, navigationEvent} = props;
        var {state, crumbs} = navigationEvent.stateNavigator.stateContext;
        return (state && crumbs.length === crumb) ? {navigationEvent} : null;
    }
    componentDidMount() {
        var {crumb, navigationEvent: {stateNavigator}} = this.props;
        if (!crumb)
            stateNavigator.onNavigate(this.renderMotion);
    }
    shouldComponentUpdate(props, state) {
        return state.navigationEvent === props.navigationEvent;
    }
    componentWillUnmount() {
        var {crumb, navigationEvent: {stateNavigator}} = this.props;
        if (!crumb)
            stateNavigator.offNavigate(this.renderMotion);
    }
    renderMotion(oldState, state, data, asyncData, {crumbs}) {
        NativeModules.NavigationMotion.render(crumbs.length);
    }
    render() {
        var {navigationEvent} = this.state;
        if (!navigationEvent) return null;
        var {state, data} = navigationEvent.stateNavigator.stateContext;
        return (
            <NavigationContext.Provider value={navigationEvent}>
                {state.renderScene(data)}
            </NavigationContext.Provider>
        );
    }
}

export default props => (
    <NavigationContext.Consumer>
        {(navigationEvent) => <Scene navigationEvent={navigationEvent} {...props} />}
    </NavigationContext.Consumer>
)
<commit_msg>Underscore'd to prevent greying out<commit_after>import React from 'react';
import {NativeModules} from 'react-native';
import {NavigationContext} from 'navigation-react';

class Scene extends React.Component {
    constructor(props) {
        super(props);
        this.state = {navigationEvent: null};
    }
    static defaultProps = {
        crumb: 0
    }
    static getDerivedStateFromProps(props) {
        var {crumb, navigationEvent} = props;
        var {state, crumbs} = navigationEvent.stateNavigator.stateContext;
        return (state && crumbs.length === crumb) ? {navigationEvent} : null;
    }
    componentDidMount() {
        var {crumb, navigationEvent: {stateNavigator}} = this.props;
        if (!crumb)
            stateNavigator.onNavigate(this.renderMotion);
    }
    shouldComponentUpdate(props, state) {
        return state.navigationEvent === props.navigationEvent;
    }
    componentWillUnmount() {
        var {crumb, navigationEvent: {stateNavigator}} = this.props;
        if (!crumb)
            stateNavigator.offNavigate(this.renderMotion);
    }
    renderMotion(_oldState, _state, _data, _asyncData, {crumbs}) {
        NativeModules.NavigationMotion.render(crumbs.length);
    }
    render() {
        var {navigationEvent} = this.state;
        if (!navigationEvent) return null;
        var {state, data} = navigationEvent.stateNavigator.stateContext;
        return (
            <NavigationContext.Provider value={navigationEvent}>
                {state.renderScene(data)}
            </NavigationContext.Provider>
        );
    }
}

export default props => (
    <NavigationContext.Consumer>
        {(navigationEvent) => <Scene navigationEvent={navigationEvent} {...props} />}
    </NavigationContext.Consumer>
)
<|endoftext|>


<commit_before>(function(module, require, window, document, undefined){
"use strict";

require = require || requireShim;
var React = window.React || require("react");
var ReactTools = window.JSXTransformer || require("react-tools");
var objectKeys = Object.keys || objectKeysShim;
var selfClosingTags = ["area","base","br","col","command","embed","hr","img","input","keygen","link","meta","param","source","track","wbr"];

///////////////////////////////////////////////////////////////////////////////
// Export / Attach / Public

module.exports = mount;
if(typeof window.React === "object") window.React.mount = mount;
if(typeof define === "function" && define.amd) define(function(){return mount});

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// html to jsx (More info: https://facebook.github.io/react/docs/tags-and-attributes.html)

function HTMLtoJSX(str){
  str = str
    // transform html comments to js comments
    .replace(/<!--((?:\s|.)*)-->/g,"")
    // .replace(/<!--/g,"{{\\*")
    // .replace(/-->/g,"*\\}}")

    // class attribute to className
    .replace(/(<(?:[^>"']|".*"|'.*')*)class(\s*=(?:[^>"']|".*"|'.*')*>)/ig, "$1"+"className"+"$2")
    // for attribute to htmlFor
    .replace(/(<(?:[^>"']|".*"|'.*')*)for(\s*=(?:[^>"']|".*"|'.*')*>)/ig, "$1"+"htmlFor"+"$2");

  // "selfclose" self closing tags: "…>" to "…/>"
  for(var k=0; k<selfClosingTags.length; k++) {
    str = str.replace(new RegExp("(<"+selfClosingTags[k]+"(?:[^>\"']|\".*\"|'.*')*)\/?>","ig"),"$1"+" />");
  }

  // Remove auto-quotes around {expressions}
  str = str
    .replace(/<(?:[^>\"']|\".*\"|'.*')*=(?:[^>\"']|\".*\"|'.*')*>/g, function(match){
      return match.replace(/(?:"|')(\{.*?\})(?:"|')/g,"$1");
    });

  // Transform style attribute string ("color:red; background-image:url()") to object ({{color:'red', backgroundImage:'url()'}})
  str = str.replace(/(<(?:[^>"']|".*"|'.*')*style\s*=\s*)((?:"(?:[^"]|\s)*")|(?:'(?:[^']|\s)*'))((?:[^>"']|".*"|'.*')*>)/ig, function(match, start, style, end){
    var styles = "";
    style
      .slice(1,-1)
      .replace(/(&quot)|'/g,'"')
      .replace(/\s*([^:;]*)\s*:\s*((?:[^;"]|".*")*)\s*;?/g, function(match, key, value){

        // transform key to camelCase
        key = key.replace(/-([^-]*)/g, function(match, part){
          if(part === "ms") return part;
          return part.charAt(0).toUpperCase()+part.slice(1);
        });

        styles+=key+":'"+value+"',";

        return match;
      });

    return start+"{{"+styles.slice(0,-1)+"}}"+end;
  });

  return str;
}

///////////////////////////////////////////////////////////////////////////////

function isDOMElement(o) {
  return (
    typeof HTMLElement === "object" ?
      o instanceof HTMLElement : //DOM2
      o && typeof o === "object" && o !== null && o.nodeType === 1 && typeof o.nodeName==="string"
  );
}

///////////////////////////////////////////////////////////////////////////////

function mount(context, tags, data){
  // Normalize Input
  if(!isDOMElement(context)){
    data = tags;
    tags = context;
    context =  document.body;
  }
  mountTags(context, tags, data || {});
}

///////////////////////////////////////////////////////////////////////////////

function mountTag(tag, tags, data) {
  var str = tag.outerHTML;
  var keys = objectKeys(tags);

  // transform tagnames to random strings starting with "A" (Aj4awwubx1or);
  var key, reactTags = {}, reactKeys=[];
  for(var i = 0; i<keys.length; i++) {
    key = "A"+Math.random().toString(36).substring(2)+Math.random().toString(36).substring(2);
    reactTags[key] = tags[keys[i]];
    reactKeys.push(key);
    str = str
      .replace(new RegExp("(<\/?)"+keys[i], "ig"), "$1"+key)
  }

  var jsx = HTMLtoJSX(str);

  // console.log(jsx);

  // replace data variables (key) with corrected variables (data['key'])
  for(key in data) {
    jsx = jsx
      .replace(/<(?:[^>\"']|\".*\"|'.*')*=(?:[^>\"']|\".*\"|'.*')*>/g, function(match){
        return match.replace(new RegExp("=(\{"+key+"\})","g"),"={data['"+key+"']}");
      });
  }

  var component = ReactTools.transform(jsx);

  // replace component variables (ComponentName) with corrected variables (reactTags['ComponentName'])
  for(var i = 0; i<reactKeys.length; i++) {
    component = component
      .replace(new RegExp(reactKeys[i],"g"), "reactTags['"+reactKeys[i]+"']");
  }

  // Render JSX and transform it into HTML
  var tempElement = document.createElement('div');
  tempElement.innerHTML = React.renderToString(eval(component));

  // Replace tag with rendered HTML
  tag.parentNode.replaceChild(tempElement.childNodes[0], tag);
}

///////////////////////////////////////////////////////////////////////////////

function mountTags(context, tags, data){
  var keys = objectKeys(tags);

  // keys to uppercase to compare to nodeName
  for(var i=0; i<keys.length; i++) keys[i]=keys[i].toUpperCase();

  // if context is tag
  if(keys.indexOf(context.nodeName) >= 0) {
    mountTag(context, tags, data);
    return;
  };

  //Mount all top-level child tags within context
  var nodes, el, mount, n;
  for(var i=0; i<keys.length; i++) {
    nodes = context.getElementsByTagName(keys[i]);
    for(n=0; n<nodes.length; n++) {
      el = nodes[n];
      mount = true;
      while(el = el.parentNode) {
        if(keys.indexOf(el.nodeName) >= 0) {
          mount = false;
          break;
        }
        if(el === context) break;
      }
      if(mount) mountTag(nodes[n], tags, data);
    }
  }
}

///////////////////////////////////////////////////////////////////////////////

function objectKeysShim(o){
  var keys = [];
  for(key in o) keys.push(key);
  return keys;
}

///////////////////////////////////////////////////////////////////////////////

function requireShim(required){
  throw "react-mount: Error - React and JSXTransformer/react-tools are required for react-mount to work";
}

})(typeof module === "object" ? module : {}, typeof require === "function" ? require : undefined, window, document);
<commit_msg>comment handling<commit_after>(function(module, require, window, document, undefined){
"use strict";

require = require || requireShim;
var React = window.React || require("react");
var ReactTools = window.JSXTransformer || require("react-tools");
var objectKeys = Object.keys || objectKeysShim;
var selfClosingTags = ["area","base","br","col","command","embed","hr","img","input","keygen","link","meta","param","source","track","wbr"];

///////////////////////////////////////////////////////////////////////////////
// Export / Attach / Public

module.exports = mount;
if(typeof window.React === "object") window.React.mount = mount;
if(typeof define === "function" && define.amd) define(function(){return mount});

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// html to jsx (More info: https://facebook.github.io/react/docs/tags-and-attributes.html)

function htmlToJsx(str){
  str = str
    // remove html comments.. doesn't work in safari.. wtf
    //.replace(/<!--((?:\s|.)*)-->/g,"")
    // class attribute to className
    .replace(/(<(?:[^>"']|".*"|'.*')*)class(\s*=(?:[^>"']|".*"|'.*')*>)/ig, "$1"+"className"+"$2")
    // for attribute to htmlFor
    .replace(/(<(?:[^>"']|".*"|'.*')*)for(\s*=(?:[^>"']|".*"|'.*')*>)/ig, "$1"+"htmlFor"+"$2");

  // "selfclose" self closing tags: "…>" to "…/>"
  for(var k=0; k<selfClosingTags.length; k++) {
    str = str.replace(new RegExp("(<"+selfClosingTags[k]+"(?:[^>\"']|\".*\"|'.*')*)\/?>","ig"),"$1"+" />");
  }

  // Remove auto-quotes around {expressions}
  str = str
    .replace(/<(?:[^>\"']|\".*\"|'.*')*=(?:[^>\"']|\".*\"|'.*')*>/g, function(match){
      return match.replace(/(?:"|')(\{.*?\})(?:"|')/g,"$1");
    });

  // Transform style attribute string ("color:red; background-image:url()") to object ({{color:'red', backgroundImage:'url()'}})
  str = str.replace(/(<(?:[^>"']|".*"|'.*')*style\s*=\s*)((?:"(?:[^"]|\s)*")|(?:'(?:[^']|\s)*'))((?:[^>"']|".*"|'.*')*>)/ig, function(match, start, style, end){
    var styles = "";
    style
      .slice(1,-1)
      .replace(/(&quot)|'/g,'"')
      .replace(/\s*([^:;]*)\s*:\s*((?:[^;"]|".*")*)\s*;?/g, function(match, key, value){

        // transform key to camelCase
        key = key.replace(/-([^-]*)/g, function(match, part){
          if(part === "ms") return part;
          return part.charAt(0).toUpperCase()+part.slice(1);
        });

        styles+=key+":'"+value+"',";

        return match;
      });

    return start+"{{"+styles.slice(0,-1)+"}}"+end;
  });

  return str;
}

///////////////////////////////////////////////////////////////////////////////

function isDOMElement(o) {
  return (
    typeof HTMLElement === "object" ?
      o instanceof HTMLElement : //DOM2
      o && typeof o === "object" && o !== null && o.nodeType === 1 && typeof o.nodeName==="string"
  );
}

///////////////////////////////////////////////////////////////////////////////

function mount(context, tags, data){
  // Normalize Input
  if(!isDOMElement(context)){
    data = tags;
    tags = context;
    context =  document.body;
  }
  mountTags(context, tags, data || {});
}

///////////////////////////////////////////////////////////////////////////////

function mountTag(tag, tags, data) {
  var str = tag.outerHTML;
  var keys = objectKeys(tags);

  // transform tagnames to random strings starting with "A" (Aj4awwubx1or);
  var key, reactTags = {}, reactKeys=[];
  for(var i = 0; i<keys.length; i++) {
    key = "A"+Math.random().toString(36).substring(2)+Math.random().toString(36).substring(2);
    reactTags[key] = tags[keys[i]];
    reactKeys.push(key);
    str = str
      .replace(new RegExp("(<\/?)"+keys[i], "ig"), "$1"+key)
  }

  // remove/disable comments
  str = str.replace(/<!--|-->/g,"");

  var jsx = htmlToJsx(str);

  // replace data variables (key) with corrected variables (data['key'])
  for(key in data) {
    jsx = jsx
      .replace(/<(?:[^>\"']|\".*\"|'.*')*=(?:[^>\"']|\".*\"|'.*')*>/g, function(match){
        return match.replace(new RegExp("=(\{"+key+"\})","g"),"={data['"+key+"']}");
      });
  }

  var component = ReactTools.transform(jsx);

  // replace component variables (ComponentName) with corrected variables (reactTags['ComponentName'])
  for(var i = 0; i<reactKeys.length; i++) {
    component = component
      .replace(new RegExp(reactKeys[i],"g"), "reactTags['"+reactKeys[i]+"']");
  }

  // Render JSX and transform it into HTML
  var tempElement = document.createElement('div');
  tempElement.innerHTML = React.renderToString(eval(component));

  // Replace tag with rendered HTML
  tag.parentNode.replaceChild(tempElement.childNodes[0], tag);
}

///////////////////////////////////////////////////////////////////////////////

function mountTags(context, tags, data){
  var keys = objectKeys(tags);

  // keys to uppercase to compare to nodeName
  for(var i=0; i<keys.length; i++) keys[i]=keys[i].toUpperCase();

  // if context is tag
  if(keys.indexOf(context.nodeName) >= 0) {
    mountTag(context, tags, data);
    return;
  };

  //Mount all top-level child tags within context
  var nodes, el, mount, n;
  for(var i=0; i<keys.length; i++) {
    nodes = context.getElementsByTagName(keys[i]);
    for(n=0; n<nodes.length; n++) {
      el = nodes[n];
      mount = true;
      while(el = el.parentNode) {
        if(keys.indexOf(el.nodeName) >= 0) {
          mount = false;
          break;
        }
        if(el === context) break;
      }
      if(mount) mountTag(nodes[n], tags, data);
    }
  }
}

///////////////////////////////////////////////////////////////////////////////

function objectKeysShim(o){
  var keys = [];
  for(key in o) keys.push(key);
  return keys;
}

///////////////////////////////////////////////////////////////////////////////

function requireShim(required){
  throw "react-mount: Error - React and JSXTransformer/react-tools are required for react-mount to work";
}

})(typeof module === "object" ? module : {}, typeof require === "function" ? require : undefined, window, document);
<|endoftext|>


<commit_before>function ddg_spice_bbc(api_result) {
    var query = DDG.get_query(),
        broadcasts = api_result.schedule.day.broadcasts,
        programmes = [],
        now = new Date();

    if (query.indexOf("night") != -1)
        now.setHours(18);

    var date = api_result.schedule.day.date,
        fulldate = new Date(date),
        date_round = 1000 * 60 * 60 * 24,
        inPast = +fulldate < Math.floor(+now / date_round)*date_round;

    for (var i=0; i<broadcasts.length; i++) {
        var end = new Date(broadcasts[i].end);
        if (end > now || inPast)
            programmes.push(broadcasts[i]);
    }

    Spice.render({
        data           : api_result.schedule,
        header1        : api_result.schedule.service.title + (api_result.schedule.service.outlet ? " "+api_result.schedule.service.outlet.title : "") + " (TV Schedule for "+date+")",
        source_url     : "http://www.bbc.co.uk/"+api_result.schedule.service.key+"/programmes/schedules/"+(api_result.schedule.service.outlet ? api_result.schedule.service.outlet.key+"/" : "")+api_result.schedule.day.date.split("-").join("/"),
        source_name    : 'BBC',
        template_frame : "carousel",
        spice_name     : "bbc",
        template_options: {
            li_width        : 120,
            li_height       : 105,
            items           : programmes,
            template_item   : "bbc",
            template_detail : "bbc_details"
        },
        force_big_header : true
    });
}

/*
 * time
 *
 * Find the start and end of a programme and format appropriately
 */
Handlebars.registerHelper("time", function() {
    function to_str (num) {
        if (num == 0) {
             return "00";
        }
        var str = (num|0)+"";
        if (str.length < 2) {
            str = "0" + str;
        }
        return str;
    };

    var start = new Date(this.start),
        end = new Date(this.end);

    return to_str(start.getHours()) + ":" + to_str(start.getMinutes()) + " - " + to_str(end.getHours()) + ":" + to_str(end.getMinutes());
});

/*
 * duration
 *
 * Find the duration of a programme and return it
 */
Handlebars.registerHelper("duration", function() {
    var pluralise = function(n) {
        return n > 1 ? "s" : "";
    };
    var dur = this.duration,
        hours = Math.floor(dur / (60 * 60));

    dur -= hours * 60 * 60;
    var minutes = Math.floor(dur / 60);
    this.duration -= minutes * 60;
    if (hours > 0 && minutes > 0) {
        return hours + " hour"+pluralise(hours)+", "+minutes+" min"+pluralise(minutes);
    } else if (hours > 0 && minutes == 0) {
        return hours + " hour"+pluralise(hours);
    } else {
        return minutes+" min"+pluralise(minutes);
    }
});

// Find the series URL and return it, or if it is not part of a series return the normal url
Handlebars.registerHelper("programme_url", function() {
    return "http://www.bbc.co.uk/programmes/"+(this.programme.programme ? this.programme.programme : this.programme).pid;
});

// Find the programme image and return it
Handlebars.registerHelper("image", function() {
    return "http://ichef.bbci.co.uk/images/ic/272x153/" + (this.programme.image ? this.programme.image.pid :  "legacy/episode/"+this.programme.pid) + ".jpg";
});

// Check if original air date is before today
Handlebars.registerHelper("checkAirDate", function(options) {
    var d = new Date(this.programme.first_broadcast_date),
        now = new Date();

    if (d < now) {
        return options.fn(this);
    } else {
        return false;
    }
});

// Find the programme's initial broadcast date/time and return it
Handlebars.registerHelper("initial_broadcast", function() {
    var aired = DDG.getDateFromString(this.programme.first_broadcast_date),
        days = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'],
        months = [ 'January','February','March','April','May','June','July','August','September','October','November','December'];

    return days[aired.getDay()] + ", " + months[aired.getMonth()] + " " + aired.getDate() + ", " + aired.getFullYear()
});

// Remove all the episode info from the subtitle
Handlebars.registerHelper("pretty_subtitle", function() {
    var seriesInfo = /series [0-9]+/ig,
        episodeInfo = /episode [0-9]+/ig,
        junk = /, ?/ig,
        subtitle = this.programme.display_titles.subtitle + "";

    subtitle = subtitle.replace(seriesInfo, "").replace(episodeInfo, "").replace(junk, "");
    subtitle = $.trim(subtitle);

    if (this.programme.type == "episode" && this.programme.position !== null) {
        subtitle = subtitle.replace(this.programme.programme.title, "");
        subtitle = "Episode "+this.programme.position+(subtitle.length > 0 ? " - " + subtitle : "");
    }

    if (this.programme.programme.type == "series" && this.programme.programme.position !== null) {
        subtitle = this.programme.programme.title+", "+subtitle;
    }

    return subtitle;
});<commit_msg>BBC - Use standard time rather than Military<commit_after>function ddg_spice_bbc(api_result) {
    var query = DDG.get_query(),
        broadcasts = api_result.schedule.day.broadcasts,
        programmes = [],
        now = new Date();

    if (query.indexOf("night") != -1)
        now.setHours(18);

    var date = api_result.schedule.day.date,
        fulldate = new Date(date),
        date_round = 1000 * 60 * 60 * 24,
        inPast = +fulldate < Math.floor(+now / date_round)*date_round;

    for (var i=0; i<broadcasts.length; i++) {
        var end = new Date(broadcasts[i].end);
        if (end > now || inPast)
            programmes.push(broadcasts[i]);
    }

    Spice.render({
        data           : api_result.schedule,
        header1        : api_result.schedule.service.title + (api_result.schedule.service.outlet ? " "+api_result.schedule.service.outlet.title : "") + " (TV Schedule for "+date+")",
        source_url     : "http://www.bbc.co.uk/"+api_result.schedule.service.key+"/programmes/schedules/"+(api_result.schedule.service.outlet ? api_result.schedule.service.outlet.key+"/" : "")+api_result.schedule.day.date.split("-").join("/"),
        source_name    : 'BBC',
        template_frame : "carousel",
        spice_name     : "bbc",
        template_options: {
            li_width        : 120,
            li_height       : 105,
            items           : programmes,
            template_item   : "bbc",
            template_detail : "bbc_details"
        },
        force_big_header : true
    });
}

/*
 * time
 *
 * Find the start and end of a programme and format appropriately
 */
Handlebars.registerHelper("time", function() {
    var start = new Date(this.start).toTimeString(),
        end = new Date(this.end).toTimeString();

    function standard_time(time) {
        var hour = time.getHours() % 12;
        if (hour == 0) hour = 1;

        var min = time.getMinutes() > 10 ? time.getMinutes() : "0" + time.getMinutes();

        return hour + ":" + min + (time.getHours() > 12 ? "PM" : "AM");
    }

    return standard_time(start) + " - " + standard_time(end);

});

/*
 * duration
 *
 * Find the duration of a programme and return it
 */
Handlebars.registerHelper("duration", function() {
    var pluralise = function(n) {
        return n > 1 ? "s" : "";
    };
    var dur = this.duration,
        hours = Math.floor(dur / (60 * 60));

    dur -= hours * 60 * 60;
    var minutes = Math.floor(dur / 60);
    this.duration -= minutes * 60;
    if (hours > 0 && minutes > 0) {
        return hours + " hour"+pluralise(hours)+", "+minutes+" min"+pluralise(minutes);
    } else if (hours > 0 && minutes == 0) {
        return hours + " hour"+pluralise(hours);
    } else {
        return minutes+" min"+pluralise(minutes);
    }
});

// Find the series URL and return it, or if it is not part of a series return the normal url
Handlebars.registerHelper("programme_url", function() {
    return "http://www.bbc.co.uk/programmes/"+(this.programme.programme ? this.programme.programme : this.programme).pid;
});

// Find the programme image and return it
Handlebars.registerHelper("image", function() {
    return "http://ichef.bbci.co.uk/images/ic/272x153/" + (this.programme.image ? this.programme.image.pid :  "legacy/episode/"+this.programme.pid) + ".jpg";
});

// Check if original air date is before today
Handlebars.registerHelper("checkAirDate", function(options) {
    var d = new Date(this.programme.first_broadcast_date),
        now = new Date();

    if (d < now) {
        return options.fn(this);
    } else {
        return false;
    }
});

// Find the programme's initial broadcast date/time and return it
Handlebars.registerHelper("initial_broadcast", function() {
    var aired = DDG.getDateFromString(this.programme.first_broadcast_date),
        days = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'],
        months = [ 'January','February','March','April','May','June','July','August','September','October','November','December'];

    return days[aired.getDay()] + ", " + months[aired.getMonth()] + " " + aired.getDate() + ", " + aired.getFullYear()
});

// Remove all the episode info from the subtitle
Handlebars.registerHelper("pretty_subtitle", function() {
    var seriesInfo = /series [0-9]+/ig,
        episodeInfo = /episode [0-9]+/ig,
        junk = /, ?/ig,
        subtitle = this.programme.display_titles.subtitle + "";

    subtitle = subtitle.replace(seriesInfo, "").replace(episodeInfo, "").replace(junk, "");
    subtitle = $.trim(subtitle);

    if (this.programme.type == "episode" && this.programme.position !== null) {
        subtitle = subtitle.replace(this.programme.programme.title, "");
        subtitle = "Episode "+this.programme.position+(subtitle.length > 0 ? " - " + subtitle : "");
    }

    if (this.programme.programme.type == "series" && this.programme.programme.position !== null) {
        subtitle = this.programme.programme.title+", "+subtitle;
    }

    return subtitle;
});<|endoftext|>


<commit_before>(function (env) {
    "use strict";
    env.ddg_spice_npm = function(api_result){

        if (!api_result || api_result.error) {
            return Spice.failed('npm');
        }

        Spice.add({
            id: "npm",
            name: "Software",
            data: api_result,
            meta: {
                sourceName: "npmjs",
                sourceUrl: 'http://npmjs.org/package/' + api_result.name
            },
            normalize: function(item) {
                var boxData = [{heading: 'Package Information:'}];

                if (item.author) {
                    boxData.push({
                        label: "Author",
                        value: item.author.name
                    });
                }

                if (item.homepage) {
                    boxData.push({
                        label: "Project Homepage",
                        value: item.homepage,
                        url: item.homepage
                    });
                }

                if (item.license) {
                    boxData.push({
                        label: "License",
                        value: item.license
                    });
                }

                if (item.repository) {
                    boxData.push({
                        label: "Repository",
                        value: item.repository.url
                    });
                }

                if (item.engines) {
                    boxData.push({
                        label: "Engines",
                        value: item.engines.node
                    });
                }

                if (item.dist) {
                    boxData.push({
                        label: "Source",
                        value: item.dist.tarball,
                        url: item.dist.tarball
                    });
                }

                if (item.dependencies) {
                    var dependencies = $.map(item.dependencies, function(val, key) {
                        return key + " (" + val + ")";
                    });

                    if (dependencies.length > 0) {
                        boxData.push({
                            label: "Dependencies",
                            value: dependencies.join(", ")
                        });
                    }
                }

                return {
                    title: item.name + " " + item.version,
                    subtitle: item.description,
                    infoboxData: boxData,
                }
            },

            templates: {
                group: 'text',
                options: {
                    content: Spice.npm.content,
                    moreAt: true
                }
            }
        });
    };
}(this));
<commit_msg>NPM: Add link to tonicdev<commit_after>(function (env) {
    "use strict";
    env.ddg_spice_npm = function(api_result){

        if (!api_result || api_result.error) {
            return Spice.failed('npm');
        }

        Spice.add({
            id: "npm",
            name: "Software",
            data: api_result,
            meta: {
                sourceName: "npmjs",
                sourceUrl: 'http://npmjs.org/package/' + api_result.name
            },
            normalize: function(item) {
                var boxData = [{heading: 'Package Information:'}];

                if (item.author) {
                    boxData.push({
                        label: "Author",
                        value: item.author.name
                    });
                }

                if (item.homepage) {
                    boxData.push({
                        label: "Project Homepage",
                        value: item.homepage,
                        url: item.homepage
                    });
                }

                if (item.license) {
                    boxData.push({
                        label: "License",
                        value: item.license
                    });
                }

                if (item.repository) {
                    boxData.push({
                        label: "Repository",
                        value: item.repository.url
                    });
                }

                if (item.engines) {
                    boxData.push({
                        label: "Engines",
                        value: item.engines.node
                    });
                }

                if (item.dist) {
                    boxData.push({
                        label: "Source",
                        value: item.dist.tarball,
                        url: item.dist.tarball
                    });
                }

                if (item.dependencies) {
                    var dependencies = $.map(item.dependencies, function(val, key) {
                        return key + " (" + val + ")";
                    });

                    if (dependencies.length > 0) {
                        boxData.push({
                            label: "Dependencies",
                            value: dependencies.join(", ")
                        });
                    }
                }

                return {
                    title: item.name + " " + item.version,
                    subtitle: item.description,
                    infoboxData: boxData,
                }
            },

            templates: {
                group: 'text',
                options: {
                    content: Spice.npm.content,
                    moreAt: true,
                    moreText: {
                        href: 'https://tonicdev.com/npm/' + api_result.name,
                        text: 'Test ' + api_result.name + ' in your browser'
                    }
                }
            }
        });
    };
}(this));
<|endoftext|>


<commit_before>/* global google */
import _ from "lodash";

import {
  default as React,
  Component,
} from "react";

import Helmet from "react-helmet";

import {
  withGoogleMap,
  GoogleMap,
  Marker,
} from "../../lib";

/*
 * This is the modify version of:
 * https://developers.google.com/maps/documentation/javascript/examples/event-arguments
 *
 * Add  to your HTML to provide google.maps reference
 */
const GettingStartedGoogleMap = withGoogleMap(props => (
  <GoogleMap
    ref={props.onMapLoad}
    defaultZoom={13}
    defaultCenter={{ lat: 28.144107, lng: -15.431281 }}
    onClick={props.onMapClick}
  >
    {props.markers.map(marker => (
      <Marker
        {...marker}
        onRightClick={() => props.onMarkerRightClick(marker)}
      />
    ))}
  </GoogleMap>
));

export default class GettingStartedExample extends Component {

  state = {
    markers: [{
      position: {
        lat: 25.0112183,
        lng: 121.52067570000001,
      },
      key: `Taiwan`,
      defaultAnimation: 2,
    }],
  };

  handleMapLoad = this.handleMapLoad.bind(this);
  handleMapClick = this.handleMapClick.bind(this);
  handleMarkerRightClick = this.handleMarkerRightClick.bind(this);

  handleMapLoad(map) {
    this._mapComponent = map;
    if (map) {
      console.log(map.getZoom());
    }
  }

  /*
   * This is called when you click on the map.
   * Go and try click now.
   */
  handleMapClick(event) {
    const nextMarkers = [
      ...this.state.markers,
      {
        position: event.latLng,
        defaultAnimation: 2,
        key: Date.now(), // Add a key property for: http://fb.me/react-warning-keys
      },
    ];
    this.setState({
      markers: nextMarkers,
    });

    if (nextMarkers.length === 3) {
      this.props.toast(
        `Right click on the marker to remove it`,
        `Also check the code!`
      );
    }
  }

  handleMarkerRightClick(targetMarker) {
    /*
     * All you modify is data, and the view is driven by data.
     * This is so called data-driven-development. (And yes, it's now in
     * web front end and even with google maps API.)
     */
    const nextMarkers = this.state.markers.filter(marker => marker !== targetMarker);
    this.setState({
      markers: nextMarkers,
    });
  }

  render() {
    return (
      <div style={{height: `100%`}}>
        <Helmet
          title="Getting Started"
        />
        <GettingStartedGoogleMap
          containerElement={
            <div style={{ height: `80%` }} />
          }
          mapElement={
            <div style={{ height: `100%` }} />
          }
          onMapLoad={this.handleMapLoad}
          onMapClick={this.handleMapClick}
          markers={this.state.markers}
          onMarkerRightClick={this.handleMarkerRightClick}
        />

    );
  }
}
<commit_msg>change center view<commit_after>/* global google */
import _ from "lodash";

import {
  default as React,
  Component,
} from "react";

import Helmet from "react-helmet";

import {
  withGoogleMap,
  GoogleMap,
  Marker,
} from "../../lib";

/*
 * This is the modify version of:
 * https://developers.google.com/maps/documentation/javascript/examples/event-arguments
 *
 * Add  to your HTML to provide google.maps reference
 */
const GettingStartedGoogleMap = withGoogleMap(props => (
  <GoogleMap
    ref={props.onMapLoad}
    defaultZoom={13}
    defaultCenter={{ lat: 28.114107, lng: -15.431281 }}
    onClick={props.onMapClick}
  >
    {props.markers.map(marker => (
      <Marker
        {...marker}
        onRightClick={() => props.onMarkerRightClick(marker)}
      />
    ))}
  </GoogleMap>
));

export default class GettingStartedExample extends Component {

  state = {
    markers: [{
      position: {
        lat: 25.0112183,
        lng: 121.52067570000001,
      },
      key: `Taiwan`,
      defaultAnimation: 2,
    }],
  };

  handleMapLoad = this.handleMapLoad.bind(this);
  handleMapClick = this.handleMapClick.bind(this);
  handleMarkerRightClick = this.handleMarkerRightClick.bind(this);

  handleMapLoad(map) {
    this._mapComponent = map;
    if (map) {
      console.log(map.getZoom());
    }
  }

  /*
   * This is called when you click on the map.
   * Go and try click now.
   */
  handleMapClick(event) {
    const nextMarkers = [
      ...this.state.markers,
      {
        position: event.latLng,
        defaultAnimation: 2,
        key: Date.now(), // Add a key property for: http://fb.me/react-warning-keys
      },
    ];
    this.setState({
      markers: nextMarkers,
    });

    if (nextMarkers.length === 3) {
      this.props.toast(
        `Right click on the marker to remove it`,
        `Also check the code!`
      );
    }
  }

  handleMarkerRightClick(targetMarker) {
    /*
     * All you modify is data, and the view is driven by data.
     * This is so called data-driven-development. (And yes, it's now in
     * web front end and even with google maps API.)
     */
    const nextMarkers = this.state.markers.filter(marker => marker !== targetMarker);
    this.setState({
      markers: nextMarkers,
    });
  }

  render() {
    return (
      <div style={{height: `100%`}}>
        <Helmet
          title="Getting Started"
        />
        <GettingStartedGoogleMap
          containerElement={
            <div style={{ height: `80%` }} />
          }
          mapElement={
            <div style={{ height: `100%` }} />
          }
          onMapLoad={this.handleMapLoad}
          onMapClick={this.handleMapClick}
          markers={this.state.markers}
          onMarkerRightClick={this.handleMarkerRightClick}
        />

    );
  }
}
<|endoftext|>


<commit_before>import React from 'react';
import {StyleSheet, View, ListView, Image, Text, TouchableWithoutFeedback, Animated} from 'react-native';
import {SharedElement} from 'navigation-react-native';
import ZoomShared from './ZoomShared';

export default class List extends React.Component {
  constructor(props, context) {
    super(props, context);
    this._rowAnimations = {};
    this.onNavigate = this.onNavigate.bind(this);
    this.url = props.stateNavigator.stateContext.url;
    const ds = new ListView.DataSource({
      rowHasChanged: (r1, r2) => r1 !== r2,
    });
    this.state = {dataSource: ds.cloneWithRows(props.places)};
  }
  shouldComponentUpdate(props) {
    return this.url === props.stateNavigator.stateContext.url;
  }
  componentDidMount() {
    this.props.stateNavigator.onNavigate(this.onNavigate);
  }
  componentWillUnmount() {
    this.props.stateNavigator.offNavigate(this.onNavigate);
  }
  onNavigate() {
    const {data, oldData, oldUrl} = this.props.stateNavigator.stateContext;
    const inverse = this.url !== oldUrl;
    const rowId = {...data, ...oldData}.rowId;
    const animations = [];
    const rowInt = parseInt(rowId, 10);
    for (let i = rowInt - 2; i <= rowInt + 2; i++) {
      if (i === rowInt) {
        continue;
      }
      const anim = this._rowAnimations[i];
      if (anim) {
        animations.push(
          Animated.timing(anim, {
            toValue: inverse ? 0 : i < rowInt ? -1 : 1,
            duration: 500,
            useNativeDriver: true,
          })
        );
      }
    }
    Animated.parallel(animations).start();
  }
  _renderRow = (rowData, sectionId, rowId) => {
    const {stateNavigator} = this.props;
    this._rowAnimations[rowId] = new Animated.Value(0);
    const rowStyle = {
      transform: [
        {
          translateY: this._rowAnimations[rowId].interpolate({
            inputRange: [-1, 0, 1],
            outputRange: [-200, 0, 200],
          }),
        },
      ],
    };
    return (
      <TouchableWithoutFeedback
        key={sectionId + rowId}
        onPress={this._onListItemPress.bind(this, rowData, sectionId, rowId)}>
        <Animated.View style={[styles.listItem, rowStyle]}>
          <SharedElement
            name={`image${rowId}`}
            data={{image: rowData.image}}
            stateNavigator={stateNavigator}>
            <View style={styles.listImage}>
              <Image
                source={rowData.image}
                style={{ flex: 1 }}
              />
            </View>
          </SharedElement>
          <SharedElement
            name={`label${rowId}`}
            data={{text: rowData.price}}
            stateNavigator={stateNavigator}>
            <View style={styles.priceLabel}>
              <Text style={styles.priceLabelText}>
                ${rowData.price}
              </Text>
            </View>
          </SharedElement>
        </Animated.View>
      </TouchableWithoutFeedback>
    );
  };
  _onListItemPress = (rowData, sectionId, rowId) => {
    const {stateNavigator} = this.props;
    if (this.url === stateNavigator.stateContext.url) {
      stateNavigator.navigate('detail', {rowId});
    }
  };
  render() {
    const {places, stateNavigator} = this.props;
    return (
      <View style={styles.container}>
        <ZoomShared stateNavigator={stateNavigator} />
        <ListView
          contentContainerStyle={styles.contentContainer}
          dataSource={this.state.dataSource}
          renderRow={this._renderRow}
        />
      </View>
    );
  }
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  contentContainer: {
    paddingTop: 40,
  },
  listItem: {
    paddingBottom: 15,
    alignSelf: 'center',
  },
  listImage: {
    height: 201,
  },
  priceLabel: {
    position: 'absolute',
    left: 0,
    bottom: 25,
    backgroundColor: 'rgba(0, 0, 0, .75)',
    paddingVertical: 7.5,
    paddingHorizontal: 10,
  },
  priceLabelText: {
    fontSize: 19,
    textAlign: 'right',
    color: 'white',
  },
});
<commit_msg>Prefixed with underscore for consistency<commit_after>import React from 'react';
import {StyleSheet, View, ListView, Image, Text, TouchableWithoutFeedback, Animated} from 'react-native';
import {SharedElement} from 'navigation-react-native';
import ZoomShared from './ZoomShared';

export default class List extends React.Component {
  constructor(props, context) {
    super(props, context);
    this._rowAnimations = {};
    this._onNavigate = this._onNavigate.bind(this);
    this.url = props.stateNavigator.stateContext.url;
    const ds = new ListView.DataSource({
      rowHasChanged: (r1, r2) => r1 !== r2,
    });
    this.state = {dataSource: ds.cloneWithRows(props.places)};
  }
  shouldComponentUpdate(props) {
    return this.url === props.stateNavigator.stateContext.url;
  }
  componentDidMount() {
    this.props.stateNavigator.onNavigate(this._onNavigate);
  }
  componentWillUnmount() {
    this.props.stateNavigator.offNavigate(this._onNavigate);
  }
  _onNavigate() {
    const {data, oldData, oldUrl} = this.props.stateNavigator.stateContext;
    const inverse = this.url !== oldUrl;
    const rowId = {...data, ...oldData}.rowId;
    const animations = [];
    const rowInt = parseInt(rowId, 10);
    for (let i = rowInt - 2; i <= rowInt + 2; i++) {
      if (i === rowInt) {
        continue;
      }
      const anim = this._rowAnimations[i];
      if (anim) {
        animations.push(
          Animated.timing(anim, {
            toValue: inverse ? 0 : i < rowInt ? -1 : 1,
            duration: 500,
            useNativeDriver: true,
          })
        );
      }
    }
    Animated.parallel(animations).start();
  }
  _renderRow = (rowData, sectionId, rowId) => {
    const {stateNavigator} = this.props;
    this._rowAnimations[rowId] = new Animated.Value(0);
    const rowStyle = {
      transform: [
        {
          translateY: this._rowAnimations[rowId].interpolate({
            inputRange: [-1, 0, 1],
            outputRange: [-200, 0, 200],
          }),
        },
      ],
    };
    return (
      <TouchableWithoutFeedback
        key={sectionId + rowId}
        onPress={this._onListItemPress.bind(this, rowData, sectionId, rowId)}>
        <Animated.View style={[styles.listItem, rowStyle]}>
          <SharedElement
            name={`image${rowId}`}
            data={{image: rowData.image}}
            stateNavigator={stateNavigator}>
            <View style={styles.listImage}>
              <Image
                source={rowData.image}
                style={{ flex: 1 }}
              />
            </View>
          </SharedElement>
          <SharedElement
            name={`label${rowId}`}
            data={{text: rowData.price}}
            stateNavigator={stateNavigator}>
            <View style={styles.priceLabel}>
              <Text style={styles.priceLabelText}>
                ${rowData.price}
              </Text>
            </View>
          </SharedElement>
        </Animated.View>
      </TouchableWithoutFeedback>
    );
  };
  _onListItemPress = (rowData, sectionId, rowId) => {
    const {stateNavigator} = this.props;
    if (this.url === stateNavigator.stateContext.url) {
      stateNavigator.navigate('detail', {rowId});
    }
  };
  render() {
    const {places, stateNavigator} = this.props;
    return (
      <View style={styles.container}>
        <ZoomShared stateNavigator={stateNavigator} />
        <ListView
          contentContainerStyle={styles.contentContainer}
          dataSource={this.state.dataSource}
          renderRow={this._renderRow}
        />
      </View>
    );
  }
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  contentContainer: {
    paddingTop: 40,
  },
  listItem: {
    paddingBottom: 15,
    alignSelf: 'center',
  },
  listImage: {
    height: 201,
  },
  priceLabel: {
    position: 'absolute',
    left: 0,
    bottom: 25,
    backgroundColor: 'rgba(0, 0, 0, .75)',
    paddingVertical: 7.5,
    paddingHorizontal: 10,
  },
  priceLabelText: {
    fontSize: 19,
    textAlign: 'right',
    color: 'white',
  },
});
<|endoftext|>