├── .babelrc ├── .eslintrc ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── dist └── vue-ip-input.min.js ├── example └── index.html ├── index.html ├── index.js ├── karma.config.js ├── package.json ├── src ├── index.js └── vue-ip-input.vue ├── test ├── index.js └── vue-ip-input.spec.js ├── webpack.base.config.js ├── webpack.dev.config.js └── webpack.prod.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"] 3 | } 4 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "eslint-config-google", 3 | "plugins": [ 4 | "html" 5 | ], 6 | "rules": { 7 | "indent": [2, 4], 8 | "semi": [2, 'always'], 9 | "no-negated-condition": 0, 10 | "eqeqeq": [2, 'allow-null'], 11 | "no-eq-null": 0, 12 | "quote-props": [2, 'as-needed'], 13 | "no-new": 0 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # node module # 2 | node_modules/ 3 | 4 | # test coveage result # 5 | coverage/ 6 | 7 | .DS_Store 8 | 9 | # log file # 10 | *.log 11 | 12 | .idea -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 6.3.0 4 | script: 5 | - npm run ci 6 | branches: 7 | only: 8 | - next 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 LinBin 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-ip-input 2 | [![Build Status](https://travis-ci.org/lakb248/vue-ip-input.svg?branch=next)](https://travis-ci.org/lakb248/vue-ip-input) 3 | [![codecov](https://codecov.io/gh/lakb248/vue-ip-input/branch/next/graph/badge.svg)](https://codecov.io/gh/lakb248/vue-ip-input) 4 | 5 | > An ip input implement by vue 2.0 6 | 7 | ## Demo 8 | 9 | [Demo](https://lakb248.github.io/vue-ip-input) 10 | 11 | ## Usage 12 | 13 | ### Install 14 | 15 | ```bash 16 | npm install vue-ip-input --save 17 | ``` 18 | 19 | ### CommonJS 20 | 21 | ```javascript 22 | var VueIpInput = require('vue-ip-input'); 23 | 24 | new Vue({ 25 | components: { 26 | 'vue-ip-input': VueIpInput 27 | }, 28 | data: function () { 29 | return { 30 | ip: '127.0.0.1' 31 | }; 32 | }, 33 | methods: { 34 | onIpChange: function(ip) { 35 | console.log('ip input change:', ip); 36 | }, 37 | onIpBlur: function (ip) { 38 | console.log('ip input blur:', ip); 39 | } 40 | }, 41 | template: '' 42 | }); 43 | ``` 44 | 45 | ### ES6 46 | ```javascript 47 | import VueIpInput from 'vue-ip-input'; 48 | 49 | new Vue({ 50 | components: { 51 | 'vue-ip-input': VueIpInput 52 | }, 53 | data() { 54 | return { 55 | ip: '127.0.0.1' 56 | }; 57 | }, 58 | methods: { 59 | onIpChange(ip) { 60 | console.log('ip input change:', ip); 61 | }, 62 | onIpBlur(ip) { 63 | console.log('ip input blur:', ip); 64 | } 65 | }, 66 | template: '' 67 | }) 68 | ``` 69 | 70 | ### Props 71 | | Property | Description | 72 | |:--|:--| 73 | | ip | the value of ip input | 74 | | onChange | trigger when the ip change | 75 | | onBlur | trigger when the input blur | 76 | 77 | ## Contribution 78 | First, install dependencies 79 | ``` 80 | npm install 81 | ``` 82 | Second, setup development environment 83 | ``` 84 | npm run dev 85 | ``` 86 | 87 | ## License 88 | 89 | [MIT](http://opensource.org/licenses/MIT) 90 | -------------------------------------------------------------------------------- /dist/vue-ip-input.min.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.VueIpInput=t():e.VueIpInput=t()}(this,function(){return function(e){function t(o){if(n[o])return n[o].exports;var i=n[o]={i:o,l:!1,exports:{}};return e[o].call(i.exports,i,i.exports,t),i.l=!0,i.exports}var n={};return t.m=e,t.c=n,t.i=function(e){return e},t.d=function(e,n,o){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:o})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=1)}([function(e,t,n){var o,i,r={};n(7),o=n(2),Object.keys(o).some(function(e){return"default"!==e&&"__esModule"!==e})&&console.warn("[vue-loader] src/vue-ip-input.vue: named exports in *.vue files are ignored."),i=n(5),e.exports=o||{},e.exports.__esModule&&(e.exports=e.exports.default);var s="function"==typeof e.exports?e.exports.options||(e.exports.options={}):e.exports;i&&(s.template=i),s.computed||(s.computed={}),Object.keys(r).forEach(function(e){var t=r[e];s.computed[e]=function(){return t}})},function(e,t,n){"use strict";var o=n(0),i=function(e){return e&&e.__esModule?e:{default:e}}(o);e.exports=i.default},function(e,t,n){"use strict";function o(e){var t,n,o,i,r,s={};return e.setSelectionRange?(s.begin=e.selectionStart,s.end=e.selectionEnd,s.result=e.value.substring(s.begin,s.end)):document.selection&&("input"===e.tagName.toLowerCase()?(t=document.selection.createRange(),n=e.createTextRange(),n.collapse(!0),n.select(),o=document.selection.createRange(),o.setEndPoint("EndToEnd",t),s.begin=o.text.length-t.text.length,s.end=o.text.length,s.result=t.text,t.select()):"textarea"===e.tagName.toLowerCase()&&(i=document.selection.createRange(),r=i.duplicate(),r.moveToElementText(e),r.setEndPoint("EndToEnd",i),s.begin=r.text.length-i.text.length,s.end=r.text.length,s.result=i.text)),e.focus(),s}Object.defineProperty(t,"__esModule",{value:!0}),t.default={props:{ip:{type:String,required:!0},placeholder:String,onChange:Function,onBlur:Function},data:function(){return{segments:["","","",""]}},watch:{ip:function(e){this.syncIp(e)}},methods:{onInputKeydown:function(e,t){var n=e.keyCode||e.which,i=e.target.value;8===n||37===n?(0===i.length||0===o(e.target).end)&&t>0&&(this.$el.getElementsByTagName("input")[t-1].focus(),e.preventDefault()):39===n&&o(e.target).end===i.length&&t<3&&this.$el.getElementsByTagName("input")[t+1].focus()},onInput:function(e,t){var n=e.target.value;e.target.value=this.segments[t];var o=Number(n);isNaN(o)||(""===n?this.segments.splice(t,1,""):o>255||o<0?this.segments.splice(t,1,255):this.segments.splice(t,1,o),(3===n.length&&t<3||"."===n[n.length-1])&&this.$el.getElementsByTagName("input")[t+1].focus())},onInputBlur:function(){var e=this;setTimeout(function(){-1===document.activeElement.className.indexOf("ip-segment-input")&&e.onBlur&&e.onBlur(e.segments.join("."))},50)},onPaste:function(e,t){var n=this;e.clipboardData.getData("text/plain").split(".").forEach(function(e,o){t+o<4&&!isNaN(e)&&e>=0&&e<=255&&n.segments.splice(t+o,1,e)}),e.preventDefault()},syncIp:function(e){var t=this;e&&-1!==e.indexOf(".")&&e.split(".").map(function(e,n){return(isNaN(e)||e<0||e>255)&&(e=255),t.segments.splice(n,1,e),e})}},mounted:function(){var e=this;this.syncIp(this.ip),this.$watch(function(){return e.segments.join(".")},function(t,n){t!==n&&("..."===t&&(t=""),e.onChange&&e.onChange(t))})}}},function(e,t,n){t=e.exports=n(4)(),t.push([e.i,".ip-input-container[_v-312b3ab3] {\n display: inline-block;\n height: 28px;\n line-height: normal;\n border: 1px solid #ccc;\n box-sizing: border-box;\n background-color: #fff; }\n\n.ip-segment[_v-312b3ab3] {\n display: inline-block;\n width: 39px;\n height: 26px;\n line-height: normal; }\n .ip-segment input[_v-312b3ab3] {\n width: 30px;\n height: 26px;\n line-height: normal;\n border: none;\n outline: none;\n text-align: center;\n text-indent: 0px;\n margin: 0px;\n padding: 0px;\n background-color: transparent; }\n .ip-segment i[_v-312b3ab3] {\n display: inline-block;\n font-size: 18px; }\n",""])},function(e,t){e.exports=function(){var e=[];return e.toString=function(){for(var e=[],t=0;t=0&&v.splice(t,1)}function s(e){var t=document.createElement("style");return t.type="text/css",i(e,t),t}function a(e,t){var n,o,i;if(t.singleton){var a=g++;n=h||(h=s(t)),o=u.bind(null,n,a,!1),i=u.bind(null,n,a,!0)}else n=s(t),o=l.bind(null,n),i=function(){r(n)};return o(e),function(t){if(t){if(t.css===e.css&&t.media===e.media&&t.sourceMap===e.sourceMap)return;o(e=t)}else i()}}function u(e,t,n,o){var i=n?"":o.css;if(e.styleSheet)e.styleSheet.cssText=m(t,i);else{var r=document.createTextNode(i),s=e.childNodes;s[t]&&e.removeChild(s[t]),s.length?e.insertBefore(r,s[t]):e.appendChild(r)}}function l(e,t){var n=t.css,o=t.media,i=t.sourceMap;if(o&&e.setAttribute("media",o),i&&(n+="\n/*# sourceURL="+i.sources[0]+" */",n+="\n/*# sourceMappingURL=data:application/json;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(i))))+" */"),e.styleSheet)e.styleSheet.cssText=n;else{for(;e.firstChild;)e.removeChild(e.firstChild);e.appendChild(document.createTextNode(n))}}var c={},p=function(e){var t;return function(){return void 0===t&&(t=e.apply(this,arguments)),t}},d=p(function(){return/msie [6-9]\b/.test(window.navigator.userAgent.toLowerCase())}),f=p(function(){return document.head||document.getElementsByTagName("head")[0]}),h=null,g=0,v=[];e.exports=function(e,t){if("undefined"!=typeof DEBUG&&DEBUG&&"object"!=typeof document)throw new Error("The style-loader cannot be used in a non-browser environment");t=t||{},void 0===t.singleton&&(t.singleton=d()),void 0===t.insertAt&&(t.insertAt="bottom");var i=o(e);return n(i,t),function(e){for(var r=[],s=0;s 2 | 3 | 4 | 5 | Vue Ip Input 6 | 7 | 8 |
9 | 10 |
Result: {{ip}}
11 |

Change Event:

12 |
    13 |
  • {{event.timestamp}}: {{event.val}}
  • 14 |
15 |

Blur Event:

16 |
    17 |
  • {{event.timestamp}}: {{event.val}}
  • 18 |
19 |
20 | 21 | 22 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Vue Component 6 | 7 | 8 |
9 | 10 |

IP: {{ip}}

11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import vueIpInput from './src/vue-ip-input.vue'; 3 | 4 | new Vue({ 5 | el: '#container', 6 | components: { 7 | 'vue-ip-input': vueIpInput 8 | }, 9 | data() { 10 | return { 11 | ip: '127.0.0.1' 12 | }; 13 | }, 14 | methods: { 15 | ipChange(ip) { 16 | this.ip = ip; 17 | }, 18 | changeIp() { 19 | this.ip = '1.1.1.1'; 20 | } 21 | } 22 | }); 23 | -------------------------------------------------------------------------------- /karma.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | var baseConfig = require('./webpack.base.config'); 3 | var merge = require('webpack-merge'); 4 | delete baseConfig.entry; 5 | console.log(baseConfig); 6 | baseConfig.entry = './test/index.js'; 7 | 8 | // add isparta-loader to vue file 9 | var webpackConfig = baseConfig; 10 | webpackConfig.plugins = (baseConfig.plugins || []).concat([ 11 | new webpack.LoaderOptionsPlugin({ 12 | options: { 13 | vue: { 14 | loaders: { 15 | js: 'isparta-loader' 16 | } 17 | } 18 | } 19 | }) 20 | ]); 21 | 22 | module.exports = function(config) { 23 | config.set({ 24 | browsers: ['PhantomJS'], 25 | frameworks: ['jasmine'], 26 | reporters: ['progress', 'coverage', 'verbose'], 27 | // this is the entry file for all our tests. 28 | files: ['./test/index.js'], 29 | // we will pass the entry file to webpack for bundling. 30 | preprocessors: { 31 | './test/index.js': ['webpack'] 32 | }, 33 | coverageReporter: { 34 | dir: './coverage', 35 | reporters: [ 36 | {type: 'html'}, 37 | {type: 'text-summary'}, 38 | {type: 'cobertura', subdir: '.'} 39 | ] 40 | }, 41 | webpack: webpackConfig, 42 | webpackMiddleware: { 43 | noInfo: true 44 | }, 45 | singleRun: true 46 | }); 47 | }; 48 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-ip-input", 3 | "version": "2.0.2", 4 | "description": "An ip input implement by vue 2.0", 5 | "main": "./dist/vue-ip-input.min.js", 6 | "scripts": { 7 | "dev": "webpack-dev-server --watch --progress --config webpack.dev.config.js --port 8888", 8 | "build": "webpack --progress --config webpack.prod.config.js", 9 | "test": "karma start karma.config.js", 10 | "ci": "npm run test && npm run build && codecov -t 4f1b7b5a-9eb3-41f5-9218-08b805826905 coverage/cobertura-coverage.xml" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/lakb248/vue-ip-input.git" 15 | }, 16 | "bugs": { 17 | "url": "https://github.com/lakb248/vue-ip-input/issues" 18 | }, 19 | "keywords": [ 20 | "vue-component", 21 | "vuejs", 22 | "ip", 23 | "ip-input" 24 | ], 25 | "author": "lakb248@gmail.com", 26 | "license": "ISC", 27 | "dependencies": { 28 | "vue": "^2.0.1" 29 | }, 30 | "devDependencies": { 31 | "babel-core": "^6.5.2", 32 | "babel-loader": "^6.2.2", 33 | "babel-plugin-transform-runtime": "^6.5.2", 34 | "babel-preset-es2015": "^6.5.0", 35 | "babel-runtime": "^6.9.2", 36 | "codecov": "^1.0.1", 37 | "css-loader": "^0.23.1", 38 | "eslint": "^3.1.1", 39 | "eslint-config-google": "^0.6.0", 40 | "eslint-config-vue": "^1.0.3", 41 | "eslint-plugin-html": "^1.5.1", 42 | "file-loader": "^0.8.5", 43 | "isparta": "^4.0.0", 44 | "isparta-loader": "^2.0.0", 45 | "istanbul": "^0.4.4", 46 | "jasmine-core": "^2.4.1", 47 | "karma": "^1.2.0", 48 | "karma-babel-preprocessor": "^6.0.1", 49 | "karma-coverage": "^1.1.1", 50 | "karma-html-reporter": "^0.2.7", 51 | "karma-jasmine": "^1.0.2", 52 | "karma-phantomjs-launcher": "^1.0.1", 53 | "karma-spec-reporter": "0.0.26", 54 | "karma-verbose-reporter": "0.0.3", 55 | "karma-webpack": "^1.8.0", 56 | "node-sass": "^3.4.2", 57 | "phantomjs": "^2.1.7", 58 | "sass-loader": "^3.1.2", 59 | "style-loader": "^0.13.0", 60 | "url-loader": "^0.5.7", 61 | "vue-hot-reload-api": "^1.3.2", 62 | "vue-html-loader": "^1.1.0", 63 | "vue-loader": "^8.1.3", 64 | "vue-style-loader": "^1.0.0", 65 | "webpack": "^2.1.0-beta.2", 66 | "webpack-dev-server": "^1.15.0", 67 | "webpack-merge": "^0.14.1" 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import vueIpInput from './vue-ip-input.vue'; 2 | 3 | module.exports = vueIpInput; 4 | -------------------------------------------------------------------------------- /src/vue-ip-input.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 171 | 172 | 204 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | // Polyfill fn.bind() for PhantomJS 2 | /* eslint-disable no-extend-native */ 3 | // Function.prototype.bind = require('function-bind'); 4 | 5 | // require all test files (files that ends with .spec.js) 6 | var testsContext = require.context('.', true, /\.spec$/); 7 | testsContext.keys().forEach(testsContext); 8 | -------------------------------------------------------------------------------- /test/vue-ip-input.spec.js: -------------------------------------------------------------------------------- 1 | /* global document, describe, it, expect */ 2 | import Vue from 'vue'; 3 | import IpInput from '../src/vue-ip-input.vue'; 4 | 5 | var initIpInput = config => { 6 | var comp = new Vue({ 7 | template: '
', 10 | components: { 11 | 'vue-ip-input': IpInput 12 | }, 13 | data() { 14 | return { 15 | ip: config.ip 16 | }; 17 | }, 18 | methods: { 19 | onChange(ip) { 20 | if (config.onChange) { 21 | config.onChange(ip); 22 | } 23 | }, 24 | onBlur(ip) { 25 | if (config.onBlur) { 26 | config.onBlur(ip); 27 | } 28 | } 29 | } 30 | }).$mount(); 31 | document.body.appendChild(comp.$el); 32 | return comp; 33 | }; 34 | 35 | var trigger = (target, event, process) => { 36 | var e = document.createEvent('HTMLEvents'); 37 | e.initEvent(event, true, true); 38 | if (process) process(e); 39 | target.dispatchEvent(e); 40 | return e; 41 | }; 42 | 43 | describe('vue-ip-input.vue', () => { 44 | it('should have correct default ip', () => { 45 | expect(IpInput.data().segments.join(',')).toBe(',,,'); 46 | }); 47 | 48 | it('should render correct ip', () => { 49 | var vm = initIpInput({ 50 | ip: '127.0.0.1' 51 | }); 52 | expect(vm.$refs['ip-input'].segments.join('.')).toBe('127.0.0.1'); 53 | }); 54 | 55 | it('should render correct if the ip is invalid', () => { 56 | var vm = initIpInput({ 57 | ip: '333.123.123.123' 58 | }); 59 | expect(vm.$refs['ip-input'].segments.join('.')).toBe('255.123.123.123'); 60 | }); 61 | it('should update the ip if the input change', done => { 62 | var vm = initIpInput({ 63 | ip: '127.0.0.1', 64 | onChange(ip) { 65 | vm.ip = ip; 66 | } 67 | }); 68 | vm.$refs['ip-input'].segments.splice(0, 1, 255); 69 | Vue.nextTick(() => { 70 | expect(vm.ip).toBe('255.0.0.1'); 71 | done(); 72 | }); 73 | }); 74 | it('should update the ip to empty string if the input is epmty', done => { 75 | var vm = initIpInput({ 76 | ip: '127.0.0.1', 77 | onChange(ip) { 78 | vm.ip = ip; 79 | } 80 | }); 81 | var segments = vm.$refs['ip-input'].segments; 82 | segments.splice(0, 1, ''); 83 | segments.splice(1, 1, ''); 84 | segments.splice(2, 1, ''); 85 | segments.splice(3, 1, ''); 86 | Vue.nextTick(() => { 87 | expect(vm.ip).toBe(''); 88 | done(); 89 | }); 90 | }); 91 | it('should call onChange if the input is changed', done => { 92 | var onChangeCalled = false; 93 | var vm = initIpInput({ 94 | ip: '127.0.0.1', 95 | onChange: () => { 96 | onChangeCalled = true; 97 | } 98 | }); 99 | var segments = vm.$refs['ip-input'].segments; 100 | segments.splice(0, 1, ''); 101 | Vue.nextTick(() => { 102 | expect(onChangeCalled).toBe(true); 103 | done(); 104 | }); 105 | }); 106 | it('should call onBlur if the input is blur', done => { 107 | var onBlurCalled = false; 108 | var vm = initIpInput({ 109 | ip: '127.0.0.1', 110 | onBlur: () => { 111 | onBlurCalled = true; 112 | } 113 | }); 114 | var input = vm.$refs['ip-input'].$el.querySelector('input'); 115 | input.focus(); 116 | input.blur(); 117 | Vue.nextTick(() => { 118 | setTimeout(() => { 119 | expect(onBlurCalled).toBe(true); 120 | done(); 121 | }, 100); 122 | }); 123 | }); 124 | it('should update the ip if keydown(Number 2)', done => { 125 | var vm = initIpInput({ 126 | ip: '127.0.0.1', 127 | onChange(ip) { 128 | vm.ip = ip; 129 | } 130 | }); 131 | var ipInput = vm.$refs['ip-input']; 132 | var input = ipInput.$el.querySelectorAll('input')[1]; 133 | input.value = '2'; 134 | trigger(input, 'input'); 135 | Vue.nextTick(() => { 136 | expect(vm.ip).toBe('127.2.0.1'); 137 | done(); 138 | }); 139 | }); 140 | it('should prevent the event if incorrect keydown(Alphabat a)', done => { 141 | var vm = initIpInput({ 142 | ip: '0.0.0.1' 143 | }); 144 | var ipInput = vm.$refs['ip-input']; 145 | var input = ipInput.$el.querySelector('input'); 146 | input.value = 'a'; 147 | trigger(input, 'input'); 148 | Vue.nextTick(() => { 149 | expect(vm.ip).toBe('0.0.0.1'); 150 | done(); 151 | }); 152 | }); 153 | it('should set to 255 if the input is over 255', done => { 154 | var vm = initIpInput({ 155 | ip: '0.0.0.1', 156 | onChange(ip) { 157 | vm.ip = ip; 158 | } 159 | }); 160 | var ipInput = vm.$refs['ip-input']; 161 | var input = ipInput.$el.querySelector('input'); 162 | input.value = '256'; 163 | trigger(input, 'input'); 164 | Vue.nextTick(() => { 165 | expect(vm.ip).toBe('255.0.0.1'); 166 | done(); 167 | }); 168 | }); 169 | it('should set to empty if the input is 0', done => { 170 | var vm = initIpInput({ 171 | ip: '0.0.0.1', 172 | onChange(ip) { 173 | vm.ip = ip; 174 | } 175 | }); 176 | var ipInput = vm.$refs['ip-input']; 177 | var input = ipInput.$el.querySelector('input'); 178 | input.value = '0'; 179 | trigger(input, 'input'); 180 | Vue.nextTick(() => { 181 | expect(vm.ip).toBe('0.0.0.1'); 182 | done(); 183 | }); 184 | }); 185 | it('should move the cursor to previous' + 186 | 'input if left arrow is pressed', done => { 187 | var vm = initIpInput({ 188 | ip: '0..0.1' 189 | }); 190 | var ipInput = vm.$refs['ip-input']; 191 | var input = ipInput.$el.querySelectorAll('input')[1]; 192 | trigger(input, 'keydown', e => { 193 | e.keyCode = 37; 194 | }); 195 | Vue.nextTick(() => { 196 | expect(document.activeElement) 197 | .toBe(ipInput.$el.querySelector('input')); 198 | done(); 199 | }); 200 | }); 201 | it('should move the cursor to next' + 202 | 'input if right arrow is pressed', done => { 203 | var vm = initIpInput({ 204 | ip: '...' 205 | }); 206 | var ipInput = vm.$refs['ip-input']; 207 | var input = ipInput.$el.querySelectorAll('input')[0]; 208 | trigger(input, 'keydown', e => { 209 | e.keyCode = 39; 210 | }); 211 | Vue.nextTick(() => { 212 | expect(document.activeElement) 213 | .toBe(ipInput.$el.querySelectorAll('input')[1]); 214 | done(); 215 | }); 216 | }); 217 | }); 218 | -------------------------------------------------------------------------------- /webpack.base.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file webpack base config file 3 | */ 4 | 5 | module.exports = { 6 | module: { 7 | loaders: [{ 8 | test: /\.js$/, 9 | loader: 'babel-loader', 10 | exclude: /node_modules/ 11 | }, { 12 | test: /\.vue$/, 13 | loader: 'vue-loader' 14 | }] 15 | }, 16 | resolve: { 17 | alias: { 18 | vue: 'vue/dist/vue.js' 19 | } 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /webpack.dev.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file webpack dev config file 3 | */ 4 | var config = require('./webpack.base.config'); 5 | var path = require('path'); 6 | 7 | config.entry = './index.js'; 8 | config.output = { 9 | path: path.resolve('build') + '/', 10 | publicPath: 'build', 11 | filename: 'build.js' 12 | }; 13 | 14 | module.exports = config; 15 | -------------------------------------------------------------------------------- /webpack.prod.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file webpack prod config file 3 | */ 4 | var webpack = require('webpack'); 5 | var config = require('./webpack.base.config'); 6 | var path = require('path'); 7 | 8 | config.entry = './src/index.js'; 9 | config.output = { 10 | path: path.resolve('dist') + '/', 11 | filename: 'vue-ip-input.min.js', 12 | library: 'VueIpInput', 13 | libraryTarget: 'umd' 14 | }; 15 | 16 | config.plugins = (config.plugins || []).concat([ 17 | new webpack.optimize.UglifyJsPlugin({ 18 | sourceMap: false, 19 | compress: { 20 | warnings: false 21 | } 22 | }) 23 | ]); 24 | module.exports = config; 25 | --------------------------------------------------------------------------------