import pathExists from "path-exists";
import assert from "assert";
import path from "path";
import fs from "fs";
import normalize from "normalize-path";

import ChildProcessUtilities from "../src/ChildProcessUtilities";
import FileSystemUtilities from "../src/FileSystemUtilities";
import BootstrapCommand from "../src/commands/BootstrapCommand";
import exitWithCode from "./_exitWithCode";
import initFixture from "./_initFixture";
import stub from "./_stub";

import assertStubbedCalls from "./_assertStubbedCalls";

const STDIO_OPT = ["ignore", "pipe", "pipe"];

const resolveSymlink = (symlinkLocation) => {
  return path.resolve(path.dirname(symlinkLocation), fs.readlinkSync(symlinkLocation));
};

describe("BootstrapCommand", () => {

  describe("lifecycle scripts", () => {
    let testDir;

    beforeEach((done) => {
      testDir = initFixture("BootstrapCommand/lifecycle-scripts", done);
    });

    it("should run preinstall, postinstall and prepublish scripts", (done) => {
      const bootstrapCommand = new BootstrapCommand([], {});

      bootstrapCommand.runValidations();
      bootstrapCommand.runPreparations();

      bootstrapCommand.runCommand(exitWithCode(0, (err) => {
        if (err) return done(err);

        try {
          assert.ok(!pathExists.sync(path.join(testDir, "lerna-debug.log")), "lerna-debug.log should not exist");
          assert.ok(pathExists.sync(path.join(testDir, "packages", "package-preinstall", "did-preinstall")));
          assert.ok(pathExists.sync(path.join(testDir, "packages", "package-postinstall", "did-postinstall")));
          assert.ok(pathExists.sync(path.join(testDir, "packages", "package-prepublish", "did-prepublish")));
          done();
        } catch (err) {
          done(err);
        }
      }));
    });
  });

  describe("dependencies between packages in the repo", () => {
    let testDir;

    beforeEach((done) => {
      testDir = initFixture("BootstrapCommand/basic", done);
    });

    it("should bootstrap packages", (done) => {
      const bootstrapCommand = new BootstrapCommand([], {});

      bootstrapCommand.runValidations();
      bootstrapCommand.runPreparations();

      assertStubbedCalls([
        [ChildProcessUtilities, "spawn", { nodeCallback: true }, [
          { args: ["npm", ["install", "foo@^1.0.0"], { cwd: path.join(testDir, "packages", "package-1"), stdio: STDIO_OPT }] }
        ]],
        [ChildProcessUtilities, "spawn", { nodeCallback: true }, [
          { args: ["npm", ["install", "foo@^1.0.0"], { cwd: path.join(testDir, "packages", "package-2"), stdio: STDIO_OPT }] }
        ]],
        [ChildProcessUtilities, "spawn", { nodeCallback: true }, [
          { args: ["npm", ["install", "foo@0.1.12"], { cwd: path.join(testDir, "packages" ,"package-3"), stdio: STDIO_OPT }] }
        ]],
        [ChildProcessUtilities, "spawn", { nodeCallback: true }, [
          { args: ["npm", ["install", "@test/package-1@^0.0.0"], { cwd: path.join(testDir, "packages", "package-4"), stdio: STDIO_OPT }] }
        ]]
      ]);

      bootstrapCommand.runCommand(exitWithCode(0, (err) => {
        if (err) return done(err);

        try {
          assert.ok(!pathExists.sync(path.join(testDir, "lerna-debug.log")), "lerna-debug.log should not exist");
          // package-1 should not have any packages symlinked
          assert.throws(() => fs.readlinkSync(path.join(testDir, "packages", "package-1", "node_modules", "package-2")));
          assert.throws(() => fs.readlinkSync(path.join(testDir, "packages", "package-1", "node_modules", "package-3")));
          assert.throws(() => fs.readlinkSync(path.join(testDir, "packages", "package-1", "node_modules", "package-4")));
          // package-2 package dependencies are symlinked
          assert.equal(
            normalize(resolveSymlink(path.join(testDir, "packages", "package-2", "node_modules", "@test", "package-1"))),
            normalize(path.join(testDir, "packages", "package-1")),
            "package-1 should be symlinked to package-2"
          );
          assert.throws(() => fs.readlinkSync(path.join(testDir, "packages", "package-2", "node_modules", "package-3")));
          assert.throws(() => fs.readlinkSync(path.join(testDir, "packages", "package-2", "node_modules", "package-4")));
          // package-3 package dependencies are symlinked
          assert.equal(
            normalize(resolveSymlink(path.join(testDir, "packages", "package-3", "node_modules", "@test", "package-1"))),
            normalize(path.join(testDir, "packages", "package-1")),
            "package-1 should be symlinked to package-3"
          );
          assert.equal(
            normalize(resolveSymlink(path.join(testDir, "packages", "package-3", "node_modules", "package-2"))),
            normalize(path.join(testDir, "packages", "package-2")),
            "package-2 should be symlinked to package-3"
          );
          assert.throws(() => fs.readlinkSync(path.join(testDir, "packages", "package-3", "node_modules", "package-4")));
          // package-4 package dependencies are symlinked
          assert.throws(() => fs.readlinkSync(path.join(testDir, "packages", "package-4", "node_modules", "package-1")));
          assert.throws(() => fs.readlinkSync(path.join(testDir, "packages", "package-4", "node_modules", "package-2")));
          assert.equal(
            normalize(resolveSymlink(path.join(testDir, "packages", "package-4", "node_modules", "package-3"))),
            normalize(path.join(testDir, "packages", "package-3")),
            "package-3 should be symlinked to package-4"
          );
          // package binaries are symlinked
          assert.equal(
            normalize(FileSystemUtilities.isSymlink(path.join(testDir, "packages", "package-3", "node_modules", ".bin", "package-2"))),
            normalize(path.join(testDir, "packages", "package-2", "cli.js")),
            "package-2 binary should be symlinked in package-3"
          );
          assert.equal(
            normalize(FileSystemUtilities.isSymlink(path.join(testDir, "packages", "package-4", "node_modules", ".bin", "package3cli1"))),
            normalize(path.join(testDir, "packages", "package-3", "cli1.js")),
            "package-3 binary should be symlinked in package-4"
          );
          assert.equal(
            normalize(FileSystemUtilities.isSymlink(path.join(testDir, "packages", "package-4", "node_modules", ".bin", "package3cli2"))),
            normalize(path.join(testDir, "packages", "package-3", "cli2.js")),
            "package-3 binary should be symlinked in package-4"
          );
          done();
        } catch (err) {
          done(err);
        }
      }));
    });

    it("should not bootstrap ignored packages", (done) => {
      const bootstrapCommand = new BootstrapCommand([], {
        ignore: "package-@(3|4)"
      });

      bootstrapCommand.runValidations();
      bootstrapCommand.runPreparations();

      assertStubbedCalls([
        [ChildProcessUtilities, "spawn", { nodeCallback: true }, [
          { args: ["npm", ["install", "foo@^1.0.0"], { cwd: path.join(testDir, "packages", "package-1"), stdio: STDIO_OPT }] }
        ]],
        [ChildProcessUtilities, "spawn", { nodeCallback: true }, [
          { args: ["npm", ["install", "foo@^1.0.0"], { cwd: path.join(testDir, "packages", "package-2"), stdio: STDIO_OPT }] }
        ]]
      ]);

      bootstrapCommand.runCommand(exitWithCode(0, (err) => {
        if (err) return done(err);

        try {
          assert.ok(!pathExists.sync(path.join(testDir, "lerna-debug.log")), "lerna-debug.log should not exist");

          done();
        } catch (err) {
          done(err);
        }
      }));
    });

    it("should only bootstrap scoped packages", (done) => {
      const bootstrapCommand = new BootstrapCommand([], {
        scope: "package-@(3|4)"
      });

      bootstrapCommand.runValidations();
      bootstrapCommand.runPreparations();

      assertStubbedCalls([
        [ChildProcessUtilities, "spawn", { nodeCallback: true }, [
          { args: ["npm", ["install", "foo@0.1.12"], { cwd: path.join(testDir, "packages" ,"package-3"), stdio: STDIO_OPT }] }
        ]],
        [ChildProcessUtilities, "spawn", { nodeCallback: true }, [
          { args: ["npm", ["install", "@test/package-1@^0.0.0"], { cwd: path.join(testDir, "packages", "package-4"), stdio: STDIO_OPT }] }
        ]]
      ]);

      bootstrapCommand.runCommand(exitWithCode(0, (err) => {
        if (err) return done(err);

        try {
          assert.ok(!pathExists.sync(path.join(testDir, "asini-debug.log")), "asini-debug.log should not exist");
          // package-3 package dependencies are symlinked
          assert.equal(
            normalize(resolveSymlink(path.join(testDir, "packages", "package-3", "node_modules", "@test", "package-1"))),
            normalize(path.join(testDir, "packages", "package-1")),
            "package-1 should be symlinked to package-3"
          );
          assert.equal(
            normalize(resolveSymlink(path.join(testDir, "packages", "package-3", "node_modules", "package-2"))),
            normalize(path.join(testDir, "packages", "package-2")),
            "package-2 should be symlinked to package-3"
          );
          assert.throws(() => fs.readlinkSync(path.join(testDir, "packages", "package-3", "node_modules", "package-4")));
          // package-4 package dependencies are symlinked
          assert.throws(() => fs.readlinkSync(path.join(testDir, "packages", "package-4", "node_modules", "package-1")));
          assert.throws(() => fs.readlinkSync(path.join(testDir, "packages", "package-4", "node_modules", "package-2")));
          assert.equal(
            normalize(resolveSymlink(path.join(testDir, "packages", "package-4", "node_modules", "package-3"))),
            normalize(path.join(testDir, "packages", "package-3")),
            "package-3 should be symlinked to package-4"
          );
          // package binaries are symlinked
          assert.equal(
            normalize(FileSystemUtilities.isSymlink(path.join(testDir, "packages", "package-3", "node_modules", ".bin", "package-2"))),
            normalize(path.join(testDir, "packages", "package-2", "cli.js")),
            "package-2 binary should be symlinked in package-3"
          );
          assert.equal(
            normalize(FileSystemUtilities.isSymlink(path.join(testDir, "packages", "package-4", "node_modules", ".bin", "package3cli1"))),
            normalize(path.join(testDir, "packages", "package-3", "cli1.js")),
            "package-3 binary should be symlinked in package-4"
          );
          assert.equal(
            normalize(FileSystemUtilities.isSymlink(path.join(testDir, "packages", "package-4", "node_modules", ".bin", "package3cli2"))),
            normalize(path.join(testDir, "packages", "package-3", "cli2.js")),
            "package-3 binary should be symlinked in package-4"
          );
          done();
        } catch (err) {
          done(err);
        }
      }));
    });
  });

  describe("a repo with packages outside of packages/", () => {
    let testDir;

    beforeEach((done) => {
      testDir = initFixture("BootstrapCommand/extra", done);
    });

    it("should bootstrap packages", (done) => {
      const bootstrapCommand = new BootstrapCommand([], {});

      bootstrapCommand.runValidations();
      bootstrapCommand.runPreparations();

      assertStubbedCalls([
        [ChildProcessUtilities, "spawn", { nodeCallback: true }, [
          { args: ["npm", ["install", "foo@^1.0.0"], { cwd: path.join(testDir, "packages", "package-1"), stdio: STDIO_OPT }] }
        ]],
        [ChildProcessUtilities, "spawn", { nodeCallback: true }, [
          { args: ["npm", ["install", "foo@^1.0.0"], { cwd: path.join(testDir, "packages", "package-2"), stdio: STDIO_OPT }] }
        ]],
        [ChildProcessUtilities, "spawn", { nodeCallback: true }, [
          { args: ["npm", ["install", "@test/package-1@^0.0.0"], { cwd: path.join(testDir, "packages", "package-4"), stdio: STDIO_OPT }] }
        ]],
        [ChildProcessUtilities, "spawn", { nodeCallback: true }, [
          { args: ["npm", ["install", "foo@0.1.12"], { cwd: path.join(testDir, "package-3"), stdio: STDIO_OPT }] }
        ]],
      ]);

      bootstrapCommand.runCommand(exitWithCode(0, (err) => {
        if (err) return done(err);

        try {
          assert.ok(!pathExists.sync(path.join(testDir, "lerna-debug.log")), "lerna-debug.log should not exist");
          // Make sure the `prepublish` script got run (index.js got created).
          assert.ok(pathExists.sync(path.join(testDir, "packages", "package-1", "index.js")));
          // package-1 should not have any packages symlinked
          assert.throws(() => fs.readlinkSync(path.join(testDir, "packages", "package-1", "node_modules", "package-2")));
          assert.throws(() => fs.readlinkSync(path.join(testDir, "packages", "package-1", "node_modules", "package-3")));
          assert.throws(() => fs.readlinkSync(path.join(testDir, "packages", "package-1", "node_modules", "package-4")));
          // package-2 package dependencies are symlinked
          assert.equal(
            path.resolve(resolveSymlink(path.join(testDir, "packages", "package-2", "node_modules", "@test", "package-1"))),
            path.resolve(path.join(testDir, "packages", "package-1")),
            "package-1 should be symlinked to package-2"
          );
          assert.throws(() => fs.readlinkSync(path.join(testDir, "packages", "package-2", "node_modules", "package-3")));
          assert.throws(() => fs.readlinkSync(path.join(testDir, "packages", "package-2", "node_modules", "package-4")));
          // package-3 package dependencies are symlinked
          assert.equal(
            normalize(resolveSymlink(path.join(testDir, "package-3", "node_modules", "@test", "package-1"))),
            normalize(path.join(testDir, "packages", "package-1")),
            "package-1 should be symlinked to package-3"
          );
          assert.equal(
            normalize(resolveSymlink(path.join(testDir, "package-3", "node_modules", "package-2"))),
            normalize(path.join(testDir, "packages", "package-2")),
            "package-2 should be symlinked to package-3"
          );
          assert.throws(() => fs.readlinkSync(path.join(testDir, "package-3", "node_modules", "package-4")));
          // package-4 package dependencies are symlinked
          assert.throws(() => fs.readlinkSync(path.join(testDir, "packages", "package-4", "node_modules", "package-1")));
          assert.throws(() => fs.readlinkSync(path.join(testDir, "packages", "package-4", "node_modules", "package-2")));
          assert.equal(
            normalize(resolveSymlink(path.join(testDir, "packages", "package-4", "node_modules", "package-3"))),
            normalize(path.join(testDir, "package-3")),
            "package-3 should be symlinked to package-4"
          );
          // package binaries are symlinked
          assert.equal(
            normalize(FileSystemUtilities.isSymlink(path.join(testDir, "package-3", "node_modules", ".bin", "package-2"))),
            normalize(path.join(testDir, "packages", "package-2", "cli.js")),
            "package-2 binary should be symlinked in package-3"
          );
          assert.equal(
            normalize(FileSystemUtilities.isSymlink(path.join(testDir, "packages", "package-4", "node_modules", ".bin", "package3cli1"))),
            normalize(path.join(testDir, "package-3", "cli1.js")),
            "package-3 binary should be symlinked in package-4"
          );
          assert.equal(
            normalize(FileSystemUtilities.isSymlink(path.join(testDir, "packages", "package-4", "node_modules", ".bin", "package3cli2"))),
            normalize(path.join(testDir, "package-3", "cli2.js")),
            "package-3 binary should be symlinked in package-4"
          );
          done();
        } catch (err) {
          done(err);
        }
      }));
    });

    it("should not bootstrap ignored packages", (done) => {
      const bootstrapCommand = new BootstrapCommand([], {
        ignore: "package-@(3|4)"
      });

      bootstrapCommand.runValidations();
      bootstrapCommand.runPreparations();

      assertStubbedCalls([
        [ChildProcessUtilities, "spawn", { nodeCallback: true }, [
          { args: ["npm", ["install", "foo@^1.0.0"], { cwd: path.join(testDir, "packages", "package-1"), stdio: STDIO_OPT }] }
        ]],
        [ChildProcessUtilities, "spawn", { nodeCallback: true }, [
          { args: ["npm", ["install", "foo@^1.0.0"], { cwd: path.join(testDir, "packages", "package-2"), stdio: STDIO_OPT }] }
        ]]
      ]);

      bootstrapCommand.runCommand(exitWithCode(0, (err) => {
        if (err) return done(err);

        try {
          assert.ok(!pathExists.sync(path.join(testDir, "lerna-debug.log")), "lerna-debug.log should not exist");

          done();
        } catch (err) {
          done(err);
        }
      }));
    });

    it("should bootstrap any dependencies not included by --scope when --include-filtered-dependencies is true", (done) => {
      // we scope to package-2 only but should still install package-1 as it is a dependency of package-2
      const bootstrapCommand = new BootstrapCommand([], {
        scope: "package-2",
        includeFilteredDependencies: true
      });

      bootstrapCommand.runValidations();
      bootstrapCommand.runPreparations();

      assertStubbedCalls([
        [ChildProcessUtilities, "spawn", { nodeCallback: true }, [
          { args: ["npm", ["install", "foo@^1.0.0"], { cwd: path.join(testDir, "packages", "package-2"), stdio: STDIO_OPT }] }
        ]],
        [ChildProcessUtilities, "spawn", { nodeCallback: true }, [
          { args: ["npm", ["install", "foo@^1.0.0"], { cwd: path.join(testDir, "packages", "package-1"), stdio: STDIO_OPT }] }
        ]]
      ]);

      bootstrapCommand.runCommand(exitWithCode(0, (err) => {
        if (err) return done(err);

        try {
          assert.ok(!pathExists.sync(path.join(testDir, "lerna-debug.log")), "lerna-debug.log should not exist");

          done();
        } catch (err) {
          done(err);
        }
      }));
    });

    it("should bootstrap any dependencies exluded by --ignore when --include-filtered-dependencies is true", (done) => {
      // we ignore package 1 but it should still be installed because it is a dependency of package-2
      const bootstrapCommand = new BootstrapCommand([], {
        ignore: "{@test/package-1,package-@(3|4)}",
        includeFilteredDependencies: true
      });

      bootstrapCommand.runValidations();
      bootstrapCommand.runPreparations();

      assertStubbedCalls([
        [ChildProcessUtilities, "spawn", { nodeCallback: true }, [
          { args: ["npm", ["install", "foo@^1.0.0"], { cwd: path.join(testDir, "packages", "package-2"), stdio: STDIO_OPT }] }
        ]],
        [ChildProcessUtilities, "spawn", { nodeCallback: true }, [
          { args: ["npm", ["install", "foo@^1.0.0"], { cwd: path.join(testDir, "packages", "package-1"), stdio: STDIO_OPT }] }
        ]]
      ]);

      bootstrapCommand.runCommand(exitWithCode(0, (err) => {
        if (err) return done(err);

        try {
          assert.ok(!pathExists.sync(path.join(testDir, "lerna-debug.log")), "lerna-debug.log should not exist");

          done();
        } catch (err) {
          done(err);
        }
      }));
    });
  });

  describe("external dependencies that haven't been installed", () => {
    let testDir;

    beforeEach((done) => {
      testDir = initFixture("BootstrapCommand/cold", done);
    });

    it("should get installed", (done) => {
      const bootstrapCommand = new BootstrapCommand([], {});

      bootstrapCommand.runValidations();
      bootstrapCommand.runPreparations();

      let installed = 0;
      let spawnArgs = [];
      let spawnOptions = [];

      stub(ChildProcessUtilities, "spawn", (command, args, options, callback) => {
        spawnArgs.push(args);
        spawnOptions.push(options);
        installed++;
        callback();
      });

      bootstrapCommand.runCommand(exitWithCode(0, (err) => {
        if (err) return done(err);

        try {
          assert.ok(!pathExists.sync(path.join(testDir, "lerna-debug.log")), "lerna-debug.log should not exist");
          assert.deepEqual(spawnArgs, [
            ["install", "external@^1.0.0"],
            ["install", "external@^2.0.0"]
          ]);
          assert.deepEqual(spawnOptions, [
            { cwd: path.join(testDir, "packages", "package-1"), stdio: STDIO_OPT },
            { cwd: path.join(testDir, "packages", "package-2"), stdio: STDIO_OPT }
          ]);
          assert.equal(installed, 2, "The external dependencies were installed");

          done();
        } catch (err) {
          done(err);
        }
      }));
    });
  });

  describe("external dependencies that have already been installed", () => {
    let testDir;

    beforeEach((done) => {
      testDir = initFixture("BootstrapCommand/warm", done);
    });

    it("should not get re-installed", (done) => {
      const bootstrapCommand = new BootstrapCommand([], {});

      bootstrapCommand.runValidations();
      bootstrapCommand.runPreparations();

      let installed = false;
      stub(ChildProcessUtilities, "spawn", (command, args, options, callback) => {
        installed = true;
        callback();
      });

      bootstrapCommand.runCommand(exitWithCode(0, (err) => {
        if (err) return done(err);

        try {
          assert.ok(!pathExists.sync(path.join(testDir, "lerna-debug.log")), "lerna-debug.log should not exist");
          assert.ok(!installed, "The external dependency was not installed");
          done();
        } catch (err) {
          done(err);
        }
      }));
    });
  });

  describe("peer dependencies in packages in the repo", () => {
    let testDir;

    beforeEach((done) => {
      testDir = initFixture("BootstrapCommand/peer", done);
    });

    it("should not bootstrap ignored peer dependencies", (done) => {
      const bootstrapCommand = new BootstrapCommand([], {
        "ignore-peer-deps": true
      });

      bootstrapCommand.runValidations();
      bootstrapCommand.runPreparations();

      let installed = false;
      stub(ChildProcessUtilities, "spawn", (command, args, options, callback) => {
        // This should never be reached
        assert.deepEqual(args, ["install", "external@^1.0.0"]);
        assert.deepEqual(options, { cwd: path.join(testDir, "packages/package-1"), stdio: STDIO_OPT });
        installed = true;
        callback();
      });

      bootstrapCommand.runCommand(exitWithCode(0, (err) => {
        if (err) return done(err);

        try {
          assert.ok(!pathExists.sync(path.join(testDir, "lerna-debug.log")), "lerna-debug.log should not exist");
          assert.ok(!pathExists.sync(path.join(testDir, "packages/package-1/node_modules/package-2")), "The linkable peer dependency should not be installed");
          assert.ok(!installed, "The external peer dependency should not be installed");
          done();
        } catch (err) {
          done(err);
        }
      }));
    });
  });

  describe("zero packages", () => {
    beforeEach((done) => {
      initFixture("BootstrapCommand/zero-pkgs", done);
    });

    it("should succeed in repositories with zero packages", (done) => {
      const bootstrapCommand = new BootstrapCommand([], {});

      bootstrapCommand.runValidations();
      bootstrapCommand.runPreparations();

      bootstrapCommand.runCommand(exitWithCode(0, (err) => {
        assert.equal(undefined, err);
        done();
      }));
    });
  });
});


