"use strict";

const diff = require("jest-diff");
const {
  EXPECTED_COLOR,
  RECEIVED_COLOR,
  ensureNoExpected,
  matcherHint,
  printExpected,
  printReceived,
  printWithType,
} = require("jest-matcher-utils");

/**
 * Jest matchers for figgy-pudding instances
 *
 *    expect.extend(require("@lerna-test/figgy-pudding-matchers"))
 */
exports.toBeFiggyPudding = toBeFiggyPudding;
exports.toHaveFiggyPudding = toHaveFiggyPudding;
exports.figgyPudding = figgyPudding;

// expect(foo).toBeFiggyPudding();
function toBeFiggyPudding(received, expected) {
  ensureNoExpected(expected, ".toBeFiggyPudding");

  return {
    pass: isFiggyInstance(received),
    message: () =>
      `${matcherHint(".toBeFiggyPudding", "received", "", {
        isNot: this.isNot,
      })}\n\nReceived: ${printReceived(received)}`,
  };
}

// expect(foo).toHaveFiggyPudding({ ...spec });
function toHaveFiggyPudding(received, expected) {
  if (!isFiggyInstance(received)) {
    throw new Error(
      `${matcherHint("[.not].toHaveFiggyPudding", "object", "expected")}\n\n` +
        `${RECEIVED_COLOR("received")} value must be a FiggyPudding instance.\n${printWithType(
          "Received",
          received,
          printReceived
        )}`
    );
  }

  if (!isObjectWithKeys(expected)) {
    throw new Error(
      `${matcherHint("[.not].toHaveFiggyPudding", "object", "expected")}\n\n` +
        `${EXPECTED_COLOR("expected")} value must be an object.\n${printWithType(
          "Expected",
          expected,
          printExpected
        )}`
    );
  }

  const pass = hasEveryMatchingProperty(received, expected);
  const message = pass
    ? () =>
        `${matcherHint(".not.toHaveFiggyPudding")}\n\n` +
        `Expected pudding to not contain:\n` +
        `  ${printExpected(expected)}\n` +
        `Received:\n` +
        `  ${printReceived(received.toJSON())}`
    : () => {
        const figgyObj = received.toJSON();
        const diffString = diff(expected, figgyObj, {
          expand: this.expand,
        });

        return (
          `${matcherHint(".toHaveFiggyPudding")}\n\n` +
          `Expected pudding to contain:\n` +
          `  ${printExpected(expected)}\n` +
          `Received:\n` +
          `  ${printReceived(figgyObj)}${diffString ? `\n\nDifference:\n\n${diffString}` : ""}`
        );
      };

  return {
    actual: received,
    expected,
    message,
    pass,
  };
}

// expect.figgyPudding({ ...spec });
function figgyPudding(received, expected) {
  // an asymmetric matcher does not have access to `this`
  const pass = hasEveryMatchingProperty(received, expected);
  // the message is never called for .toHaveBeenCalledWith() et al.
  const message = pass
    ? () =>
        `${matcherHint(".not.figgyPudding", undefined, undefined, {
          isDirectExpectCall: true,
        })}\n\n` +
        `Expected pudding to not contain:\n` +
        `  ${printExpected(expected)}\n` +
        `Received:\n` +
        `  ${printReceived(received.toJSON())}`
    : () => {
        const figgyObj = received.toJSON();
        const diffString = diff(expected, figgyObj, {
          expand: this.expand,
        });

        return (
          `${matcherHint(".figgyPudding", undefined, undefined, {
            isDirectExpectCall: true,
          })}\n\n` +
          `Expected pudding to contain:\n` +
          `  ${printExpected(expected)}\n` +
          `Received:\n` +
          `  ${printReceived(figgyObj)}${diffString ? `\n\nDifference:\n\n${diffString}` : ""}`
        );
      };

  return { message, pass };
}

function isObjectWithKeys(obj) {
  return (
    obj !== null &&
    typeof obj === "object" &&
    !(obj instanceof Error) &&
    !(obj instanceof Array) &&
    !(obj instanceof Date)
  );
}

function hasEveryMatchingProperty(received, expected) {
  return Object.keys(expected).every(
    property => property in received && received[property] === expected[property]
  );
}

function isFiggyInstance(received) {
  return hasEveryMatchingProperty(received, { __isFiggyPudding: true });
}


keep keep keep add keep keep keep keep keep

"use strict";

const versionCommand = require("@lerna/version/command");

/**
 * @see https://github.com/yargs/yargs/blob/master/docs/advanced.md#providing-a-command-module
 */
exports.command = "publish [bump]";

exports.describe = "Publish packages in the current project.";

exports.builder = yargs => {
  const opts = {
    c: {
      describe: "Publish packages after every successful merge using the sha as part of the tag.",
      alias: "canary",
      type: "boolean",
    },
    // preid is copied from ../version/command because a whitelist for one option isn't worth it
    preid: {
      describe: "Specify the prerelease identifier when publishing a prerelease",
      type: "string",
      requiresArg: true,
      defaultDescription: "alpha",
    },
    "npm-tag": {
      describe: "Publish packages with the specified npm dist-tag",
      type: "string",
      requiresArg: true,
    },
    "npm-client": {
      defaultDescription: "npm",
      describe: "Executable used to publish dependencies (npm, yarn, pnpm, ...)",
      type: "string",
      requiresArg: true,
    },
    registry: {
      describe: "Use the specified registry for all npm client operations.",
      type: "string",
      requiresArg: true,
    },
    "require-scripts": {
      describe: "Execute ./scripts/prepublish.js and ./scripts/postpublish.js, relative to package root.",
      type: "boolean",
    },
    "temp-tag": {
      describe: "Create a temporary tag while publishing.",
      type: "boolean",
    },
    "verify-access": {
      describe: "Verify package read-write access for current npm user.\nPass --no-verify-access to disable.",
      type: "boolean",
      defaultDescription: "true",
    },
    y: {
      describe: "Skip all confirmation prompts.",
      alias: "yes",
      type: "boolean",
    },
    "git-reset": {
      describe: "Reset the git working tree.\nPass --no-git-reset to disable.",
      type: "boolean",
      defaultDescription: "true",
    },
  };

  return composeVersionOptions(yargs)
    .options(opts)
    .group(Object.keys(opts), "Command Options:")
    .option("verify-registry", {
      // TODO: remove in next major release
      hidden: true,
      type: "boolean",
    })
    .option("skip-npm", {
      // TODO: remove in next major release
      // deprecation notice handled in initialize()
      hidden: true,
      type: "boolean",
    });
};

exports.handler = function handler(argv) {
  return require(".")(argv);
};

function composeVersionOptions(yargs) {
  versionCommand.addBumpPositional(yargs, ["from-git", "from-package"]);
  versionCommand.builder(yargs, "publish");

  return yargs;
}


"use strict";

const cli = require("@lerna/cli");

const addCmd = require("@lerna/add/command");
const bootstrapCmd = require("@lerna/bootstrap/command");
const changedCmd = require("@lerna/changed/command");
const cleanCmd = require("@lerna/clean/command");
const createCmd = require("@lerna/create/command");
const diffCmd = require("@lerna/diff/command");
const execCmd = require("@lerna/exec/command");
const importCmd = require("@lerna/import/command");
const initCmd = require("@lerna/init/command");
const linkCmd = require("@lerna/link/command");
const listCmd = require("@lerna/list/command");
const publishCmd = require("@lerna/publish/command");
const runCmd = require("@lerna/run/command");
const versionCmd = require("@lerna/version/command");

const pkg = require("./package.json");

module.exports = main;

function main(argv) {
  const context = {
    lernaVersion: pkg.version,
  };

  return cli()
    .command(addCmd)
    .command(bootstrapCmd)
    .command(changedCmd)
    .command(cleanCmd)
    .command(createCmd)
    .command(diffCmd)
    .command(execCmd)
    .command(importCmd)
    .command(initCmd)
    .command(linkCmd)
    .command(listCmd)
    .command(publishCmd)
    .command(runCmd)
    .command(versionCmd)
    .parse(argv, context);
}


@lerna/publish

Publish packages in the current project


"components": ["v-container", "v-layout", "v-flex", "v-spacer"],

"goto": {
"components": ["$vuetify"],
"examples": [
"usage"
],
"spec": false
},

<template lang="pug">
  example-view(:data="data")
    section(slot-scope="props")
      section-head(value="Motion.Scrolling.firstHeader")#first
      section-text(value="Motion.Scrolling.loremIpsum")
      section-text(value="Motion.Scrolling.loremIpsum")
      section-text(value="Motion.Scrolling.loremIpsum")

      section-head(value="Motion.Scrolling.secondHeader")#second
      section-text(value="Motion.Scrolling.loremIpsum")
      section-text(value="Motion.Scrolling.loremIpsum")
      section-text(value="Motion.Scrolling.loremIpsum")

      section-head(value="Motion.Scrolling.thirdHeader")#third
      section-text(value="Motion.Scrolling.loremIpsum")
      section-text(value="Motion.Scrolling.loremIpsum")
      section-text(value="Motion.Scrolling.loremIpsum")
</template>

<script>
  // Helpers
  import { mapState } from 'vuex'

  export default {
    computed: {
      ...mapState('app', ['components']),
      data () {
        return {
          folder: 'scrolling',
          components: this.components.scrolling.components,
          examples: this.examples
        }
      },
      examples () {
        const component = this.components['scrolling']

        if (!component) return []

        return component.examples.map(example => {
          let file = example

          return {
            file,
            desc: `Motion.Scrolling.examples.${file}.desc`,
            header: `Motion.Scrolling.examples.${file}.header`
          }
        })
      }
    }
  }
</script>


<template>
  <v-layout justify-space-between wrap>
    <v-flex xs12 sm6 md4>
      <v-date-picker
        :first-day-of-week="0"
        locale="zh-cn"
        v-model="picker"
      ></v-date-picker>
    </v-flex>
    <v-flex xs12 sm6 md4>
      <v-date-picker
        :first-day-of-week="1"
        locale="sv-se"
        v-model="picker"
      ></v-date-picker>
    </v-flex>
    <v-flex xs12 sm6 md4>
      <v-date-picker
        :first-day-of-week="1"
        locale="es-sp"
        v-model="picker"
      ></v-date-picker>
    </v-flex>
  </v-layout>
</template>

<script>
  export default {
    data() {
      return {
        picker: null
      }
    }
  }
</script>


// Styles
import '../../stylus/components/_toolbar.styl'

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

// Directives
import Scroll from '../../directives/scroll'
import { deprecate } from '../../util/console'

