diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..d2e5b86 --- /dev/null +++ b/.babelrc @@ -0,0 +1,11 @@ +{ + "env": { + "development": { + "presets": ["es2015", "flow-vue"], + "plugins": ["babel-plugin-espower"] + }, + "production": { + "presets": ["es2015-rollup-vue", "flow-vue"] + } + } +} diff --git a/.eslintrc b/.eslintrc index 49757d7..726bf23 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,11 +1,10 @@ { - "extends": "standard", + "root": true, + "parser": "babel-eslint", + "extends": "vue", + "plugins": ["flow-vars"], "rules": { - "complexity": 0, - "operator-linebreak": [2, "before"], - "no-trailing-spaces": 0, - "no-inline-comments": 0, - "no-multiple-empty-lines": [2, {"max": 2}], - "no-new": 0 + "flow-vars/define-flow-type": 1, + "flow-vars/use-flow-type": 1 } } diff --git a/.flowconfig b/.flowconfig new file mode 100644 index 0000000..a6368e4 --- /dev/null +++ b/.flowconfig @@ -0,0 +1,14 @@ +[ignore] +.*/node_modules/.* +.*/docs/.* +.*/test/.* +.*/config/.* +.*/examples/.* + +[include] + +[libs] +decls + +[options] +unsafe.enable_getters_and_setters=true diff --git a/issue_template.md b/.github/ISSUE_TEMPLATE.md similarity index 98% rename from issue_template.md rename to .github/ISSUE_TEMPLATE.md index b47e864..17e9bcd 100644 --- a/issue_template.md +++ b/.github/ISSUE_TEMPLATE.md @@ -29,7 +29,7 @@ Remove the template from below and provide thoughtful commentary *and code sampl ### vue & vue-validator version -1.0.17, 2.0.0-beta.1 +1.0.26, 2.0.0 ### Reproduction Link diff --git a/.gitignore b/.gitignore index d311984..9bcc2b2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,11 @@ lib coverage +dist/*.gz +docs/_book +test/e2e/report +test/e2e/screenshots node_modules .DS_Store *.log -*.gz *.swp *~ diff --git a/.npmignore b/.npmignore index 7a244ea..a00d3b0 100644 --- a/.npmignore +++ b/.npmignore @@ -2,8 +2,9 @@ *.log *.swp *.yml -bower.json coverage +docs/_book +test/e2e/report config dist/*.map lib diff --git a/README.md b/README.md index 5626965..fc067e6 100644 --- a/README.md +++ b/README.md @@ -6,24 +6,35 @@ Validator component for Vue.js +>:warning: NOTE: WIP for Vue.js 2.0 :construction: -# Compatibility -- Vue.js `1.0.20`+ +## Compatibility +- v2 later + - Vue.js `1.0.20`+ +- v3 (**WIP** :construction:) + - Vue.js `2.0 beta`+ -# Documentation -See [here](http://vuejs.github.io/vue-validator/) +## Documentation +- v2 later + - See [here](http://vuejs.github.io/vue-validator/) +- v3 + - **Not yet** (Progress: https://github.com/vuejs/vue-validator/issues/257) -# Contributing + +## Contributing - Fork it ! -- Create your top branch from `dev`: `git branch my-new-topic origin/dev` +- Create your top branch from `2.x`: `git branch my-new-topic origin/dev` - Commit your changes: `git commit -am 'Add some topic'` - Push to the branch: `git push origin my-new-topic` -- Submit a pull request to `dev` branch of `vuejs/vue-validator` repository ! +- Submit a pull request to `2.x` branch of `vuejs/vue-validator` repository ! + +### Branch +- 2.x (maintance branch for v2 later) -# Development Setup +## Development Setup # install deps npm install @@ -44,21 +55,21 @@ See [here](http://vuejs.github.io/vue-validator/) npm test -# Issues +## Issues Please make sure to read the [Issue Reporting Checklist](https://github.com/vuejs/vue/blob/dev/CONTRIBUTING.md#issue-reporting-guidelines) before opening an issue. Issues not conforming to the guidelines may be closed immediately. -# Contribution +## Contribution Please make sure to read the [Contributing Guide](https://github.com/vuejs/vue/blob/dev/CONTRIBUTING.md) before making a pull request. -# Changelog +## Changelog Details changes for each release are documented in the [CHANGELOG.md](https://github.com/vuejs/vue-validator/blob/dev/CHANGELOG.md). -# License +## License [MIT](http://opensource.org/licenses/MIT) diff --git a/circle.yml b/circle.yml index 807a2a6..b00026e 100644 --- a/circle.yml +++ b/circle.yml @@ -1,3 +1,3 @@ machine: node: - version: 5 + version: 6 diff --git a/config/.eslintrc b/config/.eslintrc new file mode 100644 index 0000000..da6830b --- /dev/null +++ b/config/.eslintrc @@ -0,0 +1,10 @@ +{ + "globals": { + "process": true + }, + "extends": "vue", + "rules": { + "no-multiple-empty-lines": [2, {"max": 2}], + "no-console": 0 + } +} diff --git a/config/banner.js b/config/banner.js index e940e89..0f2feff 100644 --- a/config/banner.js +++ b/config/banner.js @@ -1,10 +1,9 @@ -var pack = require('../package.json') -var version = process.env.VERSION || pack.version +const pack = require('../package.json') +const version = process.env.VERSION || pack.version -module.exports = '/*!\n' - + ' * ' + pack.name + ' v' + version + '\n' - + ' * (c) ' + new Date().getFullYear() + ' ' - + pack.author.name + '\n' - + ' * Released under the ' + pack.license - + ' License.\n' - + ' */' +module.exports = + '/*!\n' + + ` * ${pack.name} v${version} \n` + + ` * (c) ${new Date().getFullYear()} ${pack.author.name}\n` + + ` * Released under the ${pack.license} License.\n` + + ' */' diff --git a/config/build.js b/config/build.js index 2288d99..58fad6c 100644 --- a/config/build.js +++ b/config/build.js @@ -1,30 +1,24 @@ -var fs = require('fs') -var zlib = require('zlib') -var rollup = require('rollup') -var uglify = require('uglify-js') -var babel = require('rollup-plugin-babel') -var replace = require('rollup-plugin-replace') -var pack = require('../package.json') -var banner = require('./banner') +const zlib = require('zlib') +const rollup = require('rollup') +const uglify = require('uglify-js') +const babel = require('rollup-plugin-babel') +const replace = require('rollup-plugin-replace') +const pack = require('../package.json') +const banner = require('./banner') +const fs = require('fs') +const readFile = fs.readFileSync +const writeFile = fs.writeFileSync +const exist = fs.existsSync +const mkdir = fs.mkdirSync -// update main file -var main = fs - .readFileSync('src/index.js', 'utf-8') - .replace(/plugin\.version = '[\d\.]+[\d]+'/, "plugin.version = '" + pack.version + "'") -fs.writeFileSync('src/index.js', main) +if (!exist('dist')) { + mkdir('dist') +} -// update installation.md -var langs = ['en', 'zh-cn'] -langs.forEach(function (lang) { - var installationPath = './docs/' + lang + '/installation.md' - var installation = fs - .readFileSync(installationPath, 'utf-8') - .replace( - /\' - ) - fs.writeFileSync(installationPath, installation) -}) +// update main file +const main = readFile('src/index.js', 'utf-8') + .replace(/plugin\.version = '[\d\.]+'/, `plugin.version = '${pack.version}'`) +writeFile('src/index.js', main) // CommonJS build. // this is used as the "main" field in package.json @@ -32,78 +26,56 @@ langs.forEach(function (lang) { rollup.rollup({ entry: 'src/index.js', plugins: [ - babel({ - presets: ['es2015-loose-rollup'] - }) + babel() ] -}) -.then(function (bundle) { - return write('dist/' + pack.name + '.common.js', bundle.generate({ - format: 'cjs', - banner: banner - }).code) -}) -// Standalone Dev Build -.then(function () { +}).then(bundle => { + return write( + `dist/${pack.name}.common.js`, + bundle.generate({ format: 'cjs', banner }).code + ) +}).then(() => { // Standalone Dev Build return rollup.rollup({ entry: 'src/index.js', plugins: [ - replace({ - 'process.env.NODE_ENV': "'development'" - }), - babel({ - presets: ['es2015-loose-rollup'] - }) + replace({ 'process.env.NODE_ENV': "'development'" }), + babel() ] + }).then(bundle => { + return write( + `dist/${pack.name}.js`, + bundle.generate({ + format: 'umd', banner, moduleName: classify(pack.name) + }).code + ) }) - .then(function (bundle) { - return write('dist/' + pack.name + '.js', bundle.generate({ - format: 'umd', - banner: banner, - moduleName: classify(pack.name) - }).code) - }) -}) -.then(function () { - // Standalone Production Build +}).then(() => { // Standalone Production Build return rollup.rollup({ entry: 'src/index.js', plugins: [ - replace({ - 'process.env.NODE_ENV': "'production'" - }), - babel({ - presets: ['es2015-loose-rollup'] - }) + replace({ 'process.env.NODE_ENV': "'production'" }), + babel() ] - }) - .then(function (bundle) { - var code = bundle.generate({ + }).then(bundle => { + const code = bundle.generate({ format: 'umd', moduleName: classify(pack.name) }).code - var minified = banner + '\n' + uglify.minify(code, { + const minified = banner + '\n' + uglify.minify(code, { fromString: true }).code - return write('dist/' + pack.name + '.min.js', minified) - }) - .then(zip) -}) -.catch(logError) + return write(`dist/${pack.name}.min.js`, minified) + }).then(zip) +}).catch(logError) -function toUpper (_, c) { - return c ? c.toUpperCase() : '' -} +function toUpper (_, c) { return c ? c.toUpperCase() : '' } const classifyRE = /(?:^|[-_\/])(\w)/g -function classify (str) { - return str.replace(classifyRE, toUpper) -} +function classify (str) { return str.replace(classifyRE, toUpper) } function write (dest, code) { - return new Promise(function (resolve, reject) { - fs.writeFile(dest, code, function (err) { - if (err) return reject(err) + return new Promise((resolve, reject) => { + fs.writeFile(dest, code, err => { + if (err) { return reject(err) } console.log(blue(dest) + ' ' + getSize(code)) resolve() }) @@ -111,25 +83,19 @@ function write (dest, code) { } function zip () { - return new Promise(function (resolve, reject) { - fs.readFile('dist/' + pack.name + '.min.js', function (err, buf) { - if (err) return reject(err) - zlib.gzip(buf, function (err, buf) { - if (err) return reject(err) - write('dist/' + pack.name + '.min.js.gz', buf).then(resolve) + return new Promise((resolve, reject) => { + fs.readFile(`dist/${pack.name}.min.js`, (err, buf) => { + if (err) { return reject(err) } + zlib.gzip(buf, (err, buf) => { + if (err) { return reject(err) } + write(`dist/${pack.name}.min.js.gz`, buf).then(resolve) }) }) }) } -function getSize (code) { - return (code.length / 1024).toFixed(2) + 'kb' -} +function getSize (code) { return (code.length / 1024).toFixed(2) + 'kb' } -function logError (e) { - console.log(e) -} +function logError (e) { console.log(e) } -function blue (str) { - return '\x1b[1m\x1b[34m' + str + '\x1b[39m\x1b[22m' -} +function blue (str) { return '\x1b[1m\x1b[34m' + str + '\x1b[39m\x1b[22m' } diff --git a/config/karma.base.conf.js b/config/karma.base.conf.js new file mode 100644 index 0000000..cfe5716 --- /dev/null +++ b/config/karma.base.conf.js @@ -0,0 +1,40 @@ +const webpack = require('webpack') + +const webpackConfig = { + module: { + loaders: [{ + test: /\.js$/, + exclude: /node_modules|vue\/dist/, + loader: 'babel!eslint' + }], + postLoaders: [{ + test: /\.json$/, + loader: 'json' + }] + }, + plugins: [ + new webpack.DefinePlugin({ + 'process.env': { + NODE_ENV: '"development"' + } + }) + ], + devtool: '#inline-source-map' +} + +module.exports = { + basePath: '', + files: [ + '../test/unit/index.js' + ], + exclude: [ + ], + frameworks: ['jasmine'], + preprocessors: { + '../test/unit/index.js': ['webpack', 'sourcemap'] + }, + webpack: webpackConfig, + webpackMiddleware: { + noInfo: true + } +} diff --git a/config/karma.conf.js b/config/karma.conf.js deleted file mode 100644 index 38d27eb..0000000 --- a/config/karma.conf.js +++ /dev/null @@ -1,125 +0,0 @@ -// Karma configuration -// Generated on Tue Sep 08 2015 19:27:24 GMT+0900 (JST) - -module.exports = function (config) { - var settings = { - // base path that will be used to resolve all patterns (eg. files, exclude) - basePath: '', - - // frameworks to use - // available frameworks: https://npmjs.org/browse/keyword/karma-adapter - frameworks: ['mocha'], - - // list of files / patterns to load in the browser - files: [ - '../test/specs/index.js' - ], - - // list of files to exclude - exclude: [ - ], - - // preprocess matching files before serving them to the browser - // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor - preprocessors: { - '../test/specs/index.js': ['webpack'] - }, - - webpack: { - devtool: 'source-map', - module: { - noParse: [ - /node_modules\/sinon\// - ], - loaders: [{ - test: /\.js$/, - exclude: /node_modules|vue\/dist/, - loader: 'babel', - query: { - presets: ['es2015-loose'], - plugins: [ - 'babel-plugin-espower' - // NOTE: disable, see https://github.com/speedskater/babel-plugin-rewire#istanbul - // DISABLE: 'babel-plugin-rewire' - ] - } - }], - postLoaders: [{ - test: /\.json$/, - loader: 'json' - }, { - test: /\.js$/, - exclude: /test|node_modules|vue\/dist/, - loader: 'istanbul-instrumenter' - }] - }, - resolve: { - alias: { - sinon: 'sinon/pkg/sinon.js' - } - } - }, - - webpackMiddleware: { - noInfo: true - }, - - // web server port - port: 9876, - - // enable / disable colors in the output (reporters and logs) - colors: true, - - // level of logging - // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG - logLevel: config.LOG_INFO, - - // enable / disable watching file and executing tests whenever any file changes - autoWatch: true, - - // Continuous Integration mode - // if true, Karma captures browsers, runs the tests and exits - singleRun: true - } - - switch (process.env.VUE_VALIDATOR_TYPE) { - case 'coverage': - settings.browsers = ['PhantomJS'] - settings.reporters = ['coverage'] - settings.coverageReporter = { - reporters: [{ - type: 'html', dir: '../coverage' - }, { - type: 'text-summary', dir: '../coverage' - }] - } - break - case 'coveralls': - settings.browsers = ['PhantomJS'] - settings.reporters = ['coverage', 'coveralls'] - settings.coverageReporter = { - reporters: [{ type: 'lcov', dir: '../coverage' }] - } - break - case 'sauce': - var batch = process.env.SAUCE || 'batch1' - var sauce = require('./sauce')[batch] - settings.sauceLabs = sauce.sauceLabs - settings.captureTimeout = sauce.captureTimeout - settings.customLaunchers = sauce.customLaunchers - settings.browsers = sauce.browsers - settings.reporters = sauce.reporters - break - case 'browser': - settings.browsers = ['Chrome', 'Safari', 'Firefox'] - settings.reporters = ['progress'] - break - default: - settings.browsers = ['PhantomJS'] - settings.reporters = ['mocha'] - break - } - - - config.set(settings) -} diff --git a/config/karma.cover.conf.js b/config/karma.cover.conf.js new file mode 100644 index 0000000..7994f4c --- /dev/null +++ b/config/karma.cover.conf.js @@ -0,0 +1,23 @@ +const base = require('./karma.base.conf') + +module.exports = config => { + const options = Object.assign(base, { + browsers: ['PhantomJS'], + reporters: ['mocha', 'coverage'], + coverageReporter: { + reporters: [{ + type: 'lcov', dir: '../coverage' + }, { + type: 'text-summary', dir: '../coverage' + }] + }, + singleRun: true + }) + + // add babel-plugin-coverage for code intrumentation + options.webpack.babel = { + plugins: [['coverage', { ignore: ['test/'] }]] + } + + config.set(options) +} diff --git a/config/karma.coveralls.conf.js b/config/karma.coveralls.conf.js new file mode 100644 index 0000000..737b424 --- /dev/null +++ b/config/karma.coveralls.conf.js @@ -0,0 +1,21 @@ +const base = require('./karma.base.conf') + +module.exports = config => { + const options = Object.assign(base, { + browsers: ['PhantomJS'], + reporters: ['coverage', 'coveralls'], + coverageReporter: { + reporters: [{ + type: 'lcov', dir: '../coverage' + }] + }, + singleRun: true + }) + + // add babel-plugin-coverage for code intrumentation + options.webpack.babel = { + plugins: [['coverage', { ignore: ['test/'] }]] + } + + config.set(options) +} diff --git a/config/sauce.js b/config/karma.sauce.conf.js similarity index 64% rename from config/sauce.js rename to config/karma.sauce.conf.js index 0c7351b..03280ce 100644 --- a/config/sauce.js +++ b/config/karma.sauce.conf.js @@ -1,10 +1,5 @@ -var pack = require('../package.json') - -var sauceConfig = { - testName: pack.name + ' unit tests', - recordScreenshots: false, - build: process.env.CIRCLE_BUILD_NUM || Date.now() -} +const base = require('./karma.base.conf') +const pack = require('../package.json') /** * Having too many tests running concurrently on saucelabs @@ -12,8 +7,8 @@ var sauceConfig = { * smaller batches. */ -var batches = [ - // the cool kids +const batches = [ + // the coolkids { sl_chrome: { base: 'SauceLabs', @@ -49,14 +44,12 @@ var batches = [ browserName: 'internet explorer', platform: 'Windows 8.1', version: '11' - } - /* + }, sl_edge: { base: 'SauceLabs', platform: 'Windows 10', - browserName: 'microsoftedge' + browserName: 'MicrosoftEdge' } - */ }, // mobile { @@ -75,14 +68,22 @@ var batches = [ } ] -for (var i = 0; i < 3; i++) { - exports['batch' + (i + 1)] = { - sauceLabs: sauceConfig, - // mobile emulators are really slow +module.exports = config => { + const batch = batches[process.argv[5] || 0] + + config.set(Object.assign(base, { + singleRun: true, + browsers: Object.keys(batch), + customLaunchers: batch, + reporters: process.env.CI + ? ['dots', 'saucelabs'] // avoid spamming CI output + : ['progress', 'saucelabs'], + sauceLabs: { + testName: `${pack.name} unit tests`, + recordScreenshots: false, + build: process.env.CIRCLE_BUILD_NUM || process.env.SAUCE_BUILD_ID || Date.now() + }, captureTimeout: 300000, - browserNoActivityTimeout: 300000, - customLaunchers: batches[i], - browsers: Object.keys(batches[i]), - reporters: ['progress', 'saucelabs'] - } + browserNoActivityTimeout: 300000 + })) } diff --git a/config/karma.unit.conf.js b/config/karma.unit.conf.js new file mode 100644 index 0000000..a99503b --- /dev/null +++ b/config/karma.unit.conf.js @@ -0,0 +1,9 @@ +const base = require('./karma.base.conf') + +module.exports = config => { + config.set(Object.assign(base, { + reporters: ['progress'], + browsers: ['Chrome', 'Firefox', 'Safari'], + singleRun: true + })) +} diff --git a/config/nightwatch.conf.js b/config/nightwatch.conf.js new file mode 100644 index 0000000..df6fdf3 --- /dev/null +++ b/config/nightwatch.conf.js @@ -0,0 +1,55 @@ +// http://nightwatchjs.org/guide#settings-file +module.exports = { + src_folders: ['test/e2e/test'], + output_folder: 'test/e2e/report', + custom_commands_path: ['node_modules/nightwatch-helpers/commands'], + custom_assertions_path: ['node_modules/nightwatch-helpers/assertions'], + + selenium: { + start_process: true, + server_path: 'node_modules/selenium-server/lib/runner/selenium-server-standalone-2.53.0.jar', + host: '127.0.0.1', + port: 4444, + cli_args: { + 'webdriver.chrome.driver': require('chromedriver').path + } + }, + + test_settings: { + default: { + selenium_port: 4444, + selenium_host: 'localhost', + silent: true, + screenshots: { + enabled: true, + on_failure: true, + on_error: false, + path: 'test/e2e/screenshots' + } + }, + + chrome: { + desiredCapabilities: { + browserName: 'chrome', + javascriptEnabled: true, + acceptSslCerts: true + } + }, + + firefox: { + desiredCapabilities: { + browserName: 'firefox', + javascriptEnabled: true, + acceptSslCerts: true + } + }, + + phantomjs: { + desiredCapabilities: { + browserName: 'phantomjs', + javascriptEnabled: true, + acceptSslCerts: true + } + } + } +} diff --git a/config/webpack.dev.conf.js b/config/webpack.dev.conf.js index 6753e80..7ec673f 100644 --- a/config/webpack.dev.conf.js +++ b/config/webpack.dev.conf.js @@ -1,26 +1,22 @@ -var webpack = require('webpack') +const webpack = require('webpack') +const JasmineWebpackPlugin = require('./webpack.dev.plugin') module.exports = { - entry: './index.js', + entry: './test/unit/index.js', output: { - path: './', - publicPath: '/', - filename: 'build.js' + path: './test/unit', + filename: 'tests.js', + publicPath: '/' }, - devtool: 'source-map', module: { - preLoaders: [{ - test: /\.js$/, - exclude: /node_modules/, - loader: 'eslint-loader' - }], loaders: [{ test: /\.js$/, exclude: /node_modules|vue\/dist/, - loader: 'babel', - query: { - presets: ['es2015-loose'] - } + loader: 'babel!eslint' + }], + postLoaders: [{ + test: /\.json$/, + loader: 'json' }] }, devServer: { @@ -30,6 +26,13 @@ module.exports = { inline: true }, plugins: [ + new webpack.DefinePlugin({ + 'process.env': { + NODE_ENV: '"development"' + } + }), + new JasmineWebpackPlugin(), new webpack.HotModuleReplacementPlugin() - ] + ], + devtool: 'source-map' } diff --git a/config/webpack.dev.plugin.js b/config/webpack.dev.plugin.js new file mode 100644 index 0000000..17d52fd --- /dev/null +++ b/config/webpack.dev.plugin.js @@ -0,0 +1,29 @@ +const path = require('path') +const HtmlWebpackPlugin = require('html-webpack-plugin') +const jasmineCore = require('jasmine-core') + +const jasmineFiles = jasmineCore.files +const jasminePath = resolveJasmineDir(jasmineFiles.path) +const jasmineBootDir = resolveJasmineDir(jasmineFiles.bootDir) +const jasmineJsFiles = resolveJasmineFiles(jasminePath, jasmineFiles.jsFiles) +const jasmineCssFiles = resolveJasmineFiles(jasminePath, jasmineFiles.cssFiles) +const jasmineBootFiles = resolveJasmineFiles(jasmineBootDir, jasmineFiles.bootFiles) + +function JasmineWebpackPlugin (options = {}) { + return new HtmlWebpackPlugin({ + title: 'vue-validator test runner', + filename: options.filename || 'index.html', + template: './config/webpack.runner.template.html', + jasmineJsFiles: jasmineJsFiles.concat(jasmineBootFiles), + jasmineCssFiles + }) +} + +function resolveJasmineDir (dirname) { + return dirname.replace(process.cwd(), '').replace(/^\//, '') +} + +function resolveJasmineFiles (dirname, files) { + return files.map(file => { return path.join(dirname, file) }) +} +module.exports = JasmineWebpackPlugin diff --git a/config/webpack.e2e.conf.js b/config/webpack.e2e.conf.js deleted file mode 100644 index c1b92bc..0000000 --- a/config/webpack.e2e.conf.js +++ /dev/null @@ -1,31 +0,0 @@ -var webpack = require('webpack') - -module.exports = { - entry: './test/e2e/index.js', - output: { - path: './test/e2e', - filename: 'e2e.js', - publicPath: '/' - }, - module: { - loaders: [{ - test: /\.js$/, - exclude: /node_modules|vue\/dist/, - loader: 'babel', - query: { - presets: ['es2015-loose'], - plugins: ['babel-plugin-espower'] - } - }] - }, - devtool: 'source-map', - devServer: { - contentBase: './test/e2e', - port: 8080, - hot: true, - inline: true - }, - plugins: [ - new webpack.HotModuleReplacementPlugin() - ] -} diff --git a/config/webpack.runner.template.html b/config/webpack.runner.template.html new file mode 100644 index 0000000..ef6fe4b --- /dev/null +++ b/config/webpack.runner.template.html @@ -0,0 +1,15 @@ + + + + + <%= htmlWebpackPlugin.options.title %> + <% for (var i = 0; i < htmlWebpackPlugin.options.jasmineCssFiles.length; i++) { %> + + <% } %> + <% for (var i = 0; i < htmlWebpackPlugin.options.jasmineJsFiles.length; i++) { %> + + <% } %> + + + + diff --git a/config/webpack.test.conf.js b/config/webpack.test.conf.js deleted file mode 100644 index ab16422..0000000 --- a/config/webpack.test.conf.js +++ /dev/null @@ -1,51 +0,0 @@ -var webpack = require('webpack') - -module.exports = { - entry: 'mocha!./test/specs/index.js', - output: { - path: './test/specs', - filename: 'specs.js', - publicPath: '/' - }, - devtool: 'source-map', - module: { - noParse: [ - /node_modules\/sinon\// - ], - preLoaders: [{ - test: /\.js$/, - exclude: /node_modules/, - loader: 'eslint-loader' - }], - loaders: [{ - test: /\.js$/, - exclude: /node_modules|vue\/dist/, - loader: 'babel', - query: { - presets: ['es2015-loose'], - plugins: [ - 'babel-plugin-espower', - 'babel-plugin-rewire' - ] - } - }], - postLoaders: [{ - test: /\.json$/, - loader: 'json' - }] - }, - resolve: { - alias: { - sinon: 'sinon/pkg/sinon.js' - } - }, - devServer: { - contentBase: './test/specs', - port: 8081, - hot: true, - inline: true - }, - plugins: [ - new webpack.HotModuleReplacementPlugin() - ] -} diff --git a/decls/compiler.js b/decls/compiler.js new file mode 100644 index 0000000..36e135d --- /dev/null +++ b/decls/compiler.js @@ -0,0 +1,145 @@ +declare type CompilerOptions = { + warn?: Function, // allow customizing warning in different environments, e.g. node + isIE?: boolean, // for detecting IE SVG innerHTML bug + expectHTML?: boolean, // only false for non-web builds + modules?: Array, // platform specific modules, e.g. style, class + staticKeys?: string, // a list of AST properties to be considered static, for optimization + directives?: { [key: string]: Function }, // platform specific directives + isUnaryTag?: (tag: string) => ?boolean, // check if a tag is unary for the platform + isReservedTag?: (tag: string) => ?boolean, // check if a tag is a native for the platform + mustUseProp?: (attr: string) => ?boolean, // check if an attribute should be bound as a property + getTagNamespace?: (tag: string) => ?string, // check the namespace for a tag + transforms?: Array, // a list of transforms on parsed AST before codegen + preserveWhitespace?: boolean, + + // runtime user-configurable + delimiters?: [string, string] // template delimiters +} + +declare type CompiledResult = { + ast: ?ASTElement, + render: string, + staticRenderFns: Array, + errors?: Array +} + +declare type CompiledFunctionResult = { + render: Function, + staticRenderFns: Array +} + +declare type ModuleOptions = { + transformNode: (el: ASTElement) => void, // transform an element's AST node + genData: (el: ASTElement) => string, // generate extra data string for an element + transformCode?: (el: ASTElement, code: string) => string, // further transform generated code for an element + staticKeys?: Array // AST properties to be considered static +} + +declare type ASTElementHandler = { + value: string, + modifiers: ?{ [key: string]: true } +} + +declare type ASTElementHandlers = { + [key: string]: ASTElementHandler | Array +} + +declare type ASTElementHooks = { [key: string]: Array } + +declare type ASTDirective = { + name: string, + value: ?string, + arg: ?string, + modifiers: ?{ [key: string]: true } +} + +declare type ASTNode = ASTElement | ASTText | ASTExpression + +declare type ASTElement = { + type: 1, + tag: string, + attrsList: Array<{ name: string, value: string }>, + attrsMap: { [key: string]: string | null }, + parent: ASTElement | void, + children: Array, + + static?: boolean, + staticRoot?: boolean, + staticProcessed?: boolean, + + text?: string, + attrs?: Array<{ name: string, value: string }>, + props?: Array<{ name: string, value: string }>, + staticAttrs?: Array<{ name: string, value: string }>, + plain?: boolean, + pre?: true, + ns?: string, + + component?: string, + keepAlive?: boolean, + inlineTemplate?: true, + transitionMode?: string | null, + slotName?: ?string, + slotTarget?: ?string, + + ref?: string, + refInFor?: boolean, + + if?: string, + ifProcessed?: boolean, + else?: true, + elseBlock?: ASTElement, + + for?: string, + forProcessed?: boolean, + key?: string, + alias?: string, + iterator1?: string, + iterator2?: string, + + staticClass?: string, + classBinding?: string, + styleBinding?: string, + hooks?: ASTElementHooks, + events?: ASTElementHandlers, + + transition?: string | true, + transitionOnAppear?: boolean, + + directives?: Array, + + forbidden?: true, + once?: true +} + +declare type ASTExpression = { + type: 2, + expression: string, + text: string, + static?: boolean +} + +declare type ASTText = { + type: 3, + text: string, + static?: boolean +} + +// SFC-parser related declarations + +// an object format describing a single-file component. +declare type SFCDescriptor = { + template: ?SFCBlock, + script: ?SFCBlock, + styles: Array +} + +declare type SFCBlock = { + type: string, + content: string, + start?: number, + end?: number, + lang?: string, + src?: string, + scoped?: boolean +} diff --git a/decls/component.js b/decls/component.js new file mode 100644 index 0000000..6280f4e --- /dev/null +++ b/decls/component.js @@ -0,0 +1,120 @@ +// TODO: should be provided from vue.js +declare type VNode = {} +declare type Watcher = {} +declare type Config = { + [key: string]: any +} + +declare interface Component { + // constructor information + static cid: number; + static options: Object; + // extend + static extend: (options: Object) => Function; + static superOptions: Object; + static extendOptions: Object; + static super: Class; + // assets + static directive: (id: string, def?: Function | Object) => Function | Object | void; + static component: (id: string, def?: Class | Object) => Class; + static filter: (id: string, def?: Function) => Function | void; + + // public properties + $el: any; // so that we can attach __vue__ to it + $data: Object; + $options: ComponentOptions; + $parent: Component | void; + $root: Component; + $children: Array; + $refs: { [key: string]: Component | Element | Array | void }; + $slots: { [key: string]: Array }; + $vnode: VNode; + $isServer: boolean; + + // public methods + $mount: (el?: Element | string, hydrating?: boolean) => Component; + $forceUpdate: () => void; + $destroy: () => void; + $watch: (expOrFn: string | Function, cb: Function, options?: Object) => Function; + $on: (event: string, fn: Function) => Component; + $once: (event: string, fn: Function) => Component; + $off: (event?: string, fn?: Function) => Component; + $emit: (event: string, ...args: Array) => Component; + $nextTick: (fn: Function) => void; + $createElement: ( + tag?: string | Component, + data?: Object, + children?: VNodeChildren, + namespace?: string + ) => VNode; + + /* + * NOTE: should not be published internal interface ... + * + // private properties + _uid: number; + _isVue: true; + _self: Component; + _renderProxy: Component; + _renderParent: ?Component; + _watcher: Watcher; + _watchers: Array; + _data: Object; + _events: Object; + _inactive: boolean; + _isMounted: boolean; + _isDestroyed: boolean; + _isBeingDestroyed: boolean; + _vnode: ?VNode; + _staticTrees: ?Array; + + // private methods + // lifecycle + _init: Function; + _mount: (el?: Element | void, hydrating?: boolean) => Component; + _update: (vnode: VNode, hydrating?: boolean) => void; + _updateListeners: (listeners: Object, oldListeners: ?Object) => void; + _updateFromParent: ( + propsData: ?Object, + listeners: ?{ [key: string]: Function | Array }, + parentVnode: VNode, + renderChildren: ?VNodeChildren + ) => void; + // rendering + _render: () => VNode; + __patch__: (a: Element | VNode | void, b: VNode) => any; + // renderElementWithChildren + _h: ( + vnode?: VNode, + children?: VNodeChildren + ) => VNode | void; + // renderElement + _e: ( + tag?: string | Component | Object, + data?: Object, + namespace?: string + ) => VNode | void; + // renderStaticTree + _m: ( + index?: number + ) => Object | void; + // toString + _s: (value: any) => string; + // toNumber + _n: (value: string) => number | string; + // resolveFilter + _f: (id: string) => Function; + // renderList + _l: ( + val: any, + render: Function + ) => ?Array; + // apply v-bind object + _b: (vnode: VNodeWithData, value: any) => void; + // retrive custom keyCode + _k: (key: string) => ?number; + */ + + // allow dynamic method registration + [key: string]: any +} diff --git a/decls/global-api.js b/decls/global-api.js new file mode 100644 index 0000000..c467f14 --- /dev/null +++ b/decls/global-api.js @@ -0,0 +1,21 @@ +declare interface GlobalAPI { + cid: number; + options: Object; + config: Config; + util: Object; + + extend: (options: Object) => Function; + set: (obj: Object, key: string, value: any) => void; + delete: (obj: Object, key: string) => void; + nextTick: (fn: Function, context?: Object) => void; + use: (plugin: Function | Object) => void; + mixin: (mixin: Object) => void; + compile: (template: string) => { render: Function, staticRenderFns: Array }; + + directive: (id: string, def?: Function | Object) => Function | Object | void; + component: (id: string, def?: Class | Object) => Class; + filter: (id: string, def?: Function) => Function | void; + + // allow dynamic method registration + [key: string]: any +} diff --git a/decls/modules.js b/decls/modules.js new file mode 100644 index 0000000..829c771 --- /dev/null +++ b/decls/modules.js @@ -0,0 +1,24 @@ +declare module 'entities' { + declare function encodeHTML(html: string): string; + declare function decodeHTML(html: string): string; +} + +declare module 'source-map' { + declare class SourceMapGenerator { + setSourceContent(filename: string, content: string): void; + addMapping(mapping: Object): void; + toString(): string; + } +} + +declare module 'lru-cache' { + declare var exports: { + (): any + } +} + +declare module 'de-indent' { + declare var exports: { + (input: string): string + } +} diff --git a/decls/options.js b/decls/options.js new file mode 100644 index 0000000..f9ee711 --- /dev/null +++ b/decls/options.js @@ -0,0 +1,68 @@ +declare type InternalComponentOptions = { + _isComponent: true, + parent: Component, + propsData: ?Object, + _parentVnode: VNode, + _parentListeners: ?Object, + _renderChildren: ?VNodeChildren, + _componentTag: ?string, + render?: Function, + staticRenderFns?: Array +} + +declare type ComponentOptions = { + // data + data: Object | Function | void, + props?: { [key: string]: PropOptions }, + propsData?: ?Object, + computed?: { + [key: string]: Function | { + get?: Function, + set?: Function, + cache?: boolean + } + }, + methods?: { + [key: string]: Function + }, + watch?: { + [key: string]: Function | string + }, + // DOM + el?: string | Element, + template?: string, + render: () => VNode, + staticRenderFns?: Array<() => VNode>, + // lifecycle + init?: Function, + created?: Function, + beforeMount?: Function, + mounted?: Function, + beforeUpdate?: Function, + updated?: Function, + // assets + directives?: { [key: string]: Object }, + components?: { [key: string]: Class }, + transitions?: { [key: string]: Object }, + filters?: { [key: string]: Function }, + // misc + parent?: Component, + mixins?: Array, + name?: string, + extends?: Class | Object, + delimiters?: [string, string], + + // private + _isComponent?: true, + _propKeys?: Array, + _parentVnode?: VNode, + _parentListeners?: ?Object, + _renderChildren?: ?VNodeChildren +} + +declare type PropOptions = { + type: Function | Array | null, + default: any, + required: ?boolean, + validator: ?Function +} diff --git a/decls/validator.js b/decls/validator.js new file mode 100644 index 0000000..06c3a1a --- /dev/null +++ b/decls/validator.js @@ -0,0 +1,75 @@ +declare type ValidatorDefinition = { + check: Function, // validator function + message?: string | Function // error message +} + +declare type ValidatorAsset = Function | ValidatorDefinition + +declare type ValidationError = { + field: string, + validator: string, + message?: string +} + +declare type ValidationResult = { + valid: boolean, + invalid: boolean, + dirty: boolean, + pristine: boolean, + touched: boolean, + untouched: boolean, + modified: boolean, + // `errors` or validator result + [key: string]: Array | boolean | string +} + +declare type ValidatorProgresses = { + [key: string]: string +} + +declare type $ValidationRawResult = { + [key: string]: boolean | string | void +} + +declare type $ValidateDescriptor = { + fn: Function, + value: any, + field: string, + rule?: any, + msg?: string | Function +} + +declare type ValidityComponent = { + field: string, + child: any, + validators: string | Array | Object, + valid: boolean, + invalid: boolean, + dirty: boolean, + pristine: boolean, + touched: boolean, + untouched: boolean, + modified: boolean, + result: ValidatorAsset, + progresses: ValidatorProgresses, + + checkModified (): boolean, + willUpdateTouched (options?: any): void, + willUpdateDirty (): void, + willUpdateModified (): void, + handleInputable (e: Event): void, + watchInputable (val: any): void, + reset (): void, + validate (...args: Array): boolean, +} & Component + +declare interface ValidityElement { + initValue: any, + attachValidity (): void, + getValue (): any, + checkModified (): boolean, + listenToucheableEvent (): void, + unlistenToucheableEvent (): void, + listenInputableEvent (): void, + unlistenInputableEvent (): void +} diff --git a/decls/vnode.js b/decls/vnode.js new file mode 100644 index 0000000..d6b2aab --- /dev/null +++ b/decls/vnode.js @@ -0,0 +1,62 @@ +declare type VNodeChildren = Array | () => Array | string + +declare type VNodeComponentOptions = { + Ctor: Class, + propsData: ?Object, + listeners: ?Object, + parent: Component, + children: ?VNodeChildren, + tag?: string +} + +declare interface MountedComponentVNode { + componentOptions: VNodeComponentOptions; + child: Component; + parent: VNode; + data: VNodeData; +} + +// interface for vnodes in update modules +declare interface VNodeWithData { + tag: string; + data: VNodeData; + children: Array | void; + text: void; + elm: HTMLElement; + ns: string | void; + context: Component; + key: string | number | void; + parent?: VNodeWithData; + child?: Component; +} + +declare interface VNodeData { + key?: string | number; + slot?: string; + ref?: string; + tag?: string; + staticClass?: string; + class?: any; + style?: Array | Object; + show?: true; + props?: { [key: string]: any }; + attrs?: { [key: string]: string }; + staticAttrs?: { [key: string]: string }; + hook?: { [key: string]: Function }; + on?: { [key: string]: Function | Array }; + transition?: Object; + inlineTemplate?: { + render: Function, + staticRenderFns: Array + }; + directives?: Array; + keepAlive?: boolean; +} + +declare type VNodeDirective = { + name: string, + value?: any, + oldValue?: any, + arg?: string, + modifiers?: { [key: string]: boolean } +} diff --git a/dist/vue-validator.common.js b/dist/vue-validator.common.js index 4cd6cac..383b0d4 100644 --- a/dist/vue-validator.common.js +++ b/dist/vue-validator.common.js @@ -1,2593 +1,16 @@ /*! - * vue-validator v2.1.3 + * vue-validator v2.1.3 * (c) 2016 kazuya kawaguchi * Released under the MIT License. */ 'use strict'; -var babelHelpers = {}; -babelHelpers.typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { - return typeof obj; -} : function (obj) { - return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; -}; - -babelHelpers.classCallCheck = function (instance, Constructor) { - if (!(instance instanceof Constructor)) { - throw new TypeError("Cannot call a class as a function"); - } -}; - -babelHelpers.createClass = function () { - function defineProperties(target, props) { - for (var i = 0; i < props.length; i++) { - var descriptor = props[i]; - descriptor.enumerable = descriptor.enumerable || false; - descriptor.configurable = true; - if ("value" in descriptor) descriptor.writable = true; - Object.defineProperty(target, descriptor.key, descriptor); - } - } - - return function (Constructor, protoProps, staticProps) { - if (protoProps) defineProperties(Constructor.prototype, protoProps); - if (staticProps) defineProperties(Constructor, staticProps); - return Constructor; - }; -}(); - -babelHelpers.inherits = function (subClass, superClass) { - if (typeof superClass !== "function" && superClass !== null) { - throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); - } - - subClass.prototype = Object.create(superClass && superClass.prototype, { - constructor: { - value: subClass, - enumerable: false, - writable: true, - configurable: true - } - }); - if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; -}; - -babelHelpers.possibleConstructorReturn = function (self, call) { - if (!self) { - throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); - } - - return call && (typeof call === "object" || typeof call === "function") ? call : self; -}; - -babelHelpers; - -/** - * Utilties - */ - -// export default for holding the Vue reference -var exports$1 = {}; -/** - * warn - * - * @param {String} msg - * @param {Error} [err] - * - */ - -function warn(msg, err) { - if (window.console) { - console.warn('[vue-validator] ' + msg); - if (err) { - console.warn(err.stack); - } - } -} - -/** - * empty - * - * @param {Array|Object} target - * @return {Boolean} - */ - -function empty(target) { - if (target === null || target === undefined) { - return true; - } - - if (Array.isArray(target)) { - if (target.length > 0) { - return false; - } - if (target.length === 0) { - return true; - } - } else if (exports$1.Vue.util.isPlainObject(target)) { - for (var key in target) { - if (exports$1.Vue.util.hasOwn(target, key)) { - return false; - } - } - } - - return true; -} - -/** - * each - * - * @param {Array|Object} target - * @param {Function} iterator - * @param {Object} [context] - */ - -function each(target, iterator, context) { - if (Array.isArray(target)) { - for (var i = 0; i < target.length; i++) { - iterator.call(context || target[i], target[i], i); - } - } else if (exports$1.Vue.util.isPlainObject(target)) { - var hasOwn = exports$1.Vue.util.hasOwn; - for (var key in target) { - if (hasOwn(target, key)) { - iterator.call(context || target[key], target[key], key); - } - } - } -} - -/** - * pull - * - * @param {Array} arr - * @param {Object} item - * @return {Object|null} - */ - -function pull(arr, item) { - var index = exports$1.Vue.util.indexOf(arr, item); - return ~index ? arr.splice(index, 1) : null; -} - -/** - * trigger - * - * @param {Element} el - * @param {String} event - * @param {Object} [args] - */ - -function trigger(el, event, args) { - var e = document.createEvent('HTMLEvents'); - e.initEvent(event, true, false); - - if (args) { - for (var prop in args) { - e[prop] = args[prop]; - } - } - - // Due to Firefox bug, events fired on disabled - // non-attached form controls can throw errors - try { - el.dispatchEvent(e); - } catch (e) {} -} - -/** - * Forgiving check for a promise - * - * @param {Object} p - * @return {Boolean} - */ - -function isPromise(p) { - return p && typeof p.then === 'function'; -} - -/** - * Togging classes - * - * @param {Element} el - * @param {String} key - * @param {Function} fn - */ - -function toggleClasses(el, key, fn) { - key = key.trim(); - if (key.indexOf(' ') === -1) { - fn(el, key); - return; - } - - var keys = key.split(/\s+/); - for (var i = 0, l = keys.length; i < l; i++) { - fn(el, keys[i]); - } -} - -/** - * Fundamental validate functions - */ - -/** - * required - * - * This function validate whether the value has been filled out. - * - * @param {*} val - * @return {Boolean} - */ - -function required(val) { - if (Array.isArray(val)) { - if (val.length !== 0) { - var valid = true; - for (var i = 0, l = val.length; i < l; i++) { - valid = required(val[i]); - if (!valid) { - break; - } - } - return valid; - } else { - return false; - } - } else if (typeof val === 'number' || typeof val === 'function') { - return true; - } else if (typeof val === 'boolean') { - return val; - } else if (typeof val === 'string') { - return val.length > 0; - } else if (val !== null && (typeof val === 'undefined' ? 'undefined' : babelHelpers.typeof(val)) === 'object') { - return Object.keys(val).length > 0; - } else if (val === null || val === undefined) { - return false; - } -} - -/** - * pattern - * - * This function validate whether the value matches the regex pattern - * - * @param val - * @param {String} pat - * @return {Boolean} - */ - -function pattern(val, pat) { - if (typeof pat !== 'string') { - return false; - } - - var match = pat.match(new RegExp('^/(.*?)/([gimy]*)$')); - if (!match) { - return false; - } - - return new RegExp(match[1], match[2]).test(val); -} - -/** - * minlength - * - * This function validate whether the minimum length. - * - * @param {String|Array} val - * @param {String|Number} min - * @return {Boolean} - */ - -function minlength(val, min) { - if (typeof val === 'string') { - return isInteger(min, 10) && val.length >= parseInt(min, 10); - } else if (Array.isArray(val)) { - return val.length >= parseInt(min, 10); - } else { - return false; - } -} - -/** - * maxlength - * - * This function validate whether the maximum length. - * - * @param {String|Array} val - * @param {String|Number} max - * @return {Boolean} - */ - -function maxlength(val, max) { - if (typeof val === 'string') { - return isInteger(max, 10) && val.length <= parseInt(max, 10); - } else if (Array.isArray(val)) { - return val.length <= parseInt(max, 10); - } else { - return false; - } -} - -/** - * min - * - * This function validate whether the minimum value of the numberable value. - * - * @param {*} val - * @param {*} arg minimum - * @return {Boolean} - */ - -function min(val, arg) { - return !isNaN(+val) && !isNaN(+arg) && +val >= +arg; -} - -/** - * max - * - * This function validate whether the maximum value of the numberable value. - * - * @param {*} val - * @param {*} arg maximum - * @return {Boolean} - */ - -function max(val, arg) { - return !isNaN(+val) && !isNaN(+arg) && +val <= +arg; -} - -/** - * isInteger - * - * This function check whether the value of the string is integer. - * - * @param {String} val - * @return {Boolean} - * @private - */ - -function isInteger(val) { - return (/^(-?[1-9]\d*|0)$/.test(val) - ); -} - -var validators = Object.freeze({ - required: required, - pattern: pattern, - minlength: minlength, - maxlength: maxlength, - min: min, - max: max -}); - -function Asset (Vue) { - var extend = Vue.util.extend; - - // set global validators asset - var assets = Object.create(null); - extend(assets, validators); - Vue.options.validators = assets; - - // set option merge strategy - var strats = Vue.config.optionMergeStrategies; - if (strats) { - strats.validators = function (parent, child) { - if (!child) { - return parent; - } - if (!parent) { - return child; - } - var ret = Object.create(null); - extend(ret, parent); - for (var key in child) { - ret[key] = child[key]; - } - return ret; - }; - } - - /** - * Register or retrieve a global validator definition. - * - * @param {String} id - * @param {Function} definition - */ - - Vue.validator = function (id, definition) { - if (!definition) { - return Vue.options['validators'][id]; - } else { - Vue.options['validators'][id] = definition; - } - }; -} - -function Override (Vue) { - // override _init - var init = Vue.prototype._init; - Vue.prototype._init = function (options) { - if (!this._validatorMaps) { - this._validatorMaps = Object.create(null); - } - init.call(this, options); - }; - - // override _destroy - var destroy = Vue.prototype._destroy; - Vue.prototype._destroy = function () { - destroy.apply(this, arguments); - this._validatorMaps = null; - }; -} - -var VALIDATE_UPDATE = '__vue-validator-validate-update__'; -var PRIORITY_VALIDATE = 16; -var PRIORITY_VALIDATE_CLASS = 32; -var REGEX_FILTER = /[^|]\|[^|]/; -var REGEX_VALIDATE_DIRECTIVE = /^v-validate(?:$|:(.*)$)/; -var REGEX_EVENT = /^v-on:|^@/; - -var classId = 0; // ID for validation class - -function ValidateClass (Vue) { - var vIf = Vue.directive('if'); - var FragmentFactory = Vue.FragmentFactory; - var _Vue$util = Vue.util; - var toArray = _Vue$util.toArray; - var replace = _Vue$util.replace; - var createAnchor = _Vue$util.createAnchor; - - /** - * `v-validate-class` directive - */ - - Vue.directive('validate-class', { - terminal: true, - priority: vIf.priority + PRIORITY_VALIDATE_CLASS, - - bind: function bind() { - var _this = this; - - var id = String(classId++); - this.setClassIds(this.el, id); - - this.vm.$on(VALIDATE_UPDATE, this.cb = function (classIds, validation, results) { - if (classIds.indexOf(id) > -1) { - validation.updateClasses(results, _this.frag.node); - } - }); - - this.setupFragment(); - }, - unbind: function unbind() { - this.vm.$off(VALIDATE_UPDATE, this.cb); - this.teardownFragment(); - }, - setClassIds: function setClassIds(el, id) { - var childNodes = toArray(el.childNodes); - for (var i = 0, l = childNodes.length; i < l; i++) { - var element = childNodes[i]; - if (element.nodeType === 1) { - var hasAttrs = element.hasAttributes(); - var attrs = hasAttrs && toArray(element.attributes); - for (var k = 0, _l = attrs.length; k < _l; k++) { - var attr = attrs[k]; - if (attr.name.match(REGEX_VALIDATE_DIRECTIVE)) { - var existingId = element.getAttribute(VALIDATE_UPDATE); - var value = existingId ? existingId + ',' + id : id; - element.setAttribute(VALIDATE_UPDATE, value); - } - } - } - - if (element.hasChildNodes()) { - this.setClassIds(element, id); - } - } - }, - setupFragment: function setupFragment() { - this.anchor = createAnchor('v-validate-class'); - replace(this.el, this.anchor); - - this.factory = new FragmentFactory(this.vm, this.el); - this.frag = this.factory.create(this._host, this._scope, this._frag); - this.frag.before(this.anchor); - }, - teardownFragment: function teardownFragment() { - if (this.frag) { - this.frag.remove(); - this.frag = null; - this.factory = null; - } - - replace(this.anchor, this.el); - this.anchor = null; - } - }); -} - -function Validate (Vue) { - var vIf = Vue.directive('if'); - var FragmentFactory = Vue.FragmentFactory; - var parseDirective = Vue.parsers.directive.parseDirective; - var _Vue$util = Vue.util; - var inBrowser = _Vue$util.inBrowser; - var bind = _Vue$util.bind; - var on = _Vue$util.on; - var off = _Vue$util.off; - var createAnchor = _Vue$util.createAnchor; - var replace = _Vue$util.replace; - var camelize = _Vue$util.camelize; - var isPlainObject = _Vue$util.isPlainObject; - - // Test for IE10/11 textarea placeholder clone bug - - function checkTextareaCloneBug() { - if (inBrowser) { - var t = document.createElement('textarea'); - t.placeholder = 't'; - return t.cloneNode(true).value === 't'; - } else { - return false; - } - } - var hasTextareaCloneBug = checkTextareaCloneBug(); - - /** - * `v-validate` directive - */ - - Vue.directive('validate', { - terminal: true, - priority: vIf.priority + PRIORITY_VALIDATE, - params: ['group', 'field', 'detect-blur', 'detect-change', 'initial', 'classes'], - - paramWatchers: { - detectBlur: function detectBlur(val, old) { - if (this._invalid) { - return; - } - this.validation.detectBlur = this.isDetectBlur(val); - this.validator.validate(this.field); - }, - detectChange: function detectChange(val, old) { - if (this._invalid) { - return; - } - this.validation.detectChange = this.isDetectChange(val); - this.validator.validate(this.field); - } - }, - - bind: function bind() { - var el = this.el; - - if (process.env.NODE_ENV !== 'production' && el.__vue__) { - warn('v-validate="' + this.expression + '" cannot be used on an instance root element.'); - this._invalid = true; - return; - } - - if (process.env.NODE_ENV !== 'production' && (el.hasAttribute('v-if') || el.hasAttribute('v-for'))) { - warn('v-validate cannot be used `v-if` or `v-for` build-in terminal directive ' + 'on an element. these is wrapped with `