import UpdatedPackagesCollector from "../UpdatedPackagesCollector";
import FileSystemUtilities from "../FileSystemUtilities";
import PromptUtilities from "../PromptUtilities";
import GitUtilities from "../GitUtilities";
import NpmUtilities from "../NpmUtilities";
import Command from "../Command";
import semver from "semver";
import async from "async";
import chalk from "chalk";
import path from "path";
import { EOL } from "os";

export default class PublishCommand extends Command {
  initialize(callback) {

    if (this.flags.canary) {
      this.logger.info("Publishing canary build");
    }

    if (!this.repository.isIndependent()) {
      this.globalVersion = this.repository.version;
      this.logger.info("Current version: " + this.globalVersion);
    }

    this.configFlags = this.repository.publishConfig;

    const updatedPackagesCollector = new UpdatedPackagesCollector(
      this.repository,
      this.flags,
      this.configFlags
    );

    try {
      this.updates = updatedPackagesCollector.getUpdates();

      this.packagesToPublish = this.updates
        .map((update) => update.package)
        .filter((pkg) => !pkg.isPrivate());

    } catch (err) {
      throw err;
    }

    if (!this.updates.length) {
      callback(new Error("No updated packages to publish."));
      return;
    }

    this.getVersionsForUpdates((err, results) => {
      if (err) {
        callback(err);
        return;
      }

      let {version, versions} = results;

      if (!versions) {
        versions = {};
        this.updates.forEach((update) => {
          versions[update.package.name] = version;
        });
      }

      this.masterVersion = version;
      this.updatesVersions = versions;

      this.confirmVersions((err, confirmed) => {
        if (err) {
          callback(err);
          return;
        }

        if (!confirmed) {
          this.logger.info("Okay bye!");
          callback(null, false);
          return;
        }

        callback(null, true);
      });
    });
  }