/* @vue/component */
export default {
  name: 'v-toolbar',

  directives: { Scroll },

  mixins: [
    Applicationable('top', [
      'clippedLeft',
      'clippedRight',
      'computedHeight',
      'invertedScroll',
      'manualScroll'
    ]),
    Colorable,
    SSRBootable,
    Themeable
  ],

  props: {
    card: Boolean,
    clippedLeft: Boolean,
    clippedRight: Boolean,
    dense: Boolean,
    extended: Boolean,
    extensionHeight: {
      type: [Number, String],
      validator: v => !isNaN(parseInt(v))
    },
    flat: Boolean,
    floating: Boolean,
    height: {
      type: [Number, String],
      validator: v => !isNaN(parseInt(v))
    },
    invertedScroll: Boolean,
    manualScroll: Boolean,
    prominent: Boolean,
    scrollOffScreen: Boolean,
    /* @deprecated */
    scrollToolbarOffScreen: Boolean,
    scrollTarget: String,
    scrollThreshold: {
      type: Number,
      default: 300
    },
    tabs: Boolean
  },

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

  computed: {
    canScroll () {
      // TODO: remove
      if (this.scrollToolbarOffScreen) {
        deprecate('scrollToolbarOffScreen', 'scrollOffScreen', this)

        return true
      }

      return this.scrollOffScreen || this.invertedScroll
    },
    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.smAndDown &&
        this.$vuetify.breakpoint.width >
        this.$vuetify.breakpoint.height
      ) return this.heights.mobileLandscape

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

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

      return this.computedContentHeight + this.computedExtensionHeight
    },
    computedMarginTop () {
      if (!this.app) return 0

      return this.$vuetify.application.bar
    },
    classes () {
      return {
        'v-toolbar': true,
        'elevation-0': this.flat || (
          !this.isActive &&
          !this.tabs &&
          this.canScroll
        ),
        'v-toolbar--absolute': this.absolute,
        'v-toolbar--card': this.card,
        'v-toolbar--clipped': this.clippedLeft || this.clippedRight,
        'v-toolbar--dense': this.dense,
        'v-toolbar--extended': this.isExtended,
        'v-toolbar--fixed': !this.absolute && (this.app || this.fixed),
        'v-toolbar--floating': this.floating,
        'v-toolbar--prominent': this.prominent,
        ...this.themeClasses
      }
    },
    computedPaddingLeft () {
      if (!this.app || this.clippedLeft) return 0

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

      return this.$vuetify.application.right
    },
    computedTransform () {
      return !this.isActive
        ? this.canScroll
          ? -this.computedContentHeight
          : -this.computedHeight
        : 0
    },
    currentThreshold () {
      return Math.abs(this.currentScroll - this.savedScroll)
    },
    styles () {
      return {
        marginTop: `${this.computedMarginTop}px`,
        paddingRight: `${this.computedPaddingRight}px`,
        paddingLeft: `${this.computedPaddingLeft}px`,
        transform: `translateY(${this.computedTransform}px)`
      }
    }
  },

  watch: {
    currentThreshold (val) {
      if (this.invertedScroll) {
        return this.isActive = this.currentScroll > this.scrollThreshold
      }

      if (val < this.scrollThreshold ||
        !this.isBooted
      ) return

      this.isActive = this.isScrollingUp
      this.savedScroll = this.currentScroll
    },
    isActive () {
      this.savedScroll = 0
    },
    invertedScroll (val) {
      this.isActive = !val
    },
    manualScroll (val) {
      this.isActive = !val
    },
    isScrollingUp () {
      this.savedScroll = this.savedScroll || this.currentScroll
    }
  },

  created () {
    if (this.invertedScroll ||
      this.manualScroll
    ) this.isActive = false
  },

  mounted () {
    if (this.scrollTarget) {
      this.target = document.querySelector(this.scrollTarget)
    }
  },

  methods: {
    onScroll () {
      if (!this.canScroll ||
        this.manualScroll ||
        typeof window === 'undefined'
      ) return

      const target = this.target || window

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

      this.isScrollingUp = this.currentScroll < this.previousScroll

      this.previousScroll = this.currentScroll
    },
    /**
     * Update the application layout
     *
     * @return {number}
     */
    updateApplication () {
      return this.invertedScroll || this.manualScroll
        ? 0
        : this.computedHeight
    }
  },

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

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

    data.directives = [{
      arg: this.scrollTarget,
      name: 'scroll',
      value: this.onScroll
    }]

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

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

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


