├── .babelrc ├── .gitignore ├── doc-site ├── vue │ ├── App.vue │ ├── MainContent.vue │ ├── examples │ │ ├── FieldBasedMessageExample.vue │ │ ├── ComponentBasedMessageExample.vue │ │ ├── CustomComponentExample.vue │ │ ├── CustomRuleExample.vue │ │ ├── LocalizationExample.vue │ │ ├── BasicExample.vue │ │ ├── DynamicFormExample.vue │ │ ├── DynamicForm.vue │ │ ├── CrossFieldValidationExample1.vue │ │ ├── CheckboxGroup.vue │ │ ├── AsyncValidationExample1.vue │ │ ├── AsyncValidationExample2.vue │ │ ├── CrossFieldValidationExample2.vue │ │ └── BuiltinRulesExample.vue │ ├── chapters │ │ ├── Miscellaneous.vue │ │ ├── GettingStarted.vue │ │ ├── UsagesAndExamples.vue │ │ └── APIAndReference.vue │ ├── LeftNavBar.vue │ └── DemoWithCode.vue ├── dist │ ├── images │ │ ├── green.png │ │ └── green@2x.png │ ├── fonts │ │ ├── fontawesome-webfont.eot │ │ ├── fontawesome-webfont.ttf │ │ ├── lato-v11-latin-300.woff │ │ ├── lato-v11-latin-900.woff │ │ ├── fontawesome-webfont.woff │ │ ├── fontawesome-webfont.woff2 │ │ ├── lato-v11-latin-300.woff2 │ │ ├── lato-v11-latin-900.woff2 │ │ ├── lato-v11-latin-italic.woff │ │ ├── lato-v11-latin-italic.woff2 │ │ ├── lato-v11-latin-regular.woff │ │ ├── lato-v11-latin-regular.woff2 │ │ ├── lato-v11-latin-300italic.woff │ │ ├── lato-v11-latin-300italic.woff2 │ │ ├── lato-v11-latin-900italic.woff │ │ └── lato-v11-latin-900italic.woff2 │ ├── style.js │ └── styles │ │ └── style.css ├── fonts │ ├── lato-v11-latin-300.woff │ ├── lato-v11-latin-900.woff │ ├── lato-v11-latin-300.woff2 │ ├── lato-v11-latin-900.woff2 │ ├── lato-v11-latin-italic.woff │ ├── lato-v11-latin-italic.woff2 │ ├── lato-v11-latin-regular.woff │ ├── lato-v11-latin-regular.woff2 │ ├── lato-v11-latin-300italic.woff │ ├── lato-v11-latin-300italic.woff2 │ ├── lato-v11-latin-900italic.woff │ └── lato-v11-latin-900italic.woff2 ├── style.js ├── index.html ├── doc.js └── styles │ └── style.css ├── TODO.md ├── test ├── web │ ├── index.html │ ├── test_global.html │ └── test_requirejs.html └── rules.spec.js ├── postcss.config.js ├── bower.json ├── .jshintrc ├── src ├── validator.js ├── templates.js ├── index.js ├── utils.js ├── mixin.js ├── validation-bag.js └── rule.js ├── LICENSE ├── webpack-lib.config.js ├── README.md ├── package.json ├── webpack-doc-site.config.js └── dist └── plugin.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"] 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.idea/ 3 | /node_modules/ 4 | npm-debug.log 5 | *.map 6 | *.log -------------------------------------------------------------------------------- /doc-site/vue/App.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /doc-site/dist/images/green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semisleep/simple-vue-validator/HEAD/doc-site/dist/images/green.png -------------------------------------------------------------------------------- /doc-site/dist/images/green@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semisleep/simple-vue-validator/HEAD/doc-site/dist/images/green@2x.png -------------------------------------------------------------------------------- /doc-site/fonts/lato-v11-latin-300.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semisleep/simple-vue-validator/HEAD/doc-site/fonts/lato-v11-latin-300.woff -------------------------------------------------------------------------------- /doc-site/fonts/lato-v11-latin-900.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semisleep/simple-vue-validator/HEAD/doc-site/fonts/lato-v11-latin-900.woff -------------------------------------------------------------------------------- /doc-site/fonts/lato-v11-latin-300.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semisleep/simple-vue-validator/HEAD/doc-site/fonts/lato-v11-latin-300.woff2 -------------------------------------------------------------------------------- /doc-site/fonts/lato-v11-latin-900.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semisleep/simple-vue-validator/HEAD/doc-site/fonts/lato-v11-latin-900.woff2 -------------------------------------------------------------------------------- /doc-site/fonts/lato-v11-latin-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semisleep/simple-vue-validator/HEAD/doc-site/fonts/lato-v11-latin-italic.woff -------------------------------------------------------------------------------- /doc-site/dist/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semisleep/simple-vue-validator/HEAD/doc-site/dist/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /doc-site/dist/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semisleep/simple-vue-validator/HEAD/doc-site/dist/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /doc-site/dist/fonts/lato-v11-latin-300.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semisleep/simple-vue-validator/HEAD/doc-site/dist/fonts/lato-v11-latin-300.woff -------------------------------------------------------------------------------- /doc-site/dist/fonts/lato-v11-latin-900.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semisleep/simple-vue-validator/HEAD/doc-site/dist/fonts/lato-v11-latin-900.woff -------------------------------------------------------------------------------- /doc-site/fonts/lato-v11-latin-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semisleep/simple-vue-validator/HEAD/doc-site/fonts/lato-v11-latin-italic.woff2 -------------------------------------------------------------------------------- /doc-site/fonts/lato-v11-latin-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semisleep/simple-vue-validator/HEAD/doc-site/fonts/lato-v11-latin-regular.woff -------------------------------------------------------------------------------- /doc-site/fonts/lato-v11-latin-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semisleep/simple-vue-validator/HEAD/doc-site/fonts/lato-v11-latin-regular.woff2 -------------------------------------------------------------------------------- /doc-site/dist/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semisleep/simple-vue-validator/HEAD/doc-site/dist/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /doc-site/dist/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semisleep/simple-vue-validator/HEAD/doc-site/dist/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /doc-site/dist/fonts/lato-v11-latin-300.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semisleep/simple-vue-validator/HEAD/doc-site/dist/fonts/lato-v11-latin-300.woff2 -------------------------------------------------------------------------------- /doc-site/dist/fonts/lato-v11-latin-900.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semisleep/simple-vue-validator/HEAD/doc-site/dist/fonts/lato-v11-latin-900.woff2 -------------------------------------------------------------------------------- /doc-site/dist/fonts/lato-v11-latin-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semisleep/simple-vue-validator/HEAD/doc-site/dist/fonts/lato-v11-latin-italic.woff -------------------------------------------------------------------------------- /doc-site/fonts/lato-v11-latin-300italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semisleep/simple-vue-validator/HEAD/doc-site/fonts/lato-v11-latin-300italic.woff -------------------------------------------------------------------------------- /doc-site/fonts/lato-v11-latin-300italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semisleep/simple-vue-validator/HEAD/doc-site/fonts/lato-v11-latin-300italic.woff2 -------------------------------------------------------------------------------- /doc-site/fonts/lato-v11-latin-900italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semisleep/simple-vue-validator/HEAD/doc-site/fonts/lato-v11-latin-900italic.woff -------------------------------------------------------------------------------- /doc-site/fonts/lato-v11-latin-900italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semisleep/simple-vue-validator/HEAD/doc-site/fonts/lato-v11-latin-900italic.woff2 -------------------------------------------------------------------------------- /doc-site/dist/fonts/lato-v11-latin-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semisleep/simple-vue-validator/HEAD/doc-site/dist/fonts/lato-v11-latin-italic.woff2 -------------------------------------------------------------------------------- /doc-site/dist/fonts/lato-v11-latin-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semisleep/simple-vue-validator/HEAD/doc-site/dist/fonts/lato-v11-latin-regular.woff -------------------------------------------------------------------------------- /doc-site/dist/fonts/lato-v11-latin-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semisleep/simple-vue-validator/HEAD/doc-site/dist/fonts/lato-v11-latin-regular.woff2 -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | ## TODO 2 | 3 | * debounced validator not cancelled after validation.reset 4 | * set isPassed only if there's actually some operation in rule 5 | 6 | ## DONE -------------------------------------------------------------------------------- /doc-site/dist/fonts/lato-v11-latin-300italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semisleep/simple-vue-validator/HEAD/doc-site/dist/fonts/lato-v11-latin-300italic.woff -------------------------------------------------------------------------------- /doc-site/dist/fonts/lato-v11-latin-300italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semisleep/simple-vue-validator/HEAD/doc-site/dist/fonts/lato-v11-latin-300italic.woff2 -------------------------------------------------------------------------------- /doc-site/dist/fonts/lato-v11-latin-900italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semisleep/simple-vue-validator/HEAD/doc-site/dist/fonts/lato-v11-latin-900italic.woff -------------------------------------------------------------------------------- /doc-site/dist/fonts/lato-v11-latin-900italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semisleep/simple-vue-validator/HEAD/doc-site/dist/fonts/lato-v11-latin-900italic.woff2 -------------------------------------------------------------------------------- /doc-site/style.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import 'font-awesome/css/font-awesome.css'; 4 | import 'prismjs/themes/prism-tomorrow.css'; 5 | import 'icheck/skins/square/green.css'; 6 | import './styles/style.css'; 7 | -------------------------------------------------------------------------------- /test/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Test - Simple Vue Validator 6 | 7 | 8 | 9 | test global 10 |
11 | test with requirejs 12 | 13 | 14 | -------------------------------------------------------------------------------- /test/web/test_global.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Test - Simple Vue Validator 6 | 7 | 8 | 9 |
Test here!
10 | 11 | 12 | 13 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /doc-site/vue/MainContent.vue: -------------------------------------------------------------------------------- 1 | 16 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | var postcssImport = require('postcss-import'); 3 | var postcssUrl = require('postcss-url'); 4 | var autoprefixer = require('autoprefixer'); 5 | var csswring = require('csswring'); 6 | 7 | module.exports = { 8 | plugins: [ 9 | postcssImport({ addDependencyTo: webpack }), 10 | postcssUrl(), 11 | autoprefixer({ browsers: ['last 2 versions', '> 5%', 'Explorer >= 10', 'Android >= 2.3'] }), 12 | csswring() 13 | ] 14 | }; 15 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simple-vue-validator", 3 | "homepage": "https://github.com/semisleep/simple-vue-validator", 4 | "authors": [ 5 | "semisleep" 6 | ], 7 | "description": "lightweight yet flexible plugin for vue.js", 8 | "main": "dist/plugin.js", 9 | "keywords": [ 10 | "validation", 11 | "form", 12 | "vue", 13 | "vue.js" 14 | ], 15 | "license": "MIT", 16 | "ignore": [ 17 | "**/.*", 18 | "node_modules", 19 | "bower_components", 20 | "test", 21 | "tests" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "browser": false, 3 | "node": true, 4 | "esnext": true, 5 | "bitwise": true, 6 | "camelcase": false, 7 | "curly": true, 8 | "eqeqeq": false, 9 | "immed": true, 10 | "indent": 2, 11 | "newcap": true, 12 | "noarg": true, 13 | "quotmark": "single", 14 | "undef": true, 15 | "unused": true, 16 | "strict": true, 17 | "jquery": false, 18 | "latedef": false, 19 | "futurehostile": false, 20 | "globals": { 21 | "window": true, 22 | "document": true, 23 | "Promise": true 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /test/web/test_requirejs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Test - Simple Vue Validator 6 | 7 | 8 | 9 |
Test here!
10 | 11 | 12 | 13 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /doc-site/dist/style.js: -------------------------------------------------------------------------------- 1 | !function(n){function t(e){if(r[e])return r[e].exports;var o=r[e]={i:e,l:!1,exports:{}};return n[e].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var r={};return t.m=n,t.c=r,t.i=function(n){return n},t.d=function(n,r,e){t.o(n,r)||Object.defineProperty(n,r,{configurable:!1,enumerable:!0,get:e})},t.n=function(n){var r=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(r,"a",r),r},t.o=function(n,t){return Object.prototype.hasOwnProperty.call(n,t)},t.p="/dist/",t(t.s=87)}({13:function(n,t){},14:function(n,t){},15:function(n,t){},16:function(n,t){},87:function(n,t,r){"use strict";r(14),r(16),r(15),r(13)}}); 2 | //# sourceMappingURL=style.js.map -------------------------------------------------------------------------------- /src/validator.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('./utils'); 4 | var Rule = require('./rule'); 5 | 6 | var Validator = newValidator(); 7 | 8 | Validator.create = function(options) { 9 | return newValidator(options); 10 | }; 11 | 12 | function newValidator(options) { 13 | options = options || {}; 14 | var validator = {}; 15 | 16 | // clone methods from Rule to validator 17 | Object.keys(Rule.prototype).forEach(function (methodName) { 18 | validator[methodName] = function () { 19 | var rule = new Rule(options.templates); 20 | return rule[methodName].apply(rule, arguments); 21 | }; 22 | }); 23 | 24 | validator.isEmpty = utils.isEmpty; 25 | 26 | validator.format = utils.format; 27 | 28 | return validator; 29 | } 30 | 31 | module.exports = Validator; 32 | -------------------------------------------------------------------------------- /doc-site/vue/examples/FieldBasedMessageExample.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | -------------------------------------------------------------------------------- /doc-site/vue/examples/ComponentBasedMessageExample.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 semisleep 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/templates.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | error: 'Error.', 5 | required: 'Required.', 6 | float: 'Must be a number.', 7 | integer: 'Must be an integer.', 8 | number: 'Must be a number.', 9 | lessThan: 'Must be less than {0}.', 10 | lessThanOrEqualTo: 'Must be less than or equal to {0}.', 11 | greaterThan: 'Must be greater than {0}.', 12 | greaterThanOrEqualTo: 'Must greater than or equal to {0}.', 13 | between: 'Must be between {0} and {1}.', 14 | size: 'Size must be {0}.', 15 | length: 'Length must be {0}.', 16 | minLength: 'Must have at least {0} characters.', 17 | maxLength: 'Must have up to {0} characters.', 18 | lengthBetween: 'Length must between {0} and {1}.', 19 | in: 'Must be {0}.', 20 | notIn: 'Must not be {0}.', 21 | match: 'Not matched.', 22 | regex: 'Invalid format.', 23 | digit: 'Must be a digit.', 24 | email: 'Invalid email.', 25 | url: 'Invalid url.', 26 | optionCombiner: function (options) { 27 | if (options.length > 2) { 28 | options = [options.slice(0, options.length - 1).join(', '), options[options.length - 1]]; 29 | } 30 | return options.join(' or '); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /doc-site/vue/examples/CustomComponentExample.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | -------------------------------------------------------------------------------- /doc-site/vue/examples/CustomRuleExample.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | -------------------------------------------------------------------------------- /doc-site/vue/examples/LocalizationExample.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | -------------------------------------------------------------------------------- /doc-site/vue/examples/BasicExample.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | -------------------------------------------------------------------------------- /doc-site/vue/examples/DynamicFormExample.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var ValidationBag = require('./validation-bag'); 4 | var Rule = require('./rule'); 5 | var Validator = require('./validator'); 6 | var mixin = require('./mixin'); 7 | var utils = require('./utils'); 8 | 9 | /* plugin install 10 | ----------------------------------- */ 11 | 12 | function install(Vue, options) { 13 | Vue.mixin(mixin); 14 | if (options && options.templates) { 15 | extendTemplates(options.templates); 16 | } 17 | if (options && options.mode) { 18 | setMode(options.mode); 19 | } 20 | if (options && options.Promise) { 21 | mixin.Promise = options.Promise; 22 | } 23 | } 24 | 25 | function extendTemplates(newTemplates) { 26 | Object.keys(newTemplates).forEach(function (key) { 27 | utils.templates[key] = newTemplates[key]; 28 | }); 29 | } 30 | 31 | function setMode(mode) { 32 | if (mode !== 'interactive' && mode !== 'conservative' && mode !== 'manual') { 33 | throw new Error('Invalid mode: ' + mode); 34 | } 35 | utils.mode = mode; 36 | } 37 | 38 | /* exports 39 | ----------------------------------- */ 40 | 41 | module.exports.name = 'SimpleVueValidator'; 42 | module.exports.ValidationBag = ValidationBag; 43 | module.exports.Rule = Rule; 44 | module.exports.Validator = Validator; 45 | module.exports.mixin = mixin; 46 | module.exports.install = install; 47 | module.exports.extendTemplates = extendTemplates; 48 | module.exports.setMode = setMode; 49 | -------------------------------------------------------------------------------- /webpack-lib.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var webpack = require('webpack'); 5 | 6 | module.exports = { 7 | entry: { 8 | 'plugin': './src/index.js' 9 | }, 10 | output: { 11 | library: 'SimpleVueValidator', 12 | libraryTarget: 'umd', 13 | path: path.resolve(__dirname, 'dist'), 14 | publicPath: '/dist/', 15 | filename: '[name].js' 16 | }, 17 | module: { 18 | rules: [ 19 | /* 20 | { 21 | test: /\.vue$/, 22 | loader: 'vue-loader', 23 | options: { 24 | // vue-loader options go here 25 | } 26 | }, 27 | { 28 | test: /\.js$/, 29 | loader: 'babel-loader', 30 | exclude: /node_modules/ 31 | } 32 | */ 33 | ] 34 | }, 35 | devServer: { 36 | historyApiFallback: true, 37 | noInfo: false, 38 | hot: true, 39 | inline: true, 40 | // https: true, 41 | port: 8080, 42 | contentBase: path.resolve(__dirname, 'test/web') 43 | }, 44 | devtool: '#source-map' 45 | }; 46 | 47 | module.exports.plugins = []; 48 | 49 | if (process.env.NODE_ENV === 'production') { 50 | // http://vue-loader.vuejs.org/en/workflow/production.html 51 | module.exports.plugins = (module.exports.plugins || []).concat([ 52 | new webpack.DefinePlugin({ 53 | 'process.env': { 54 | NODE_ENV: '"production"' 55 | } 56 | }), 57 | new webpack.optimize.UglifyJsPlugin({ 58 | compress: { 59 | warnings: false 60 | } 61 | }), 62 | new webpack.LoaderOptionsPlugin({ 63 | minimize: true 64 | }) 65 | ]); 66 | } 67 | -------------------------------------------------------------------------------- /doc-site/vue/chapters/Miscellaneous.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /doc-site/vue/examples/DynamicForm.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | -------------------------------------------------------------------------------- /doc-site/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Simple Vue Validator - Documentation 9 | 10 | 11 | 12 | 13 | 20 | 21 | 42 | 43 | 44 | 45 | 46 |
47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # NOTE FOR VUE3 3 | 4 | Please use this fork if you are using vue3: [simple-vue3-validator](https://github.com/rak-phillip/simple-vue3-validator) 5 | 6 | # Simple Vue Validator 7 | 8 | Simple Vue validator is a lightweight yet flexible plugin for Vue.js 2.0 that allows you to validate input fields, and display errors. It watches changes of your model data, validates them and informs you with the validation result. 9 | 10 | It supports the following features: 11 | * Fully customized validation rules. 12 | * Cross field validation. 13 | * Async/ajax validation (with supports of loading indicator, result caching, debounced user input). 14 | * Validating custom component. 15 | * Dynamic form / multiple validation instances. 16 | 17 | # Documentation 18 | 19 | Please checkout the [full documentation](http://simple-vue-validator.magictek.cn) for more detail. 20 | 21 | ## Installation 22 | Package is installable via npm. 23 | ``` 24 | npm install --save simple-vue-validator 25 | ``` 26 | You can also install it via bower. 27 | ``` 28 | bower install --save simple-vue-validator 29 | ``` 30 | NOTE: for bower package, please use /dist/plugin.js. 31 | 32 | ## Configuration 33 | ``` 34 | import Vue from 'vue'; 35 | import SimpleVueValidation from 'simple-vue-validator'; 36 | Vue.use(SimpleVueValidation); 37 | ``` 38 | 39 | ## Basic Usage 40 | Define the `validators` object in your vue / component instance: 41 | ```javascript 42 | validators: { 43 | email: function (value) { 44 | return Validator.value(value).required().email(); 45 | } 46 | } 47 | ``` 48 | In the template HTML use the `validation` object injected by the library to display validation status / results. 49 | ```html 50 |
{{ validation.firstError('email') }}
51 | ``` 52 | Please checkout the [full documentation](http://simple-vue-validator.magictek.cn) for more detail. 53 | 54 | ## license MIT 55 | -------------------------------------------------------------------------------- /doc-site/vue/examples/CrossFieldValidationExample1.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | -------------------------------------------------------------------------------- /doc-site/vue/examples/CheckboxGroup.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | -------------------------------------------------------------------------------- /doc-site/vue/examples/AsyncValidationExample1.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | -------------------------------------------------------------------------------- /doc-site/vue/LeftNavBar.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /doc-site/vue/examples/AsyncValidationExample2.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simple-vue-validator", 3 | "version": "0.16.0", 4 | "description": "lightweight yet flexible validation lib for vue.js", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "mocha --require babel-core/register test/**/**.spec.js", 8 | "lib-build": "cross-env NODE_ENV=production webpack --progress --config webpack-lib.config.js", 9 | "lib-web-test": "cross-env NODE_ENV=development webpack-dev-server --open --inline --hot --config webpack-lib.config.js", 10 | "doc-site-build": "cross-env NODE_ENV=production webpack --progress --hide-modules --config webpack-doc-site.config.js", 11 | "doc-site-dev": "cross-env NODE_ENV=development webpack-dev-server --open --inline --hot --config webpack-doc-site.config.js --host 0.0.0.0" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/semisleep/simple-vue-validator.git" 16 | }, 17 | "keywords": [ 18 | "validation", 19 | "form", 20 | "vue", 21 | "vue.js" 22 | ], 23 | "author": "semisleep", 24 | "license": "MIT", 25 | "bugs": { 26 | "url": "https://github.com/semisleep/simple-vue-validator/issues" 27 | }, 28 | "homepage": "https://github.com/semisleep/simple-vue-validator#readme", 29 | "dependencies": { 30 | "deep-equal": "1.0.1", 31 | "es6-promise": "4.0.5" 32 | }, 33 | "devDependencies": { 34 | "@types/chai": "4.1.1", 35 | "@types/mocha": "2.2.46", 36 | "autoprefixer": "6.5.3", 37 | "babel-core": "6.26.0", 38 | "babel-loader": "7.1.2", 39 | "babel-preset-es2015": "6.24.1", 40 | "bluebird": "3.4.7", 41 | "chai": "4.1.2", 42 | "cross-env": "3.0.0", 43 | "css-loader": "0.28.4", 44 | "csswring": "6.0.0", 45 | "extract-text-webpack-plugin": "3.0.0", 46 | "file-loader": "0.11.2", 47 | "font-awesome": "4.7.0", 48 | "icheck": "1.0.2", 49 | "jquery": "3.1.1", 50 | "mocha": "5.0.0", 51 | "postcss-import": "10.0.0", 52 | "postcss-loader": "2.0.6", 53 | "postcss-url": "7.1.1", 54 | "prismjs": "1.6.0", 55 | "style-loader": "0.18.2", 56 | "url-loader": "0.5.9", 57 | "vue": "2.5.13", 58 | "vue-loader": "13.7.0", 59 | "vue-template-compiler": "2.5.13", 60 | "waypoints": "4.0.1", 61 | "webpack": "3.4.1", 62 | "webpack-dev-server": "2.6.1" 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /doc-site/vue/examples/CrossFieldValidationExample2.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | -------------------------------------------------------------------------------- /webpack-doc-site.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var webpack = require('webpack'); 5 | var ExtractTextPlugin = require('extract-text-webpack-plugin'); 6 | 7 | module.exports = { 8 | entry: { 9 | 'doc': './doc-site/doc.js', 10 | 'style': './doc-site/style.js' 11 | }, 12 | output: { 13 | path: path.resolve(__dirname, 'doc-site/dist'), 14 | publicPath: '/dist/', 15 | filename: '[name].js' 16 | }, 17 | module: { 18 | rules: [ 19 | { 20 | test: /\.vue$/, 21 | loader: 'vue-loader', 22 | options: { 23 | // vue-loader options go here 24 | } 25 | }, 26 | /* 27 | { 28 | test: /\.js$/, 29 | loader: 'babel-loader', 30 | exclude: /node_modules/ 31 | }, 32 | */ 33 | { 34 | test: /\.(png|jpg|gif|svg)$/, 35 | loader: 'file-loader', 36 | options: { 37 | name: 'images/[name].[ext]?[hash]' 38 | } 39 | }, 40 | { 41 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 42 | loader: 'url-loader', 43 | query: { 44 | limit: 10000, 45 | name: 'fonts/[name].[ext]?[hash]' 46 | } 47 | }, 48 | { 49 | test: /\.css/, 50 | loaders: ExtractTextPlugin.extract(['css-loader?sourceMap', 'postcss-loader']) 51 | } 52 | ] 53 | }, 54 | devServer: { 55 | historyApiFallback: true, 56 | noInfo: false, 57 | hot: true, 58 | inline: true, 59 | // https: true, 60 | port: 8080, 61 | contentBase: path.resolve(__dirname, 'doc-site') 62 | }, 63 | devtool: '#source-map' 64 | }; 65 | 66 | module.exports.plugins = [ 67 | new ExtractTextPlugin('styles/[name].css'), 68 | new webpack.ProvidePlugin({ 69 | 'window.$': 'jquery', 70 | $: 'jquery', 71 | 'window.jQuery': 'jquery', 72 | jQuery: 'jquery' 73 | }) 74 | ]; 75 | 76 | if (process.env.NODE_ENV === 'production') { 77 | // http://vue-loader.vuejs.org/en/workflow/production.html 78 | module.exports.plugins = (module.exports.plugins || []).concat([ 79 | new webpack.DefinePlugin({ 80 | 'process.env': { 81 | NODE_ENV: '"production"' 82 | } 83 | }), 84 | new webpack.optimize.UglifyJsPlugin({ 85 | sourceMap: true, 86 | compress: { 87 | warnings: false 88 | } 89 | }), 90 | new webpack.LoaderOptionsPlugin({ 91 | minimize: true 92 | }) 93 | ]); 94 | } 95 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var deepEqual = require('deep-equal'); 4 | 5 | // This implementation of debounce was taken from the blog of David Walsh. 6 | // See here: https://davidwalsh.name/javascript-debounce-function 7 | module.exports.debounce = function (func, wait, immediate) { 8 | var timeout; 9 | 10 | return function () { 11 | var context = this; 12 | var args = arguments; 13 | var later = function () { 14 | timeout = null; 15 | if (!immediate) { 16 | func.apply(context, args); 17 | } 18 | }; 19 | 20 | var callNow = immediate && !timeout; 21 | clearTimeout(timeout); 22 | timeout = setTimeout(later, wait); 23 | if (callNow) { 24 | func.apply(context, args); 25 | } 26 | }; 27 | }; 28 | 29 | module.exports.format = function (template) { 30 | var args = Array.prototype.slice.call(arguments, 1); 31 | return template.replace(/{(\d+)}/g, function (match, number) { 32 | return typeof args[number] != 'undefined' ? args[number] : match; 33 | }); 34 | }; 35 | 36 | module.exports.isArray = function (arg) { 37 | if (typeof Array.isArray === 'function') { 38 | return Array.isArray(arg); 39 | } 40 | 41 | return Object.prototype.toString.call(arg) === '[object Array]'; 42 | }; 43 | 44 | module.exports.isEmpty = function (value) { 45 | if (module.exports.isArray(value)) { 46 | return !value.length; 47 | } else if (value === undefined || value === null) { 48 | return true; 49 | } else { 50 | return !String(value).trim().length; 51 | } 52 | }; 53 | 54 | module.exports.isEqual = function (o1, o2) { 55 | return deepEqual(o1, o2); 56 | }; 57 | 58 | module.exports.isFunction = function (arg) { 59 | return typeof arg === 'function'; 60 | }; 61 | 62 | module.exports.isNaN = function (arg) { 63 | return /^\s*$/.test(arg) || isNaN(arg); 64 | }; 65 | 66 | module.exports.isNull = function (arg) { 67 | return arg === null; 68 | }; 69 | 70 | module.exports.isString = function (arg) { 71 | return typeof arg === 'string' || arg instanceof String; 72 | }; 73 | 74 | module.exports.isUndefined = function (arg) { 75 | return typeof arg === 'undefined'; 76 | }; 77 | 78 | module.exports.omit = function omit(obj, key) { 79 | var result = {}; 80 | 81 | for (var name in obj) { 82 | if (name !== key) { 83 | result[name] = obj[name]; 84 | } 85 | } 86 | 87 | return result; 88 | }; 89 | 90 | module.exports.templates = require('./templates'); 91 | 92 | module.exports.mode = 'interactive'; // other values: conservative and manual 93 | 94 | -------------------------------------------------------------------------------- /doc-site/doc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import 'prismjs'; 4 | 5 | import Vue from 'vue'; 6 | import VueSimpleValidator from '../src'; 7 | import App from './vue/App.vue'; 8 | 9 | import LeftNavBar from './vue/LeftNavBar.vue'; 10 | import MainContent from './vue/MainContent.vue'; 11 | import DemoWithCode from './vue/DemoWithCode.vue'; 12 | import GettingStarted from './vue/chapters/GettingStarted.vue'; 13 | import UsagesAndExamples from './vue/chapters/UsagesAndExamples.vue'; 14 | import Miscellaneous from './vue/chapters/Miscellaneous.vue'; 15 | import APIAndReference from './vue/chapters/APIAndReference.vue'; 16 | 17 | import BasicExample from './vue/examples/BasicExample.vue'; 18 | import BuiltinRulesExample from './vue/examples/BuiltinRulesExample.vue'; 19 | import CustomRuleExample from './vue/examples/CustomRuleExample.vue'; 20 | import CrossFieldValidationExample1 from './vue/examples/CrossFieldValidationExample1.vue'; 21 | import CrossFieldValidationExample2 from './vue/examples/CrossFieldValidationExample2.vue'; 22 | import AsyncValidationExample1 from './vue/examples/AsyncValidationExample1.vue'; 23 | import AsyncValidationExample2 from './vue/examples/AsyncValidationExample2.vue'; 24 | import CheckboxGroup from './vue/examples/CheckboxGroup.vue'; 25 | import CustomComponentExample from './vue/examples/CustomComponentExample.vue'; 26 | import DynamicForm from './vue/examples/DynamicForm.vue'; 27 | import DynamicFormExample from './vue/examples/DynamicFormExample.vue'; 28 | import LocalizationExample from './vue/examples/LocalizationExample.vue'; 29 | import ComponentBasedMessageExample from './vue/examples/ComponentBasedMessageExample.vue'; 30 | import FieldBasedMessageExample from './vue/examples/FieldBasedMessageExample.vue'; 31 | 32 | Vue.use(VueSimpleValidator); 33 | 34 | Vue.component('LeftNavBar', LeftNavBar); 35 | Vue.component('MainContent', MainContent); 36 | Vue.component('DemoWithCode', DemoWithCode); 37 | 38 | // chapters 39 | Vue.component('GettingStarted', GettingStarted); 40 | Vue.component('UsagesAndExamples', UsagesAndExamples); 41 | Vue.component('Miscellaneous', Miscellaneous); 42 | Vue.component('APIAndReference', APIAndReference); 43 | 44 | 45 | // examples 46 | Vue.component('BasicExample', BasicExample); 47 | Vue.component('BuiltinRulesExample', BuiltinRulesExample); 48 | Vue.component('CustomRuleExample', CustomRuleExample); 49 | Vue.component('CrossFieldValidationExample1', CrossFieldValidationExample1); 50 | Vue.component('CrossFieldValidationExample2', CrossFieldValidationExample2); 51 | Vue.component('AsyncValidationExample1', AsyncValidationExample1); 52 | Vue.component('AsyncValidationExample2', AsyncValidationExample2); 53 | Vue.component('CheckboxGroup', CheckboxGroup); 54 | Vue.component('CustomComponentExample', CustomComponentExample); 55 | Vue.component('DynamicForm', DynamicForm); 56 | Vue.component('DynamicFormExample', DynamicFormExample); 57 | Vue.component('LocalizationExample', LocalizationExample); 58 | Vue.component('ComponentBasedMessageExample', ComponentBasedMessageExample); 59 | Vue.component('FieldBasedMessageExample', FieldBasedMessageExample); 60 | 61 | new Vue({ 62 | el: '#app', 63 | render: function (h) { 64 | return h(App); 65 | } 66 | }); 67 | -------------------------------------------------------------------------------- /doc-site/vue/examples/BuiltinRulesExample.vue: -------------------------------------------------------------------------------- 1 | 40 | 41 | -------------------------------------------------------------------------------- /test/rules.spec.js: -------------------------------------------------------------------------------- 1 | import {expect, Assertion} from 'chai' 2 | import Rule from '../src/rule' 3 | 4 | function makeRule(value) { 5 | return new Rule().value(value) 6 | } 7 | 8 | Assertion.addProperty('rule', function () {}) 9 | 10 | Assertion.addMethod('errors', function () { 11 | new Assertion(this._obj).to.be.instanceof(Rule) 12 | 13 | this.assert( 14 | this._obj._messages.length > 0, 15 | `expected '${this._obj._value}' to generate an error`, 16 | `expected '${this._obj._value}' to not generate an errors`, 17 | null, 18 | this._obj._messages[0], 19 | ) 20 | }) 21 | 22 | describe('Rules', () => { 23 | describe.skip('valid input values', () => { 24 | it('empty values are considered invalid', () => { 25 | expect(() => makeRule().integer()).to.throw() 26 | expect(() => makeRule(null).integer()).to.throw() 27 | expect(() => makeRule(undefined).integer()).to.throw() 28 | expect(() => makeRule('').integer()).to.throw() 29 | expect(() => makeRule(' ').integer()).to.throw() 30 | }) 31 | }) 32 | 33 | describe('integer', () => { 34 | it('valid integer values', () => { 35 | [1, 1.0, -1, +1, -1.0, +1.0, 10] 36 | .forEach(value => expect(makeRule(value).integer()).to.not.have.rule.errors()) 37 | }) 38 | 39 | it('string equivalents are considered integers', () => { 40 | ['1', '01', '0', '+1', '-1'] 41 | .forEach(value => expect(makeRule(value).integer()).to.not.have.rule.errors()) 42 | }) 43 | 44 | it('will fail if a float value is provided', () => { 45 | [1.1, '01.1', '1.0', '0.', '-1.0', '+1.0'] 46 | .forEach(value => expect(makeRule(value).integer()).to.have.rule.errors()) 47 | }) 48 | 49 | it('does not allow characters', () => { 50 | ['a', '1a', 'a1', '1a1'] 51 | .forEach(value => expect(makeRule(value).integer()).to.have.rule.errors()) 52 | }) 53 | }) 54 | 55 | describe('float', () => { 56 | it('valid float values', () => { 57 | [0, 1, -1, +1, 1.0, -1.0, +1.0, 1.1, -1.1, +1.1] 58 | .forEach(value => expect(makeRule(value).float()).to.not.have.rule.errors()) 59 | }) 60 | 61 | it('string equivalents are considered floats', () => { 62 | ['0', '1', '-1', '+1', '1.0', '-1.0', '+1.0', '1.1', '-1.1', '+1.1', '01.1', '-01.1', '+01.1'] 63 | .forEach(value => expect(makeRule(value).float()).to.not.have.rule.errors()) 64 | }) 65 | 66 | it('does not allow characters', () => { 67 | ['a', '1a', 'a1', '1a1'] 68 | .forEach(value => expect(makeRule(value).float()).to.have.rule.errors()) 69 | }) 70 | }) 71 | 72 | describe('size', () => { 73 | it('should accept any size array', () => { 74 | expect(makeRule([]).size(0)).to.not.have.rule.errors() 75 | expect(makeRule(['']).size(1)).to.not.have.rule.errors() 76 | expect(makeRule([[]]).size(1)).to.not.have.rule.errors() 77 | expect(makeRule([null]).size(1)).to.not.have.rule.errors() 78 | }) 79 | 80 | it('treats strings like arrays', () => { 81 | expect(makeRule('hello world').size(11)).to.not.have.rule.errors() 82 | expect(makeRule('').size(0)).to.not.have.rule.errors() 83 | }) 84 | 85 | it.skip('does not treat null like an array', () => { 86 | expect(makeRule(null).size(0)).to.have.rule.errors() 87 | }) 88 | }) 89 | }) 90 | -------------------------------------------------------------------------------- /doc-site/vue/DemoWithCode.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | -------------------------------------------------------------------------------- /doc-site/vue/chapters/GettingStarted.vue: -------------------------------------------------------------------------------- 1 | 118 | -------------------------------------------------------------------------------- /src/mixin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('./utils'); 4 | var ValidationBag = require('./validation-bag'); 5 | 6 | var mixin = { 7 | 8 | Promise: null, 9 | 10 | beforeMount: function () { 11 | this.$setValidators(this.$options.validators); 12 | 13 | if (this.validation) { 14 | // set vm to validation 15 | this.validation._setVM(this); 16 | } 17 | }, 18 | 19 | beforeDestroy: function () { 20 | unwatch(this.$options.validatorsUnwatchCallbacks); 21 | }, 22 | 23 | data: function () { 24 | if (this.$options.validators) { 25 | return { 26 | validation: new ValidationBag() 27 | }; 28 | } 29 | return {}; 30 | }, 31 | 32 | methods: { 33 | $setValidators: function (validators) { 34 | unwatch(this.$options.validatorsUnwatchCallbacks); 35 | // validate methods contains all application validate codes 36 | var validateMethods = {}; 37 | this.$options.validateMethods = validateMethods; 38 | var unwatchCallbacks = []; 39 | this.$options.validatorsUnwatchCallbacks = unwatchCallbacks; 40 | // generate validate methods and watch properties change for validators 41 | if (validators) { 42 | Object.keys(validators).forEach(function (key) { 43 | var properties = key.split(','); 44 | properties = properties.map(function (property) { 45 | return property.trim(); 46 | }); 47 | var getters = properties.map(function (property) { 48 | return generateGetter(this, property); 49 | }, this); 50 | var validator = validators[key]; 51 | var options = {}; 52 | if (!utils.isFunction(validator)) { 53 | options = utils.omit(validator, 'validator'); 54 | validator = validator.validator; 55 | } 56 | if (options.cache) { 57 | // cache the validation result, so that async validator can be fast when submitting the form 58 | var option = options.cache === 'last' ? 'last' : 'all'; 59 | validator = cache(validator, option); 60 | } 61 | var validation = this.validation; 62 | var validateMethod = function () { 63 | if (utils.mode === 'conservative' && !validation.activated) { // do nothing if in conservative mode and $validate() method is not called before 64 | return getPromise().resolve(false); 65 | } 66 | var args = getters.map(function (getter) { 67 | return getter(); 68 | }); 69 | var rule = validator.apply(this, args); 70 | if (rule) { 71 | if (!rule._field) { 72 | // field defaults to the first property 73 | rule.field(properties[0]); 74 | } 75 | return this.validation.checkRule(rule); 76 | } else { 77 | return getPromise().resolve(false); 78 | } 79 | }.bind(this); 80 | 81 | // add to validate method list 82 | validateMethods[properties[0]] = validateMethod; 83 | 84 | // watch change and invoke validate method 85 | var validateMethodForWatch = validateMethod; 86 | if (options.debounce) { 87 | // TODO what if custom field name is used? 88 | var decoratedValidateMethod = function () { 89 | if (decoratedValidateMethod.sessionId !== this.validation.sessionId) { 90 | // skip validation if it's reset before 91 | return getPromise().resolve(false); 92 | } 93 | return validateMethod.apply(this, arguments); 94 | }.bind(this); 95 | var debouncedValidateMethod = utils.debounce(decoratedValidateMethod, parseInt(options.debounce)); 96 | var field = properties[0]; 97 | validateMethodForWatch = function () { 98 | // eagerly resetting passed flag if debouncing is used. 99 | this.validation.resetPassed(field); 100 | // store sessionId 101 | decoratedValidateMethod.sessionId = this.validation.sessionId; 102 | debouncedValidateMethod.apply(this, arguments); 103 | }.bind(this); 104 | } 105 | if (utils.mode !== 'manual') { // have to call $validate() to trigger validation in manual mode, so don't watch, 106 | watchProperties(this, properties, validateMethodForWatch).forEach(function (unwatch) { 107 | unwatchCallbacks.push(unwatch); 108 | }); 109 | } 110 | }, this); 111 | } 112 | }, 113 | $validate: function (fields) { 114 | if (this.validation._validate) { 115 | return this.validation._validate; 116 | } 117 | this.validation.activated = true; 118 | var validateMethods = this.$options.validateMethods; 119 | if (utils.isUndefined(fields)) { 120 | validateMethods = Object.keys(validateMethods).map(function (key) { 121 | return validateMethods[key]; 122 | }); 123 | } else { 124 | fields = utils.isArray(fields) ? fields : [fields]; 125 | validateMethods = fields.map(function (field) { 126 | return validateMethods[field]; 127 | }); 128 | } 129 | if (utils.isEmpty(validateMethods)) { 130 | return getPromise().resolve(true); 131 | } else { 132 | var always = function() { 133 | this.validation._validate = null; 134 | }.bind(this); 135 | this.validation._validate = getPromise() 136 | .all(validateMethods.map(function (validateMethod) { 137 | return validateMethod(); 138 | })) 139 | .then(function (results) { 140 | always(); 141 | return results.filter(function (result) { 142 | return !!result; 143 | }).length <= 0; 144 | }.bind(this)) 145 | .catch(function(e) { 146 | always(); 147 | throw e; 148 | }); 149 | return this.validation._validate; 150 | } 151 | } 152 | } 153 | }; 154 | 155 | function unwatch(list) { 156 | if (list) { 157 | list.forEach(function (unwatch) { 158 | unwatch(); 159 | }); 160 | } 161 | } 162 | 163 | function generateGetter(vm, property) { 164 | var names = property.split('.'); 165 | return function () { 166 | var value = vm; 167 | for (var i = 0; i < names.length; i++) { 168 | if (utils.isNull(value) || utils.isUndefined(value)) { 169 | break; 170 | } 171 | value = value[names[i]]; 172 | } 173 | return value; 174 | }; 175 | } 176 | 177 | function watchProperties(vm, properties, callback) { 178 | return properties.map(function (property) { 179 | return vm.$watch(property, function () { 180 | vm.validation.setTouched(property); 181 | callback.call(); 182 | }); 183 | }); 184 | } 185 | 186 | function cache(validator, option) { 187 | return function () { 188 | var cache = validator.cache; 189 | if (!cache) { 190 | cache = []; 191 | validator.cache = cache; 192 | } 193 | var args = Array.prototype.slice.call(arguments); 194 | var cachedResult = findInCache(cache, args); 195 | if (!utils.isUndefined(cachedResult)) { 196 | return cachedResult; 197 | } 198 | var result = validator.apply(this, args); 199 | if (!utils.isUndefined(result)) { 200 | if (result.then) { 201 | return result.tab(function (promiseResult) { 202 | if (!utils.isUndefined(promiseResult)) { 203 | if (option !== 'all') { 204 | cache.splice(0, cache.length); 205 | } 206 | cache.push({args: args, result: promiseResult}); 207 | } 208 | }); 209 | } else { 210 | if (option !== 'all') { 211 | cache.splice(0, cache.length); 212 | } 213 | cache.push({args: args, result: result}); 214 | return result; 215 | } 216 | } 217 | }; 218 | } 219 | 220 | function getPromise() { 221 | if (mixin.Promise) { 222 | return mixin.Promise; 223 | } 224 | return require('es6-promise').Promise; 225 | } 226 | 227 | function findInCache(cache, args) { 228 | var items = cache.filter(function (item) { 229 | return utils.isEqual(args, item.args); 230 | }); 231 | if (!utils.isEmpty(items)) { 232 | return items[0].result; 233 | } 234 | } 235 | 236 | module.exports = mixin; 237 | -------------------------------------------------------------------------------- /src/validation-bag.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Promise = require('es6-promise').Promise; 4 | 5 | var utils = require('./utils'); 6 | 7 | function ValidationBag() { 8 | this.sessionId = 0; // async validator will check this before adding error 9 | this.resetting = 0; // do not allow to add error while reset is in progress 10 | this.errors = []; 11 | this.validatingRecords = []; 12 | this.passedRecords = []; 13 | this.touchedRecords = []; 14 | this.activated = false; // set when $validate() is call, this flag works with the conservative mode 15 | } 16 | 17 | ValidationBag.prototype._setVM = function(vm) { 18 | this._vm = vm; 19 | }; 20 | 21 | ValidationBag.prototype.addError = function (field, message) { 22 | if (this.resetting) { 23 | return; 24 | } 25 | this.errors.push({field: field, message: message}); 26 | }; 27 | 28 | ValidationBag.prototype.removeErrors = function (field) { 29 | if (utils.isUndefined(field)) { 30 | this.errors = []; 31 | } else { 32 | this.errors = this.errors.filter(function (e) { 33 | return e.field !== field; 34 | }); 35 | } 36 | }; 37 | 38 | ValidationBag.prototype.hasError = function (field) { 39 | return utils.isUndefined(field) ? !!this.errors.length : !!this.firstError(field); 40 | }; 41 | 42 | ValidationBag.prototype.firstError = function (field) { 43 | for (var i = 0; i < this.errors.length; i++) { 44 | if (utils.isUndefined(field) || this.errors[i].field === field) { 45 | return this.errors[i].message; 46 | } 47 | } 48 | return null; 49 | }; 50 | 51 | ValidationBag.prototype.allErrors = function (field) { 52 | return this.errors 53 | .filter(function (e) { 54 | return utils.isUndefined(field) || e.field === field; 55 | }) 56 | .map(function (e) { 57 | return e.message; 58 | }); 59 | }; 60 | 61 | ValidationBag.prototype.countErrors = function (field) { 62 | return utils.isUndefined(field) ? this.errors.length : this.errors.filter(function (e) { 63 | return field === e.field; 64 | }).length; 65 | }; 66 | 67 | ValidationBag.prototype.setValidating = function (field, id) { 68 | if (this.resetting) { 69 | return; 70 | } 71 | id = id || ValidationBag.newValidatingId(); 72 | var existingValidatingRecords = this.validatingRecords.filter(function (validating) { 73 | return validating.field === field && validating.id === id; 74 | }); 75 | if (!utils.isEmpty(existingValidatingRecords)) { 76 | throw new Error('Validating id already set: ' + id); 77 | } 78 | this.validatingRecords.push({field: field, id: id}); 79 | return id; 80 | }; 81 | 82 | ValidationBag.prototype.resetValidating = function (field, id) { 83 | if (!field) { 84 | this.validatingRecords = []; 85 | return; 86 | } 87 | 88 | function idMatched(validating) { 89 | return utils.isUndefined(id) ? true : (validating.id === id); 90 | } 91 | 92 | var hasMore = true; 93 | while (hasMore) { 94 | var index = -1; 95 | for (var i = 0; i < this.validatingRecords.length; i++) { 96 | if (this.validatingRecords[i].field === field && idMatched(this.validatingRecords[i])) { 97 | index = i; 98 | break; 99 | } 100 | } 101 | if (index >= 0) { 102 | this.validatingRecords.splice(index, 1); 103 | } else { 104 | hasMore = false; 105 | } 106 | } 107 | }; 108 | 109 | ValidationBag.prototype.isValidating = function (field, id) { 110 | function idMatched(validating) { 111 | return utils.isUndefined(id) ? true : (validating.id === id); 112 | } 113 | 114 | var existingValidatingRecords = this.validatingRecords.filter(function (validating) { 115 | return (utils.isUndefined(field) || validating.field === field) && idMatched(validating); 116 | }); 117 | return !utils.isEmpty(existingValidatingRecords); 118 | }; 119 | 120 | ValidationBag.prototype.setPassed = function (field) { 121 | if (this.resetting) { 122 | return; 123 | } 124 | setValue(this.passedRecords, field); 125 | }; 126 | 127 | ValidationBag.prototype.resetPassed = function (field) { 128 | resetValue(this.passedRecords, field); 129 | }; 130 | 131 | ValidationBag.prototype.isPassed = function (field) { 132 | return isValueSet(this.passedRecords, field); 133 | }; 134 | 135 | ValidationBag.prototype.setTouched = function (field) { 136 | if (this.resetting) { 137 | return; 138 | } 139 | setValue(this.touchedRecords, field); 140 | }; 141 | 142 | ValidationBag.prototype.resetTouched = function (field) { 143 | resetValue(this.touchedRecords, field); 144 | }; 145 | 146 | ValidationBag.prototype.isTouched = function (field) { 147 | return isValueSet(this.touchedRecords, field); 148 | }; 149 | 150 | function setValue(records, field) { 151 | var existingRecords = records.filter(function (record) { 152 | return record.field === field; 153 | }); 154 | if (!utils.isEmpty(existingRecords)) { 155 | existingRecords[0].value = true; 156 | } else { 157 | records.push({field: field, value: true}); 158 | } 159 | } 160 | 161 | function resetValue(records, field) { 162 | if (!field) { 163 | records.splice(0, records.length); 164 | return; 165 | } 166 | var existingRecords = records.filter(function (record) { 167 | return record.field === field; 168 | }); 169 | if (!utils.isEmpty(existingRecords)) { 170 | existingRecords[0].value = false; 171 | } 172 | } 173 | 174 | function isValueSet(records, field) { 175 | var existingRecords = records.filter(function (record) { 176 | return record.field === field; 177 | }); 178 | return !utils.isEmpty(existingRecords) && existingRecords[0].value; 179 | } 180 | 181 | ValidationBag.prototype.reset = function () { 182 | this.sessionId++; 183 | this.errors = []; 184 | this.validatingRecords = []; 185 | this.passedRecords = []; 186 | this.touchedRecords = []; 187 | if (this._vm) { 188 | // prevent field updates at the same tick to change validation status 189 | this.resetting++; 190 | this._vm.$nextTick(function() { 191 | this.resetting--; 192 | }.bind(this)); 193 | } 194 | this.activated = false; 195 | }; 196 | 197 | // returns true if any error is added 198 | ValidationBag.prototype.setError = function (field, message) { 199 | if (this.resetting) { 200 | return; 201 | } 202 | this.removeErrors(field); 203 | this.resetPassed(field); 204 | 205 | var messages = utils.isArray(message) ? message : [message]; 206 | var addMessages = function (messages) { 207 | var hasError = false; 208 | messages.forEach(function (message) { 209 | if (message) { 210 | this.addError(field, message); 211 | hasError = true; 212 | } 213 | }, this); 214 | if (!hasError) { 215 | this.setPassed(field); 216 | } 217 | return hasError; 218 | }.bind(this); 219 | 220 | var hasPromise = messages.filter(function (message) { 221 | return message && message.then; 222 | }).length > 0; 223 | if (!hasPromise) { 224 | return Promise.resolve(addMessages(messages)); 225 | } else { 226 | // if message is promise, we are encountering async validation, set validating flag and wait for message to resolve 227 | // reset previous validating status for this field 228 | this.resetValidating(field); 229 | var validatingId = this.setValidating(field); 230 | var always = function() { 231 | //console.log(validatingId + ' | ' + 'end'); 232 | this.resetValidating(field, validatingId); 233 | }.bind(this); 234 | //console.log(validatingId + ' | ' + 'start'); 235 | return Promise.all(messages) 236 | .then(function (messages) { 237 | // check if the validating id is is still valid 238 | if (this.isValidating(field, validatingId)) { 239 | //console.log(validatingId + ' | ' + 'processed'); 240 | return addMessages(messages); 241 | } 242 | return false; 243 | }.bind(this)) 244 | .then(function(result) { 245 | always(); 246 | return result; 247 | }) 248 | .catch(function (e) { 249 | always(); 250 | return Promise.reject(e); 251 | }.bind(this)); 252 | } 253 | }; 254 | 255 | ValidationBag.prototype.checkRule = function (rule) { 256 | if (this.resetting) { 257 | return; 258 | } 259 | return this.setError(rule._field, rule._messages); 260 | }; 261 | 262 | var validatingId = 0; 263 | 264 | ValidationBag.newValidatingId = function () { 265 | return (++validatingId).toString(); 266 | }; 267 | 268 | module.exports = ValidationBag; 269 | -------------------------------------------------------------------------------- /src/rule.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('./utils'); 4 | 5 | function Rule(templates) { 6 | this._field = ''; 7 | this._value = undefined; 8 | this._messages = []; 9 | if (templates) { 10 | // merge given template and utils.template 11 | this.templates = {}; 12 | Object.keys(utils.templates).forEach(function (key) { 13 | this.templates[key] = utils.templates[key]; 14 | }.bind(this)); 15 | Object.keys(templates).forEach(function (key) { 16 | this.templates[key] = templates[key]; 17 | }.bind(this)); 18 | } else { 19 | this.templates = utils.templates; 20 | } 21 | } 22 | 23 | Rule.prototype.field = function (field) { 24 | this._field = field; 25 | return this; 26 | }; 27 | 28 | Rule.prototype.value = function (value) { 29 | this._value = value; 30 | return this; 31 | }; 32 | 33 | Rule.prototype.custom = function (callback, context) { 34 | var message = context ? callback.call(context) : callback(); 35 | if (message) { 36 | if (message.then) { 37 | var that = this; 38 | message = Promise.resolve(message) 39 | .then(function (result) { 40 | return result; 41 | }) 42 | .catch(function (e) { 43 | console.error(e.toString()); 44 | return that.templates.error; 45 | }); 46 | } 47 | this._messages.push(message); 48 | } 49 | return this; 50 | }; 51 | 52 | Rule.prototype._checkValue = function() { 53 | if (this._value === undefined) { 54 | throw new Error('Validator.value not set'); 55 | } 56 | return this._value; 57 | }; 58 | 59 | Rule.prototype.required = function (message) { 60 | var value = this._checkValue(); 61 | if (utils.isEmpty(value)) { 62 | this._messages.push(message || this.templates.required); 63 | } 64 | return this; 65 | }; 66 | 67 | Rule.prototype.float = function (message) { 68 | var value = this._checkValue(); 69 | var regex = /^([-+])?([0-9]+(\.[0-9]+)?|Infinity)$/; 70 | if (!utils.isEmpty(value) && !regex.test(value)) { 71 | this._messages.push(message || this.templates.float); 72 | } 73 | return this; 74 | }; 75 | 76 | Rule.prototype.integer = function (message) { 77 | var value = this._checkValue(); 78 | var regex = /^([-+])?([0-9]+|Infinity)$/; 79 | if (!utils.isEmpty(value) && !regex.test(value)) { 80 | this._messages.push(message || this.templates.integer); 81 | } 82 | return this; 83 | }; 84 | 85 | Rule.prototype.lessThan = function (bound, message) { 86 | var value = this._checkValue(); 87 | if (!utils.isEmpty(value)) { 88 | var number = parseFloat(value); 89 | if (utils.isNaN(number)) { 90 | this._messages.push(message || this.templates.number); 91 | } else if (number >= bound) { 92 | this._messages.push(message || utils.format(this.templates.lessThan, bound)); 93 | } 94 | } 95 | return this; 96 | }; 97 | 98 | Rule.prototype.lessThanOrEqualTo = function (bound, message) { 99 | var value = this._checkValue(); 100 | if (!utils.isEmpty(value)) { 101 | var number = parseFloat(value); 102 | if (utils.isNaN(number)) { 103 | this._messages.push(message || this.templates.number); 104 | } else if (number > bound) { 105 | this._messages.push(message || utils.format(this.templates.lessThanOrEqualTo, bound)); 106 | } 107 | } 108 | return this; 109 | }; 110 | 111 | Rule.prototype.greaterThan = function (bound, message) { 112 | var value = this._checkValue(); 113 | if (!utils.isEmpty(value)) { 114 | var number = parseFloat(value); 115 | if (utils.isNaN(number)) { 116 | this._messages.push(message || this.templates.number); 117 | } else if (number <= bound) { 118 | this._messages.push(message || utils.format(this.templates.greaterThan, bound)); 119 | } 120 | } 121 | return this; 122 | }; 123 | 124 | Rule.prototype.greaterThanOrEqualTo = function (bound, message) { 125 | var value = this._checkValue(); 126 | if (!utils.isEmpty(value)) { 127 | var number = parseFloat(value); 128 | if (utils.isNaN(number)) { 129 | this._messages.push(message || this.templates.number); 130 | } else if (number < bound) { 131 | this._messages.push(message || utils.format(this.templates.greaterThanOrEqualTo, bound)); 132 | } 133 | } 134 | return this; 135 | }; 136 | 137 | Rule.prototype.between = function (lowBound, highBound, message) { 138 | var value = this._checkValue(); 139 | if (!utils.isEmpty(value)) { 140 | var number = parseFloat(value); 141 | if (utils.isNaN(number)) { 142 | this._messages.push(message || this.templates.number); 143 | } else if (number < lowBound || number > highBound) { 144 | this._messages.push(message || utils.format(this.templates.between, lowBound, highBound)); 145 | } 146 | } 147 | return this; 148 | }; 149 | 150 | Rule.prototype.size = function (size, message) { 151 | var value = this._checkValue(); 152 | if (!utils.isEmpty(value) && utils.isArray(value) && value.length !== size) { 153 | this._messages.push(message || utils.format(this.templates.size, size)); 154 | } 155 | return this; 156 | }; 157 | 158 | Rule.prototype.length = function (length, message) { 159 | var value = this._checkValue(); 160 | if (!utils.isEmpty(value) && String(value).length !== length) { 161 | this._messages.push(message || utils.format(this.templates.length, length)); 162 | } 163 | return this; 164 | }; 165 | 166 | Rule.prototype.minLength = function (length, message) { 167 | var value = this._checkValue(); 168 | if (!utils.isEmpty(value) && String(value).length < length) { 169 | this._messages.push(message || utils.format(this.templates.minLength, length)); 170 | } 171 | return this; 172 | }; 173 | 174 | Rule.prototype.maxLength = function (length, message) { 175 | var value = this._checkValue(); 176 | if (!utils.isEmpty(value) && String(value).length > length) { 177 | this._messages.push(message || utils.format(this.templates.maxLength, length)); 178 | 179 | } 180 | return this; 181 | }; 182 | 183 | Rule.prototype.lengthBetween = function (minLength, maxLength, message) { 184 | var value = this._checkValue(); 185 | if (!utils.isEmpty(value)) { 186 | var string = String(value); 187 | if (string.length < minLength || string.length > maxLength) { 188 | this._messages.push(message || utils.format(this.templates.lengthBetween, minLength, maxLength)); 189 | } 190 | } 191 | return this; 192 | }; 193 | 194 | Rule.prototype.in = function (options, message) { 195 | var value = this._checkValue(); 196 | if (!utils.isEmpty(value) && options.indexOf(value) < 0) { 197 | this._messages.push(message || utils.format(this.templates.in, this.templates.optionCombiner(options))); 198 | } 199 | return this; 200 | }; 201 | 202 | Rule.prototype.notIn = function (options, message) { 203 | var value = this._checkValue(); 204 | if (!utils.isEmpty(value) && options.indexOf(value) >= 0) { 205 | this._messages.push(message || utils.format(this.templates.notIn, this.templates.optionCombiner(options))); 206 | } 207 | return this; 208 | }; 209 | 210 | Rule.prototype.match = function (valueToCompare, message) { 211 | var value = this._checkValue(); 212 | if (!utils.isEmpty(value) && value !== valueToCompare) { 213 | this._messages.push(message || this.templates.match); 214 | } 215 | return this; 216 | }; 217 | 218 | Rule.prototype.regex = function (regex, message) { 219 | var value = this._checkValue(); 220 | if (!utils.isEmpty(value)) { 221 | if (utils.isString(regex)) { 222 | regex = new RegExp(regex); 223 | } 224 | if (!regex.test(value)) { 225 | this._messages.push(message || this.templates.regex); 226 | } 227 | } 228 | return this; 229 | }; 230 | 231 | Rule.prototype.digit = function (message) { 232 | return this.regex(/^\d*$/, message || this.templates.digit); 233 | }; 234 | 235 | Rule.prototype.email = function (message) { 236 | return this.regex(/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/, message || this.templates.email); 237 | }; 238 | 239 | Rule.prototype.url = function (message) { 240 | return this.regex(/(http|https):\/\/[\w-]+(\.[\w-]+)+([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])?/, message || this.templates.url); 241 | }; 242 | 243 | Rule.prototype.hasImmediateError = function () { 244 | for (var i = 0; i < this._messages.length; i++) { 245 | if (this._messages[i] && !this._messages[i].then) { 246 | return true; 247 | } 248 | } 249 | return false; 250 | }; 251 | 252 | module.exports = Rule; 253 | 254 | -------------------------------------------------------------------------------- /doc-site/styles/style.css: -------------------------------------------------------------------------------- 1 | /* lato-300 - latin */ 2 | @font-face { 3 | font-family: 'Lato'; 4 | font-style: normal; 5 | font-weight: 300; 6 | src: local('Lato Light'), local('Lato-Light'), 7 | url('../fonts/lato-v11-latin-300.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */ 8 | url('../fonts/lato-v11-latin-300.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ 9 | } 10 | /* lato-300italic - latin */ 11 | @font-face { 12 | font-family: 'Lato'; 13 | font-style: italic; 14 | font-weight: 300; 15 | src: local('Lato Light Italic'), local('Lato-LightItalic'), 16 | url('../fonts/lato-v11-latin-300italic.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */ 17 | url('../fonts/lato-v11-latin-300italic.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ 18 | } 19 | /* lato-regular - latin */ 20 | @font-face { 21 | font-family: 'Lato'; 22 | font-style: normal; 23 | font-weight: 400; 24 | src: local('Lato Regular'), local('Lato-Regular'), 25 | url('../fonts/lato-v11-latin-regular.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */ 26 | url('../fonts/lato-v11-latin-regular.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ 27 | } 28 | /* lato-italic - latin */ 29 | @font-face { 30 | font-family: 'Lato'; 31 | font-style: italic; 32 | font-weight: 400; 33 | src: local('Lato Italic'), local('Lato-Italic'), 34 | url('../fonts/lato-v11-latin-italic.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */ 35 | url('../fonts/lato-v11-latin-italic.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ 36 | } 37 | /* lato-900 - latin */ 38 | @font-face { 39 | font-family: 'Lato'; 40 | font-style: normal; 41 | font-weight: 900; 42 | src: local('Lato Black'), local('Lato-Black'), 43 | url('../fonts/lato-v11-latin-900.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */ 44 | url('../fonts/lato-v11-latin-900.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ 45 | } 46 | /* lato-900italic - latin */ 47 | @font-face { 48 | font-family: 'Lato'; 49 | font-style: italic; 50 | font-weight: 900; 51 | src: local('Lato Black Italic'), local('Lato-BlackItalic'), 52 | url('../fonts/lato-v11-latin-900italic.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */ 53 | url('../fonts/lato-v11-latin-900italic.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ 54 | } 55 | 56 | html, 57 | body { 58 | font-size: 16px; 59 | line-height: 24px; 60 | font-family: "Lato", "Helvetica Neue", Helvetica, Arial, sans-serif; 61 | color: #374853; 62 | font-weight: lighter; 63 | background-color: #fff; 64 | } 65 | 66 | body, 67 | body * { 68 | box-sizing: border-box; 69 | } 70 | 71 | a, 72 | a:active, 73 | a:focus, 74 | a:hover, 75 | a:active:focus, 76 | a:active:hover, 77 | a:hover:focus { 78 | outline: 0; 79 | text-decoration: none; 80 | color: #41b883; 81 | } 82 | 83 | p { 84 | margin: 20px 0; 85 | } 86 | 87 | b { 88 | font-weight: normal; 89 | } 90 | 91 | hr { 92 | margin: 20px 0; 93 | border-top: solid #e8e8e8 1px; 94 | border-bottom: none; 95 | border-left: none; 96 | border-right: none; 97 | } 98 | 99 | .text-center { 100 | text-align: center; 101 | } 102 | 103 | .text-left { 104 | text-align: left; 105 | } 106 | 107 | .text-right { 108 | text-align: right; 109 | } 110 | 111 | .text-success { 112 | color: #41b883; 113 | } 114 | 115 | .inline .i-checks-inline { 116 | display: inline-block; 117 | } 118 | 119 | @media (max-width: 768px) { 120 | .hidden-mobile { 121 | display: none; 122 | } 123 | } 124 | 125 | .layout-left-nav-bar { 126 | position: fixed; 127 | top: 0; 128 | left: 0; 129 | width: 260px; 130 | padding: 20px 20px 20px 40px; 131 | height: 100vh; 132 | overflow-y: auto; 133 | border-right: solid #e8e8e8 1px; 134 | } 135 | 136 | @media (max-width: 768px) { 137 | .layout-left-nav-bar { 138 | display: none; 139 | } 140 | } 141 | 142 | .layout-left-nav-bar .left-nav { 143 | padding-left: 0; 144 | } 145 | 146 | .layout-left-nav-bar .left-nav li { 147 | list-style: none; 148 | } 149 | 150 | .layout-left-nav-bar .left-nav li a { 151 | color: inherit; 152 | } 153 | 154 | .layout-left-nav-bar .left-nav li a:hover { 155 | background-color: transparent; 156 | } 157 | 158 | .layout-left-nav-bar .left-nav li.heading { 159 | font-size: 12px; 160 | font-weight: normal; 161 | padding: 20px 0 10px 0; 162 | color: #41b883; 163 | text-transform: uppercase; 164 | } 165 | 166 | .layout-left-nav-bar .left-nav li.item { 167 | padding:3px 5px; 168 | } 169 | 170 | .layout-main { 171 | margin-left: 260px; 172 | padding: 40px; 173 | } 174 | 175 | @media (max-width: 768px) { 176 | .layout-main { 177 | margin-left: 0; 178 | padding: 20px; 179 | } 180 | } 181 | 182 | @media (min-width: 1200px) { 183 | .layout-main { 184 | padding: 40px 60px; 185 | } 186 | } 187 | 188 | .layout-main .hero { 189 | margin: 40px 0 80px 0; 190 | } 191 | 192 | .layout-content-block { 193 | margin: 120px 0; 194 | } 195 | 196 | .layout-content-block .chapter-title { 197 | text-align: center; 198 | font-weight: normal; 199 | color: #41b883; 200 | text-transform: uppercase; 201 | } 202 | 203 | .layout-content-block .section-title { 204 | position: relative; 205 | font-size: 20px; 206 | font-weight: normal; 207 | margin: 30px 0 15px 0; 208 | } 209 | 210 | .layout-content-block .section-title:after { 211 | content: '#'; 212 | display: block; 213 | position: absolute; 214 | top: 0; 215 | left: -20px; 216 | color: #41b883; 217 | font-style: italic; 218 | } 219 | 220 | .layout-content-block .section-sub-title { 221 | position: relative; 222 | font-weight: normal; 223 | margin: 20px 0 10px 0; 224 | } 225 | 226 | .layout-content-block .section-sub-title:after { 227 | content: '#'; 228 | display: block; 229 | position: absolute; 230 | top: 0; 231 | left: -20px; 232 | color: #41b883; 233 | font-style: italic; 234 | } 235 | 236 | .layout-content-block .section-content { 237 | margin: 15px 0; 238 | } 239 | 240 | .layout-content-block .section-content a { 241 | font-weight: normal; 242 | } 243 | 244 | .layout-content-block .section-content .note { 245 | margin: 20px 0; 246 | padding: 20px; 247 | background-color: #f8f8f8; 248 | border-left: solid #ff6666 4px; 249 | position: relative; 250 | } 251 | 252 | .layout-content-block .section-content .note:after { 253 | content: '!'; 254 | display: block; 255 | width: 20px; 256 | height: 20px; 257 | line-height: 20px; 258 | text-align: center; 259 | font-size: 12px; 260 | color: #fff; 261 | background-color: #ff6666; 262 | border-radius: 50%; 263 | position: absolute; 264 | top: 8px; 265 | left: -12px; 266 | } 267 | 268 | .layout-content-block .section-content .table-wrapper { 269 | overflow-x: auto; 270 | } 271 | 272 | .layout-content-block .section-content table { 273 | width: 100%; 274 | min-width: 600px; 275 | border-spacing: 0; 276 | border-collapse: collapse; 277 | text-align: left; 278 | font-size: 14px; 279 | } 280 | 281 | .layout-content-block .section-content table tr { 282 | border: solid #e8e8e8 1px; 283 | } 284 | 285 | .layout-content-block .section-content table thead tr, 286 | .layout-content-block .section-content table tbody tr:hover { 287 | background-color: #f8f8f8; 288 | } 289 | 290 | .layout-content-block .section-content table tr th, 291 | .layout-content-block .section-content table tr td { 292 | padding: 3px 8px; 293 | min-width: 110px; 294 | } 295 | 296 | .layout-content-block .tab-container { 297 | display: flex; 298 | overflow-x: auto; 299 | } 300 | 301 | .layout-content-block .tab-container .tab { 302 | padding: 0 25px; 303 | line-height: 40px; 304 | color: #9ba3a9; 305 | font-size: 12px; 306 | font-weight: normal; 307 | overflow: hidden; 308 | white-space: nowrap; 309 | text-overflow: ellipsis; 310 | min-width: 120px; 311 | text-align: center; 312 | } 313 | 314 | @media (max-width: 768px) { 315 | .layout-content-block .tab-container .tab { 316 | min-width: 80px; 317 | padding: 0 10px; 318 | } 319 | } 320 | 321 | .layout-content-block .tab-container .tab.active { 322 | color: #fff; 323 | background-color: #2d2d2d; 324 | cursor: default; 325 | } 326 | 327 | .layout-content-block .tab-container .tab.demo.active { 328 | color: #374853; 329 | background-color: #f8f8f8; 330 | } 331 | 332 | .layout-content-block .page-container .page { 333 | display: none; 334 | background-color: #2d2d2d; 335 | overflow-y: auto; 336 | overflow-x: hidden; 337 | } 338 | 339 | .layout-content-block .page-container .page.demo { 340 | background-color: #f8f8f8; 341 | } 342 | 343 | .layout-content-block .page-container .page.active { 344 | display: block; 345 | } 346 | 347 | .layout-content-block .page-container .page pre { 348 | margin: 0; 349 | } 350 | 351 | .layout-content-block .page-container .page .indicator { 352 | margin: 30px 0; 353 | text-align: center; 354 | font-size: 18px; 355 | } 356 | 357 | .layout-form { 358 | padding: 30px; 359 | margin: 25px 25px; 360 | font-size: 14px; 361 | line-height: 20px; 362 | box-shadow: 0px 3px 20px 0px rgba(0,0,0,0.3); 363 | background-color: #fff; 364 | } 365 | 366 | @media (max-width: 768px) { 367 | .layout-form { 368 | padding: 10px; 369 | } 370 | } 371 | 372 | .layout-form .form-group { 373 | display: flex; 374 | margin: 15px 0; 375 | } 376 | 377 | .layout-form .form-group .label { 378 | width: 100px; 379 | padding: 5px 20px 0 0; 380 | font-weight: bold; 381 | } 382 | 383 | .layout-form .form-group.error .label { 384 | color: #ff6666; 385 | } 386 | 387 | .layout-form .form-group .content { 388 | width: 50%; 389 | min-width: 120px; 390 | } 391 | 392 | .layout-form .form-group .content label { 393 | display: inline-block; 394 | padding: 5px 20px 0 0; 395 | } 396 | 397 | .layout-form .form-group .message { 398 | flex: 1; 399 | padding: 5px 0 0 20px; 400 | overflow: hidden; 401 | white-space: nowrap; 402 | text-overflow: ellipsis; 403 | font-weight: bold; 404 | } 405 | 406 | .layout-form .form-group.error .message { 407 | color: #ff6666; 408 | } 409 | 410 | .layout-form .form-group .form-control { 411 | font-size: 14px; 412 | } 413 | 414 | .layout-form .form-group .actions { 415 | margin-left: 100px; 416 | width: 50%; 417 | text-align: right; 418 | } 419 | 420 | @media (max-width: 768px) { 421 | .layout-form { 422 | font-size: 12px; 423 | } 424 | 425 | .layout-form .form-group .label { 426 | width: 60px; 427 | padding-right: 10px; 428 | } 429 | 430 | .layout-form .form-group .form-control { 431 | font-size: 12px; 432 | } 433 | 434 | 435 | .layout-form .form-group .actions { 436 | margin-left: 60px; 437 | } 438 | } 439 | 440 | .layout-form .form-group input[type="text"].form-control, 441 | .layout-form .form-group input[type="password"].form-control { 442 | width: 100%; 443 | font-weight: lighter; 444 | padding: 4px 6px; 445 | border: solid #acacac 1px; 446 | outline: none; 447 | } 448 | 449 | .layout-form .form-group input[type="text"].form-control:focus, 450 | .layout-form .form-group input[type="password"].form-control:focus { 451 | border-color: #374853; 452 | } 453 | 454 | .layout-form .form-group.error input[type="text"].form-control, 455 | .layout-form .form-group.error input[type="password"].form-control, 456 | .layout-form .form-group.error input[type="text"].form-control:focus, 457 | .layout-form .form-group.error input[type="password"].form-control:focus { 458 | border-color: #ff6666; 459 | } 460 | 461 | .layout-form .form-group .btn { 462 | font-size: 12px; 463 | font-weight: normal; 464 | padding: 6px 10px; 465 | border-radius: 4px; 466 | outline: none; 467 | appearance: none; 468 | cursor: pointer; 469 | text-transform: uppercase; 470 | } 471 | 472 | .layout-form .form-group .btn + .btn { 473 | margin-left: 8px; 474 | } 475 | 476 | @media (max-width: 768px) { 477 | .layout-form .form-group .btn { 478 | padding: 3px 6px; 479 | } 480 | 481 | .layout-form .form-group .btn + .btn { 482 | margin-left: 4px; 483 | } 484 | } 485 | 486 | .layout-form .form-group .btn-primary { 487 | color: #fff; 488 | background-color: #41b883; 489 | border: solid #39a073 1px; 490 | } 491 | 492 | .layout-form .form-group .btn-primary:focus { 493 | background-color: #3dad7c; 494 | } 495 | 496 | .layout-form .form-group .btn-primary:active { 497 | background-color: #39a073; 498 | } 499 | 500 | .layout-form .form-group .btn-default { 501 | color: #374853; 502 | background-color: #fff; 503 | border: solid #acacac 1px; 504 | } 505 | 506 | .layout-form .form-group .btn-default:focus { 507 | background-color: #eee; 508 | } 509 | 510 | .layout-form .form-group .btn-default:active { 511 | background-color: #ccc; 512 | } 513 | 514 | .layout-demo { 515 | margin: 20px 0; 516 | } 517 | 518 | .code { 519 | color: #e96900; 520 | background-color: #f8f8f8; 521 | display: inline-block; 522 | padding: 0 5px; 523 | } -------------------------------------------------------------------------------- /doc-site/vue/chapters/UsagesAndExamples.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /doc-site/vue/chapters/APIAndReference.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dist/plugin.js: -------------------------------------------------------------------------------- 1 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.SimpleVueValidator=e():t.SimpleVueValidator=e()}(this,function(){return function(t){function e(n){if(r[n])return r[n].exports;var i=r[n]={i:n,l:!1,exports:{}};return t[n].call(i.exports,i,i.exports,e),i.l=!0,i.exports}var r={};return e.m=t,e.c=r,e.d=function(t,r,n){e.o(t,r)||Object.defineProperty(t,r,{configurable:!1,enumerable:!0,get:n})},e.n=function(t){var r=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(r,"a",r),r},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="/dist/",e(e.s=4)}([function(t,e,r){"use strict";var n=r(8);t.exports.debounce=function(t,e,r){var n;return function(){var i=this,s=arguments,o=function(){n=null,r||t.apply(i,s)},a=r&&!n;clearTimeout(n),n=setTimeout(o,e),a&&t.apply(i,s)}},t.exports.format=function(t){var e=Array.prototype.slice.call(arguments,1);return t.replace(/{(\d+)}/g,function(t,r){return void 0!==e[r]?e[r]:t})},t.exports.isArray=function(t){return"function"==typeof Array.isArray?Array.isArray(t):"[object Array]"===Object.prototype.toString.call(t)},t.exports.isEmpty=function(e){return t.exports.isArray(e)?!e.length:void 0===e||null===e||!String(e).trim().length},t.exports.isEqual=function(t,e){return n(t,e)},t.exports.isFunction=function(t){return"function"==typeof t},t.exports.isNaN=function(t){return/^\s*$/.test(t)||isNaN(t)},t.exports.isNull=function(t){return null===t},t.exports.isString=function(t){return"string"==typeof t||t instanceof String},t.exports.isUndefined=function(t){return void 0===t},t.exports.omit=function(t,e){var r={};for(var n in t)n!==e&&(r[n]=t[n]);return r},t.exports.templates=r(11),t.exports.mode="interactive"},function(t,e,r){"use strict";function n(){this.sessionId=0,this.resetting=0,this.errors=[],this.validatingRecords=[],this.passedRecords=[],this.touchedRecords=[],this.activated=!1}function i(t,e){var r=t.filter(function(t){return t.field===e});u.isEmpty(r)?t.push({field:e,value:!0}):r[0].value=!0}function s(t,e){if(!e)return void t.splice(0,t.length);var r=t.filter(function(t){return t.field===e});u.isEmpty(r)||(r[0].value=!1)}function o(t,e){var r=t.filter(function(t){return t.field===e});return!u.isEmpty(r)&&r[0].value}var a=r(2).Promise,u=r(0);n.prototype._setVM=function(t){this._vm=t},n.prototype.addError=function(t,e){this.resetting||this.errors.push({field:t,message:e})},n.prototype.removeErrors=function(t){u.isUndefined(t)?this.errors=[]:this.errors=this.errors.filter(function(e){return e.field!==t})},n.prototype.hasError=function(t){return u.isUndefined(t)?!!this.errors.length:!!this.firstError(t)},n.prototype.firstError=function(t){for(var e=0;e=0?this.validatingRecords.splice(n,1):r=!1}},n.prototype.isValidating=function(t,e){function r(t){return!!u.isUndefined(e)||t.id===e}var n=this.validatingRecords.filter(function(e){return(u.isUndefined(t)||e.field===t)&&r(e)});return!u.isEmpty(n)},n.prototype.setPassed=function(t){this.resetting||i(this.passedRecords,t)},n.prototype.resetPassed=function(t){s(this.passedRecords,t)},n.prototype.isPassed=function(t){return o(this.passedRecords,t)},n.prototype.setTouched=function(t){this.resetting||i(this.touchedRecords,t)},n.prototype.resetTouched=function(t){s(this.touchedRecords,t)},n.prototype.isTouched=function(t){return o(this.touchedRecords,t)},n.prototype.reset=function(){this.sessionId++,this.errors=[],this.validatingRecords=[],this.passedRecords=[],this.touchedRecords=[],this._vm&&(this.resetting++,this._vm.$nextTick(function(){this.resetting--}.bind(this))),this.activated=!1},n.prototype.setError=function(t,e){if(!this.resetting){this.removeErrors(t),this.resetPassed(t);var r=u.isArray(e)?e:[e],n=function(e){var r=!1;return e.forEach(function(e){e&&(this.addError(t,e),r=!0)},this),r||this.setPassed(t),r}.bind(this);if(r.filter(function(t){return t&&t.then}).length>0){this.resetValidating(t);var i=this.setValidating(t),s=function(){this.resetValidating(t,i)}.bind(this);return a.all(r).then(function(e){return!!this.isValidating(t,i)&&n(e)}.bind(this)).then(function(t){return s(),t}).catch(function(t){return s(),a.reject(t)}.bind(this))}return a.resolve(n(r))}},n.prototype.checkRule=function(t){if(!this.resetting)return this.setError(t._field,t._messages)};var c=0;n.newValidatingId=function(){return(++c).toString()},t.exports=n},function(t,e,r){(function(e,n){/*! 2 | * @overview es6-promise - a tiny implementation of Promises/A+. 3 | * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald) 4 | * @license Licensed under MIT license 5 | * See https://raw.githubusercontent.com/stefanpenner/es6-promise/master/LICENSE 6 | * @version 4.0.5 7 | */ 8 | !function(e,r){t.exports=r()}(0,function(){"use strict";function t(t){return"function"==typeof t||"object"==typeof t&&null!==t}function i(t){return"function"==typeof t}function s(t){Y=t}function o(t){Z=t}function a(){return void 0!==D?function(){D(c)}:u()}function u(){var t=setTimeout;return function(){return t(c,1)}}function c(){for(var t=0;t=t&&this._messages.push(e||i.format(this.templates.lessThan,t))}return this},n.prototype.lessThanOrEqualTo=function(t,e){var r=this._checkValue();if(!i.isEmpty(r)){var n=parseFloat(r);i.isNaN(n)?this._messages.push(e||this.templates.number):n>t&&this._messages.push(e||i.format(this.templates.lessThanOrEqualTo,t))}return this},n.prototype.greaterThan=function(t,e){var r=this._checkValue();if(!i.isEmpty(r)){var n=parseFloat(r);i.isNaN(n)?this._messages.push(e||this.templates.number):n<=t&&this._messages.push(e||i.format(this.templates.greaterThan,t))}return this},n.prototype.greaterThanOrEqualTo=function(t,e){var r=this._checkValue();if(!i.isEmpty(r)){var n=parseFloat(r);i.isNaN(n)?this._messages.push(e||this.templates.number):ne)&&this._messages.push(r||i.format(this.templates.between,t,e))}return this},n.prototype.size=function(t,e){var r=this._checkValue();return!i.isEmpty(r)&&i.isArray(r)&&r.length!==t&&this._messages.push(e||i.format(this.templates.size,t)),this},n.prototype.length=function(t,e){var r=this._checkValue();return i.isEmpty(r)||String(r).length===t||this._messages.push(e||i.format(this.templates.length,t)),this},n.prototype.minLength=function(t,e){var r=this._checkValue();return!i.isEmpty(r)&&String(r).lengtht&&this._messages.push(e||i.format(this.templates.maxLength,t)),this},n.prototype.lengthBetween=function(t,e,r){var n=this._checkValue();if(!i.isEmpty(n)){var s=String(n);(s.lengthe)&&this._messages.push(r||i.format(this.templates.lengthBetween,t,e))}return this},n.prototype.in=function(t,e){var r=this._checkValue();return!i.isEmpty(r)&&t.indexOf(r)<0&&this._messages.push(e||i.format(this.templates.in,this.templates.optionCombiner(t))),this},n.prototype.notIn=function(t,e){var r=this._checkValue();return!i.isEmpty(r)&&t.indexOf(r)>=0&&this._messages.push(e||i.format(this.templates.notIn,this.templates.optionCombiner(t))),this},n.prototype.match=function(t,e){var r=this._checkValue();return i.isEmpty(r)||r===t||this._messages.push(e||this.templates.match),this},n.prototype.regex=function(t,e){var r=this._checkValue();return i.isEmpty(r)||(i.isString(t)&&(t=new RegExp(t)),t.test(r)||this._messages.push(e||this.templates.regex)),this},n.prototype.digit=function(t){return this.regex(/^\d*$/,t||this.templates.digit)},n.prototype.email=function(t){return this.regex(/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,t||this.templates.email)},n.prototype.url=function(t){return this.regex(/(http|https):\/\/[\w-]+(\.[\w-]+)+([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])?/,t||this.templates.url)},n.prototype.hasImmediateError=function(){for(var t=0;t1)for(var r=1;r0&&"number"!=typeof t[0]))}function s(t,e,r){var s,h;if(n(t)||n(e))return!1;if(t.prototype!==e.prototype)return!1;if(u(t))return!!u(e)&&(t=o.call(t),e=o.call(e),c(t,e,r));if(i(t)){if(!i(e))return!1;if(t.length!==e.length)return!1;for(s=0;s=0;s--)if(l[s]!=f[s])return!1;for(s=l.length-1;s>=0;s--)if(h=l[s],!c(t[h],e[h],r))return!1;return typeof t==typeof e}var o=Array.prototype.slice,a=r(9),u=r(10),c=t.exports=function(t,e,r){return r||(r={}),t===e||(t instanceof Date&&e instanceof Date?t.getTime()===e.getTime():!t||!e||"object"!=typeof t&&"object"!=typeof e?r.strict?t===e:t==e:s(t,e,r))}},function(t,e){function r(t){var e=[];for(var r in t)e.push(r);return e}e=t.exports="function"==typeof Object.keys?Object.keys:r,e.shim=r},function(t,e){function r(t){return"[object Arguments]"==Object.prototype.toString.call(t)}function n(t){return t&&"object"==typeof t&&"number"==typeof t.length&&Object.prototype.hasOwnProperty.call(t,"callee")&&!Object.prototype.propertyIsEnumerable.call(t,"callee")||!1}var i="[object Arguments]"==function(){return Object.prototype.toString.call(arguments)}();e=t.exports=i?r:n,e.supported=r,e.unsupported=n},function(t,e,r){"use strict";t.exports={error:"Error.",required:"Required.",float:"Must be a number.",integer:"Must be an integer.",number:"Must be a number.",lessThan:"Must be less than {0}.",lessThanOrEqualTo:"Must be less than or equal to {0}.",greaterThan:"Must be greater than {0}.",greaterThanOrEqualTo:"Must greater than or equal to {0}.",between:"Must be between {0} and {1}.",size:"Size must be {0}.",length:"Length must be {0}.",minLength:"Must have at least {0} characters.",maxLength:"Must have up to {0} characters.",lengthBetween:"Length must between {0} and {1}.",in:"Must be {0}.",notIn:"Must not be {0}.",match:"Not matched.",regex:"Invalid format.",digit:"Must be a digit.",email:"Invalid email.",url:"Invalid url.",optionCombiner:function(t){return t.length>2&&(t=[t.slice(0,t.length-1).join(", "),t[t.length-1]]),t.join(" or ")}}},function(t,e,r){"use strict";function n(t){t=t||{};var e={};return Object.keys(s.prototype).forEach(function(r){e[r]=function(){var e=new s(t.templates);return e[r].apply(e,arguments)}}),e.isEmpty=i.isEmpty,e.format=i.format,e}var i=r(0),s=r(3),o=n();o.create=function(t){return n(t)},t.exports=o},function(t,e,r){"use strict";function n(t){t&&t.forEach(function(t){t()})}function i(t,e){var r=e.split(".");return function(){for(var e=t,n=0;nli{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:.08em solid #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0,mirror=1)";-webkit-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2,mirror=1)";-webkit-transform:scaleY(-1);transform:scaleY(-1)}:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{-webkit-filter:none;filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\F000"}.fa-music:before{content:"\F001"}.fa-search:before{content:"\F002"}.fa-envelope-o:before{content:"\F003"}.fa-heart:before{content:"\F004"}.fa-star:before{content:"\F005"}.fa-star-o:before{content:"\F006"}.fa-user:before{content:"\F007"}.fa-film:before{content:"\F008"}.fa-th-large:before{content:"\F009"}.fa-th:before{content:"\F00A"}.fa-th-list:before{content:"\F00B"}.fa-check:before{content:"\F00C"}.fa-close:before,.fa-remove:before,.fa-times:before{content:"\F00D"}.fa-search-plus:before{content:"\F00E"}.fa-search-minus:before{content:"\F010"}.fa-power-off:before{content:"\F011"}.fa-signal:before{content:"\F012"}.fa-cog:before,.fa-gear:before{content:"\F013"}.fa-trash-o:before{content:"\F014"}.fa-home:before{content:"\F015"}.fa-file-o:before{content:"\F016"}.fa-clock-o:before{content:"\F017"}.fa-road:before{content:"\F018"}.fa-download:before{content:"\F019"}.fa-arrow-circle-o-down:before{content:"\F01A"}.fa-arrow-circle-o-up:before{content:"\F01B"}.fa-inbox:before{content:"\F01C"}.fa-play-circle-o:before{content:"\F01D"}.fa-repeat:before,.fa-rotate-right:before{content:"\F01E"}.fa-refresh:before{content:"\F021"}.fa-list-alt:before{content:"\F022"}.fa-lock:before{content:"\F023"}.fa-flag:before{content:"\F024"}.fa-headphones:before{content:"\F025"}.fa-volume-off:before{content:"\F026"}.fa-volume-down:before{content:"\F027"}.fa-volume-up:before{content:"\F028"}.fa-qrcode:before{content:"\F029"}.fa-barcode:before{content:"\F02A"}.fa-tag:before{content:"\F02B"}.fa-tags:before{content:"\F02C"}.fa-book:before{content:"\F02D"}.fa-bookmark:before{content:"\F02E"}.fa-print:before{content:"\F02F"}.fa-camera:before{content:"\F030"}.fa-font:before{content:"\F031"}.fa-bold:before{content:"\F032"}.fa-italic:before{content:"\F033"}.fa-text-height:before{content:"\F034"}.fa-text-width:before{content:"\F035"}.fa-align-left:before{content:"\F036"}.fa-align-center:before{content:"\F037"}.fa-align-right:before{content:"\F038"}.fa-align-justify:before{content:"\F039"}.fa-list:before{content:"\F03A"}.fa-dedent:before,.fa-outdent:before{content:"\F03B"}.fa-indent:before{content:"\F03C"}.fa-video-camera:before{content:"\F03D"}.fa-image:before,.fa-photo:before,.fa-picture-o:before{content:"\F03E"}.fa-pencil:before{content:"\F040"}.fa-map-marker:before{content:"\F041"}.fa-adjust:before{content:"\F042"}.fa-tint:before{content:"\F043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\F044"}.fa-share-square-o:before{content:"\F045"}.fa-check-square-o:before{content:"\F046"}.fa-arrows:before{content:"\F047"}.fa-step-backward:before{content:"\F048"}.fa-fast-backward:before{content:"\F049"}.fa-backward:before{content:"\F04A"}.fa-play:before{content:"\F04B"}.fa-pause:before{content:"\F04C"}.fa-stop:before{content:"\F04D"}.fa-forward:before{content:"\F04E"}.fa-fast-forward:before{content:"\F050"}.fa-step-forward:before{content:"\F051"}.fa-eject:before{content:"\F052"}.fa-chevron-left:before{content:"\F053"}.fa-chevron-right:before{content:"\F054"}.fa-plus-circle:before{content:"\F055"}.fa-minus-circle:before{content:"\F056"}.fa-times-circle:before{content:"\F057"}.fa-check-circle:before{content:"\F058"}.fa-question-circle:before{content:"\F059"}.fa-info-circle:before{content:"\F05A"}.fa-crosshairs:before{content:"\F05B"}.fa-times-circle-o:before{content:"\F05C"}.fa-check-circle-o:before{content:"\F05D"}.fa-ban:before{content:"\F05E"}.fa-arrow-left:before{content:"\F060"}.fa-arrow-right:before{content:"\F061"}.fa-arrow-up:before{content:"\F062"}.fa-arrow-down:before{content:"\F063"}.fa-mail-forward:before,.fa-share:before{content:"\F064"}.fa-expand:before{content:"\F065"}.fa-compress:before{content:"\F066"}.fa-plus:before{content:"\F067"}.fa-minus:before{content:"\F068"}.fa-asterisk:before{content:"\F069"}.fa-exclamation-circle:before{content:"\F06A"}.fa-gift:before{content:"\F06B"}.fa-leaf:before{content:"\F06C"}.fa-fire:before{content:"\F06D"}.fa-eye:before{content:"\F06E"}.fa-eye-slash:before{content:"\F070"}.fa-exclamation-triangle:before,.fa-warning:before{content:"\F071"}.fa-plane:before{content:"\F072"}.fa-calendar:before{content:"\F073"}.fa-random:before{content:"\F074"}.fa-comment:before{content:"\F075"}.fa-magnet:before{content:"\F076"}.fa-chevron-up:before{content:"\F077"}.fa-chevron-down:before{content:"\F078"}.fa-retweet:before{content:"\F079"}.fa-shopping-cart:before{content:"\F07A"}.fa-folder:before{content:"\F07B"}.fa-folder-open:before{content:"\F07C"}.fa-arrows-v:before{content:"\F07D"}.fa-arrows-h:before{content:"\F07E"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\F080"}.fa-twitter-square:before{content:"\F081"}.fa-facebook-square:before{content:"\F082"}.fa-camera-retro:before{content:"\F083"}.fa-key:before{content:"\F084"}.fa-cogs:before,.fa-gears:before{content:"\F085"}.fa-comments:before{content:"\F086"}.fa-thumbs-o-up:before{content:"\F087"}.fa-thumbs-o-down:before{content:"\F088"}.fa-star-half:before{content:"\F089"}.fa-heart-o:before{content:"\F08A"}.fa-sign-out:before{content:"\F08B"}.fa-linkedin-square:before{content:"\F08C"}.fa-thumb-tack:before{content:"\F08D"}.fa-external-link:before{content:"\F08E"}.fa-sign-in:before{content:"\F090"}.fa-trophy:before{content:"\F091"}.fa-github-square:before{content:"\F092"}.fa-upload:before{content:"\F093"}.fa-lemon-o:before{content:"\F094"}.fa-phone:before{content:"\F095"}.fa-square-o:before{content:"\F096"}.fa-bookmark-o:before{content:"\F097"}.fa-phone-square:before{content:"\F098"}.fa-twitter:before{content:"\F099"}.fa-facebook-f:before,.fa-facebook:before{content:"\F09A"}.fa-github:before{content:"\F09B"}.fa-unlock:before{content:"\F09C"}.fa-credit-card:before{content:"\F09D"}.fa-feed:before,.fa-rss:before{content:"\F09E"}.fa-hdd-o:before{content:"\F0A0"}.fa-bullhorn:before{content:"\F0A1"}.fa-bell:before{content:"\F0F3"}.fa-certificate:before{content:"\F0A3"}.fa-hand-o-right:before{content:"\F0A4"}.fa-hand-o-left:before{content:"\F0A5"}.fa-hand-o-up:before{content:"\F0A6"}.fa-hand-o-down:before{content:"\F0A7"}.fa-arrow-circle-left:before{content:"\F0A8"}.fa-arrow-circle-right:before{content:"\F0A9"}.fa-arrow-circle-up:before{content:"\F0AA"}.fa-arrow-circle-down:before{content:"\F0AB"}.fa-globe:before{content:"\F0AC"}.fa-wrench:before{content:"\F0AD"}.fa-tasks:before{content:"\F0AE"}.fa-filter:before{content:"\F0B0"}.fa-briefcase:before{content:"\F0B1"}.fa-arrows-alt:before{content:"\F0B2"}.fa-group:before,.fa-users:before{content:"\F0C0"}.fa-chain:before,.fa-link:before{content:"\F0C1"}.fa-cloud:before{content:"\F0C2"}.fa-flask:before{content:"\F0C3"}.fa-cut:before,.fa-scissors:before{content:"\F0C4"}.fa-copy:before,.fa-files-o:before{content:"\F0C5"}.fa-paperclip:before{content:"\F0C6"}.fa-floppy-o:before,.fa-save:before{content:"\F0C7"}.fa-square:before{content:"\F0C8"}.fa-bars:before,.fa-navicon:before,.fa-reorder:before{content:"\F0C9"}.fa-list-ul:before{content:"\F0CA"}.fa-list-ol:before{content:"\F0CB"}.fa-strikethrough:before{content:"\F0CC"}.fa-underline:before{content:"\F0CD"}.fa-table:before{content:"\F0CE"}.fa-magic:before{content:"\F0D0"}.fa-truck:before{content:"\F0D1"}.fa-pinterest:before{content:"\F0D2"}.fa-pinterest-square:before{content:"\F0D3"}.fa-google-plus-square:before{content:"\F0D4"}.fa-google-plus:before{content:"\F0D5"}.fa-money:before{content:"\F0D6"}.fa-caret-down:before{content:"\F0D7"}.fa-caret-up:before{content:"\F0D8"}.fa-caret-left:before{content:"\F0D9"}.fa-caret-right:before{content:"\F0DA"}.fa-columns:before{content:"\F0DB"}.fa-sort:before,.fa-unsorted:before{content:"\F0DC"}.fa-sort-desc:before,.fa-sort-down:before{content:"\F0DD"}.fa-sort-asc:before,.fa-sort-up:before{content:"\F0DE"}.fa-envelope:before{content:"\F0E0"}.fa-linkedin:before{content:"\F0E1"}.fa-rotate-left:before,.fa-undo:before{content:"\F0E2"}.fa-gavel:before,.fa-legal:before{content:"\F0E3"}.fa-dashboard:before,.fa-tachometer:before{content:"\F0E4"}.fa-comment-o:before{content:"\F0E5"}.fa-comments-o:before{content:"\F0E6"}.fa-bolt:before,.fa-flash:before{content:"\F0E7"}.fa-sitemap:before{content:"\F0E8"}.fa-umbrella:before{content:"\F0E9"}.fa-clipboard:before,.fa-paste:before{content:"\F0EA"}.fa-lightbulb-o:before{content:"\F0EB"}.fa-exchange:before{content:"\F0EC"}.fa-cloud-download:before{content:"\F0ED"}.fa-cloud-upload:before{content:"\F0EE"}.fa-user-md:before{content:"\F0F0"}.fa-stethoscope:before{content:"\F0F1"}.fa-suitcase:before{content:"\F0F2"}.fa-bell-o:before{content:"\F0A2"}.fa-coffee:before{content:"\F0F4"}.fa-cutlery:before{content:"\F0F5"}.fa-file-text-o:before{content:"\F0F6"}.fa-building-o:before{content:"\F0F7"}.fa-hospital-o:before{content:"\F0F8"}.fa-ambulance:before{content:"\F0F9"}.fa-medkit:before{content:"\F0FA"}.fa-fighter-jet:before{content:"\F0FB"}.fa-beer:before{content:"\F0FC"}.fa-h-square:before{content:"\F0FD"}.fa-plus-square:before{content:"\F0FE"}.fa-angle-double-left:before{content:"\F100"}.fa-angle-double-right:before{content:"\F101"}.fa-angle-double-up:before{content:"\F102"}.fa-angle-double-down:before{content:"\F103"}.fa-angle-left:before{content:"\F104"}.fa-angle-right:before{content:"\F105"}.fa-angle-up:before{content:"\F106"}.fa-angle-down:before{content:"\F107"}.fa-desktop:before{content:"\F108"}.fa-laptop:before{content:"\F109"}.fa-tablet:before{content:"\F10A"}.fa-mobile-phone:before,.fa-mobile:before{content:"\F10B"}.fa-circle-o:before{content:"\F10C"}.fa-quote-left:before{content:"\F10D"}.fa-quote-right:before{content:"\F10E"}.fa-spinner:before{content:"\F110"}.fa-circle:before{content:"\F111"}.fa-mail-reply:before,.fa-reply:before{content:"\F112"}.fa-github-alt:before{content:"\F113"}.fa-folder-o:before{content:"\F114"}.fa-folder-open-o:before{content:"\F115"}.fa-smile-o:before{content:"\F118"}.fa-frown-o:before{content:"\F119"}.fa-meh-o:before{content:"\F11A"}.fa-gamepad:before{content:"\F11B"}.fa-keyboard-o:before{content:"\F11C"}.fa-flag-o:before{content:"\F11D"}.fa-flag-checkered:before{content:"\F11E"}.fa-terminal:before{content:"\F120"}.fa-code:before{content:"\F121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\F122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\F123"}.fa-location-arrow:before{content:"\F124"}.fa-crop:before{content:"\F125"}.fa-code-fork:before{content:"\F126"}.fa-chain-broken:before,.fa-unlink:before{content:"\F127"}.fa-question:before{content:"\F128"}.fa-info:before{content:"\F129"}.fa-exclamation:before{content:"\F12A"}.fa-superscript:before{content:"\F12B"}.fa-subscript:before{content:"\F12C"}.fa-eraser:before{content:"\F12D"}.fa-puzzle-piece:before{content:"\F12E"}.fa-microphone:before{content:"\F130"}.fa-microphone-slash:before{content:"\F131"}.fa-shield:before{content:"\F132"}.fa-calendar-o:before{content:"\F133"}.fa-fire-extinguisher:before{content:"\F134"}.fa-rocket:before{content:"\F135"}.fa-maxcdn:before{content:"\F136"}.fa-chevron-circle-left:before{content:"\F137"}.fa-chevron-circle-right:before{content:"\F138"}.fa-chevron-circle-up:before{content:"\F139"}.fa-chevron-circle-down:before{content:"\F13A"}.fa-html5:before{content:"\F13B"}.fa-css3:before{content:"\F13C"}.fa-anchor:before{content:"\F13D"}.fa-unlock-alt:before{content:"\F13E"}.fa-bullseye:before{content:"\F140"}.fa-ellipsis-h:before{content:"\F141"}.fa-ellipsis-v:before{content:"\F142"}.fa-rss-square:before{content:"\F143"}.fa-play-circle:before{content:"\F144"}.fa-ticket:before{content:"\F145"}.fa-minus-square:before{content:"\F146"}.fa-minus-square-o:before{content:"\F147"}.fa-level-up:before{content:"\F148"}.fa-level-down:before{content:"\F149"}.fa-check-square:before{content:"\F14A"}.fa-pencil-square:before{content:"\F14B"}.fa-external-link-square:before{content:"\F14C"}.fa-share-square:before{content:"\F14D"}.fa-compass:before{content:"\F14E"}.fa-caret-square-o-down:before,.fa-toggle-down:before{content:"\F150"}.fa-caret-square-o-up:before,.fa-toggle-up:before{content:"\F151"}.fa-caret-square-o-right:before,.fa-toggle-right:before{content:"\F152"}.fa-eur:before,.fa-euro:before{content:"\F153"}.fa-gbp:before{content:"\F154"}.fa-dollar:before,.fa-usd:before{content:"\F155"}.fa-inr:before,.fa-rupee:before{content:"\F156"}.fa-cny:before,.fa-jpy:before,.fa-rmb:before,.fa-yen:before{content:"\F157"}.fa-rouble:before,.fa-rub:before,.fa-ruble:before{content:"\F158"}.fa-krw:before,.fa-won:before{content:"\F159"}.fa-bitcoin:before,.fa-btc:before{content:"\F15A"}.fa-file:before{content:"\F15B"}.fa-file-text:before{content:"\F15C"}.fa-sort-alpha-asc:before{content:"\F15D"}.fa-sort-alpha-desc:before{content:"\F15E"}.fa-sort-amount-asc:before{content:"\F160"}.fa-sort-amount-desc:before{content:"\F161"}.fa-sort-numeric-asc:before{content:"\F162"}.fa-sort-numeric-desc:before{content:"\F163"}.fa-thumbs-up:before{content:"\F164"}.fa-thumbs-down:before{content:"\F165"}.fa-youtube-square:before{content:"\F166"}.fa-youtube:before{content:"\F167"}.fa-xing:before{content:"\F168"}.fa-xing-square:before{content:"\F169"}.fa-youtube-play:before{content:"\F16A"}.fa-dropbox:before{content:"\F16B"}.fa-stack-overflow:before{content:"\F16C"}.fa-instagram:before{content:"\F16D"}.fa-flickr:before{content:"\F16E"}.fa-adn:before{content:"\F170"}.fa-bitbucket:before{content:"\F171"}.fa-bitbucket-square:before{content:"\F172"}.fa-tumblr:before{content:"\F173"}.fa-tumblr-square:before{content:"\F174"}.fa-long-arrow-down:before{content:"\F175"}.fa-long-arrow-up:before{content:"\F176"}.fa-long-arrow-left:before{content:"\F177"}.fa-long-arrow-right:before{content:"\F178"}.fa-apple:before{content:"\F179"}.fa-windows:before{content:"\F17A"}.fa-android:before{content:"\F17B"}.fa-linux:before{content:"\F17C"}.fa-dribbble:before{content:"\F17D"}.fa-skype:before{content:"\F17E"}.fa-foursquare:before{content:"\F180"}.fa-trello:before{content:"\F181"}.fa-female:before{content:"\F182"}.fa-male:before{content:"\F183"}.fa-gittip:before,.fa-gratipay:before{content:"\F184"}.fa-sun-o:before{content:"\F185"}.fa-moon-o:before{content:"\F186"}.fa-archive:before{content:"\F187"}.fa-bug:before{content:"\F188"}.fa-vk:before{content:"\F189"}.fa-weibo:before{content:"\F18A"}.fa-renren:before{content:"\F18B"}.fa-pagelines:before{content:"\F18C"}.fa-stack-exchange:before{content:"\F18D"}.fa-arrow-circle-o-right:before{content:"\F18E"}.fa-arrow-circle-o-left:before{content:"\F190"}.fa-caret-square-o-left:before,.fa-toggle-left:before{content:"\F191"}.fa-dot-circle-o:before{content:"\F192"}.fa-wheelchair:before{content:"\F193"}.fa-vimeo-square:before{content:"\F194"}.fa-try:before,.fa-turkish-lira:before{content:"\F195"}.fa-plus-square-o:before{content:"\F196"}.fa-space-shuttle:before{content:"\F197"}.fa-slack:before{content:"\F198"}.fa-envelope-square:before{content:"\F199"}.fa-wordpress:before{content:"\F19A"}.fa-openid:before{content:"\F19B"}.fa-bank:before,.fa-institution:before,.fa-university:before{content:"\F19C"}.fa-graduation-cap:before,.fa-mortar-board:before{content:"\F19D"}.fa-yahoo:before{content:"\F19E"}.fa-google:before{content:"\F1A0"}.fa-reddit:before{content:"\F1A1"}.fa-reddit-square:before{content:"\F1A2"}.fa-stumbleupon-circle:before{content:"\F1A3"}.fa-stumbleupon:before{content:"\F1A4"}.fa-delicious:before{content:"\F1A5"}.fa-digg:before{content:"\F1A6"}.fa-pied-piper-pp:before{content:"\F1A7"}.fa-pied-piper-alt:before{content:"\F1A8"}.fa-drupal:before{content:"\F1A9"}.fa-joomla:before{content:"\F1AA"}.fa-language:before{content:"\F1AB"}.fa-fax:before{content:"\F1AC"}.fa-building:before{content:"\F1AD"}.fa-child:before{content:"\F1AE"}.fa-paw:before{content:"\F1B0"}.fa-spoon:before{content:"\F1B1"}.fa-cube:before{content:"\F1B2"}.fa-cubes:before{content:"\F1B3"}.fa-behance:before{content:"\F1B4"}.fa-behance-square:before{content:"\F1B5"}.fa-steam:before{content:"\F1B6"}.fa-steam-square:before{content:"\F1B7"}.fa-recycle:before{content:"\F1B8"}.fa-automobile:before,.fa-car:before{content:"\F1B9"}.fa-cab:before,.fa-taxi:before{content:"\F1BA"}.fa-tree:before{content:"\F1BB"}.fa-spotify:before{content:"\F1BC"}.fa-deviantart:before{content:"\F1BD"}.fa-soundcloud:before{content:"\F1BE"}.fa-database:before{content:"\F1C0"}.fa-file-pdf-o:before{content:"\F1C1"}.fa-file-word-o:before{content:"\F1C2"}.fa-file-excel-o:before{content:"\F1C3"}.fa-file-powerpoint-o:before{content:"\F1C4"}.fa-file-image-o:before,.fa-file-photo-o:before,.fa-file-picture-o:before{content:"\F1C5"}.fa-file-archive-o:before,.fa-file-zip-o:before{content:"\F1C6"}.fa-file-audio-o:before,.fa-file-sound-o:before{content:"\F1C7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\F1C8"}.fa-file-code-o:before{content:"\F1C9"}.fa-vine:before{content:"\F1CA"}.fa-codepen:before{content:"\F1CB"}.fa-jsfiddle:before{content:"\F1CC"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-ring:before,.fa-life-saver:before,.fa-support:before{content:"\F1CD"}.fa-circle-o-notch:before{content:"\F1CE"}.fa-ra:before,.fa-rebel:before,.fa-resistance:before{content:"\F1D0"}.fa-empire:before,.fa-ge:before{content:"\F1D1"}.fa-git-square:before{content:"\F1D2"}.fa-git:before{content:"\F1D3"}.fa-hacker-news:before,.fa-y-combinator-square:before,.fa-yc-square:before{content:"\F1D4"}.fa-tencent-weibo:before{content:"\F1D5"}.fa-qq:before{content:"\F1D6"}.fa-wechat:before,.fa-weixin:before{content:"\F1D7"}.fa-paper-plane:before,.fa-send:before{content:"\F1D8"}.fa-paper-plane-o:before,.fa-send-o:before{content:"\F1D9"}.fa-history:before{content:"\F1DA"}.fa-circle-thin:before{content:"\F1DB"}.fa-header:before{content:"\F1DC"}.fa-paragraph:before{content:"\F1DD"}.fa-sliders:before{content:"\F1DE"}.fa-share-alt:before{content:"\F1E0"}.fa-share-alt-square:before{content:"\F1E1"}.fa-bomb:before{content:"\F1E2"}.fa-futbol-o:before,.fa-soccer-ball-o:before{content:"\F1E3"}.fa-tty:before{content:"\F1E4"}.fa-binoculars:before{content:"\F1E5"}.fa-plug:before{content:"\F1E6"}.fa-slideshare:before{content:"\F1E7"}.fa-twitch:before{content:"\F1E8"}.fa-yelp:before{content:"\F1E9"}.fa-newspaper-o:before{content:"\F1EA"}.fa-wifi:before{content:"\F1EB"}.fa-calculator:before{content:"\F1EC"}.fa-paypal:before{content:"\F1ED"}.fa-google-wallet:before{content:"\F1EE"}.fa-cc-visa:before{content:"\F1F0"}.fa-cc-mastercard:before{content:"\F1F1"}.fa-cc-discover:before{content:"\F1F2"}.fa-cc-amex:before{content:"\F1F3"}.fa-cc-paypal:before{content:"\F1F4"}.fa-cc-stripe:before{content:"\F1F5"}.fa-bell-slash:before{content:"\F1F6"}.fa-bell-slash-o:before{content:"\F1F7"}.fa-trash:before{content:"\F1F8"}.fa-copyright:before{content:"\F1F9"}.fa-at:before{content:"\F1FA"}.fa-eyedropper:before{content:"\F1FB"}.fa-paint-brush:before{content:"\F1FC"}.fa-birthday-cake:before{content:"\F1FD"}.fa-area-chart:before{content:"\F1FE"}.fa-pie-chart:before{content:"\F200"}.fa-line-chart:before{content:"\F201"}.fa-lastfm:before{content:"\F202"}.fa-lastfm-square:before{content:"\F203"}.fa-toggle-off:before{content:"\F204"}.fa-toggle-on:before{content:"\F205"}.fa-bicycle:before{content:"\F206"}.fa-bus:before{content:"\F207"}.fa-ioxhost:before{content:"\F208"}.fa-angellist:before{content:"\F209"}.fa-cc:before{content:"\F20A"}.fa-ils:before,.fa-shekel:before,.fa-sheqel:before{content:"\F20B"}.fa-meanpath:before{content:"\F20C"}.fa-buysellads:before{content:"\F20D"}.fa-connectdevelop:before{content:"\F20E"}.fa-dashcube:before{content:"\F210"}.fa-forumbee:before{content:"\F211"}.fa-leanpub:before{content:"\F212"}.fa-sellsy:before{content:"\F213"}.fa-shirtsinbulk:before{content:"\F214"}.fa-simplybuilt:before{content:"\F215"}.fa-skyatlas:before{content:"\F216"}.fa-cart-plus:before{content:"\F217"}.fa-cart-arrow-down:before{content:"\F218"}.fa-diamond:before{content:"\F219"}.fa-ship:before{content:"\F21A"}.fa-user-secret:before{content:"\F21B"}.fa-motorcycle:before{content:"\F21C"}.fa-street-view:before{content:"\F21D"}.fa-heartbeat:before{content:"\F21E"}.fa-venus:before{content:"\F221"}.fa-mars:before{content:"\F222"}.fa-mercury:before{content:"\F223"}.fa-intersex:before,.fa-transgender:before{content:"\F224"}.fa-transgender-alt:before{content:"\F225"}.fa-venus-double:before{content:"\F226"}.fa-mars-double:before{content:"\F227"}.fa-venus-mars:before{content:"\F228"}.fa-mars-stroke:before{content:"\F229"}.fa-mars-stroke-v:before{content:"\F22A"}.fa-mars-stroke-h:before{content:"\F22B"}.fa-neuter:before{content:"\F22C"}.fa-genderless:before{content:"\F22D"}.fa-facebook-official:before{content:"\F230"}.fa-pinterest-p:before{content:"\F231"}.fa-whatsapp:before{content:"\F232"}.fa-server:before{content:"\F233"}.fa-user-plus:before{content:"\F234"}.fa-user-times:before{content:"\F235"}.fa-bed:before,.fa-hotel:before{content:"\F236"}.fa-viacoin:before{content:"\F237"}.fa-train:before{content:"\F238"}.fa-subway:before{content:"\F239"}.fa-medium:before{content:"\F23A"}.fa-y-combinator:before,.fa-yc:before{content:"\F23B"}.fa-optin-monster:before{content:"\F23C"}.fa-opencart:before{content:"\F23D"}.fa-expeditedssl:before{content:"\F23E"}.fa-battery-4:before,.fa-battery-full:before,.fa-battery:before{content:"\F240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\F241"}.fa-battery-2:before,.fa-battery-half:before{content:"\F242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\F243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\F244"}.fa-mouse-pointer:before{content:"\F245"}.fa-i-cursor:before{content:"\F246"}.fa-object-group:before{content:"\F247"}.fa-object-ungroup:before{content:"\F248"}.fa-sticky-note:before{content:"\F249"}.fa-sticky-note-o:before{content:"\F24A"}.fa-cc-jcb:before{content:"\F24B"}.fa-cc-diners-club:before{content:"\F24C"}.fa-clone:before{content:"\F24D"}.fa-balance-scale:before{content:"\F24E"}.fa-hourglass-o:before{content:"\F250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\F251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\F252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\F253"}.fa-hourglass:before{content:"\F254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\F255"}.fa-hand-paper-o:before,.fa-hand-stop-o:before{content:"\F256"}.fa-hand-scissors-o:before{content:"\F257"}.fa-hand-lizard-o:before{content:"\F258"}.fa-hand-spock-o:before{content:"\F259"}.fa-hand-pointer-o:before{content:"\F25A"}.fa-hand-peace-o:before{content:"\F25B"}.fa-trademark:before{content:"\F25C"}.fa-registered:before{content:"\F25D"}.fa-creative-commons:before{content:"\F25E"}.fa-gg:before{content:"\F260"}.fa-gg-circle:before{content:"\F261"}.fa-tripadvisor:before{content:"\F262"}.fa-odnoklassniki:before{content:"\F263"}.fa-odnoklassniki-square:before{content:"\F264"}.fa-get-pocket:before{content:"\F265"}.fa-wikipedia-w:before{content:"\F266"}.fa-safari:before{content:"\F267"}.fa-chrome:before{content:"\F268"}.fa-firefox:before{content:"\F269"}.fa-opera:before{content:"\F26A"}.fa-internet-explorer:before{content:"\F26B"}.fa-television:before,.fa-tv:before{content:"\F26C"}.fa-contao:before{content:"\F26D"}.fa-500px:before{content:"\F26E"}.fa-amazon:before{content:"\F270"}.fa-calendar-plus-o:before{content:"\F271"}.fa-calendar-minus-o:before{content:"\F272"}.fa-calendar-times-o:before{content:"\F273"}.fa-calendar-check-o:before{content:"\F274"}.fa-industry:before{content:"\F275"}.fa-map-pin:before{content:"\F276"}.fa-map-signs:before{content:"\F277"}.fa-map-o:before{content:"\F278"}.fa-map:before{content:"\F279"}.fa-commenting:before{content:"\F27A"}.fa-commenting-o:before{content:"\F27B"}.fa-houzz:before{content:"\F27C"}.fa-vimeo:before{content:"\F27D"}.fa-black-tie:before{content:"\F27E"}.fa-fonticons:before{content:"\F280"}.fa-reddit-alien:before{content:"\F281"}.fa-edge:before{content:"\F282"}.fa-credit-card-alt:before{content:"\F283"}.fa-codiepie:before{content:"\F284"}.fa-modx:before{content:"\F285"}.fa-fort-awesome:before{content:"\F286"}.fa-usb:before{content:"\F287"}.fa-product-hunt:before{content:"\F288"}.fa-mixcloud:before{content:"\F289"}.fa-scribd:before{content:"\F28A"}.fa-pause-circle:before{content:"\F28B"}.fa-pause-circle-o:before{content:"\F28C"}.fa-stop-circle:before{content:"\F28D"}.fa-stop-circle-o:before{content:"\F28E"}.fa-shopping-bag:before{content:"\F290"}.fa-shopping-basket:before{content:"\F291"}.fa-hashtag:before{content:"\F292"}.fa-bluetooth:before{content:"\F293"}.fa-bluetooth-b:before{content:"\F294"}.fa-percent:before{content:"\F295"}.fa-gitlab:before{content:"\F296"}.fa-wpbeginner:before{content:"\F297"}.fa-wpforms:before{content:"\F298"}.fa-envira:before{content:"\F299"}.fa-universal-access:before{content:"\F29A"}.fa-wheelchair-alt:before{content:"\F29B"}.fa-question-circle-o:before{content:"\F29C"}.fa-blind:before{content:"\F29D"}.fa-audio-description:before{content:"\F29E"}.fa-volume-control-phone:before{content:"\F2A0"}.fa-braille:before{content:"\F2A1"}.fa-assistive-listening-systems:before{content:"\F2A2"}.fa-american-sign-language-interpreting:before,.fa-asl-interpreting:before{content:"\F2A3"}.fa-deaf:before,.fa-deafness:before,.fa-hard-of-hearing:before{content:"\F2A4"}.fa-glide:before{content:"\F2A5"}.fa-glide-g:before{content:"\F2A6"}.fa-sign-language:before,.fa-signing:before{content:"\F2A7"}.fa-low-vision:before{content:"\F2A8"}.fa-viadeo:before{content:"\F2A9"}.fa-viadeo-square:before{content:"\F2AA"}.fa-snapchat:before{content:"\F2AB"}.fa-snapchat-ghost:before{content:"\F2AC"}.fa-snapchat-square:before{content:"\F2AD"}.fa-pied-piper:before{content:"\F2AE"}.fa-first-order:before{content:"\F2B0"}.fa-yoast:before{content:"\F2B1"}.fa-themeisle:before{content:"\F2B2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\F2B3"}.fa-fa:before,.fa-font-awesome:before{content:"\F2B4"}.fa-handshake-o:before{content:"\F2B5"}.fa-envelope-open:before{content:"\F2B6"}.fa-envelope-open-o:before{content:"\F2B7"}.fa-linode:before{content:"\F2B8"}.fa-address-book:before{content:"\F2B9"}.fa-address-book-o:before{content:"\F2BA"}.fa-address-card:before,.fa-vcard:before{content:"\F2BB"}.fa-address-card-o:before,.fa-vcard-o:before{content:"\F2BC"}.fa-user-circle:before{content:"\F2BD"}.fa-user-circle-o:before{content:"\F2BE"}.fa-user-o:before{content:"\F2C0"}.fa-id-badge:before{content:"\F2C1"}.fa-drivers-license:before,.fa-id-card:before{content:"\F2C2"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"\F2C3"}.fa-quora:before{content:"\F2C4"}.fa-free-code-camp:before{content:"\F2C5"}.fa-telegram:before{content:"\F2C6"}.fa-thermometer-4:before,.fa-thermometer-full:before,.fa-thermometer:before{content:"\F2C7"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\F2C8"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\F2C9"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\F2CA"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\F2CB"}.fa-shower:before{content:"\F2CC"}.fa-bath:before,.fa-bathtub:before,.fa-s15:before{content:"\F2CD"}.fa-podcast:before{content:"\F2CE"}.fa-window-maximize:before{content:"\F2D0"}.fa-window-minimize:before{content:"\F2D1"}.fa-window-restore:before{content:"\F2D2"}.fa-times-rectangle:before,.fa-window-close:before{content:"\F2D3"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"\F2D4"}.fa-bandcamp:before{content:"\F2D5"}.fa-grav:before{content:"\F2D6"}.fa-etsy:before{content:"\F2D7"}.fa-imdb:before{content:"\F2D8"}.fa-ravelry:before{content:"\F2D9"}.fa-eercast:before{content:"\F2DA"}.fa-microchip:before{content:"\F2DB"}.fa-snowflake-o:before{content:"\F2DC"}.fa-superpowers:before{content:"\F2DD"}.fa-wpexplorer:before{content:"\F2DE"}.fa-meetup:before{content:"\F2E0"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}code[class*=language-],pre[class*=language-]{color:#ccc;background:none;font-family:Consolas,Monaco,Andale Mono,Ubuntu Mono,monospace;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;tab-size:4;-webkit-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#2d2d2d}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.block-comment,.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#999}.token.punctuation{color:#ccc}.token.attr-name,.token.deleted,.token.namespace,.token.tag{color:#e2777a}.token.function-name{color:#6196cc}.token.boolean,.token.function,.token.number{color:#f08d49}.token.class-name,.token.constant,.token.property,.token.symbol{color:#f8c555}.token.atrule,.token.builtin,.token.important,.token.keyword,.token.selector{color:#cc99cd}.token.attr-value,.token.char,.token.regex,.token.string,.token.variable{color:#7ec699}.token.entity,.token.operator,.token.url{color:#67cdcc}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.token.inserted{color:green}.icheckbox_square-green,.iradio_square-green{display:inline-block;vertical-align:middle;margin:0;padding:0;width:22px;height:22px;background:url(/dist/images/green.png?869a3a67e8e1ca55bc5ee0a70438f320) no-repeat;border:none;cursor:pointer}.icheckbox_square-green{background-position:0 0}.icheckbox_square-green.hover{background-position:-24px 0}.icheckbox_square-green.checked{background-position:-48px 0}.icheckbox_square-green.disabled{background-position:-72px 0;cursor:default}.icheckbox_square-green.checked.disabled{background-position:-96px 0}.iradio_square-green{background-position:-120px 0}.iradio_square-green.hover{background-position:-144px 0}.iradio_square-green.checked{background-position:-168px 0}.iradio_square-green.disabled{background-position:-192px 0;cursor:default}.iradio_square-green.checked.disabled{background-position:-216px 0}@media(-webkit-min-device-pixel-ratio:1.25),(min-resolution:120dpi){.icheckbox_square-green,.iradio_square-green{background-image:url(/dist/images/green@2x.png?1a0de24f0bfb1a31dd5d2a11c94484e7);background-size:240px 24px}}@font-face{font-family:Lato;font-weight:300;src:local("Lato Light"),local("Lato-Light"),url(/dist/fonts/lato-v11-latin-300.woff2?3e86c4948704e8b9a85f00f98feb963f) format("woff2"),url(/dist/fonts/lato-v11-latin-300.woff?6f934cd8100db640286be1ff3d110977) format("woff")}@font-face{font-family:Lato;font-style:italic;font-weight:300;src:local("Lato Light Italic"),local("Lato-LightItalic"),url(/dist/fonts/lato-v11-latin-300italic.woff2?5f662e8ae2643cb62d51bad100ac2d82) format("woff2"),url(/dist/fonts/lato-v11-latin-300italic.woff?5c5b445aed08fdee559c47925290c9a5) format("woff")}@font-face{font-family:Lato;src:local("Lato Regular"),local("Lato-Regular"),url(/dist/fonts/lato-v11-latin-regular.woff2?6748e0e1c0bef825e16c649fd2ad5691) format("woff2"),url(/dist/fonts/lato-v11-latin-regular.woff?f137ee4862785a1518fb3056eccdc99b) format("woff")}@font-face{font-family:Lato;font-style:italic;src:local("Lato Italic"),local("Lato-Italic"),url(/dist/fonts/lato-v11-latin-italic.woff2?f8a9a7a93c2c322957c8d8b61929bfe5) format("woff2"),url(/dist/fonts/lato-v11-latin-italic.woff?a8fc7661c63e697c2b71a11b93a6fcf4) format("woff")}@font-face{font-family:Lato;font-weight:900;src:local("Lato Black"),local("Lato-Black"),url(/dist/fonts/lato-v11-latin-900.woff2?f377f4433f32e365e4c3162cfb0b0f8c) format("woff2"),url(/dist/fonts/lato-v11-latin-900.woff?c208c97845844a067d4a8f900c225847) format("woff")}@font-face{font-family:Lato;font-style:italic;font-weight:900;src:local("Lato Black Italic"),local("Lato-BlackItalic"),url(/dist/fonts/lato-v11-latin-900italic.woff2?9d33e2f45478437bac5afbbdf1b5712d) format("woff2"),url(/dist/fonts/lato-v11-latin-900italic.woff?59f84cca32a20f23afdf3304535f1b01) format("woff")}body,html{font-size:16px;line-height:24px;font-family:Lato,Helvetica Neue,Helvetica,Arial,sans-serif;color:#374853;font-weight:lighter;background-color:#fff}body,body *{box-sizing:border-box}a,a:active,a:active:focus,a:active:hover,a:focus,a:hover,a:hover:focus{outline:0;text-decoration:none;color:#41b883}p{margin:20px 0}b{font-weight:400}hr{margin:20px 0;border-top:1px solid #e8e8e8;border-bottom:none;border-left:none;border-right:none}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.text-success{color:#41b883}.inline .i-checks-inline{display:inline-block}@media(max-width:768px){.hidden-mobile{display:none}}.layout-left-nav-bar{position:fixed;top:0;left:0;width:260px;padding:20px 20px 20px 40px;height:100vh;overflow-y:auto;border-right:1px solid #e8e8e8}@media(max-width:768px){.layout-left-nav-bar{display:none}}.layout-left-nav-bar .left-nav{padding-left:0}.layout-left-nav-bar .left-nav li{list-style:none}.layout-left-nav-bar .left-nav li a{color:inherit}.layout-left-nav-bar .left-nav li a:hover{background-color:transparent}.layout-left-nav-bar .left-nav li.heading{font-size:12px;font-weight:400;padding:20px 0 10px;color:#41b883;text-transform:uppercase}.layout-left-nav-bar .left-nav li.item{padding:3px 5px}.layout-main{margin-left:260px;padding:40px}@media(max-width:768px){.layout-main{margin-left:0;padding:20px}}@media(min-width:1200px){.layout-main{padding:40px 60px}}.layout-main .hero{margin:40px 0 80px}.layout-content-block{margin:120px 0}.layout-content-block .chapter-title{text-align:center;font-weight:400;color:#41b883;text-transform:uppercase}.layout-content-block .section-title{position:relative;font-size:20px;font-weight:400;margin:30px 0 15px}.layout-content-block .section-title:after{content:"#";display:block;position:absolute;top:0;left:-20px;color:#41b883;font-style:italic}.layout-content-block .section-sub-title{position:relative;font-weight:400;margin:20px 0 10px}.layout-content-block .section-sub-title:after{content:"#";display:block;position:absolute;top:0;left:-20px;color:#41b883;font-style:italic}.layout-content-block .section-content{margin:15px 0}.layout-content-block .section-content a{font-weight:400}.layout-content-block .section-content .note{margin:20px 0;padding:20px;background-color:#f8f8f8;border-left:4px solid #f66;position:relative}.layout-content-block .section-content .note:after{content:"!";display:block;width:20px;height:20px;line-height:20px;text-align:center;font-size:12px;color:#fff;background-color:#f66;border-radius:50%;position:absolute;top:8px;left:-12px}.layout-content-block .section-content .table-wrapper{overflow-x:auto}.layout-content-block .section-content table{width:100%;min-width:600px;border-spacing:0;border-collapse:collapse;text-align:left;font-size:14px}.layout-content-block .section-content table tr{border:1px solid #e8e8e8}.layout-content-block .section-content table tbody tr:hover,.layout-content-block .section-content table thead tr{background-color:#f8f8f8}.layout-content-block .section-content table tr td,.layout-content-block .section-content table tr th{padding:3px 8px;min-width:110px}.layout-content-block .tab-container{display:-webkit-box;display:-ms-flexbox;display:flex;overflow-x:auto}.layout-content-block .tab-container .tab{padding:0 25px;line-height:40px;color:#9ba3a9;font-size:12px;font-weight:400;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;min-width:120px;text-align:center}@media(max-width:768px){.layout-content-block .tab-container .tab{min-width:80px;padding:0 10px}}.layout-content-block .tab-container .tab.active{color:#fff;background-color:#2d2d2d;cursor:default}.layout-content-block .tab-container .tab.demo.active{color:#374853;background-color:#f8f8f8}.layout-content-block .page-container .page{display:none;background-color:#2d2d2d;overflow-y:auto;overflow-x:hidden}.layout-content-block .page-container .page.demo{background-color:#f8f8f8}.layout-content-block .page-container .page.active{display:block}.layout-content-block .page-container .page pre{margin:0}.layout-content-block .page-container .page .indicator{margin:30px 0;text-align:center;font-size:18px}.layout-form{padding:30px;margin:25px;font-size:14px;line-height:20px;box-shadow:0 3px 20px 0 rgba(0,0,0,.3);background-color:#fff}@media(max-width:768px){.layout-form{padding:10px}}.layout-form .form-group{display:-webkit-box;display:-ms-flexbox;display:flex;margin:15px 0}.layout-form .form-group .label{width:100px;padding:5px 20px 0 0;font-weight:700}.layout-form .form-group.error .label{color:#f66}.layout-form .form-group .content{width:50%;min-width:120px}.layout-form .form-group .content label{display:inline-block;padding:5px 20px 0 0}.layout-form .form-group .message{-webkit-box-flex:1;-ms-flex:1;flex:1;padding:5px 0 0 20px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;font-weight:700}.layout-form .form-group.error .message{color:#f66}.layout-form .form-group .form-control{font-size:14px}.layout-form .form-group .actions{margin-left:100px;width:50%;text-align:right}@media(max-width:768px){.layout-form{font-size:12px}.layout-form .form-group .label{width:60px;padding-right:10px}.layout-form .form-group .form-control{font-size:12px}.layout-form .form-group .actions{margin-left:60px}}.layout-form .form-group input[type=password].form-control,.layout-form .form-group input[type=text].form-control{width:100%;font-weight:lighter;padding:4px 6px;border:1px solid #acacac;outline:none}.layout-form .form-group input[type=password].form-control:focus,.layout-form .form-group input[type=text].form-control:focus{border-color:#374853}.layout-form .form-group.error input[type=password].form-control,.layout-form .form-group.error input[type=password].form-control:focus,.layout-form .form-group.error input[type=text].form-control,.layout-form .form-group.error input[type=text].form-control:focus{border-color:#f66}.layout-form .form-group .btn{font-size:12px;font-weight:400;padding:6px 10px;border-radius:4px;outline:none;-webkit-appearance:none;-moz-appearance:none;appearance:none;cursor:pointer;text-transform:uppercase}.layout-form .form-group .btn+.btn{margin-left:8px}@media(max-width:768px){.layout-form .form-group .btn{padding:3px 6px}.layout-form .form-group .btn+.btn{margin-left:4px}}.layout-form .form-group .btn-primary{color:#fff;background-color:#41b883;border:1px solid #39a073}.layout-form .form-group .btn-primary:focus{background-color:#3dad7c}.layout-form .form-group .btn-primary:active{background-color:#39a073}.layout-form .form-group .btn-default{color:#374853;background-color:#fff;border:1px solid #acacac}.layout-form .form-group .btn-default:focus{background-color:#eee}.layout-form .form-group .btn-default:active{background-color:#ccc}.layout-demo{margin:20px 0}.code{color:#e96900;background-color:#f8f8f8;display:inline-block;padding:0 5px} 5 | /*# sourceMappingURL=style.css.map*/ --------------------------------------------------------------------------------