  execute(callback) {
    try {
      if (!this.repository.isIndependent() && !this.flags.canary) {
        this.updateVersionInLernaJson();
      }

      this.updateUpdatedPackages();
      if (!this.flags.skipGit) {
        this.commitAndTagUpdates();
      }
    } catch (err) {
      callback(err);
      return;
    }

    if (this.flags.skipNpm) {
      callback(null, true);
    } else {
      this.publishPackagesToNpm(callback);
    }
  }

  publishPackagesToNpm(callback) {

    this.logger.newLine();
    this.logger.info("Publishing packages to npm...");

    this.npmPublishAsPrerelease((err) => {
      if (err) {
        callback(err);
        return;
      }

      if (this.flags.canary) {
        this.logger.info("Resetting git state");
        // reset since the package.json files are changed
        GitUtilities.checkoutChanges("packages/*/package.json");
      }

      this.npmUpdateAsLatest((err) => {
        if (err) {
          callback(err);
          return;
        }

        if (!(this.flags.canary || this.flags.skipGit)) {
          this.logger.info("Pushing tags to git...");
          this.logger.newLine();
          GitUtilities.pushWithTags(this.flags.gitRemote || this.repository.publishConfig.gitRemote || "origin", this.tags);
        }

        let message = "Successfully published:";

        this.updates.forEach((update) => {
          message += `${EOL} - ${update.package.name}@${update.package.version}`;
        });

        this.logger.success(message);
        callback(null, true);
      });
    });
  }

  getVersionsForUpdates(callback) {
    if (this.flags.repoVersion) {
      return callback(null, {
        version: this.flags.repoVersion
      });
    }

    // Non-Independent Canary Mode
    if (!this.repository.isIndependent() && this.flags.canary) {
      const version = this.globalVersion + this.getCanaryVersionSuffix();
      callback(null, { version });

    // Non-Independent Non-Canary Mode
    } else if (!this.repository.isIndependent()) {
      this.promptVersion(null, this.globalVersion, (err, version) => {
        if (err) {
          callback(err);
        } else {
          callback(null, { version });
        }
      });

    // Independent Canary Mode
    } else if (this.flags.canary) {
      const versions = {};
      const canaryVersionSuffix = this.getCanaryVersionSuffix();

      this.updates.forEach((update) => {
        versions[update.package.name] = update.package.version + canaryVersionSuffix;
      });

      callback(null, { versions });

    // Independent Non-Canary Mode
    } else {
      async.mapLimit(this.updates, 1, (update, cb) => {
        this.promptVersion(update.package.name, update.package.version, cb);
      }, (err, versions) => {
        if (err) {
          return callback(err);
        }

        this.updates.forEach((update, index) => {
          versions[update.package.name] = versions[index];
        });

        callback(null, { versions });
      });
    }
  }

  getCanaryVersionSuffix() {
    return "-alpha." + GitUtilities.getCurrentSHA().slice(0, 8);
  }

  promptVersion(packageName, currentVersion, callback) {
    const patch = semver.inc(currentVersion, "patch");
    const minor = semver.inc(currentVersion, "minor");
    const major = semver.inc(currentVersion, "major");

    let message = "Select a new version";
    if (packageName) message += ` for ${packageName}`;
    message += ` (currently ${currentVersion})`;

    PromptUtilities.select(message, {
      choices: [
        { value: patch, name: `Patch (${patch})` },
        { value: minor, name: `Minor (${minor})` },
        { value: major, name: `Major (${major})` },
        { value: false, name: "Custom" }
      ]
    }, (choice) => {
      if (choice) {
        callback(null, choice);
        return;
      }

      PromptUtilities.input("Enter a custom version", {
        filter: semver.valid,
        validate: (v) => semver.valid(v) ? true : "Must be a valid semver version",
      }, (input) => {
        callback(null, input);
      });
    });
  }

  confirmVersions(callback) {
    this.logger.newLine();
    this.logger.info("Changes:");
    this.logger.info(this.updates.map((update) => {
      const pkg = update.package;
      return `- ${pkg.name}: ${pkg.version} => ${this.updatesVersions[pkg.name]}${pkg.isPrivate() ? ` (${chalk.red("private")})` : ""}`;
    }).join(EOL));
    this.logger.newLine();

    if (!this.flags.yes) {
      PromptUtilities.confirm("Are you sure you want to publish the above changes?", (confirm) => {
        callback(null, confirm);
      });
    } else {
      this.logger.info("Assuming confirmation.");
      callback(null, true);
    }
  }

  updateVersionInLernaJson() {
    this.repository.lernaJson.version = this.masterVersion;
    FileSystemUtilities.writeFileSync(this.repository.lernaJsonLocation, JSON.stringify(this.repository.lernaJson, null, "  "));
    if (!this.flags.skipGit) {
      GitUtilities.addFile(this.repository.lernaJsonLocation);
    }
  }

  updateUpdatedPackages() {
    const changedFiles = [];

    this.updates.forEach((update) => {
      const pkg = update.package;
      const packageLocation = pkg.location;
      const packageJsonLocation = path.join(packageLocation, "package.json");

      // set new version
      pkg.version = this.updatesVersions[pkg.name] || pkg.version;

      // update pkg dependencies
      this.updatePackageDepsObject(pkg, "dependencies");
      this.updatePackageDepsObject(pkg, "devDependencies");
      this.updatePackageDepsObject(pkg, "peerDependencies");

      // write new package
      FileSystemUtilities.writeFileSync(packageJsonLocation, pkg.toJsonString());

      // push to be git committed
      changedFiles.push(packageJsonLocation);
    });

    if (!(this.flags.canary || this.flags.skipGit)) {
      changedFiles.forEach(GitUtilities.addFile);
    }
  }

  updatePackageDepsObject(pkg, depsKey) {
    const deps = pkg[depsKey];

    if (!deps) {
      return;
    }

    this.packageGraph.get(pkg.name).dependencies.forEach((depName) => {
      const version = this.updatesVersions[depName];

      if (deps[depName] && version) {
        deps[depName] = this.flags.exact ? version : "^" + version;
      }
    });
  }

  commitAndTagUpdates() {
    if (!this.flags.canary) {
      if (this.repository.isIndependent()) {
        this.tags = this.gitCommitAndTagVersionForUpdates();
      } else {
        this.tags = [this.gitCommitAndTagVersion(this.masterVersion)];
      }
    }
  }

  gitCommitAndTagVersionForUpdates() {
    const tags = this.updates.map((update) => `${update.package.name}@${this.updatesVersions[update.package.name]}`);
    const message = this.flags.message || tags.reduce((msg, tag) => msg + `${EOL} - ${tag}`, `Publish${EOL}`);

    GitUtilities.commit(message);
    tags.forEach(GitUtilities.addTag);
    return tags;
  }

  gitCommitAndTagVersion(version) {
    const tag = "v" + version;
    const message = this.flags.message || tag;
    GitUtilities.commit(message);
    GitUtilities.addTag(tag);
    return tag;
  }

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

    if (FileSystemUtilities.existsSync(scriptLocation)) {
      require(scriptLocation);
    } else {
      this.logger.verbose(`No ${script} script found at ${scriptLocation}`);
    }
  }

  npmPublishAsPrerelease(callback) {
    this.updates.forEach((update) => {
      this.execScript(update.package, "prepublish");
    });

    this.progressBar.init(this.packagesToPublish.length);

    async.parallelLimit(this.packagesToPublish.map((pkg) => {
      let attempts = 0;

      const run = (cb) => {
        this.logger.verbose("Publishing " + pkg.name + "...");

        NpmUtilities.publishTaggedInDir("lerna-temp", pkg.location, (err) => {
          err = err && err.stack || err;

          if (!err ||
            // publishing over an existing package which is likely due to a timeout or something
            err.indexOf("You cannot publish over the previously published version") > -1
          ) {
            this.progressBar.tick(pkg.name);
            this.execScript(pkg, "postpublish");
            cb();
            return;
          }

          attempts++;

          if (attempts < 5) {
            this.logger.error("Attempting to retry publishing " + pkg.name + "...", err);
            run(cb);
          } else {
            this.logger.error("Ran out of retries while publishing " + pkg.name, err);
            cb(err);
          }
        });
      };

      return run;
    }), this.concurrency, (err) => {
      this.progressBar.terminate();
      callback(err);
    });
  }

  npmUpdateAsLatest(callback) {
    this.progressBar.init(this.packagesToPublish.length);

    async.parallelLimit(this.packagesToPublish.map((pkg) => (cb) => {
      let attempts = 0;

      while (true) {
        attempts++;

        try {
          if (NpmUtilities.checkDistTag(pkg.name, "lerna-temp")) {
            NpmUtilities.removeDistTag(pkg.name, "lerna-temp");
          }

          if (this.flags.npmTag) {
            NpmUtilities.addDistTag(pkg.name, this.updatesVersions[pkg.name], this.flags.npmTag);
          } else if (this.flags.canary) {
            NpmUtilities.addDistTag(pkg.name, pkg.version, "canary");
          } else {
            NpmUtilities.addDistTag(pkg.name, this.updatesVersions[pkg.name], "latest");
          }

          this.progressBar.tick(pkg.name);
          cb();
          break;
        } catch (err) {
          if (attempts < 5) {
            this.logger.error("Error updating version as latest", err);
            continue;
          } else {
            cb(err);
            return;
          }
        }
      }
    }), 4, (err) => {
      this.progressBar.terminate();
      callback(err);
    });
  }
}


// Make sure the `prepublish` script got run (index.js got created), even though we --ignored package-1


#! /usr/bin/env node
console.log("Hello, world!");


// import assert from "assert";
//
// import InitCommand from "../src/commands/InitCommand";


keep keep keep add keep keep keep keep keep

require('../../stylus/components/_system-bars.styl')

import Applicationable from '../../mixins/applicationable'
import Colorable from '../../mixins/colorable'
import Themeable from '../../mixins/themeable'

export default {
  name: 'v-system-bar',

  mixins: [Applicationable, Colorable, Themeable],

  props: {
    absolute: Boolean,
    fixed: Boolean,
    lightsOut: Boolean,
    status: Boolean,
    window: Boolean
  },

  computed: {
    classes () {
      return this.addBackgroundColorClassChecks(Object.assign({
        'system-bar--lights-out': this.lightsOut,
        'system-bar--absolute': this.absolute,
        'system-bar--fixed': this.fixed,
        'system-bar--status': this.status,
        'system-bar--window': this.window
      }, this.themeClasses))
    },
    computedHeight () {
      return this.window ? 32 : 24
    }
  },

  watch: {
    window () {
      this.updateApplication()
    },
    fixed () {
      this.updateApplication()
    },
    absolute () {
      this.updateApplication()
    }
  },

  methods: {
    updateApplication () {
      if (this.app && this.$vuetify) {
        this.$vuetify.application.bar = (this.fixed || this.absolute) ? this.computedHeight : 0
      }
    }
  },

  destroyed () {
    if (this.app && this.$vuetify) this.$vuetify.application.bar = 0
  },

  render (h) {
    const data = {
      staticClass: 'system-bar',
      'class': this.classes,
      style: {
        height: `${this.computedHeight}px`
      }
    }

    return h('div', data, this.$slots.default)
  }
}