"htmlElement"
],
"spec": false
},
"scrolling": {
"components": ["$vuetify"],
"examples": [

contextual closable icon transition outline

character overlap inline icon visibility

v-card v-card-media v-card-title v-card-actions

components mediaWithText horizontal grids customActions

default customTransition customIcons hideControls hideDelimiters

default colored icon outline label closable inSelects

standard noData headers headerCell progress footer expand pageText select search customIcons paginate sort server headerless editdialog crud

dateLight dateColorable dateDialogAndMenu dateFormatting dateAllowedDates dateWidth dateBirthday datePickerDate dateEvents dateInternationalization dateIcons dateReadonly dateCurrent monthLight monthColorable monthDialogAndMenu monthAllowedMonths monthWidth monthInternationalization monthIcons monthReadonly monthCurrent

simple withoutActivator modal fullscreen form scrollable overflowed advanced loader

accordion expand external disabledAndReadonly popout customIcons focusable

promotedAction small displayAnimation lateralScreens speedDial

default companyFooter indigoFooter tealFooter

basicValidation validationWithSubmitAndClear vuelidate veeValidate

grid offset order directionAndAlign rowColumnBreakpoint nestedGrid uniqueLayouts spacer tags

v-list v-list-tile v-list-group v-list-tile-action v-list-tile-avatar v-list-tile-content v-list-tile-title v-list-tile-sub-title v-list-tile-action-text

avatarTwoLines avatarTitleAndAction iconTwoLinesAndAction avatarThreeLines avatarSubheaderTitleAndAction subheadingsAndDividers cardList titleSubtitleActionsAndActionText actionTitleAndSubtitle expansionLists

activator absolute absoluteWithoutActivator hover menus customTransition popover

permanent permanentClipped permanentFloating persistent mini temporary dark combined nested

circularDefault circularColored circularIndeterminate circularSizeAndWidth circularRotate linearDeterminate linearIndeterminate linearBuffer linearQueryIndeterminateAndDeterminate linearCustomHeightAndContextualColors linearCustomColors

buttons customColor tabs navigationDrawers toolbars expansionPanels customRipple htmlElement

v-radio-group v-radio v-checkbox v-switch

example checkboxesBoolean checkboxesArray checkboxesStates checkboxesColors checkboxesInlineTexfield radiosDefault radiosDirection radiosColors switchesBoolean switchesArray switchesStates switchesColors

usage light icons multiple customTextAndValue

v-stepper v-stepper-step v-stepper-content v-stepper-header

example editable nonEditable optional horizontal vertical linear nonLinear alternateLabels error alternateError verticalError dynamic

v-tabs v-tab v-tabs-items v-tab-item v-tabs-slider

toolbar fixedTabs right content search iconsAndText desktop alignWithTitle grow overflowToMenu pagination icons

usage singleLine disabledAndReadonly icon clearable iconEvents characterCounter password validation fullWidthWithCharacterCounter hint prefixesAndSuffixes customValidation box solo outline customColors masks progressBar

timeLight timeColorable timeDialogAndMenu time24hFormat timeAllowedTimes timeWidth

usage appBar appBarItems appBarExtension columnWidthWithHierarchy flexibleAndCard floatingWithSearch variations prominent dense scrolling appBarMenu systemBarsStatusWindow


<template>
  <div>
    <v-btn color="success">Success</v-btn>
    <v-btn color="error">Error</v-btn>
    <v-btn color="warning">Warning</v-btn>
    <v-btn color="info">Info</v-btn>
  </div>
</template>


<template lang="pug">
  component-view(v-bind:doc="doc")
</template>


<template lang="pug">
  component-view(v-bind:doc="doc")
    mask-table
</template>


"examples": [
"usage"
],
"spec": false
},

contextual closable icon transition outline

character overlap inline icon visibility

components mediaWithText horizontal grids customActions

default customTransition customIcons hideControls hideDelimiters

default colored icon outline label closable inSelects

standard noData headers headerCell progress footer expand pageText select search customIcons paginate sort server headerless editdialog crud

dateLight dateColorable dateDialogAndMenu dateFormatting dateAllowedDates dateWidth dateBirthday datePickerDate dateEvents dateInternationalization dateIcons dateReadonly dateCurrent monthLight monthColorable monthDialogAndMenu monthAllowedMonths monthWidth monthInternationalization monthIcons monthReadonly monthCurrent

simple withoutActivator modal fullscreen form scrollable overflowed advanced loader

accordion expand external disabledAndReadonly popout customIcons focusable

promotedAction small displayAnimation lateralScreens speedDial

default companyFooter indigoFooter tealFooter

basicValidation validationWithSubmitAndClear vuelidate veeValidate

grid offset order directionAndAlign rowColumnBreakpoint nestedGrid uniqueLayouts spacer tags

avatarTwoLines avatarTitleAndAction iconTwoLinesAndAction avatarThreeLines avatarSubheaderTitleAndAction subheadingsAndDividers cardList titleSubtitleActionsAndActionText actionTitleAndSubtitle expansionLists

activator absolute absoluteWithoutActivator hover menus customTransition popover

permanent permanentClipped permanentFloating persistent mini temporary dark combined nested

circularDefault circularColored circularIndeterminate circularSizeAndWidth circularRotate linearDeterminate linearIndeterminate linearBuffer linearQueryIndeterminateAndDeterminate linearCustomHeightAndContextualColors linearCustomColors

buttons customColor tabs navigationDrawers toolbars expansionPanels customRipple htmlElement

example checkboxesBoolean checkboxesArray checkboxesStates checkboxesColors checkboxesInlineTexfield radiosDefault radiosDirection radiosColors switchesBoolean switchesArray switchesStates switchesColors

usage light icons multiple customTextAndValue

example editable nonEditable optional horizontal vertical linear nonLinear alternateLabels error alternateError verticalError dynamic

toolbar fixedTabs right content search iconsAndText desktop alignWithTitle grow overflowToMenu pagination icons

usage singleLine disabledAndReadonly icon clearable iconEvents characterCounter password validation fullWidthWithCharacterCounter hint prefixesAndSuffixes customValidation box solo outline customColors masks progressBar

timeLight timeColorable timeDialogAndMenu time24hFormat timeAllowedTimes timeWidth

usage appBar appBarItems appBarExtension columnWidthWithHierarchy flexibleAndCard floatingWithSearch variations prominent dense scrolling appBarMenu systemBarsStatusWindow


<template>
  <div class="text-xs-center">
    <v-btn fab dark small primary>
      <v-icon dark>remove</v-icon>
    </v-btn>
    <v-btn fab dark small class="pink">
      <v-icon dark>favorite</v-icon>
    </v-btn>
    <v-btn fab dark class="indigo">
      <v-icon dark>add</v-icon>
    </v-btn>
    <v-btn fab dark class="teal">
      <v-icon dark>list</v-icon>
    </v-btn>
    <v-btn fab dark large class="cyan">
      <v-icon dark>edit</v-icon>
    </v-btn>
    <v-btn fab dark large class="purple">
      <v-icon dark>android</v-icon>
    </v-btn>
  </div>
</template>


<template>
  <v-container fluid>
    <v-layout row wrap>
      <v-flex xs12 md6>
        <v-subheader>Light</v-subheader>
        <v-card class="elevation-0">
          <v-card-text>
            <v-container fluid>
              <v-layout row wrap class="dark--text">
                <v-flex xs6>on</v-flex>
                <v-flex xs6>off</v-flex>
              </v-layout>
              <v-layout row wrap>
                <v-flex xs6>
                  <v-switch value input-value="true" ></v-switch>
                </v-flex>
                <v-flex xs6>
                  <v-switch ></v-switch>
                </v-flex>
              </v-layout>
              <v-layout row wrap class="dark--text">
                <v-flex xs6>on disabled</v-flex>
                <v-flex xs6>off disabled</v-flex>
              </v-layout>
              <v-layout row wrap>
                <v-flex xs6>
                  <v-switch value input-value="true" disabled></v-switch>
                </v-flex>
                <v-flex xs6>
                  <v-switch disabled></v-switch>
                </v-flex>
              </v-layout>
            </v-container>
          </v-card-text>
        </v-card>
      </v-flex>
      <v-flex xs12 md6>
        <v-subheader>Dark</v-subheader>
        <v-card dark class="secondary elevation-0">
          <v-card-text>
            <v-container fluid>
              <v-layout row wrap class="light--text">
                <v-flex xs6>on</v-flex>
                <v-flex xs6>off</v-flex>
              </v-layout>
              <v-layout row wrap>
                <v-flex xs6>
                  <v-switch value input-value="true" dark></v-switch>
                </v-flex>
                <v-flex xs6>
                  <v-switch dark></v-switch>
                </v-flex>
              </v-layout>
              <v-layout row wrap class="light--text">
                <v-flex xs6>on disabled</v-flex>
                <v-flex xs6>off disabled</v-flex>
              </v-layout>
              <v-layout row wrap>
                <v-flex xs6>
                  <v-switch value input-value="true" dark disabled></v-switch>
                </v-flex>
                <v-flex xs6>
                  <v-switch dark disabled></v-switch>
                </v-flex>
              </v-layout>
            </v-container>
          </v-card-text>
        </v-card>
      </v-flex>
    </v-layout>
  </v-container>
</template>


The display helpers allow you to control the display of content. This includes being conditionally visible based upon the current viewport, or the actual element display type.

Conditionally display an element based upon the current **viewport**. These classes can be applied using the following format `hidden-{breakpoint}-{condition}`

The _breakpoint_ sets the viewport size:

`xs` - extra small devices
`sm` - small devices
`md` - medium devices
`lg` - large devices
`xl` - extra large

The _condition_ applies the class base on:

`only` - hide the element only on `xs` through `xl` breakpoints

`and-down` - hide the element on the specified breakpoint and down `sm` through `lg` breakpoints

`and-up` - hide the element on the specified breakpoint and up `sm` through `lg` breakpoints

Specify the elements `display` property. These classes can be applied using the following format `d-{display}`.

`d-inline-flex` - sets the element display property to `inline-flex`

`d-flex` - sets the element display property to `flex`

`d-inline-block` - sets the element display property to `inline-block`

`d-block` - sets the element display property to `block`

`d-inline` - sets the element display property to `inline`


keep keep keep replace keep keep keep keep


Компонент `v-toolbar` является ключевым для любого gui, поскольку он обычно является основным источником навигации по сайту. Компонент панели инструментов отлично работает в сочетании с панелью навигации для скрытия ссылок и представления активатора для открытия боковой панели на мобильном устройстве.

Использование

Панель инструментов представляет собой гибкий контейнер, который можно использовать несколькими способами. По умолчанию панель инструментов имеет 64px на десктопе, 56px на мобиле в портретном режиме и 48px на мобиле в ландшафте. Существует ряд вспомогательных компонентов, доступных для использования с панелью инструментов. Это `v-toolbar-side-icon`, `v-toolbar-title` и `v-toolbar-items`.

Панель приложений

Панель приложений является основной панелью приложения. Он может содержать значки, меню и многое другое.

Панель приложений с элементами

Используя компонент `v-toolbar-items`, вы можете использовать регулярные компоненты` v-btn` для создания элементов панели приложения. Не забывайте применять «плоскую» опору к каждой кнопке.Используя компонент `v-toolbar-items`, вы можете использовать регулярные компоненты `v-btn` для создания элементов панели приложения. Не забывайте применять `flat` prop к каждой кнопке.

Панель приложений с расширением

Можно использовать расширение, которое создает гораздо более заметную панель. Если заголовок помещен в расширение, он будет автоматически смещен вправо, чтобы соответствовать нормальному расположению, на котором он находился бы в основной строке.

Панели инструментов представлены в двух вариантах: светлых и темных. Светлые панели инструментов имеют темные тонированные кнопки и темный текст, тогда как темные панели имеют белые тонированные кнопки и белый текст.

С помощью нескольких пользовательских стилей вы можете легко создавать свои удивительные пользовательские интерфейсы.

Плавающая панель инструментов может быть размещена над содержимым, на которое оно ссылается.

Панель приложений имеет несколько вариантов, которые могут применяться с темами и вспомогательными классами. Они варьируются от светлых и темных тем, цветных и прозрачных.

Prominent панели инструментов добавляют слот, в который вы можете поместить элементы в расширенную область. Области содержимого и расширения будут заблокированы до 64px в высоту.

Dense панели инструментов уменьшают их высоту до 48px. Это также поведение по умолчанию для мобильной ландшафтной ориентации.

Вы можете комбинировать как prominent, так и dense, чтобы расширить малый вариант панели инструментов.

Методы прокрутки

При прокрутке панели инструментов можно перейти с экрана. Здесь мы демонстрируем, как вы можете указать цель прокрутки (по умолчанию это окно). Имейте в виду, для этого примера есть специальная разметка, которая не потребуется в вашем приложении.

Вы можете добавлять компоненты меню на панель инструментов.

Системные панели полезны для иконок уведомлений на мобильных устройствах и на десктопах. Для живых примеров, перейдите к заранее определенным макетам.

Наследует радиус границы при использовании внутри `v-card`

Указывает, что приложения `v-navigation-drawer` обрезаются с левой стороны панели инструментов

Указывает, что приложения `v-navigation-drawer` обрезаются с правой стороны панели инструментов

Уменьшает высоту содержимого панели инструментов и расширения

Настройте панель инструментов для создания расширения без использования слота

Удаляет тень панели инструментов

Создает встроенную панель инструментов

Определяет определенную высоту для панели инструментов

Уменьшает непрозрачность панели

Вручную применить функциональность **scroll-off-screen**

Увеличивает высоту содержимого панели инструментов и расширения

При перемещении панели инструментов и расширении экрана

Если вы прокрутите вниз, перейдет панель инструментов

Назначить цель прокрутки для прокрутки экрана

Количество прокрутки вниз до панели инструментов использует прокрутку за кадром

Уменьшает высоту панели

Увеличивает высоту панели


<template>
  <v-container fluid>
    <v-checkbox :label="`Checkbox 1: ${checkbox1.toString()}`" v-model="checkbox1"></v-checkbox>
    <v-checkbox :label="`Checkbox 2: ${checkbox2.toString()}`" v-model="checkbox2"></v-checkbox>
  </v-container>
</template>

<script>
  export default {
    data () {
      return {
        checkbox1: true,
        checkbox2: false
      }
    }
  }
</script>


export default {
  dataIterator: {
    rowsPerPageText: '每頁記錄數：',
    rowsPerPageAll: '全部',
    pageText: '{0}-{1} 共 {2} 條',
    noResultsText: '沒有找到匹配記錄',
    nextPage: '下一頁',
    prevPage: '上一頁'
  },
  dataTable: {
    rowsPerPageText: '每頁行數：'
  },
  noDataText: '無可用數據'
}


const component = this.components['scrolling']

const component = this.components['goto']

contextual closable icon transition outline

character overlap inline icon visibility

components mediaWithText horizontal grids customActions

default customTransition customIcons hideControls hideDelimiters

default colored icon outline label closable inSelects

standard noData headers headerCell progress footer expand pageText select search customIcons paginate sort server headerless editdialog crud

dateLight dateColorable dateDialogAndMenu dateFormatting dateAllowedDates dateWidth dateBirthday datePickerDate dateEvents dateInternationalization dateIcons dateReadonly dateCurrent monthLight monthColorable monthDialogAndMenu monthAllowedMonths monthWidth monthInternationalization monthIcons monthReadonly monthCurrent

simple withoutActivator modal fullscreen form scrollable overflowed advanced loader

accordion expand external disabledAndReadonly popout customIcons focusable

promotedAction small displayAnimation lateralScreens speedDial

default companyFooter indigoFooter tealFooter

basicValidation validationWithSubmitAndClear vuelidate veeValidate

grid offset order directionAndAlign rowColumnBreakpoint nestedGrid uniqueLayouts spacer tags

avatarTwoLines avatarTitleAndAction iconTwoLinesAndAction avatarThreeLines avatarSubheaderTitleAndAction subheadingsAndDividers cardList titleSubtitleActionsAndActionText actionTitleAndSubtitle expansionLists

activator absolute absoluteWithoutActivator hover menus customTransition popover

permanent permanentClipped permanentFloating persistent mini temporary dark combined nested

circularDefault circularColored circularIndeterminate circularSizeAndWidth circularRotate linearDeterminate linearIndeterminate linearBuffer linearQueryIndeterminateAndDeterminate linearCustomHeightAndContextualColors linearCustomColors

buttons customColor tabs navigationDrawers toolbars expansionPanels customRipple htmlElement

example checkboxesBoolean checkboxesArray checkboxesStates checkboxesColors checkboxesInlineTexfield radiosDefault radiosDirection radiosColors switchesBoolean switchesArray switchesStates switchesColors

usage light icons multiple customTextAndValue

example editable nonEditable optional horizontal vertical linear nonLinear alternateLabels error alternateError verticalError dynamic

toolbar fixedTabs right content search iconsAndText desktop alignWithTitle grow overflowToMenu pagination icons

usage singleLine disabledAndReadonly icon clearable iconEvents characterCounter password validation fullWidthWithCharacterCounter hint prefixesAndSuffixes customValidation box solo outline customColors masks progressBar

timeLight timeColorable timeDialogAndMenu time24hFormat timeAllowedTimes timeWidth

usage appBar appBarItems appBarExtension columnWidthWithHierarchy flexibleAndCard floatingWithSearch variations prominent dense scrolling appBarMenu systemBarsStatusWindow


<template>
  <v-layout row wrap>
    <v-flex xs12>
      <v-btn
        :color="allowedMonths === thirtyOneDays ? 'primary' : ''"
        :class="{ 'white--text': allowedMonths === thirtyOneDays }"
        @click.native="allowedMonths = thirtyOneDays"
      >Function</v-btn>
      <v-btn
        :color="allowedMonths === randomMonths ? 'primary' : ''"
        :class="{ 'white--text': allowedMonths === randomMonths }"
        @click.native="allowedMonths = randomMonths"
      >Array</v-btn>
      <v-btn
        :color="allowedMonths === lastFiveMonths ? 'primary' : ''"
        :class="{ 'white--text': allowedMonths === lastFiveMonths }"
        @click.native="allowedMonths = lastFiveMonths"
      >Object</v-btn>
      <v-date-picker
        type="month"
        class="mt-3"
        v-model="date"
        :allowed-dates="allowedMonths"
      ></v-date-picker>
    </v-flex>
  </v-layout>
</template>

<script>
  export default {
    data () {
      return {
        date: null,
        allowedMonths: null,
        thirtyOneDays: function (date) {
          return [0, 2, 4, 6, 7, 9, 11].includes(parseInt(date.split('-')[1], 10))
        },
        randomMonths: [],
        lastFiveMonths: {
          min: null,
          max: null
        }
      }
    },
    mounted () {
      const date = new Date()
      this.randomMonths = [...Array(4)].map(() => {
        const month = Math.floor(Math.random() * 12)
        const d = new Date()
        d.setMonth(month)
        return d
      })

      const d = new Date()
      d.setMonth(date.getMonth() - 4)
      this.lastFiveMonths.min = d
      this.lastFiveMonths.max = date

      this.allowedMonths = this.thirtyOneDays
    }
  }
</script>


import { test } from '@/test'
import VIcon from '@/components/VIcon'
import { VToolbarSideIcon } from '@/components/VToolbar'

test('VToolbarSideIcon.js', ({ mount, functionalContext }) => {
  it('should create default icon when no slot used', () => {
    const context = functionalContext()
    const wrapper = mount(VToolbarSideIcon, context)

    expect(wrapper.find('i')[0].hasClass('material-icons')).toBe(true)
    expect(wrapper.html()).toMatchSnapshot()
  })

  it('should create slot icon when present', () => {
    const iconWrapper = mount(VIcon, functionalContext({}, 'fa-add'))

    const context = functionalContext({}, iconWrapper.vNode)
    const wrapper = mount(VToolbarSideIcon, context)

    expect(wrapper.find('i')[0].hasClass('fa-add')).toBe(true)
    expect(wrapper.html()).toMatchSnapshot()
  })

  it('should pass through events properly', () => {
    const click = jest.fn()
    const context = functionalContext({
      on: { click }
    })
    const wrapper = mount(VToolbarSideIcon, context)

    wrapper.trigger('click')

    expect(click).toBeCalled()
  })

  it('should pass through props to button component', () => {
    const context = functionalContext({
      props: {
        dark: true
      }
    })
    const wrapper = mount(VToolbarSideIcon, context)

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

  it('should pass through css classes to button component', () => {
    const context = functionalContext({
      staticClass: 'hidden-sm-and-up'
    })
    const wrapper = mount(VToolbarSideIcon, context)

    expect(wrapper.hasClass('hidden-sm-and-up')).toBe(true)
  })

  it('should pass through directives to button component', () => {
    const context = functionalContext({
      directives: [
        {
          name: 'show',
          value: false
        }
      ]
    })
    const wrapper = mount(VToolbarSideIcon, context)

    expect(wrapper.hasStyle('display', 'none')).toBe(true)
  })
})


'use strict';

const { createSha1Hash, Permalink, slugize } = require('hexo-util');
const { basename } = require('path');
let permalink;

function postPermalinkFilter(data) {
  const { config } = this;
  const { id, _id, slug, title, date } = data;
  const hash = slug && date
    ? createSha1Hash().update(slug + date.unix().toString()).digest('hex').slice(0, 12)
    : null;
  const meta = {
    id: id || _id,
    title: slug,
    name: typeof slug === 'string' ? basename(slug) : '',
    post_title: slugize(title, {transform: 1}),
    year: date.format('YYYY'),
    month: date.format('MM'),
    day: date.format('DD'),
    hour: date.format('HH'),
    minute: date.format('mm'),
    i_month: date.format('M'),
    i_day: date.format('D'),
    hash
  };

  if (!permalink || permalink.rule !== config.permalink) {
    permalink = new Permalink(config.permalink);
  }

  const { categories } = data;

  if (categories.length) {
    meta.category = categories.last().slug;
  } else {
    meta.category = config.default_category;
  }

  const keys = Object.keys(data);

  for (const key of keys) {
    if (Object.prototype.hasOwnProperty.call(meta, key)) continue;

    // Use Object.getOwnPropertyDescriptor to copy getters to avoid "Maximum call
    // stack size exceeded" error
    Object.defineProperty(meta, key, Object.getOwnPropertyDescriptor(data, key));
  }

  if (config.permalink_defaults) {
    const keys2 = Object.keys(config.permalink_defaults);

    for (const key of keys2) {
      if (Object.prototype.hasOwnProperty.call(meta, key)) continue;

      meta[key] = config.permalink_defaults[key];
    }
  }

  return permalink.stringify(meta);
}

module.exports = postPermalinkFilter;


'use strict';

const crypto = require('crypto');

describe('gravatar', () => {
  const gravatar = require('../../../lib/plugins/helper/gravatar');

  function md5(str) {
    return crypto.createHash('md5').update(str).digest('hex');
  }

  const email = 'abc@abc.com';
  const hash = md5(email);

  it('default', () => {
    gravatar(email).should.eql('https://www.gravatar.com/avatar/' + hash);
  });

  it('size', () => {
    gravatar(email, 100).should.eql('https://www.gravatar.com/avatar/' + hash + '?s=100');
  });

  it('options', () => {
    gravatar(email, {
      s: 200,
      r: 'pg',
      d: 'mm'
    }).should.eql('https://www.gravatar.com/avatar/' + hash + '?s=200&r=pg&d=mm');
  });
});


'use strict';

require('chai');

describe('Hexo', () => {
  require('./scripts/box');
  require('./scripts/console');
  require('./scripts/extend');
  require('./scripts/filters');
  require('./scripts/generators');
  require('./scripts/helpers');
  require('./scripts/hexo');
  require('./scripts/models');
  require('./scripts/processors');
  require('./scripts/renderers');
  require('./scripts/tags');
  require('./scripts/theme');
  require('./scripts/theme_processors');
});


'use strict';

const { join, extname } = require('path');
const moment = require('moment');
const Promise = require('bluebird');
const { createSha1Hash, Permalink } = require('hexo-util');
const fs = require('hexo-fs');
let permalink;

const reservedKeys = {
  year: true,
  month: true,
  i_month: true,
  day: true,
  i_day: true,
  title: true,
  hash: true
};

function newPostPathFilter(data = {}, replace) {
  const sourceDir = this.source_dir;
  const draftDir = join(sourceDir, '_drafts');
  const postDir = join(sourceDir, '_posts');
  const { config } = this;
  const newPostName = config.new_post_name;
  const permalinkDefaults = config.permalink_defaults;
  const { path, layout, slug } = data;

  if (!permalink || permalink.rule !== newPostName) {
    permalink = new Permalink(newPostName);
  }

  let target = '';

  if (path) {
    switch (layout) {
      case 'page':
        target = join(sourceDir, path);
        break;

      case 'draft':
        target = join(draftDir, path);
        break;

      default:
        target = join(postDir, path);
    }
  } else if (slug) {
    switch (layout) {
      case 'page':
        target = join(sourceDir, slug, 'index');
        break;

      case 'draft':
        target = join(draftDir, slug);
        break;

      default: {
        const date = moment(data.date || Date.now());
        const keys = Object.keys(data);
        const hash = createSha1Hash().update(slug + date.unix().toString())
          .digest('hex').slice(0, 12);

        const filenameData = {
          year: date.format('YYYY'),
          month: date.format('MM'),
          i_month: date.format('M'),
          day: date.format('DD'),
          i_day: date.format('D'),
          title: slug,
          hash
        };

        for (let i = 0, len = keys.length; i < len; i++) {
          const key = keys[i];
          if (!reservedKeys[key]) filenameData[key] = data[key];
        }

        target = join(postDir, permalink.stringify({
          ...permalinkDefaults,
          ...filenameData
        }));
      }
    }
  } else {
    return Promise.reject(new TypeError('Either data.path or data.slug is required!'));
  }

  if (!extname(target)) {
    target += extname(newPostName) || '.md';
  }

  if (replace) {
    return Promise.resolve(target);
  }

  return fs.ensurePath(target);
}

module.exports = newPostPathFilter;


keep keep keep keep replace replace keep keep keep keep keep

it('hour minute and second', async () => {

'use strict';

const moment = require('moment');

describe('post_permalink', () => {
  const Hexo = require('../../../lib/hexo');
  const hexo = new Hexo();
  const postPermalink = require('../../../lib/plugins/filter/post_permalink').bind(hexo);
  const Post = hexo.model('Post');
  let post;

  before(async () => {
    hexo.config.permalink = ':year/:month/:day/:title/';
    hexo.config.permalink_defaults = {};

    await hexo.init();
    const apost = await Post.insert({
      source: 'foo.md',
      slug: 'foo',
      date: moment('2014-01-02')
    });
    const id = apost._id;
    await apost.setCategories(['foo', 'bar']);
    post = Post.findById(id);
  });

  it('default', () => {
    postPermalink(post).should.eql('2014/01/02/foo/');
  });

  it('categories', () => {
    hexo.config.permalink = ':category/:title/';
    postPermalink(post).should.eql('foo/bar/foo/');
  });

  it('uncategorized', async () => {
    hexo.config.permalink = ':category/:title/';

    const post = await Post.insert({
      source: 'bar.md',
      slug: 'bar'
    });
    postPermalink(post).should.eql(hexo.config.default_category + '/bar/');
    Post.removeById(post._id);
  });

  it('extra data', () => {
    hexo.config.permalink = ':layout/:title/';
    postPermalink(post).should.eql(post.layout + '/foo/');
  });

  it('id', () => {
    hexo.config.permalink = ':id';

    postPermalink(post).should.eql(post._id);

    post.id = 1;
    postPermalink(post).should.eql('1');
  });

  it('name', async () => {
    hexo.config.permalink = ':title/:name';

    const post = await Post.insert({
      source: 'sub/bar.md',
      slug: 'sub/bar'
    });
    postPermalink(post).should.eql('sub/bar/bar');
    Post.removeById(post._id);
  });

  it('post_title', async () => {
    hexo.config.permalink = ':year/:month/:day/:post_title/';

    const post = await Post.insert({
      source: 'sub/2015-05-06-my-new-post.md',
      slug: '2015-05-06-my-new-post',
      title: 'My New Post',
      date: moment('2015-05-06')
    });
    postPermalink(post).should.eql('2015/05/06/my-new-post/');
    Post.removeById(post._id);
  });

  it('hour and minute', async () => {
    hexo.config.permalink = ':year/:month/:day/:hour/:minute/:post_title/';

    const post = await Post.insert({
      source: 'sub/2015-05-06-my-new-post.md',
      slug: '2015-05-06-my-new-post',
      title: 'My New Post',
      date: moment('2015-05-06 12:13')
    });
    postPermalink(post).should.eql('2015/05/06/12/13/my-new-post/');
    Post.removeById(post._id);
  });

  it('time is omitted in front-matter', async () => {
    hexo.config.permalink = ':year/:month/:day/:hour/:minute/:post_title/';

    const post = await Post.insert({
      source: 'sub/2015-05-06-my-new-post.md',
      slug: '2015-05-06-my-new-post',
      title: 'My New Post',
      date: moment('2015-05-06')
    });
    postPermalink(post).should.eql('2015/05/06/00/00/my-new-post/');
    Post.removeById(post._id);
  });

  it('permalink_defaults', async () => {
    hexo.config.permalink = 'posts/:lang/:title/';
    hexo.config.permalink_defaults = {lang: 'en'};

    const posts = await Post.insert([{
      source: 'my-new-post.md',
      slug: 'my-new-post',
      title: 'My New Post1'
    }, {
      source: 'my-new-fr-post.md',
      slug: 'my-new-fr-post',
      title: 'My New Post2',
      lang: 'fr'
    }]);
    postPermalink(posts[0]).should.eql('posts/en/my-new-post/');
    postPermalink(posts[1]).should.eql('posts/fr/my-new-fr-post/');

    await Promise.all(posts.map(post => Post.removeById(post._id)));
  });

  it('permalink_defaults - null', async () => {
    hexo.config.permalink = 'posts/:lang/:title/';
    hexo.config.permalink_defaults = null;

    const posts = await Post.insert([{
      source: 'my-new-post.md',
      slug: 'my-new-post',
      title: 'My New Post1',
      lang: 'en'
    }, {
      source: 'my-new-post-2.md',
      slug: 'my-new-post-2',
      title: 'My New Post2',
      lang: 'fr'
    }]);
    postPermalink(posts[0]).should.eql('posts/en/my-new-post/');
    postPermalink(posts[1]).should.eql('posts/fr/my-new-post-2/');

    await Promise.all(posts.map(post => Post.removeById(post._id)));
  });
});


'use strict';

const { htmlTag } = require('hexo-util');

/* flatten() to be replaced by Array.flat()
after Node 10 has reached EOL */
const flatten = function(arr, result = []) {
  for (const i in arr) {
    const value = arr[i];
    if (Array.isArray(value)) {
      flatten(value, result);
    } else {
      result.push(value);
    }
  }
  return result;
};

function jsHelper(...args) {
  let result = '\n';
  let items = args;

  if (!Array.isArray(args)) items = [args];

  items = flatten(items);

  items.forEach(item => {
    // Old syntax
    if (typeof item === 'string' || item instanceof String) {
      let path = item;
      if (!path.endsWith('.js')) {
        path += '.js';
      }
      result += `<script src="${this.url_for(path)}"></script>\n`;
    } else {
      // New syntax
      item.src = this.url_for(item.src);
      if (!item.src.endsWith('.js')) item.src += '.js';
      result += htmlTag('script', { ...item }, '') + '\n';
    }
  });
  return result;
}

module.exports = jsHelper;


'use strict';

describe('Renderer', () => {
  const Renderer = require('../../../lib/extend/renderer');

  it('register()', () => {
    const r = new Renderer();

    // name, output, fn
    r.register('yaml', 'json', () => {});

    r.get('yaml').should.exist;
    r.get('yaml').output.should.eql('json');

    // name, output, fn, sync
    r.register('yaml', 'json', () => {}, true);

    r.get('yaml').should.exist;
    r.get('yaml').output.should.eql('json');
    r.get('yaml', true).should.exist;
    r.get('yaml', true).output.should.eql('json');

    // no fn
    should.throw(() => r.register('yaml', 'json'), TypeError, 'fn must be a function');

    // no output
    should.throw(() => r.register('yaml'), TypeError, 'output is required');

    // no name
    should.throw(() => r.register(), TypeError, 'name is required');
  });

  it('register() - promisify', async () => {
    const r = new Renderer();

    // async
    r.register('yaml', 'json', (data, options, callback) => {
      callback(null, 'foo');
    });

    const yaml = await r.get('yaml')({}, {});
    yaml.should.eql('foo');

    // sync
    r.register('swig', 'html', (data, options) => 'foo', true);

    const swig = await r.get('swig')({}, {});
    swig.should.eql('foo');
  });

  it('register() - compile', () => {
    const r = new Renderer();

    function renderer(data, locals) {}

    renderer.compile = data => {
      //
    };

    r.register('swig', 'html', renderer);
    r.get('swig').compile.should.eql(renderer.compile);
  });

  it('getOutput()', () => {
    const r = new Renderer();

    r.register('yaml', 'json', () => {});

    r.getOutput('yaml').should.eql('json');
    r.getOutput('.yaml').should.eql('json');
    r.getOutput('config.yaml').should.eql('json');
    r.getOutput('foo.xml').should.not.ok;
  });

  it('isRenderable()', () => {
    const r = new Renderer();

    r.register('yaml', 'json', () => {});

    r.isRenderable('yaml').should.be.true;
    r.isRenderable('.yaml').should.be.true;
    r.isRenderable('config.yaml').should.be.true;
    r.isRenderable('foo.xml').should.be.false;
  });

  it('isRenderableSync()', () => {
    const r = new Renderer();

    r.register('yaml', 'json', () => {});

    r.isRenderableSync('yaml').should.be.false;

    r.register('swig', 'html', () => {}, true);

    r.isRenderableSync('swig').should.be.true;
    r.isRenderableSync('.swig').should.be.true;
    r.isRenderableSync('layout.swig').should.be.true;
    r.isRenderableSync('foo.html').should.be.false;
  });

  it('get()', () => {
    const r = new Renderer();

    r.register('yaml', 'json', () => {});

    r.get('yaml').should.exist;
    r.get('.yaml').should.exist;
    r.get('config.yaml').should.exist;
    should.not.exist(r.get('foo.xml'));
    should.not.exist(r.get('yaml', true));

    r.register('swig', 'html', () => {}, true);

    r.get('swig').should.exist;
    r.get('swig', true).should.exist;
  });

  it('list()', () => {
    const r = new Renderer();

    r.register('yaml', 'json', () => {});

    r.register('swig', 'html', () => {}, true);

    r.list().should.have.all.keys(['yaml', 'swig']);
    r.list(true).should.have.all.keys(['swig']);
  });
});


'use strict';

module.exports = ctx => {
  const { filter } = ctx.extend;

  require('./after_render')(ctx);
  require('./after_post_render')(ctx);
  require('./before_post_render')(ctx);
  require('./before_exit')(ctx);
  require('./before_generate')(ctx);
  require('./template_locals')(ctx);

  filter.register('new_post_path', require('./new_post_path'));
  filter.register('post_permalink', require('./post_permalink'));
};


'use strict';

const { join, sep } = require('path');
const Promise = require('bluebird');
const File = require('./file');
const { Pattern, HashStream } = require('hexo-util');
const { createReadStream, readdir, stat, watch } = require('hexo-fs');
const { magenta } = require('chalk');
const { EventEmitter } = require('events');
const { isMatch, makeRe } = require('micromatch');
const ignore = ['**/themes/*/node_modules/**', '**/themes/*/.git/**'];

const defaultPattern = new Pattern(() => ({}));

class Box extends EventEmitter {
  constructor(ctx, base, options) {
    super();

    this.options = Object.assign({
      persistent: true
    }, options);

    if (!base.endsWith(sep)) {
      base += sep;
    }

    this.context = ctx;
    this.base = base;
    this.processors = [];
    this._processingFiles = {};
    this.watcher = null;
    this.Cache = ctx.model('Cache');
    this.File = this._createFileClass();

    let ignoreCfg = ignore;
    if (ctx.config.ignore) {
      if (ctx.config.ignore.length) ignoreCfg = ignoreCfg.concat(ctx.config.ignore);
    }
    this.ignore = ignoreCfg;
    const targets = ignoreCfg;
    this.options.ignored = (this.options.ignored || []).concat(targets.map(s => toRegExp(ctx, s)).filter(x => x));
  }
  _createFileClass() {
    const ctx = this.context;

    class _File extends File {
      render(options, callback) {
        if (!callback && typeof options === 'function') {
          callback = options;
          options = {};
        }

        return ctx.render.render({
          path: this.source
        }, options).asCallback(callback);
      }

      renderSync(options) {
        return ctx.render.renderSync({
          path: this.source
        }, options);
      }
    }

    _File.prototype.box = this;

    return _File;
  }

  addProcessor(pattern, fn) {
    if (!fn && typeof pattern === 'function') {
      fn = pattern;
      pattern = defaultPattern;
    }

    if (typeof fn !== 'function') throw new TypeError('fn must be a function');
    if (!(pattern instanceof Pattern)) pattern = new Pattern(pattern);

    this.processors.push({
      pattern,
      process: fn
    });
  }
  _readDir(base, fn, prefix = '') {
    const { ignore } = this;

    if (base && ignore) {
      if (ignore.length && isMatch(base, ignore)) {
        return Promise.resolve([]);
      }
    }

    return readdir(base).map(path => stat(join(base, path)).then(stats => {
      const fullpath = join(base, path);
      if (stats.isDirectory()) {
        return this._readDir(fullpath, fn, `${prefix + path}/`);
      }

      if (ignore && ignore.length && isMatch(fullpath, ignore)) {
        return Promise.resolve([]);
      }

      return this._checkFileStatus(prefix + path).then(file => fn(file).thenReturn(file));
    })).catch(err => {
      if (err.cause && err.cause.code === 'ENOENT') return;
      throw err;
    }).reduce((files, item) => files.concat(item), []);
  }

  _checkFileStatus(path) {
    const { Cache, context: ctx } = this;
    const src = join(this.base, path);

    return Cache.compareFile(
      escapeBackslash(src.substring(ctx.base_dir.length)),
      () => getHash(src),

      () => stat(src)
    ).then(result => ({
      type: result.type,
      path
    }));
  }

  process(callback) {
    const { base, Cache, context: ctx } = this;

    return stat(base).then(stats => {
      if (!stats.isDirectory()) return;

      // Check existing files in cache
      const relativeBase = escapeBackslash(base.substring(ctx.base_dir.length));
      const cacheFiles = Cache.filter(item => item._id.startsWith(relativeBase)).map(item => item._id.substring(relativeBase.length));

      // Read files from directory
      return this._readDir(base, file => this._processFile(file.type, file.path)).map(file => file.path).then(files => // Handle deleted files
        Promise.filter(cacheFiles, path => !files.includes(path)).map(path => this._processFile(File.TYPE_DELETE, path)));
    }).catch(err => {
      if (err.cause && err.cause.code !== 'ENOENT') throw err;
    }).asCallback(callback);
  }

  _processFile(type, path) {
    if (this._processingFiles[path]) {
      return Promise.resolve();
    }

    this._processingFiles[path] = true;

    const { base, File, context: ctx } = this;

    this.emit('processBefore', {
      type,
      path
    });

    return Promise.reduce(this.processors, (count, processor) => {
      const params = processor.pattern.match(path);
      if (!params) return count;

      const file = new File({
        source: join(base, path),
        path,
        params,
        type
      });

      return Reflect.apply(Promise.method(processor.process), ctx, [file])
        .thenReturn(count + 1);
    }, 0).then(count => {
      if (count) {
        ctx.log.debug('Processed: %s', magenta(path));
      }

      this.emit('processAfter', {
        type,
        path
      });
    }).catch(err => {
      ctx.log.error({err}, 'Process failed: %s', magenta(path));
    }).finally(() => {
      this._processingFiles[path] = false;
    }).thenReturn(path);
  }

  watch(callback) {
    if (this.isWatching()) {
      return Promise.reject(new Error('Watcher has already started.')).asCallback(callback);
    }

    const { base } = this;

    function getPath(path) {
      return escapeBackslash(path.substring(base.length));
    }

    return this.process().then(() => watch(base, this.options)).then(watcher => {
      this.watcher = watcher;

      watcher.on('add', path => {
        this._processFile(File.TYPE_CREATE, getPath(path));
      });

      watcher.on('change', path => {
        this._processFile(File.TYPE_UPDATE, getPath(path));
      });

      watcher.on('unlink', path => {
        this._processFile(File.TYPE_DELETE, getPath(path));
      });

      watcher.on('addDir', path => {
        let prefix = getPath(path);
        if (prefix) prefix += '/';

        this._readDir(path, file => this._processFile(file.type, file.path), prefix);
      });
    }).asCallback(callback);
  }

  unwatch() {
    if (!this.isWatching()) return;

    this.watcher.close();
    this.watcher = null;
  }

  isWatching() {
    return Boolean(this.watcher);
  }
}

function escapeBackslash(path) {
  // Replace backslashes on Windows
  return path.replace(/\\/g, '/');
}

function getHash(path) {
  return new Promise((resolve, reject) => {
    const src = createReadStream(path);
    const hasher = new HashStream();

    src.pipe(hasher)
      .on('finish', () => {
        resolve(hasher.read().toString('hex'));
      })
      .on('error', reject);
  });
}

function toRegExp(ctx, arg) {
  if (!arg) return null;
  if (typeof arg !== 'string') {
    ctx.log.warn('A value of "ignore:" section in "_config.yml" is not invalid (not a string)');
    return null;
  }
  const result = makeRe(arg);
  if (!result) {
    ctx.log.warn('A value of "ignore:" section in "_config.yml" can not be converted to RegExp:' + arg);
    return null;
  }
  return result;
}

module.exports = Box;


'use strict';

const moment = require('moment');

describe('common', () => {
  const common = require('../../../lib/plugins/processor/common');

  it('isTmpFile()', () => {
    common.isTmpFile('foo').should.be.false;
    common.isTmpFile('foo%').should.be.true;
    common.isTmpFile('foo~').should.be.true;
  });

  it('isHiddenFile()', () => {
    common.isHiddenFile('foo').should.be.false;
    common.isHiddenFile('_foo').should.be.true;
    common.isHiddenFile('foo/_bar').should.be.true;
    common.isHiddenFile('.foo').should.be.true;
    common.isHiddenFile('foo/.bar').should.be.true;
  });

  it('ignoreTmpAndHiddenFile()', () => {
    const pattern = common.ignoreTmpAndHiddenFile;

    pattern.match('foo').should.be.true;
    pattern.match('foo%').should.be.false;
    pattern.match('foo~').should.be.false;
    pattern.match('_foo').should.be.false;
    pattern.match('foo/_bar').should.be.false;
    pattern.match('.foo').should.be.false;
    pattern.match('foo/.bar').should.be.false;
  });

  it('toDate()', () => {
    const m = moment();
    const d = new Date();

    should.not.exist(common.toDate());
    common.toDate(m).should.eql(m);
    common.toDate(d).should.eql(d);
    common.toDate(1e8).should.eql(new Date(1e8));
    common.toDate('2014-04-25T01:32:21.196Z').should.eql(new Date('2014-04-25T01:32:21.196Z'));
    common.toDate('Apr 24 2014').should.eql(new Date(2014, 3, 24));
    should.not.exist(common.toDate('foo'));
  });

  it('timezone() - date', () => {
    const d = new Date(Date.UTC(1972, 2, 29, 0, 0, 0));
    const d_timezone_UTC = common.timezone(d, 'UTC');
    (common.timezone(d, 'Asia/Shanghai') - d_timezone_UTC).should.eql(-8 * 3600 * 1000);
    (common.timezone(d, 'Asia/Bangkok') - d_timezone_UTC).should.eql(-7 * 3600 * 1000);
    (common.timezone(d, 'America/Los_Angeles') - d_timezone_UTC).should.eql(8 * 3600 * 1000);
  });

  it('timezone() - moment', () => {
    const d = moment(new Date(Date.UTC(1972, 2, 29, 0, 0, 0)));
    const d_timezone_UTC = common.timezone(d, 'UTC');
    (common.timezone(d, 'Europe/Moscow') - d_timezone_UTC).should.eql(-3 * 3600 * 1000);
  });

  it('isMatch() - string', () => {
    // String
    common.isMatch('foo/test.html', 'foo/*.html').should.be.true;
    common.isMatch('foo/test.html', 'bar/*.html').should.be.false;

    // Array
    common.isMatch('foo/test.html', []).should.be.false;
    common.isMatch('foo/test.html', ['foo/*.html']).should.be.true;
    common.isMatch('foo/test.html', ['bar/*.html', 'foo/*.html']).should.be.true;
    common.isMatch('foo/test.html', ['bar/*.html', 'baz/*.html']).should.be.false;

    // Undefined
    common.isMatch('foo/test.html').should.be.false;
  });
});


'use strict';

const Promise = require('bluebird');

describe('page', () => {
  const Hexo = require('../../../lib/hexo');
  const hexo = new Hexo(__dirname, {silent: true});
  const Page = hexo.model('Page');
  const generator = Promise.method(require('../../../lib/plugins/generator/page').bind(hexo));

  const locals = () => {
    hexo.locals.invalidate();
    return hexo.locals.toObject();
  };

  it('default layout', async () => {
    const page = await Page.insert({
      source: 'foo',
      path: 'bar'
    });
    const data = await generator(locals());
    page.__page = true;

    data.should.eql([
      {
        path: page.path,
        layout: ['page', 'post', 'index'],
        data: page
      }
    ]);

    page.remove();
  });

  it('custom layout', async () => {
    const page = await Page.insert({
      source: 'foo',
      path: 'bar',
      layout: 'photo'
    });
    const data = await generator(locals());
    data[0].layout.should.eql(['photo', 'page', 'post', 'index']);

    page.remove();
  });

  [false, 'false', 'off'].forEach(layout => {
    it('layout = ' + JSON.stringify(layout), async () => {
      const page = await Page.insert({
        source: 'foo',
        path: 'bar',
        layout
      });
      const data = await generator(locals());
      should.not.exist(data[0].layout);

      page.remove();
    });
  });
});


it('time is omitted in front-matter', async () => {


hexo

A fast, simple & powerful blog framework, powered by Node.js.

Tommy Chen <tommy351@gmail.com> (https://zespia.tw)

Abner Chou <hi@abnerchou.me> (https://abnerchou.me)


'use strict';

const { spy } = require('sinon');
const Promise = require('bluebird');

describe('Tag', () => {
  const Tag = require('../../../lib/extend/tag');
  const tag = new Tag();

  it('register()', async () => {
    const tag = new Tag();

    tag.register('test', (args, content) => args.join(' '));

    const result = await tag.render('{% test foo.bar | abcdef > fn(a, b, c) < fn() %}');
    result.should.eql('foo.bar | abcdef > fn(a, b, c) < fn()');
  });

  it('register() - async', async () => {
    const tag = new Tag();

    tag.register('test', (args, content) => Promise.resolve(args.join(' ')), { async: true });

    const result = await tag.render('{% test foo bar %}');
    result.should.eql('foo bar');
  });

  it('register() - block', async () => {
    const tag = new Tag();

    tag.register('test', (args, content) => args.join(' ') + ' ' + content, true);

    const str = [
      '{% test foo bar %}',
      'test content',
      '{% endtest %}'
    ].join('\n');

    const result = await tag.render(str);
    result.should.eql('foo bar test content');
  });

  it('register() - async block', async () => {
    const tag = new Tag();

    tag.register('test', (args, content) => Promise.resolve(args.join(' ') + ' ' + content), { ends: true, async: true });

    const str = [
      '{% test foo bar %}',
      'test content',
      '{% endtest %}'
    ].join('\n');

    const result = await tag.render(str);
    result.should.eql('foo bar test content');
  });

  it('register() - nested test', async () => {
    const tag = new Tag();

    tag.register('test', (args, content) => content, true);

    const str = [
      '{% test %}',
      '123456',
      '  {% raw %}',
      '  raw',
      '  {% endraw %}',
      '  {% test %}',
      '  test',
      '  {% endtest %}',
      '789012',
      '{% endtest %}'
    ].join('\n');

    const result = await tag.render(str);
    result.replace(/\s/g, '').should.eql('123456rawtest789012');
  });

  it('register() - nested async / async test', async () => {
    const tag = new Tag();

    tag.register('test', (args, content) => content, {ends: true, async: true});
    tag.register('async', (args, content) => {
      return Promise.resolve(args.join(' ') + ' ' + content);
    }, { ends: true, async: true });

    const str = [
      '{% test %}',
      '123456',
      '  {% async %}',
      '  async',
      '  {% endasync %}',
      '789012',
      '{% endtest %}'
    ].join('\n');

    const result = await tag.render(str);
    result.replace(/\s/g, '').should.eql('123456async789012');
  });

  it('register() - strip indention', async () => {
    const tag = new Tag();

    tag.register('test', (args, content) => content, true);

    const str = [
      '{% test %}',
      '  test content',
      '{% endtest %}'
    ].join('\n');

    const result = await tag.render(str);
    result.should.eql('test content');
  });

  it('register() - async callback', async () => {
    const tag = new Tag();

    tag.register('test', (args, content, callback) => {
      callback(null, args.join(' '));
    }, { async: true });

    const result = await tag.render('{% test foo bar %}');
    result.should.eql('foo bar');
  });

  it('register() - name is required', () => {
    should.throw(() => tag.register(), 'name is required');
  });

  it('register() - fn must be a function', () => {
    should.throw(() => tag.register('test'), 'fn must be a function');
  });

  it('unregister()', () => {
    const tag = new Tag();

    tag.register('test', (args, content) => Promise.resolve(args.join(' ')), {async: true});
    tag.unregister('test');

    return tag.render('{% test foo bar %}')
      .then(result => {
        console.log(result);
        throw new Error('should return error');
      })
      .catch(err => {
        err.should.have.property('type', 'unknown block tag: test');
      });
  });

  it('unregister() - name is required', () => {
    should.throw(() => tag.unregister(), 'name is required');
  });

  it('render() - context', async () => {
    const tag = new Tag();

    tag.register('test', function() {
      return this.foo;
    });

    const result = await tag.render('{% test %}', { foo: 'bar' });
    result.should.eql('bar');
  });

  it('render() - callback', () => {
    const tag = new Tag();

    const callback = spy();

    tag.register('test', () => 'foo');

    return tag.render('{% test %}', callback()).then(result => {
      result.should.eql('foo');
      callback.calledOnce.should.be.true;
    });
  });
});


'use strict';

const { dirname, join } = require('path');
const { mkdirs, rmdir, stat, unlink, writeFile } = require('hexo-fs');

const dateFormat = 'YYYY-MM-DD HH:mm:ss';

describe('asset', () => {
  const Hexo = require('../../../lib/hexo');
  const defaults = require('../../../lib/hexo/default_config');
  const baseDir = join(__dirname, 'asset_test');
  const hexo = new Hexo(baseDir);
  const asset = require('../../../lib/plugins/processor/asset')(hexo);
  const process = asset.process.bind(hexo);
  const { pattern } = asset;
  const { source } = hexo;
  const { File } = source;
  const Asset = hexo.model('Asset');
  const Page = hexo.model('Page');

  function newFile(options) {
    options.source = join(source.base, options.path);
    options.params = {
      renderable: options.renderable
    };

    return new File(options);
  }

  before(async () => {
    await mkdirs(baseDir);
    await hexo.init();
  });

  beforeEach(() => { hexo.config = Object.assign({}, defaults); });

  after(() => rmdir(baseDir));

  it('pattern', () => {
    // Renderable files
    pattern.match('foo.json').should.have.property('renderable', true);

    // Non-renderable files
    pattern.match('foo.txt').should.have.property('renderable', false);

    // Tmp files
    should.not.exist(pattern.match('foo.txt~'));
    should.not.exist(pattern.match('foo.txt%'));

    // Hidden files
    should.not.exist(pattern.match('_foo.txt'));
    should.not.exist(pattern.match('test/_foo.txt'));
    should.not.exist(pattern.match('.foo.txt'));
    should.not.exist(pattern.match('test/.foo.txt'));

    // Include files
    hexo.config.include = ['fff/**'];
    pattern.match('fff/_foo.txt').should.exist;
    hexo.config.include = [];

    // Exclude files
    hexo.config.exclude = ['fff/**'];
    should.not.exist(pattern.match('fff/foo.txt'));
    hexo.config.exclude = [];

    // Skip render files
    hexo.config.skip_render = ['fff/**'];
    pattern.match('fff/foo.json').should.have.property('renderable', false);
    hexo.config.skip_render = [];
  });

  it('asset - type: create', async () => {
    const file = newFile({
      path: 'foo.jpg',
      type: 'create',
      renderable: false
    });

    await writeFile(file.source, 'foo');
    await process(file);
    const id = 'source/' + file.path;
    const asset = Asset.findById(id);

    asset._id.should.eql(id);
    asset.path.should.eql(file.path);
    asset.modified.should.be.true;
    asset.renderable.should.be.false;

    asset.remove();
    unlink(file.source);
  });

  it('asset - type: create (when source path is configed to parent directory)', async () => {
    const file = newFile({
      path: '../../source/foo.jpg',
      type: 'create',
      renderable: false
    });

    await writeFile(file.source, 'foo');
    await process(file);
    const id = '../source/foo.jpg'; // The id should a relative path,because the 'lib/models/assets.js' use asset path by joining base path with "_id" directly.
    const asset = Asset.findById(id);
    asset._id.should.eql(id);
    asset.path.should.eql(file.path);
    asset.modified.should.be.true;
    asset.renderable.should.be.false;

    asset.remove();
    await unlink(file.source);
    rmdir(dirname(file.source));
  });

  it('asset - type: update', async () => {
    const file = newFile({
      path: 'foo.jpg',
      type: 'update',
      renderable: false
    });

    const id = 'source/' + file.path;

    await Promise.all([
      writeFile(file.source, 'test'),
      Asset.insert({
        _id: id,
        path: file.path,
        modified: false
      })
    ]);
    await process(file);
    const asset = Asset.findById(id);

    asset._id.should.eql(id);
    asset.path.should.eql(file.path);
    asset.modified.should.be.true;
    asset.renderable.should.be.false;

    asset.remove();
    unlink(file.source);
  });

  it('asset - type: skip', async () => {
    const file = newFile({
      path: 'foo.jpg',
      type: 'skip',
      renderable: false
    });

    const id = 'source/' + file.path;

    await Promise.all([
      writeFile(file.source, 'test'),
      Asset.insert({
        _id: id,
        path: file.path,
        modified: false
      })
    ]);
    await process(file);
    const asset = Asset.findById(id);
    asset.modified.should.be.false;
    await Promise.all([
      Asset.removeById(id),
      unlink(file.source)
    ]);
  });

  it('asset - type: delete', async () => {
    const file = newFile({
      path: 'foo.jpg',
      type: 'delete',
      renderable: false
    });

    const id = 'source/' + file.path;

    await Asset.insert({
      _id: id,
      path: file.path
    });
    await process(file);

    should.not.exist(Asset.findById(id));
  });

  it('page - type: create', async () => {
    const body = [
      'title: "Hello world"',
      'date: 2006-01-02 15:04:05',
      'updated: 2014-12-13 01:02:03',
      '---',
      'The quick brown fox jumps over the lazy dog'
    ].join('\n');

    const file = newFile({
      path: 'hello.swig',
      type: 'create',
      renderable: true
    });

    await writeFile(file.source, body);
    await process(file);

    const page = Page.findOne({ source: file.path });
    page.title.should.eql('Hello world');
    page.date.format(dateFormat).should.eql('2006-01-02 15:04:05');
    page.updated.format(dateFormat).should.eql('2014-12-13 01:02:03');
    page._content.should.eql('The quick brown fox jumps over the lazy dog');
    page.source.should.eql(file.path);
    page.raw.should.eql(body);
    page.path.should.eql('hello.html');
    page.layout.should.eql('page');

    await Promise.all([
      page.remove(),
      unlink(file.source)
    ]);
  });

  it('page - type: update', async () => {
    const body = [
      'title: "Hello world"',
      '---'
    ].join('\n');

    const file = newFile({
      path: 'hello.swig',
      type: 'update',
      renderable: true
    });

    const doc = await Page.insert({ source: file.path, path: 'hello.html' });
    await writeFile(file.source, body);
    const id = doc._id;
    await process(file);
    const page = Page.findOne({ source: file.path });
    page._id.should.eql(id);
    page.title.should.eql('Hello world');

    await Promise.all([
      page.remove(),
      unlink(file.source)
    ]);
  });

  it('page - type: delete', async () => {
    const file = newFile({
      path: 'hello.swig',
      type: 'delete',
      renderable: true
    });

    await Page.insert({
      source: file.path,
      path: 'hello.html'
    });
    await process(file);
    should.not.exist(Page.findOne({ source: file.path }));
  });

  it('page - use the status of the source file if date not set', async () => {
    const file = newFile({
      path: 'hello.swig',
      type: 'create',
      renderable: true
    });

    await writeFile(file.source, '');
    await process(file);
    const stats = await stat(file.source);
    const page = Page.findOne({source: file.path});

    page.date.toDate().should.eql(stats.ctime);
    page.updated.toDate().should.eql(stats.mtime);

    await Promise.all([
      page.remove(),
      unlink(file.source)
    ]);
  });

  it('page - use the date for updated if use_date_for_updated is set', async () => {
    const file = newFile({
      path: 'hello.swig',
      type: 'create',
      renderable: true
    });

    hexo.config.use_date_for_updated = true;

    await writeFile(file.source, '');
    await process(file);
    const stats = await stat(file.source);
    const page = Page.findOne({source: file.path});

    page.date.toDate().should.eql(stats.ctime);
    page.updated.toDate().should.eql(page.date.toDate());

    await Promise.all([
      page.remove(),
      unlink(file.source)
    ]);
  });

  it('page - permalink', async () => {
    const body = [
      'title: "Hello world"',
      'permalink: foo.html',
      '---'
    ].join('\n');

    const file = newFile({
      path: 'hello.swig',
      type: 'create',
      renderable: true
    });

    await writeFile(file.source, body);
    await process(file);
    const page = Page.findOne({ source: file.path });
    page.path.should.eql('foo.html');

    await Promise.all([
      page.remove(),
      unlink(file.source)
    ]);
  });

  it('page - permalink (without extension name)', async () => {
    const body = [
      'title: "Hello world"',
      'permalink: foo',
      '---'
    ].join('\n');

    const file = newFile({
      path: 'hello.swig',
      type: 'create',
      renderable: true
    });

    await writeFile(file.source, body);
    await process(file);
    const page = Page.findOne({ source: file.path });
    page.path.should.eql('foo.html');

    await Promise.all([
      page.remove(),
      unlink(file.source)
    ]);
  });

  it('page - permalink (with trailing slash)', async () => {
    const body = [
      'title: "Hello world"',
      'permalink: foo/',
      '---'
    ].join('\n');

    const file = newFile({
      path: 'hello.swig',
      type: 'create',
      renderable: true
    });

    await writeFile(file.source, body);
    await process(file);
    const page = Page.findOne({ source: file.path });
    page.path.should.eql('foo/index.html');

    await Promise.all([
      page.remove(),
      unlink(file.source)
    ]);
  });

  it('page - set layout to false if output is not html', async () => {
    const body = 'foo: 1';

    const file = newFile({
      path: 'test.yml',
      type: 'create',
      renderable: true
    });

    await writeFile(file.source, body);
    await process(file);
    const page = Page.findOne({ source: file.path });
    page.layout.should.eql('false');

    await Promise.all([
      page.remove(),
      unlink(file.source)
    ]);
  });

  it('page - don\'t set layout to false if layout is set but output is not html', async () => {
    const body = [
      'layout: something',
      '---',
      'foo: 1'
    ].join('\n');

    const file = newFile({
      path: 'test.yml',
      type: 'create',
      renderable: true
    });

    await writeFile(file.source, body);
    await process(file);
    const page = Page.findOne({ source: file.path });
    page.layout.should.eql('something');

    await Promise.all([
      page.remove(),
      unlink(file.source)
    ]);
  });

  it('page - parse date', async () => {
    const body = [
      'title: "Hello world"',
      'date: Apr 24 2014',
      'updated: May 5 2015',
      '---'
    ].join('\n');

    const file = newFile({
      path: 'hello.swig',
      type: 'create',
      renderable: true
    });

    await writeFile(file.source, body);
    await process(file);
    const page = Page.findOne({ source: file.path });
    page.date.format(dateFormat).should.eql('2014-04-24 00:00:00');
    page.updated.format(dateFormat).should.eql('2015-05-05 00:00:00');

    await Promise.all([
      page.remove(),
      unlink(file.source)
    ]);
  });

  it('page - use file stats instead if date is invalid', async () => {
    const body = [
      'title: "Hello world"',
      'date: yomama',
      'updated: isfat',
      '---'
    ].join('\n');

    const file = newFile({
      path: 'hello.swig',
      type: 'create',
      renderable: true
    });

    await writeFile(file.source, body);
    await process(file);
    const stats = await file.stat();
    const page = Page.findOne({source: file.path});

    page.date.toDate().should.eql(stats.ctime);
    page.updated.toDate().should.eql(page.date.toDate());

    await Promise.all([
      page.remove(),
      unlink(file.source)
    ]);
  });

  it('page - don\'t remove extension name', async () => {
    const body = '';

    const file = newFile({
      path: 'test.min.js',
      type: 'create',
      renderable: true
    });

    await writeFile(file.source, body);
    await process(file);
    const page = Page.findOne({ source: file.path });
    page.path.should.eql('test.min.js');

    await Promise.all([
      page.remove(),
      unlink(file.source)
    ]);
  });

  it('page - timezone', async () => {
    const body = [
      'title: "Hello world"',
      'date: Apr 24 2014',
      'updated: May 5 2015',
      '---'
    ].join('\n');

    const file = newFile({
      path: 'hello.swig',
      type: 'create',
      renderable: true
    });

    hexo.config.timezone = 'UTC';

    await writeFile(file.source, body);
    await process(file);
    const page = Page.findOne({source: file.path});

    page.date.utc().format(dateFormat).should.eql('2014-04-24 00:00:00');
    page.updated.utc().format(dateFormat).should.eql('2015-05-05 00:00:00');

    await Promise.all([
      page.remove(),
      unlink(file.source)
    ]);
  });
});


goTo: (target: string | number | HTMLElement | Vue, options?: VuetifyGoToOptions) => void

keep keep keep keep replace keep keep keep

import { intToHex } from '../../../util/colorUtils'
import * as Theme from '../../../util/theme'

export default {
  data: () => ({
    style: null
  }),

  computed: {
    parsedTheme () {
      return Theme.parse(this.$vuetify.theme)
    },
    /** @return string */
    generatedStyles () {
      const theme = this.parsedTheme
      let css

      if (this.$vuetify.options.themeCache != null) {
        css = this.$vuetify.options.themeCache.get(theme)
        if (css != null) return css
      }

      const colors = Object.keys(theme)
      css = `a { color: ${intToHex(theme.primary)}; }`

      for (let i = 0; i < colors.length; ++i) {
        const name = colors[i]
        const value = theme[name]
        if (this.$vuetify.options.themeVariations.includes(name)) {
          css += Theme.genVariations(name, value).join('')
        } else {
          css += Theme.genBaseColor(name, value)
        }
      }

      if (this.$vuetify.options.minifyTheme != null) {
        css = this.$vuetify.options.minifyTheme(css)
      }

      if (this.$vuetify.options.themeCache != null) {
        this.$vuetify.options.themeCache.set(theme, css)
      }

      return css
    },
    vueMeta () {
      return {
        style: [{
          cssText: this.generatedStyles,
          type: 'text/css',
          id: 'vuetify-theme-stylesheet'
        }]
      }
    }
  },

  // Regular vue-meta
  metaInfo () {
    return this.vueMeta
  },

  // Nuxt
  head () {
    return this.vueMeta
  },

  watch: {
    generatedStyles () {
      !this.meta && this.applyTheme()
    }
  },

  created () {
    if (this.$meta) {
      // Vue-meta
      // Handled by metaInfo()/nuxt()
    } else if (typeof document === 'undefined' && this.$ssrContext) {
      // SSR
      this.$ssrContext.head = this.$ssrContext.head || ''
      this.$ssrContext.head += `<style type="text/css" id="vuetify-theme-stylesheet">${this.generatedStyles}</style>`
    } else if (typeof document !== 'undefined') {
      // Client-side
      this.genStyle()
      this.applyTheme()
    }
  },

  methods: {
    applyTheme () {
      if (this.style) this.style.innerHTML = this.generatedStyles
    },
    genStyle () {
      let style = document.getElementById('vuetify-theme-stylesheet')

      if (!style) {
        style = document.createElement('style')
        style.type = 'text/css'
        style.id = 'vuetify-theme-stylesheet'
        document.head.appendChild(style)
      }

      this.style = style
    }
  }
}


bootable()
  transition: none

  &[data-booted="true"]
    transition: .2s $transition.fast-out-slow-in


import VPagination from './VPagination'

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

export default VPagination


import '../../stylus/components/_grid.styl'

import Grid from './grid'

export default Grid('container')


css = `a { color: ${intToHex(theme.primary)}; }`

for (let i = 0; i < colors.length; ++i) {

/* eslint-disable no-multi-spaces */
const THEME_DEFAULTS = {
  primary: '#1976D2',   // blue.darken2
  secondary: '#424242', // grey.darken3
  accent: '#82B1FF',    // blue.accent1
  error: '#FF5252',     // red.accent2
  info: '#2196F3',      // blue.base
  success: '#4CAF50',   // green.base
  warning: '#FFC107'    // amber.base
}

export default function theme (theme = {}) {
  return Object.assign({}, THEME_DEFAULTS, theme)
}


import { test } from '@/test'
import VSystemBar from '@/components/VSystemBar'

test('VSystemBar.vue', ({ mount }) => {
  it('should render a colored system bar', () => {
    const wrapper = mount(VSystemBar, {
      propsData: {
        color: 'blue lighten-1'
      }
    })

    expect(wrapper.element.classList).toContain('blue')
    expect(wrapper.element.classList).toContain('lighten-1')
  })

  it('should render system bar with fixed prop', () => {
    const wrapper = mount(VSystemBar, {
      propsData: {
        fixed: true
      }
    })

    expect(wrapper.element.classList).toContain('system-bar--fixed')
  })

  it('should render system bar with absolute prop', () => {
    const wrapper = mount(VSystemBar, {
      propsData: {
        absolute: true
      }
    })

    expect(wrapper.element.classList).toContain('system-bar--absolute')
  })

  it('should update height when window prop change', async () => {
    const wrapper = mount(VSystemBar, {
      propsData: {
        app: true
      }
    })

    expect(wrapper.vm.$vuetify.application.bar).toBe(24)

    wrapper.setProps({
      window: true
    })
    expect(wrapper.vm.$vuetify.application.bar).toBe(32)

    wrapper.setProps({
      height: 90
    })
    expect(wrapper.vm.$vuetify.application.bar).toBe(90)
  })

  it('should warn for improper height', () => {
    const wrapper = mount(VSystemBar, {
      propsData: {
        height: 'foo'
      }
    })

    expect('custom validator check failed for prop "height"').toHaveBeenWarned()
  })
})


export default {
  name: 'themeable',

  props: {
    dark: Boolean,
    light: Boolean
  },

  computed: {
    themeClasses () {
      return {
        'theme--light': this.light,
        'theme--dark': this.dark
      }
    }
  }
}


import { compileToFunctions } from 'vue-template-compiler'
import VDatePickerMonthTable from '@/components/VDatePicker/VDatePickerMonthTable'
import { test } from '@/test'

test('VDatePickerMonthTable.js', ({ mount }) => {
  it('should render component and match snapshot', () => {
    const wrapper = mount(VDatePickerMonthTable, {
      propsData: {
        tableDate: '2005',
        current: '2005-05',
        value: '2005-11'
      }
    })

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

  it('should watch tableDate value and run transition', async () => {
    const wrapper = mount(VDatePickerMonthTable, {
      propsData: {
        tableDate: '2005',
        current: '2005-05',
        value: '2005-11'
      }
    })

    wrapper.setProps({
      tableDate: '2006'
    })
    await wrapper.vm.$nextTick()
    expect(wrapper.find('table')[0].element.className).toBe('tab-transition-enter tab-transition-enter-active')
  })

  it('should watch tableDate value and run reverse transition', async () => {
    const wrapper = mount(VDatePickerMonthTable, {
      propsData: {
        tableDate: '2005',
        current: '2005-05',
        value: '2005-11'
      }
    })

    wrapper.setProps({
      tableDate: '2004'
    })
    await wrapper.vm.$nextTick()
    expect(wrapper.find('table')[0].element.className).toBe('tab-reverse-transition-enter tab-reverse-transition-enter-active')
  })

  it('should emit event when month button is clicked', () => {
    const wrapper = mount(VDatePickerMonthTable, {
      propsData: {
        tableDate: '2005',
        current: '2005-05',
        value: '2005-11'
      }
    })

    const input = jest.fn()
    wrapper.vm.$on('input', input)

    wrapper.find('tbody button')[0].trigger('click')
    expect(input).toBeCalledWith('2005-01')
  })

  it('should not emit event when disabled month button is clicked', () => {
    const wrapper = mount(VDatePickerMonthTable, {
      propsData: {
        tableDate: '2005',
        current: '2005-05',
        value: '2005-11',
        allowedDates: () => false
      }
    })

    const input = jest.fn()
    wrapper.vm.$on('input', input)

    wrapper.find('tbody button')[0].trigger('click')
    expect(input).not.toBeCalled()
  })

  it('should emit tableDate event when scrolled and scrollable', () => {
    const wrapper = mount(VDatePickerMonthTable, {
      propsData: {
        tableDate: '2005',
        scrollable: true
      }
    })

    const tableDate = jest.fn()
    wrapper.vm.$on('tableDate', tableDate)

    wrapper.trigger('wheel')
    expect(tableDate).toBeCalledWith('2006')
  })

  it('should not emit tableDate event when scrolled and not scrollable', () => {
    const wrapper = mount(VDatePickerMonthTable, {
      propsData: {
        tableDate: '2005'
      }
    })

    const tableDate = jest.fn()
    wrapper.vm.$on('tableDate', tableDate)

    wrapper.trigger('wheel')
    expect(tableDate).not.toBeCalled()
  })

  // TODO
  it.skip('should emit tableDate event when swiped', () => {
    const wrapper = mount(VDatePickerMonthTable, {
      propsData: {
        tableDate: '2005'
      }
    })

    const tableDate = jest.fn()
    wrapper.vm.$on('tableDate', tableDate)

    wrapper.trigger('touchstart')
    wrapper.trigger('touchend')
    expect(tableDate).toBeCalledWith(2006)
  })

  it('should change tableDate when touch is called', () => {
    const wrapper = mount(VDatePickerMonthTable, {
      propsData: {
        tableDate: '2005'
      }
    })

    const tableDate = jest.fn()
    wrapper.vm.$on('tableDate', tableDate)

    wrapper.vm.touch(1)
    expect(tableDate).toBeCalledWith('2006')
    wrapper.vm.touch(-1)
    expect(tableDate).toBeCalledWith('2004')
  })
})


if (this.$vuetify.theme === false) return

import 'vuetify/src/util/helpers'
import 'vuetify/src/util/colors'
import Vue, { PluginFunction } from 'vue'

declare class Vuetify {
  static install: PluginFunction<never>
}

declare interface VuetifyApplication {
  bar: number
  bottom: number
  left: number
  right: number
  top: number
}

declare interface VuetifyBreakpoint {
  height: number
  lg: boolean
  lgAndDown: boolean
  lgAndUp: boolean
  lgOnly: boolean
  md: boolean
  mdAndDown: boolean
  mdAndUp: boolean
  mdOnly: boolean
  name: string
  sm: boolean
  smAndDown: boolean
  smAndUp: boolean
  smOnly: boolean
  width: number
  xl: boolean
  xlOnly: boolean
  xs: boolean
  xsOnly: boolean
}

declare interface VuetifyTheme {
  primary: VuetifyThemeItem
  accent: VuetifyThemeItem
  secondary: VuetifyThemeItem
  info: VuetifyThemeItem
  warning: VuetifyThemeItem
  error: VuetifyThemeItem
  success: VuetifyThemeItem

  [name: string]: VuetifyThemeItem
}

declare type VuetifyThemeItem = string | number

declare interface VuetifyThemeCache {
  get: (parsedTheme: VuetifyTheme) => string | null
  set: (parsedTheme: VuetifyTheme, css: string) => void
}

declare interface VuetifyOptions {
  themeVariations?: string[]
  minifyTheme?: (css: string) => string
  themeCache?: VuetifyThemeCache
}

declare type VuetifyGoToEasing =
  ((t: number) => number) |
  'linear' |
  'easeInQuad' |
  'easeOutQuad' |
  'easeInOutQuad' |
  'easeInCubic' |
  'easeOutCubic' |
  'easeInOutCubic' |
  'easeInQuart' |
  'easeOutQuart' |
  'easeInOutQuart' |
  'easeInQuint' |
  'easeOutQuint' |
  'easeInOutQuint'

declare interface VuetifyGoToOptions {
  duration?: number
  offset?: number
  easing?: VuetifyGoToEasing
}

declare interface VuetifyObject {
  application: VuetifyApplication
  breakpoint: VuetifyBreakpoint
  dark: boolean
  theme: VuetifyTheme
  options: VuetifyOptions
  goTo: (target: string | number | HTMLElement | Vue, options?: VuetifyGoToOptions) => void
}

declare module 'vue/types/vue' {
  interface Vue {
    $vuetify: VuetifyObject
  }
}

export default Vuetify


import '../../stylus/components/_app.styl'

// Component level mixins
import AppTheme from './mixins/app-theme'
import AppBreakpoint from './mixins/app-breakpoint'

// Directives
import Resize from '../../directives/resize'

export default {
  name: 'v-app',

  mixins: [
    AppBreakpoint,
    AppTheme
  ],

  directives: {
    Resize
  },

  props: {
    id: {
      type: String,
      default: 'app'
    },
    dark: Boolean
  },

  computed: {
    classes () {
      return {
        [`theme--${this.dark ? 'dark' : 'light'}`]: true
      }
    }
  },

  mounted () {
    this.$vuetify.dark = this.dark
  },

  watch: {
    dark () {
      this.$vuetify.dark = this.dark
    }
  },

  render (h) {
    const data = {
      staticClass: 'application',
      'class': this.classes,
      attrs: { 'data-app': true },
      domProps: { id: this.id },
      directives: [{
        name: 'resize',
        value: this.onResize
      }]
    }

    const wrapper = h('div', { staticClass: 'application--wrap' }, this.$slots.default)

    return h('div', data, [wrapper])
  }
}


import VBreadcrumbs from './VBreadcrumbs'
import VBreadcrumbsItem from './VBreadcrumbsItem'

export { VBreadcrumbs, VBreadcrumbsItem }

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

export default VBreadcrumbs


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

exports[`VParallax.js should render 1`] = `

`;

exports[`VParallax.js should use alt tag when supplied 1`] = `

`;


}
},
created () {
if (this.$meta) {
// Vue-meta
// Handled by metaInfo()/nuxt()