import { mount } from 'avoriaz'
import { VCardMedia } from '~components/VCard'

describe('VCardMedia.js', () => {
  it('should render component and match snapshot', () => {
    const wrapper = mount(VCardMedia)

    expect(wrapper.html()).toMatchSnapshot()
  })

  it('should render component with contained background and match snapshot', () => {
    const wrapper = mount(VCardMedia, {
      propsData: {
        src: 'file.jpg',
        contain: true
      }
    })

    expect(wrapper.html()).toMatchSnapshot()
  })

  it('should render component with custom height (string) and match snapshot', () => {
    const wrapper = mount(VCardMedia, {
      propsData: {
        height: '100px'
      }
    })

    expect(wrapper.html()).toMatchSnapshot()
  })

  it('should render component with custom height (number) and match snapshot', () => {
    const wrapper = mount(VCardMedia, {
      propsData: {
        height: 100
      }
    })

    expect(wrapper.html()).toMatchSnapshot()
  })

  it('should render matching components with custom height', () => {
    const wrapper1 = mount(VCardMedia, {
      propsData: {
        height: '100px'
      }
    })
    const wrapper2 = mount(VCardMedia, {
      propsData: {
        height: 100
      }
    })

    expect(wrapper1.html()).toEqual(wrapper2.html())
  })

  it('should render component with custom background image and match snapshot', () => {
    const wrapper = mount(VCardMedia, {
      propsData: {
        src: 'https://vuetifyjs.com/static/doc-images/cards/sunshine.jpg'
      }
    })

    expect(wrapper.html()).toMatchSnapshot()
  })
})


import ExpandTransitionGenerator from '../../transitions/expand-transition'

export default {
  methods: {
    genTBody () {
      const children = this.genItems()

      return this.$createElement('tbody', children)
    },
    genExpandedRow (props) {
      const children = []

      if (this.isExpanded(props.item)) {
        const expand = this.$createElement('div', {
          class: 'datatable__expand-content',
          key: props.item[this.itemKey]
        }, this.$scopedSlots.expand(props))

        children.push(expand)
      }

      const transition = this.$createElement('transition-group', {
        class: 'datatable__expand-col',
        attrs: { colspan: '100%' },
        props: {
          tag: 'td'
        },
        on: ExpandTransitionGenerator('datatable__expand-col--expanded')
      }, children)

      return this.genTR([transition], { class: 'datatable__expand-row' })
    },
    genFilteredItems () {
      if (!this.$scopedSlots.items) {
        return null
      }

      const rows = []
      for (let index = 0, len = this.filteredItems.length; index < len; ++index) {
        const item = this.filteredItems[index]
        const props = this.createProps(item, index)
        const row = this.$scopedSlots.items(props)

        rows.push(this.needsTR(row)
          ? this.genTR(row, {
            key: index,
            attrs: { active: this.isSelected(item) }
          })
          : row)

        if (this.$scopedSlots.expand) {
          const expandRow = this.genExpandedRow(props)
          rows.push(expandRow)
        }
      }

      return rows
    },
    genEmptyItems (content) {
      return this.genTR([this.$createElement('td', {
        'class': 'text-xs-center',
        attrs: { colspan: '100%' }
      }, content)])
    }
  }
}


import { mount } from 'avoriaz'
import VDivider from '~components/VDivider'
import { test, functionalContext } from '~util/testing'

test('VDivider.js', () => {
  it('should render component and match snapshot', () => {
    const wrapper = mount(VDivider, functionalContext())

    expect(wrapper.html()).toMatchSnapshot()
  })

  it('should render an inset component', () => {
    const wrapper = mount(VDivider, functionalContext({
      props: {
        inset: true
      }
    }))

    expect(wrapper.hasClass('divider--inset')).toBe(true)
  })

  it('should render a light component', () => {
    const wrapper = mount(VDivider, functionalContext({
      props: {
        light: true
      }
    }))

    expect(wrapper.hasClass('theme--light')).toBe(true)
  })

  it('should render a dark component', () => {
    const wrapper = mount(VDivider, functionalContext({
      props: {
        dark: true
      }
    }))

    expect(wrapper.hasClass('theme--dark')).toBe(true)
  })
})


keep keep keep keep add keep keep keep keep keep

require('../../stylus/components/_toolbar.styl')

import Applicationable from '../../mixins/applicationable'
import Colorable from '../../mixins/colorable'
import Themeable from '../../mixins/themeable'
import SSRBootable from '../../mixins/ssr-bootable'

export default {
  name: 'v-toolbar',

  mixins: [
    Applicationable,
    Colorable,
    SSRBootable,
    Themeable
  ],

  data: () => ({
    activeTimeout: null,
    currentScroll: 0,
    heights: {
      mobileLandscape: 48,
      mobile: 56,
      desktop: 64,
      dense: 48
    },
    isActiveProxy: false,
    isExtended: false,
    isScrollingUp: false,
    previousScroll: null,
    previousScrollDirection: null,
    target: null
  }),

  props: {
    absolute: Boolean,
    card: Boolean,
    clippedLeft: Boolean,
    clippedRight: Boolean,
    dense: Boolean,
    extended: Boolean,
    extensionHeight: [Number, String],
    fixed: Boolean,
    flat: Boolean,
    floating: Boolean,
    height: [Number, String],
    invertedScroll: Boolean,
    manualScroll: {
      type: Boolean,
      default: null
    },
    prominent: Boolean,
    scrollOffScreen: Boolean,
    scrollTarget: String,
    scrollThreshold: {
      type: Number,
      default: 300
    },
    tabs: Boolean
  },

  computed: {
    computedContentHeight () {
      if (this.height) return parseInt(this.height)
      if (this.dense) return this.heights.dense

      if (this.prominent ||
        this.$vuetify.breakpoint.mdAndUp
      ) return this.heights.desktop

      if (this.$vuetify.breakpoint.width >
        this.$vuetify.breakpoint.height
      ) return this.heights.mobileLandscape

      return this.heights.mobile
    },
    computedExtensionHeight () {
      if (this.extensionHeight) return parseInt(this.extensionHeight)

      return this.computedContentHeight
    },
    computedHeight () {
      if (!this.isExtended) return this.computedContentHeight

      return this.computedContentHeight + this.computedExtensionHeight
    },
    classes () {
      return this.addBackgroundColorClassChecks({
        'toolbar': true,
        'elevation-0': this.flat || (!this.isActiveProxy && !this.tabs),
        'toolbar--absolute': this.absolute,
        'toolbar--card': this.card,
        'toolbar--clipped': this.clippedLeft || this.clippedRight,
        'toolbar--dense': this.dense,
        'toolbar--extended': this.isExtended,
        'toolbar--fixed': this.fixed,
        'toolbar--floating': this.floating,
        'toolbar--is-booted': this.isBooted,
        'toolbar--prominent': this.prominent,
        'theme--dark': this.dark,
        'theme--light': this.light
      })
    },
    paddingLeft () {
      if (!this.app || this.clippedLeft) return 0

      return this.$vuetify.application.left
    },
    paddingRight () {
      if (!this.app || this.clippedRight) return 0

      return this.$vuetify.application.right
    },
    isActive () {
      if (!this.scrollOffScreen) return true
      if (this.manualScroll) return this.manualScroll

      return this.invertedScroll
        ? this.currentScroll > this.scrollThreshold
        : this.currentScroll < this.scrollThreshold ||
          this.isScrollingUp
    },
    styles () {
      const style = {}

      if (!this.isActiveProxy) {
        style.transform = `translateY(-${this.computedHeight}px)`
      }

      if (this.app) {
        style.paddingRight = `${this.paddingRight}px`
        style.paddingLeft = `${this.paddingLeft}px`
      }

      return style
    }
  },

  watch: {
    // This is to avoid an accidental
    // false positive when scrolling.
    // sometimes for 1 frame it appears
    // as if the direction has changed
    // but it actually has not
    isActive: {
      immediate: true,
      handler (val) {
        clearTimeout(this.activeTimeout)

        this.activeTimeout = setTimeout(() => {
          this.isActiveProxy = val
        }, 20)
      }
    },
    clippedLeft (val) {
      this.updateApplication()
    },
    height (val) {
      this.updateApplication()
    },
    clippedRight (val) {
      this.updateApplication()
    },
    invertedScroll () {
      this.updateApplication()
    }
  },

  destroyed () {
    if (this.app) this.$vuetify.application.top -= this.computedHeight
  },

  methods: {
    onScroll () {
      if (typeof window === 'undefined') return

      if (!this.target) {
        this.target = this.scrollTarget
          ? document.querySelector(this.scrollTarget)
          : window
      }

      this.currentScroll = this.scrollTarget
        ? this.target.scrollTop
        : this.target.pageYOffset || document.documentElement.scrollTop

      this.isScrollingUp = this.currentScroll < this.previousScroll

      this.previousScroll = this.currentScroll
    },
    updateApplication () {
      if (!this.app) return

      this.$vuetify.application.top = (!this.fixed &&
        !this.absolute) ||
        this.invertedScroll
        ? 0
        : this.computedHeight
    }
  },

  render (h) {
    this.isExtended = this.extended || !!this.$slots.extension

    const children = []
    const data = {
      'class': this.classes,
      style: this.styles,
      on: this.$listeners
    }

    if (this.scrollOffScreen) {
      data.directives = [{
        name: 'scroll',
        value: {
          callback: this.onScroll,
          target: this.scrollTarget
        }
      }]
    }

    children.push(h('div', {
      staticClass: 'toolbar__content',
      style: { height: `${this.computedContentHeight}px` },
      ref: 'content'
    }, this.$slots.default))

    if (this.isExtended) {
      children.push(h('div', {
        staticClass: 'toolbar__extension',
        style: { height: `${this.computedExtensionHeight}px` }
      }, this.$slots.extension))
    }

    return h('nav', data, children)
  }
}


import VApp from '~components/VApp'
import { test } from '~util/testing'

test('VApp.js', ({ mount }) => {
  it('should match a snapshot', () => {
    const wrapper = mount(VApp)

    expect(wrapper.html()).toMatchSnapshot()
  })

  it('should have data-app attribute', () => {
    const wrapper = mount(VApp)
    const app = wrapper.find('.application')[0]

    expect(app.getAttribute('data-app')).toBe('true')
  })

  it('should allow a custom id', () => {
    const wrapper = mount(VApp, {
      propsData: {
        id: 'inspire'
      }
    })
    const app = wrapper.find('.application')[0]

    expect(app.getAttribute('id')).toBe('inspire')
    expect(wrapper.html()).toMatchSnapshot()
  })

  it('should watch dark prop', async () => {
    const wrapper = mount(VApp, {
      propsData: {
        dark: true
      }
    })

    expect(wrapper.vm.$vuetify.dark).toBe(true)
    wrapper.setProps({
      dark: false
    })
    expect(wrapper.vm.$vuetify.dark).toBe(false)
  })

  it('should watch theme', async () => {
    const wrapper = mount(VApp)

    expect(wrapper.vm.style).toMatchSnapshot()
    wrapper.vm.$vuetify.theme.primary = '#000'
    await wrapper.vm.$nextTick()
    expect(wrapper.vm.style).toMatchSnapshot()
  })
})


import Routable from '../../mixins/routable'

export default {
  name: 'v-breadcrumbs-item',

  mixins: [Routable],

  props: {
    // In a breadcrumb, the currently
    // active item should be dimmed
    activeClass: {
      type: String,
      default: 'breadcrumbs__item--disabled'
    }
  },

  computed: {
    classes () {
      return {
        'breadcrumbs__item': true,
        [this.activeClass]: this.disabled
      }
    }
  },

  render (h) {
    const { tag, data } = this.generateRouteLink()

    return h('li', [
      h(tag, data, this.$slots.default)
    ])
  }
}


@import '../bootstrap'

.menu
  display: inline-block
  position: relative
  vertical-align: middle

  &--disabled
    cursor: default

    .menu__activator,
    .menu__activator > *
      cursor: default
      pointer-events: none

  &__activator
    align-items: center
    cursor: pointer
    position: relative

    input[readonly]
      cursor: pointer

    .toolbar__side-icon
      margin: 0

  &__content
    position: absolute
    display: inline-block
    border-radius: 2px
    max-width: 80%
    overflow-y: auto
    overflow-x: hidden
    transition: .3s $transition.swing
    contain: content
    elevation(8)

    &--active
      pointer-events: none

    &--dropdown
      border-top-left-radius: 0
      border-top-right-radius: 0
      border-top: 1px solid $material-light.dividers

    & > .card
      contain: content
      backface-visibility: hidden

  &-transition
    &-enter
      .list__tile
        min-width: 0
        transition-delay: .4s
        opacity: 0
        transform: translateY(-15px)
        pointer-events: none

    &-enter-to
      .list__tile
        pointer-events: auto
        opacity: 1

      .list__tile--active
        transform: none !important

    &-leave-to
      transform: translateY(-10px)

    &-leave-active, &-leave-to
      pointer-events: none

    &-enter, &-leave-to
      opacity: 0

    &-enter-to, &-leave
      opacity: 1

    &-enter-active, &-leave-active
      transition: all .5s $transition.swing

.menu-transition-enter
  &.menu__content--auto
    .list__tile--active
      opacity: 1
      transform: none !important
      pointer-events: auto


return this.window ? 32 : 24
}
},
watch: {
window () {
this.updateApplication()
},
fixed () {
this.updateApplication()
},

keep keep keep keep keep replace keep keep keep keep keep


import Touch from '../../directives/touch'

export default {
  name: 'v-tabs-items',

  directives: { Touch },

  inject: ['next', 'prev'],

  props: {
    cycle: Boolean,
    touchless: Boolean
  },

  methods: {
    swipeLeft () {
      this.next(this.cycle)
    },
    swipeRight () {
      this.prev(this.cycle)
    }
  },

  render (h) {
    const data = {
      staticClass: 'tabs__items',
      directives: []
    }

    !this.touchless && data.directives.push({
      name: 'touch',
      value: {
        left: this.swipeLeft,
        right: this.swipeRight
      }
    })

    return h('div', data, this.$slots.default)
  }
}


import { createSimpleFunctional } from '../../util/helpers'

import VList from './VList'
import VListGroup from './VListGroup'
import VListTile from './VListTile'
import VListTileAction from './VListTileAction'
import VListTileAvatar from './VListTileAvatar'

export { VList, VListGroup, VListTile, VListTileAction, VListTileAvatar }
export const VListTileActionText = createSimpleFunctional('list__tile__action-text', 'span')
export const VListTileContent = createSimpleFunctional('list__tile__content', 'div')
export const VListTileTitle = createSimpleFunctional('list__tile__title', 'div')
export const VListTileSubTitle = createSimpleFunctional('list__tile__sub-title', 'div')

/* istanbul ignore next */
VList.install = function install (Vue) {
  Vue.component(VList.name, VList)
  Vue.component(VListGroup.name, VListGroup)
  Vue.component(VListTile.name, VListTile)
  Vue.component(VListTileAction.name, VListTileAction)
  Vue.component(VListTileActionText.name, VListTileActionText)
  Vue.component(VListTileAvatar.name, VListTileAvatar)
  Vue.component(VListTileContent.name, VListTileContent)
  Vue.component(VListTileSubTitle.name, VListTileSubTitle)
  Vue.component(VListTileTitle.name, VListTileTitle)
}

export default VList


},
fixed () {
this.updateApplication()
},
absolute () {
this.updateApplication()
}
},

keep keep keep keep replace keep keep keep keep

height () {
this.updateApplication()
},
window () {


import { test } from '~util/testing'
import { VBreadcrumbsItem } from '~components/VBreadcrumbs'

// TODO: Enable when Vue has optional injects
test.skip('VBreadcrumbsItem.js', ({ mount }) => {
  it('should render component and match snapshot', () => {
    const wrapper = mount(VBreadcrumbsItem)

    expect(wrapper.html()).toMatchSnapshot()
  })

  // TODO: Use vue-router or nuxt in tests
  it.skip('should have a custom active class', () => {
    const wrapper = mount(VBreadcrumbsItem, {
      propsData: {
        activeClass: 'breadcrumbs-item--active',
        to: '/'
      }
    })

    expect(wrapper.html()).toMatchSnapshot()
  })
})


require('../../stylus/components/_input-groups.styl')
require('../../stylus/components/_selection-controls.styl')

import VIcon from '../VIcon'
import { VFadeTransition } from '../transitions'
import Rippleable from '../../mixins/rippleable'
import Selectable from '../../mixins/selectable'

export default {
  name: 'v-checkbox',

  components: {
    VFadeTransition,
    VIcon
  },

  mixins: [Rippleable, Selectable],

  data () {
    return {
      inputIndeterminate: this.indeterminate
    }
  },

  props: {
    indeterminate: Boolean
  },

  computed: {
    classes () {
      const classes = {
        'checkbox': true,
        'input-group--selection-controls': true,
        'input-group--active': this.isActive
      }

      if (this.hasError) {
        classes['error--text'] = true
      } else {
        return this.addTextColorClassChecks(classes)
      }

      return classes
    },
    icon () {
      if (this.inputIndeterminate) {
        return 'indeterminate_check_box'
      } else if (this.isActive) {
        return 'check_box'
      } else {
        return 'check_box_outline_blank'
      }
    }
  },

  methods: {
    groupFocus (e) {
      this.isFocused = true
      this.$emit('focus', e)
    },
    groupBlur (e) {
      this.isFocused = false
      this.tabFocused = false
      this.$emit('blur', this.inputValue)
    }
  },

  render (h) {
    const transition = h('v-fade-transition', [
      h('v-icon', {
        staticClass: 'icon--selection-control',
        'class': {
          'icon--checkbox': this.icon === 'check_box'
        },
        key: this.icon,
        on: Object.assign({
          click: this.toggle
        }, this.$listeners)
      }, this.icon)
    ])

    const data = {
      attrs: {
        tabindex: this.disabled
          ? -1
          : this.internalTabIndex || this.tabindex,
        role: 'checkbox',
        'aria-checked': this.inputIndeterminate && 'mixed' || this.isActive && 'true' || 'false',
        'aria-label': this.label
      }
    }

    const ripple = this.ripple ? this.genRipple() : null

    return this.genInputGroup([transition, ripple], data)
  }
}


this.$vuetify.application.bar = (this.fixed || this.absolute) ? this.computedHeight : 0

keep keep keep keep keep replace replace keep keep keep keep


@import '../bootstrap'
@import '../theme'

/** Themes */
date-picker($material)
  .picker--date__years
    li
      color: $material.text.primary

  .picker--date__header-selector-date
    strong:not(:hover)
      color: $material.text.primary !important

theme(date-picker, "picker--date")

.picker--date__table
  .btn.btn--active
    color: #fff

/** Years */
.picker--date__years
  font-size: 16px
  font-weight: 400
  list-style-type: none
  max-height: 290px
  overflow: auto
  padding: 0
  text-align: center

  li
    cursor: pointer
    padding: 8px 0
    transition: none

    &.active
      font-size: 24px
      font-weight: 500
      padding: 10px 0

    &:hover
      background: rgba(0, 0, 0, 0.12)

/** Title */
.picker--date__title
  justify-content: space-between
  flex-direction: column
  flex-wrap: wrap

  &-year
    align-items: center
    display: inline-flex
    font-size: 14px

  &-date
    font-size: 34px
    text-align: left

    > div
      position: relative

  &-year, &-date
    font-weight: 500
    transition: $primary-transition
    width: 100%

    &:not(.active)
      cursor: pointer

/** Header */
.picker--date__header
  padding: 4px 16px

  &-selector
    align-items: center
    display: flex
    justify-content: space-between
    position: relative

    .btn
      margin: 0

    .icon
      cursor: pointer
      user-select: none

    &-date
      flex: 1
      text-align: center
      position: relative
      overflow: hidden

      strong
        cursor: pointer
        transition: $primary-transition
        display: block
        width: 100%

.picker--date
  .btn
    z-index: auto

  &__table
    position: relative

  table
    transition: $primary-transition
    top: 0
    table-layout: fixed

    th
      padding: 8px 0
      font-weight: 600
      font-size: 12px

    th, td
      text-align: center
      width: 45px

    .btn
      margin: 0
      height: 32px
      width: 32px

.picker--month__table
  table
    width: 100%

    td
      width: 33.333333%
      height: 56px
      vertical-align: middle


var types = require('babel-types');
var pathLib = require('path');
var wrapListener = require('babel-plugin-detective/wrap-listener');

module.exports = wrapListener(listener, 'transform-stylus-paths');

function listener(path, file, opts) {
  const regex = /((?:\.\.\/)+)/gi
  if (path.isLiteral() && path.node.value.endsWith('.styl')) {
    const matches = regex.exec(path.node.value)
    if (!matches) return

    path.node.value = path.node.value.replace(matches[0], `${matches[0]}../src/`)
  }
}


@import '../bootstrap'

.content
  flex: 1 1 auto
  max-width: 100%
  transition: none
  will-change: padding

  &--is-booted
    transition: .2s padding $transition.fast-out-slow-in

  &--wrap
    flex: 1 0 auto
    display: flex
    max-width: 100%


if (this.app && this.$vuetify) this.$vuetify.application.bar = 0

keep keep keep keep keep replace keep keep keep

if (this.app) this.$vuetify.application.bar = 0


html
  font-size: $font-size-root
  text-rendering: optimizeLegibility
  -webkit-font-smoothing: antialiased
  -moz-osx-font-smoothing: grayscale
  -webkit-tap-highlight-color: rgba(0, 0, 0, 0)
  overflow-x: hidden

.application
  font-family: $body-font-family
  line-height: $line-height-root

::-ms-clear
::-ms-reveal
  display: none


import Ripple from '../directives/ripple'

/** @mixin */
export default {
  directives: { Ripple },

  props: {
    ripple: {
      type: [Boolean, Object],
      default: true
    }
  },

  methods: {
    genRipple (data = { directives: [] }) {
      data.class = this.rippleClasses || 'input-group--selection-controls__ripple'
      data.directives.push({
        name: 'ripple',
        value: this.ripple && !this.disabled && { center: true }
      })
      data.on = Object.assign({
        click: this.toggle
      }, this.$listeners)

      return this.$createElement('div', data)
    }
  }
}


@import '../bootstrap'
@import '../theme'

.application
  display: flex

  &--wrap
    flex: 1 1 auto
    backface-visibility: hidden
    display: flex
    flex-direction: column
    min-height: 100vh
    max-width: 100%
    position: relative

application($material)
  background: $material.background
  color: $material.text.primary

  a
    cursor: pointer

  .text
    &--primary
      color: $material.text.primary !important

    &--secondary
      color: $material.text.secondary !important

    &--disabled
      color: $material.text.disabled !important

app(application)


require('../../stylus/components/_system-bars.styl')

import Applicationable from '../../mixins/applicationable'
import Colorable from '../../mixins/colorable'
import Themeable from '../../mixins/themeable'

export default {
  name: 'v-system-bar',

  mixins: [Applicationable, Colorable, Themeable],

  props: {
    absolute: Boolean,
    fixed: Boolean,
    lightsOut: Boolean,
    status: Boolean,
    window: Boolean
  },

  computed: {
    classes () {
      return this.addBackgroundColorClassChecks(Object.assign({
        'system-bar--lights-out': this.lightsOut,
        'system-bar--absolute': this.absolute,
        'system-bar--fixed': this.fixed,
        'system-bar--status': this.status,
        'system-bar--window': this.window
      }, this.themeClasses))
    },
    computedHeight () {
      return this.window ? 32 : 24
    }
  },

  watch: {
    window () {
      this.updateApplication()
    },
    fixed () {
      this.updateApplication()
    },
    absolute () {
      this.updateApplication()
    }
  },

  methods: {
    updateApplication () {
      if (this.app && this.$vuetify) {
        this.$vuetify.application.bar = (this.fixed || this.absolute) ? this.computedHeight : 0
      }
    }
  },

  destroyed () {
    if (this.app && this.$vuetify) this.$vuetify.application.bar = 0
  },

  render (h) {
    const data = {
      staticClass: 'system-bar',
      'class': this.classes,
      style: {
        height: `${this.computedHeight}px`
      }
    }

    return h('div', data, this.$slots.default)
  }
}


import VForm from './VForm'

/* istanbul ignore next */
VForm.install = function install (Vue) {
  Vue.component(VForm.name, VForm)
}

export default VForm


export default {
  data () {
    return {
      tabFocused: false
    }
  }
}


import VSubheader from './VSubheader'

/* istanbul ignore next */
VSubheader.install = function install (Vue) {
  Vue.component(VSubheader.name, VSubheader)
}

export default VSubheader


if (!this.isExtended) return this.computedContentHeight

return this.computedContentHeight + this.computedExtensionHeight

'elevation-0': this.flat || (!this.isActiveProxy && !this.tabs),


// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`VMenu.js should render component with custom activator and match snapshot 1`] = `

`;

exports[`VMenu.js should render component with custom auto and match snapshot 1`] = `

`;

exports[`VMenu.js should render component with custom bottom and match snapshot 1`] = `

`;

exports[`VMenu.js should render component with custom closeOnClick and match snapshot 1`] = `

`;

exports[`VMenu.js should render component with custom closeOnContentClick and match snapshot 1`] = `

`;

exports[`VMenu.js should render component with custom disabled and match snapshot 1`] = `

`;

exports[`VMenu.js should render component with custom fullWidth and match snapshot 1`] = `

`;

exports[`VMenu.js should render component with custom lazy and match snapshot 1`] = `

`;

exports[`VMenu.js should render component with custom left and match snapshot 1`] = `

`;

exports[`VMenu.js should render component with custom maxHeight and match snapshot 1`] = `

`;

exports[`VMenu.js should render component with custom maxWidth and match snapshot 1`] = `

`;

exports[`VMenu.js should render component with custom minWidth and match snapshot 1`] = `

`;

exports[`VMenu.js should render component with custom nudgeBottom and match snapshot 1`] = `

`;

exports[`VMenu.js should render component with custom nudgeLeft and match snapshot 1`] = `

`;

exports[`VMenu.js should render component with custom nudgeRight and match snapshot 1`] = `

`;

exports[`VMenu.js should render component with custom nudgeTop and match snapshot 1`] = `

`;

exports[`VMenu.js should render component with custom nudgeWidth and match snapshot 1`] = `

`;

exports[`VMenu.js should render component with custom offsetX and match snapshot 1`] = `

`;

exports[`VMenu.js should render component with custom offsetY and match snapshot 1`] = `

`;

exports[`VMenu.js should render component with custom openOnClick and match snapshot 1`] = `

`;

exports[`VMenu.js should render component with custom openOnHover and match snapshot 1`] = `

`;

exports[`VMenu.js should render component with custom origin and match snapshot 1`] = `

`;

exports[`VMenu.js should render component with custom positionAbsolutely and match snapshot 1`] = `

`;

exports[`VMenu.js should render component with custom positionX and match snapshot 1`] = `

`;

exports[`VMenu.js should render component with custom positionY and match snapshot 1`] = `

`;

exports[`VMenu.js should render component with custom right and match snapshot 1`] = `

`;

exports[`VMenu.js should render component with custom top and match snapshot 1`] = `

`;

exports[`VMenu.js should render component with custom transition and match snapshot 1`] = `

`;

exports[`VMenu.js should work 1`] = `

    <button type="button"
            class="btn"
            data-ripple="true"
    >

    </button>

`;


export default {
  data () {
    return {
      buttons: [],
      listeners: []
    }
  },

  methods: {
    getValue (i) {
      if (this.buttons[i].value != null) {
        return this.buttons[i].value
      }

      // Fix for testing, this should always be false in the browser
      if (this.buttons[i].$el.value != null && this.buttons[i].$el.value !== '') {
        return this.buttons[i].$el.value
      }

      return i
    },
    update () {
      const selected = []

      this.buttons
        .forEach((button, i) => {
          const elm = button.$el

          // Fix for testing, dataset does not exist on elm?
          if (!elm.dataset) elm.dataset = {}

          elm.removeAttribute('data-only-child')

          if (this.isSelected(i)) {
            elm.setAttribute('data-selected', true)

            if (!elm.classList.contains('btn--router')) {
              elm.classList.add('btn--active')
            }

            selected.push(i)
          } else {
            elm.removeAttribute('data-selected')

            if (!elm.classList.contains('btn--router')) {
              elm.classList.remove('btn--active')
            }
          }

          elm.dataset.index = i
        })

      if (selected.length === 1) {
        this.buttons[selected[0]].$el.setAttribute('data-only-child', true)
      }
    }
  },

  mounted () {
    this.buttons = this.$children

    this.buttons.forEach((button, i) => {
      this.listeners.push(this.updateValue.bind(this, i))
      button.$on('click', this.listeners[i])
    })

    this.update()
  },

  beforeDestroy () {
    this.buttons.forEach((button, i) => {
      button.$off('click', this.listeners[i])
    })
  }
}


import VDataIterator from './VDataIterator'

VDataIterator.install = function install (Vue) {
  Vue.component(VDataIterator.name, VDataIterator)
}

export default VDataIterator


function style (el, value) {
  [
    'transform',
    'webkitTransform'
  ].forEach(i => {
    el.style[i] = value
  })
}

const RippleDataAttribute = 'data-ripple'

const ripple = {
  /**
   * @param {Event} e
   * @param {Element} el
   * @param {{ class?: string, center?: boolean }} [value={}]
   */
  show: (e, el, { value = {} }) => {
    if (el.getAttribute(RippleDataAttribute) !== 'true') {
      return
    }

    const container = document.createElement('span')
    const animation = document.createElement('span')

    container.appendChild(animation)
    container.className = 'ripple__container'

    if (value.class) {
      container.className += ` ${value.class}`
    }

    const size = el.clientWidth > el.clientHeight
      ? el.clientWidth
      : el.clientHeight
    animation.className = 'ripple__animation'
    animation.style.width = `${size * (value.center ? 1 : 2)}px`
    animation.style.height = animation.style.width

    el.appendChild(container)
    const computed = window.getComputedStyle(el)
    if (computed.position !== 'absolute' && computed.position !== 'fixed') el.style.position = 'relative'

    const offset = el.getBoundingClientRect()
    const x = value.center ? '50%' : `${e.clientX - offset.left}px`
    const y = value.center ? '50%' : `${e.clientY - offset.top}px`

    animation.classList.add('ripple__animation--enter')
    animation.classList.add('ripple__animation--visible')
    style(animation, `translate(-50%, -50%) translate(${x}, ${y}) scale3d(0.01,0.01,0.01)`)
    animation.dataset.activated = Date.now()

    setTimeout(() => {
      animation.classList.remove('ripple__animation--enter')
      style(animation, `translate(-50%, -50%) translate(${x}, ${y})  scale3d(0.99,0.99,0.99)`)
    }, 0)
  },

  hide: (el) => {
    if (el.getAttribute(RippleDataAttribute) !== 'true') {
      return
    }

    const ripples = el.getElementsByClassName('ripple__animation')

    if (ripples.length === 0) return
    const animation = ripples[ripples.length - 1]
    const diff = Date.now() - Number(animation.dataset.activated)
    let delay = 400 - diff

    delay = delay < 0 ? 0 : delay

    setTimeout(() => {
      animation.classList.remove('ripple__animation--visible')

      setTimeout(() => {
        // Need to figure out a new way to do this
        try {
          if (ripples.length < 1) el.style.position = null
          animation.parentNode && el.removeChild(animation.parentNode)
        } catch (e) {}
      }, 300)
    }, delay)
  }
}

function isRippleEnabled (binding) {
  return typeof binding.value === 'undefined' || !!binding.value
}

function directive (el, binding) {
  el.setAttribute(RippleDataAttribute, isRippleEnabled(binding))

  if ('ontouchstart' in window) {
    el.addEventListener('touchend', () => ripple.hide(el), false)
    el.addEventListener('touchcancel', () => ripple.hide(el), false)
  }

  el.addEventListener('mousedown', e => ripple.show(e, el, binding), false)
  el.addEventListener('mouseup', () => ripple.hide(el), false)
  el.addEventListener('mouseleave', () => ripple.hide(el), false)
  // Anchor tags can be dragged, causes other hides to fail - #1537
  el.addEventListener('dragstart', () => ripple.hide(el), false)
}

function unbind (el, binding) {
  el.removeEventListener('touchstart', e => ripple.show(e, el, binding), false)
  el.removeEventListener('mousedown', e => ripple.show(e, el, binding), false)
  el.removeEventListener('touchend', () => ripple.hide(el), false)
  el.removeEventListener('touchcancel', () => ripple.hide(el), false)
  el.removeEventListener('mouseup', () => ripple.hide(el), false)
  el.removeEventListener('mouseleave', () => ripple.hide(el), false)
  el.removeEventListener('dragstart', () => ripple.hide(el), false)
}

function update (el, binding) {
  if (binding.value === binding.oldValue) {
    return
  }

  el.setAttribute(RippleDataAttribute, isRippleEnabled(binding))
}

export default {
  name: 'ripple',
  bind: directive,
  unbind: unbind,
  update: update
}


import VBadge from './VBadge'

/* istanbul ignore next */
VBadge.install = function install (Vue) {
  Vue.component(VBadge.name, VBadge)
}

export default VBadge


$i = 0
for level, spacer in $spacers
  .mt-{$i}
    margin-top: spacer.y !important
  .mr-{$i}
    margin-right: spacer.x !important
  .mb-{$i}
    margin-bottom: spacer.y !important
  .ml-{$i}
    margin-left: spacer.x !important
  .mx-{$i}
    margin-left: spacer.x !important
    margin-right: spacer.x !important
  .my-{$i}
    margin-top: spacer.y !important
    margin-bottom: spacer.y !important
  .ma-{$i}
    margin: spacer.y spacer.x !important

  .pt-{$i}
    padding-top: spacer.y !important
  .pr-{$i}
    padding-right: spacer.x !important
  .pb-{$i}
    padding-bottom: spacer.y !important
  .pl-{$i}
    padding-left: spacer.x !important
  .px-{$i}
    padding-left: spacer.x !important
    padding-right: spacer.x !important
  .py-{$i}
    padding-top: spacer.y !important
    padding-bottom: spacer.y !important
  .pa-{$i}
    padding: spacer.y spacer.x !important

  $i = $i + 1


// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`VListTileAction.js should render component and match snapshot 1`] = `

`;

exports[`VListTileAction.js should render component with many children and match snapshot 1`] = `

`;

exports[`VListTileAction.js should render component with static class and match snapshot 1`] = `

`;


import VDivider from './VDivider'

/* istanbul ignore next */
VDivider.install = function install (Vue) {
  Vue.component(VDivider.name, VDivider)
}

export default VDivider


// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`VDatePicker.js should match snapshot with allowed dates as array 1`] = `

      <button type="button"
              class="btn btn--flat"
      >

          Jan

      </button>

      <button type="button"
              class="btn btn--flat btn--disabled"
      >

          Feb

      </button>

      <button type="button"
              class="btn btn--flat"
      >

          Mar

      </button>

      <button type="button"
              class="btn btn--flat btn--disabled"
      >

          Apr

      </button>

      <button type="button"
              class="btn btn--active accent"
      >

          May

      </button>

      <button type="button"
              class="btn btn--flat btn--disabled"
      >

          Jun

      </button>

      <button type="button"
              class="btn btn--flat"
      >

          Jul

      </button>

      <button type="button"
              class="btn btn--flat btn--disabled"
      >

          Aug

      </button>

      <button type="button"
              class="btn btn--flat btn--disabled"
      >

          Sep

      </button>

      <button type="button"
              class="btn btn--flat btn--disabled"
      >

          Oct

      </button>

      <button type="button"
              class="btn btn--flat btn--disabled"
      >

          Nov

      </button>

      <button type="button"
              class="btn btn--flat btn--disabled"
      >

          Dec

      </button>

`;

exports[`VDatePicker.js should match snapshot with allowed dates as function 1`] = `

      <button type="button"
              class="btn btn--flat"
      >

          Jan

      </button>

      <button type="button"
              class="btn btn--flat btn--disabled"
      >

          Feb

      </button>

      <button type="button"
              class="btn btn--flat btn--disabled"
      >

          Mar

      </button>

      <button type="button"
              class="btn btn--flat btn--disabled"
      >

          Apr

      </button>

      <button type="button"
              class="btn btn--active btn--disabled accent"
      >

          May

      </button>

      <button type="button"
              class="btn btn--flat btn--disabled"
      >

          Jun

      </button>

      <button type="button"
              class="btn btn--flat btn--disabled"
      >

          Jul

      </button>

      <button type="button"
              class="btn btn--flat btn--disabled"
      >

          Aug

      </button>

      <button type="button"
              class="btn btn--flat btn--disabled"
      >

          Sep

      </button>

      <button type="button"
              class="btn btn--flat btn--disabled"
      >

          Oct

      </button>

      <button type="button"
              class="btn btn--flat"
      >

          Nov

      </button>

      <button type="button"
              class="btn btn--flat btn--disabled"
      >

          Dec

      </button>

`;

exports[`VDatePicker.js should match snapshot with allowed dates as object 1`] = `

      <button type="button"
              class="btn btn--flat btn--disabled"
      >

          Jan

      </button>

      <button type="button"
              class="btn btn--flat btn--disabled"
      >

          Feb

      </button>

      <button type="button"
              class="btn btn--flat"
      >

          Mar

      </button>

      <button type="button"
              class="btn btn--flat"
      >

          Apr

      </button>

      <button type="button"
              class="btn btn--active accent"
      >

          May

      </button>

      <button type="button"
              class="btn btn--flat"
      >

          Jun

      </button>

      <button type="button"
              class="btn btn--flat"
      >

          Jul

      </button>

      <button type="button"
              class="btn btn--flat btn--disabled"
      >

          Aug

      </button>

      <button type="button"
              class="btn btn--flat btn--disabled"
      >

          Sep

      </button>

      <button type="button"
              class="btn btn--flat btn--disabled"
      >

          Oct

      </button>

      <button type="button"
              class="btn btn--flat btn--disabled"
      >

          Nov

      </button>

      <button type="button"
              class="btn btn--flat btn--disabled"
      >

          Dec

      </button>

`;

exports[`VDatePicker.js should match snapshot with colored picker 1`] = `

      2005

        November

        <button type="button"
                class="btn btn--icon"
                data-ripple="true"
        >

              chevron_left

        </button>

            2005

        <button type="button"
                class="btn btn--icon"
                data-ripple="true"
        >

              chevron_right

        </button>

              <button type="button"
                      class="btn btn--flat"
              >

                  Jan

              </button>

              <button type="button"
                      class="btn btn--flat"
              >

                  Feb

              </button>

              <button type="button"
                      class="btn btn--flat"
              >

                  Mar

              </button>

              <button type="button"
                      class="btn btn--flat"
              >

                  Apr

              </button>

              <button type="button"
                      class="btn btn--flat"
              >

                  May

              </button>

              <button type="button"
                      class="btn btn--flat"
              >

                  Jun

              </button>

              <button type="button"
                      class="btn btn--flat"
              >

                  Jul

              </button>

              <button type="button"
                      class="btn btn--flat"
              >

                  Aug

              </button>

              <button type="button"
                      class="btn btn--flat"
              >

                  Sep

              </button>

              <button type="button"
                      class="btn btn--flat"
              >

                  Oct

              </button>

              <button type="button"
                      class="btn btn--active primary"
              >

                  Nov

              </button>

              <button type="button"
                      class="btn btn--flat"
              >

                  Dec

              </button>

`;

exports[`VDatePicker.js should match snapshot with colored picker 2`] = `

      2005

        November

        <button type="button"
                class="btn btn--icon"
                data-ripple="true"
        >

              chevron_left

        </button>

            2005

        <button type="button"
                class="btn btn--icon"
                data-ripple="true"
        >

              chevron_right

        </button>

              <button type="button"
                      class="btn btn--flat"
              >

                  Jan

              </button>

              <button type="button"
                      class="btn btn--flat"
              >

                  Feb

              </button>

              <button type="button"
                      class="btn btn--flat"
              >

                  Mar

              </button>

              <button type="button"
                      class="btn btn--flat"
              >

                  Apr

              </button>

              <button type="button"
                      class="btn btn--flat"
              >

                  May

              </button>

              <button type="button"
                      class="btn btn--flat"
              >

                  Jun

              </button>

              <button type="button"
                      class="btn btn--flat"
              >

                  Jul

              </button>

              <button type="button"
                      class="btn btn--flat"
              >

                  Aug

              </button>

              <button type="button"
                      class="btn btn--flat"
              >

                  Sep

              </button>

              <button type="button"
                      class="btn btn--flat"
              >

                  Oct

              </button>

              <button type="button"
                      class="btn btn--active orange darken-1"
              >

                  Nov

              </button>

              <button type="button"
                      class="btn btn--flat"
              >

                  Dec

              </button>

`;

exports[`VDatePicker.js should match snapshot with month formatting functions 1`] = `

      <button type="button"
              class="btn btn--flat"
      >

          (01)

      </button>

      <button type="button"
              class="btn btn--flat"
      >

          (02)

      </button>

      <button type="button"
              class="btn btn--flat"
      >

          (03)

      </button>

      <button type="button"
              class="btn btn--flat"
      >

          (04)

      </button>

      <button type="button"
              class="btn btn--flat"
      >

          (05)

      </button>

      <button type="button"
              class="btn btn--flat"
      >

          (06)

      </button>

      <button type="button"
              class="btn btn--flat"
      >

          (07)

      </button>

      <button type="button"
              class="btn btn--flat"
      >

          (08)

      </button>

      <button type="button"
              class="btn btn--flat"
      >

          (09)

      </button>

      <button type="button"
              class="btn btn--flat"
      >

          (10)

      </button>

      <button type="button"
              class="btn btn--active accent"
      >

          (11)

      </button>

      <button type="button"
              class="btn btn--flat"
      >

          (12)

      </button>

`;

exports[`VDatePicker.js should match snapshot with pick-month prop 1`] = `

      2013

        May

        <button type="button"
                class="btn btn--icon"
                data-ripple="true"
        >

              chevron_left

        </button>

            2013

        <button type="button"
                class="btn btn--icon"
                data-ripple="true"
        >

              chevron_right

        </button>

              <button type="button"
                      class="btn btn--flat"
              >

                  Jan

              </button>

              <button type="button"
                      class="btn btn--flat"
              >

                  Feb

              </button>

              <button type="button"
                      class="btn btn--flat"
              >

                  Mar

              </button>

              <button type="button"
                      class="btn btn--flat"
              >

                  Apr

              </button>

              <button type="button"
                      class="btn btn--active accent"
              >

                  May

              </button>

              <button type="button"
                      class="btn btn--flat"
              >

                  Jun

              </button>

              <button type="button"
                      class="btn btn--flat"
              >

                  Jul

              </button>

              <button type="button"
                      class="btn btn--flat"
              >

                  Aug

              </button>

              <button type="button"
                      class="btn btn--flat"
              >

                  Sep

              </button>

              <button type="button"
                      class="btn btn--flat"
              >

                  Oct

              </button>

              <button type="button"
                      class="btn btn--flat"
              >

                  Nov

              </button>

              <button type="button"
                      class="btn btn--flat"
              >

                  Dec

              </button>

`;


keep keep keep keep keep replace keep keep keep keep

isSubscribed = (_id) ->
	return ChatSubscription.find({ rid: _id }).count() > 0

katexSyntax = ->
	if RocketChat.katex.katex_enabled()
		return "$$KaTeX$$"   if RocketChat.katex.dollar_syntax_enabled()
		return "\\[KaTeX\\]" if RocketChat.katex.parenthesis_syntax_enabled()

	return false

Template.messageBox.helpers
	roomName: ->
		roomData = Session.get('roomData' + this._id)
		return '' unless roomData

		if roomData.t is 'd'
			return ChatSubscription.findOne({ rid: this._id }, { fields: { name: 1 } })?.name
		else
			return roomData.name
	showMarkdown: ->
		return RocketChat.Markdown
	showMarkdownCode: ->
		return RocketChat.MarkdownCode
	showKatex: ->
		return RocketChat.katex
	katexSyntax: ->
		return katexSyntax()
	showFormattingTips: ->
		return RocketChat.settings.get('Message_ShowFormattingTips') and (RocketChat.Markdown or RocketChat.MarkdownCode or katexSyntax())
	canJoin: ->
		return !! ChatRoom.findOne { _id: @_id, t: 'c' }
	subscribed: ->
		return isSubscribed(this._id)
	getPopupConfig: ->
		template = Template.instance()
		return {
			getInput: ->
				return template.find('.input-message')
		}
	usersTyping: ->
		users = MsgTyping.get @_id
		if users.length is 0
			return
		if users.length is 1
			return {
				multi: false
				selfTyping: MsgTyping.selfTyping.get()
				users: users[0]
			}
		# usernames = _.map messages, (message) -> return message.u.username
		last = users.pop()
		if users.length > 4
			last = t('others')
		# else
		usernames = users.join(', ')
		usernames = [usernames, last]
		return {
			multi: true
			selfTyping: MsgTyping.selfTyping.get()
			users: usernames.join " #{t 'and'} "
		}
	fileUploadAllowedMediaTypes: ->
		return RocketChat.settings.get('FileUpload_MediaTypeWhiteList')

	showMic: ->
		if not Template.instance().isMessageFieldEmpty.get()
			return

		if Template.instance().showMicButton.get()
			return 'show-mic'

	showVRec: ->
		if not Template.instance().isMessageFieldEmpty.get()
			return

		if Template.instance().showVideoRec.get()
			return ''
		else
			return 'hide-vrec'

	showSend: ->
		if not Template.instance().isMessageFieldEmpty.get() or not Template.instance().showMicButton.get()
			return 'show-send'

Template.messageBox.events
	'click .join': (event) ->
		event.stopPropagation()
		event.preventDefault()
		Meteor.call 'joinRoom', @_id

	'focus .input-message': (event) ->
		KonchatNotification.removeRoomNotification @_id

	'click .send-button': (event, instance) ->
		input = instance.find('.input-message')
		chatMessages[@_id].send(@_id, input, =>
			# fixes https://github.com/RocketChat/Rocket.Chat/issues/3037
			# at this point, the input is cleared and ready for autogrow
			input.updateAutogrow()
			instance.isMessageFieldEmpty.set(chatMessages[@_id].isEmpty())
		)
		input.focus()

	'keyup .input-message': (event, instance) ->
		chatMessages[@_id].keyup(@_id, event, instance)
		instance.isMessageFieldEmpty.set(chatMessages[@_id].isEmpty())

	'paste .input-message': (e) ->
		if not e.originalEvent.clipboardData?
			return

		items = e.originalEvent.clipboardData.items
		files = []
		for item in items
			if item.kind is 'file' and item.type.indexOf('image/') isnt -1
				e.preventDefault()
				files.push
					file: item.getAsFile()
					name: 'Clipboard'

		if files.length > 0
			fileUpload files

	'keydown .input-message': (event) ->
		chatMessages[@_id].keydown(@_id, event, Template.instance())

	"click .editing-commands-cancel > button": (e) ->
		chatMessages[@_id].clearEditing()

	"click .editing-commands-save > button": (e) ->
		chatMessages[@_id].send(@_id, chatMessages[@_id].input)

	'change .message-form input[type=file]': (event, template) ->
		e = event.originalEvent or event
		files = e.target.files
		if not files or files.length is 0
			files = e.dataTransfer?.files or []

		filesToUpload = []
		for file in files
			filesToUpload.push
				file: file
				name: file.name

		fileUpload filesToUpload

	'click .message-form .mic': (e, t) ->
		AudioRecorder.start ->
			t.$('.stop-mic').removeClass('hidden')
			t.$('.mic').addClass('hidden')

	'click .message-form .video-button': (e, t) ->
		if VRecDialog.opened
			VRecDialog.close()
		else
			VRecDialog.open(e.currentTarget)

	'click .message-form .stop-mic': (e, t) ->
		AudioRecorder.stop (blob) ->
			fileUpload [{
				file: blob
				type: 'audio'
				name: TAPi18n.__('Audio record') + '.wav'
			}]

		t.$('.stop-mic').addClass('hidden')
		t.$('.mic').removeClass('hidden')

Template.messageBox.onCreated ->
	@isMessageFieldEmpty = new ReactiveVar true
	@showMicButton = new ReactiveVar false
	@showVideoRec = new ReactiveVar false

	@autorun =>
		videoRegex = /video\/webm/i
		videoEnabled = !RocketChat.settings.get("FileUpload_MediaTypeWhiteList") || RocketChat.settings.get("FileUpload_MediaTypeWhiteList").match(wavRegex)
		if RocketChat.settings.get('Message_VideoRecorderEnabled') and (navigator.getUserMedia? or navigator.webkitGetUserMedia?) and videoEnabled and RocketChat.settings.get('FileUpload_Enabled')
			@showVideoRec.set true
		else
			@showVideoRec.set false

		wavRegex = /audio\/wav|audio\/\*/i
		wavEnabled = !RocketChat.settings.get("FileUpload_MediaTypeWhiteList") || RocketChat.settings.get("FileUpload_MediaTypeWhiteList").match(wavRegex)
		if RocketChat.settings.get('Message_AudioRecorderEnabled') and (navigator.getUserMedia? or navigator.webkitGetUserMedia?) and wavEnabled and RocketChat.settings.get('FileUpload_Enabled')
			@showMicButton.set true
		else
			@showMicButton.set false


<template name="videoCall">
	{{#if videoAvaliable}}
		{{#if videoActive}}

					<video src="{{mainVideoUrl}}" autoplay muted="true"></video>

{{mainVideoUsername}}

					{{#if selfVideoUrl}}

							<video src="{{selfVideoUrl}}" autoplay muted="true"></video>
							{{#unless audioAndVideoEnabled}}

									{{#unless audioEnabled}}
										<button></button>
									{{/unless}}
									{{#unless videoEnabled}}
										<button></button>
									{{/unless}}

							{{/unless}}

{{_ "you"}}

					{{/if}}
					{{#each remoteVideoItems}}

							<video src="{{url}}" autoplay></video>

{{usernameByUserId id}}

					{{/each}}

					{{#if videoActive}}
						<button class="stop-call button red">{{_ "Stop"}}</button>
						{{#if audioEnabled}}
							<button class="disable-audio button red" title="{{_ 'Mute'}}" aria-label="{{_ 'Mute'}}"></button>
						{{else}}
							<button class="enable-audio button" title="{{_ 'Unmute'}}" aria-label="{{_ 'Unmute'}}"></button>
						{{/if}}
						{{#if videoEnabled}}
							<button class="disable-video button red"></button>
						{{else}}
							<button class="enable-video button"></button>
						{{/if}}
						{{#if screenShareAvailable}}
							{{#if screenShareEnabled}}
								<button class="disable-screen-share button red"></button>
							{{else}}
								<button class="enable-screen-share button"></button>
							{{/if}}
						{{/if}}
						{{#if overlayEnabled}}
							<button class="disable-overlay button red"></button>
						{{else}}
							<button class="enable-overlay button"></button>
						{{/if}}
					{{/if}}

		{{/if}}
	{{/if}}
</template>


RocketChat.settings.addGroup('Piwik', function addSettings() {
	this.add('PiwikAnalytics_url', '', {
		type: 'string',
		public: true,
		i18nLabel: 'URL'
	});
	this.add('PiwikAnalytics_siteId', '', {
		type: 'string',
		public: true,
		i18nLabel: 'Client_ID'
	});

	this.section('Analytics_features_enabled', function addFeaturesEnabledSettings() {
		this.add('PiwikAnalytics_features_messages', true, {
			type: 'boolean',
			public: true,
			i18nLabel: 'Messages',
			i18nDescription: 'Analytics_features_messages_Description'
		});
		this.add('PiwikAnalytics_features_rooms', true, {
			type: 'boolean',
			public: true,
			i18nLabel: 'Rooms',
			i18nDescription: 'Analytics_features_rooms_Description'
		});
		this.add('PiwikAnalytics_features_users', true, {
			type: 'boolean',
			public: true,
			i18nLabel: 'Users',
			i18nDescription: 'Analytics_features_users_Description'
		});
	});
});


Template.adminInviteUser.helpers
	isAdmin: ->
		return RocketChat.authz.hasRole(Meteor.userId(), 'admin')
	inviteEmails: ->
		return Template.instance().inviteEmails.get()

Template.adminInviteUser.events
	'click .send': (e, instance) ->
		emails = $('#inviteEmails').val().split /[\s,;]/
		rfcMailPattern = /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
		validEmails = _.compact _.map emails, (email) -> return email if rfcMailPattern.test email
		if validEmails.length
			Meteor.call 'sendInvitationEmail', validEmails, (error, result) ->
				if result
					instance.clearForm()
					instance.inviteEmails.set validEmails
				if error
					handleError(error)
		else
			toastr.error t('Send_invitation_email_error')

	'click .cancel': (e, instance) ->
		instance.clearForm()
		instance.inviteEmails.set []
		RocketChat.TabBar.closeFlex()

Template.adminInviteUser.onCreated ->
	@inviteEmails = new ReactiveVar []
	@clearForm = ->
		$('#inviteEmails').val('')


videoEnabled = !RocketChat.settings.get("FileUpload_MediaTypeWhiteList") || RocketChat.settings.get("FileUpload_MediaTypeWhiteList").match(wavRegex)

if RocketChat.settings.get('Message_VideoRecorderEnabled') and (navigator.getUserMedia? or navigator.webkitGetUserMedia?) and videoEnabled and RocketChat.settings.get('FileUpload_Enabled')

keep keep keep keep replace keep keep keep keep keep

videoEnabled = !RocketChat.settings.get("FileUpload_MediaTypeWhiteList") || RocketChat.settings.get("FileUpload_MediaTypeWhiteList").match(videoRegex)

@VideoRecorder = new class
	started: false
	cameraStarted: new ReactiveVar false
	recording: new ReactiveVar false
	recordingAvailable: new ReactiveVar false

	start: (videoel, cb) ->
		navigator.getUserMedia = navigator.getUserMedia or navigator.webkitGetUserMedia or navigator.mozGetUserMedia
		window.URL = window.URL or window.webkitURL

		@videoel = videoel
		ok = (stream) =>
			@startUserMedia(stream)
			cb?.call(@)

		if not navigator.getUserMedia?
			return cb false

		navigator.getUserMedia {audio: true, video: true}, ok, (e) ->
			console.log('No live video input: ' + e)

	record: ->
		@chunks = []
		if not @stream?
			return
		@mediaRecorder = new MediaRecorder(@stream)
		@mediaRecorder.stream = @stream
		@mediaRecorder.mimeType = 'video/webm'
		@mediaRecorder.ondataavailable = (blobev) =>
			@chunks.push(blobev.data)
			if not @recordingAvailable.get()
				@recordingAvailable.set true
		@mediaRecorder.start()
		@recording.set true

	startUserMedia: (stream) ->
		@stream = stream
		@videoel.src = URL.createObjectURL(stream)
		@videoel.onloadedmetadata = (e) =>
			@videoel.play()

		@started = true
		@cameraStarted.set true

	stop: (cb) ->
		if @started
			@stopRecording()

			if @stream?
				@stream.getVideoTracks()[0].stop()

			if @videoel?
				@videoel.pause
				@videoel.src = ''

			@started = false
			@cameraStarted.set false
			@recordingAvailable.set false

			if cb? and @chunks?
				blob = new Blob(@chunks, { 'type' :  'video/webm' })
				cb(blob)

			delete @recorder
			delete @stream
			delete @videoel

	stopRecording: ->
		if @started and @recording and @mediaRecorder?
			@mediaRecorder.stop()
			@recording.set false
			delete @mediaRecorder


//! moment.js locale configuration
//! locale : french (fr)
//! author : John Fischer : https://github.com/jfroffice

(function (global, factory) {
   typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('../moment')) :
   typeof define === 'function' && define.amd ? define(['moment'], factory) :
   factory(global.moment)
}(this, function (moment) { 'use strict';

    var fr = moment.defineLocale('fr', {
        months : 'janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre'.split('_'),
        monthsShort : 'janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.'.split('_'),
        weekdays : 'dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi'.split('_'),
        weekdaysShort : 'dim._lun._mar._mer._jeu._ven._sam.'.split('_'),
        weekdaysMin : 'Di_Lu_Ma_Me_Je_Ve_Sa'.split('_'),
        longDateFormat : {
            LT : 'HH:mm',
            LTS : 'LT:ss',
            L : 'DD/MM/YYYY',
            LL : 'D MMMM YYYY',
            LLL : 'D MMMM YYYY LT',
            LLLL : 'dddd D MMMM YYYY LT'
        },
        calendar : {
            sameDay: '[Aujourd\'hui à] LT',
            nextDay: '[Demain à] LT',
            nextWeek: 'dddd [à] LT',
            lastDay: '[Hier à] LT',
            lastWeek: 'dddd [dernier à] LT',
            sameElse: 'L'
        },
        relativeTime : {
            future : 'dans %s',
            past : 'il y a %s',
            s : 'quelques secondes',
            m : 'une minute',
            mm : '%d minutes',
            h : 'une heure',
            hh : '%d heures',
            d : 'un jour',
            dd : '%d jours',
            M : 'un mois',
            MM : '%d mois',
            y : 'un an',
            yy : '%d ans'
        },
        ordinalParse: /\d{1,2}(er|)/,
        ordinal : function (number) {
            return number + (number === 1 ? 'er' : '');
        },
        week : {
            dow : 1, // Monday is the first day of the week.
            doy : 4  // The week that contains Jan 4th is the first week of the year.
        }
    });

    return fr;

}));


Template.oauthApps.onCreated ->
	@subscribe 'oauthApps'

Template.oauthApps.helpers
	hasPermission: ->
		return RocketChat.authz.hasAllPermission 'manage-oauth-apps'

	applications: ->
		return ChatOAuthApps.find()

	dateFormated: (date) ->
		return moment(date).format('L LT')