├── .babelrc ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── dev ├── App.vue ├── index.js └── main.js ├── dist ├── static │ └── icons.svg └── vue-focus-keyboard.js ├── index.html ├── package.json ├── src ├── FocusKeyboard.vue └── layout.js ├── static └── icons.svg ├── utils.js ├── webpack.build.conf.js ├── webpack.demo.js └── webpack.dev.conf.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-0"], 3 | "plugins": ["transform-runtime"], 4 | "comments": false 5 | } 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | demo/ 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | package-lock.json* -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Sinan Mutlu 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. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | BUILD_DEMO = npm run demo 2 | DELETEFILES = rm -rf /tmp/demo 3 | COPYBUILD = cp -a demo /tmp/demo 4 | CHECKOUT = git checkout gh-pages 5 | PASTEBUILD = cp -a /tmp/demo/ . 6 | 7 | .PHONY: demo 8 | demo: 9 | $(BUILD_DEMO); $(DELETEFILES); $(COPYBUILD); $(CHECKOUT); $(PASTEBUILD); 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vue Focus Keyboard 2 | 3 | [](https://www.npmjs.com/package/vue-focus-keyboard) 4 | [](https://www.npmjs.com/package/vue-focus-keyboard) 5 | 6 | > A keyboard component for Vue. Start to write immediately. No input element definition. Plug and play!- [Demo](https://sinanmtl.github.io/vue-focus-keyboard/) 7 | 8 | ## Installation and usage 9 | 10 | Install the Vue Focus Keyboard component for your project 11 | 12 | ```bash 13 | npm install vue-focus-keyboard --save 14 | ``` 15 | 16 | Then, add component into your app 17 | 18 | ```javascript 19 | import FocusKeyboard from 'vue-focus-keyboard'; 20 | 21 | Vue.use(FocusKeyboard) 22 | ``` 23 | 24 | Use HTML template 25 | 26 | ```html 27 | 28 | ``` 29 | 30 | ## Props 31 | ### `theme` 32 | 33 | There are 2 theme in Vue Focus Keyboard. They are `dark` and `light` (default: `dark`). You can use one of them with `theme` prop. 34 | 35 | ```html 36 | 37 | ``` 38 | 39 | ### `keyboardWidth` 40 | 41 | Also, you can set keyboard's width. 42 | 43 | ```html 44 | 45 | ``` 46 | 47 | ### `keyboard` 48 | 49 | You don't like current keyboards? Well, set your custom keyboard layout. You can view default layouts in `src/layout.js` file 50 | 51 | ```javascript 52 | export default { 53 | data () { 54 | return { 55 | customLayout: { 56 | default: [ 57 | // Normal layout 58 | ], 59 | shifted: [ 60 | // Layout when press shift button 61 | ], 62 | capsed: [ 63 | // Layout when press capslock button 64 | ], 65 | alted: [ 66 | // Layout when press alt button 67 | ], 68 | shifted_capsed: [ 69 | // Layout when press shift + capslock button 70 | ], 71 | shifted_alted: [ 72 | // Layout when press shift + alt button 73 | ] 74 | }, 75 | } 76 | } 77 | } 78 | ``` 79 | 80 | Then, set your keyboard. 81 | ```html 82 | 83 | ``` 84 | 85 | ## License 86 | 87 | MIT. 88 | -------------------------------------------------------------------------------- /dev/App.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Vue Focus Keyboard Component 5 | 6 | 7 | 8 | 9 | 10 | Keyboard Type 11 | 12 | {{ keyboardType }} 13 | 14 | 15 | 16 | 17 | Theme 18 | 19 | Dark 20 | Light 21 | 22 | 23 | 24 | 25 | 26 | 27 | Standard Input 28 | 29 | 30 | 31 | Textarea 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 62 | 63 | 180 | 181 | -------------------------------------------------------------------------------- /dev/index.js: -------------------------------------------------------------------------------- 1 | let FocusKeyboard = require('../src/FocusKeyboard.vue') 2 | 3 | module.exports = { 4 | component: FocusKeyboard, 5 | layouts: require('../src/layout'), 6 | install(Vue) { 7 | Vue.component(FocusKeyboard.default.name, FocusKeyboard.default) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /dev/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from '@/App' 3 | 4 | export default new Vue({ 5 | el: '#app', 6 | template: '', 7 | components: { App } 8 | }) 9 | -------------------------------------------------------------------------------- /dist/static/icons.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /dist/vue-focus-keyboard.js: -------------------------------------------------------------------------------- 1 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define("VueFocusKeyboard",[],e):"object"==typeof exports?exports.VueFocusKeyboard=e():t.VueFocusKeyboard=e()}(window,function(){return function(t){var e={};function n(r){if(e[r])return e[r].exports;var o=e[r]={i:r,l:!1,exports:{}};return t[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=t,n.c=e,n.d=function(t,e,r){n.o(t,e)||Object.defineProperty(t,e,{configurable:!1,enumerable:!0,get:r})},n.r=function(t){Object.defineProperty(t,"__esModule",{value:!0})},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="/dist/",n(n.s=80)}([function(t,e){var n=t.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=n)},function(t,e){var n={}.hasOwnProperty;t.exports=function(t,e){return n.call(t,e)}},function(t,e,n){var r=n(23)("wks"),o=n(10),i=n(0).Symbol,a="function"==typeof i;(t.exports=function(t){return r[t]||(r[t]=a&&i[t]||(a?i:o)("Symbol."+t))}).store=r},function(t,e,n){var r=n(37),o=n(26);t.exports=function(t){return r(o(t))}},function(t,e,n){t.exports=!n(7)(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},function(t,e,n){var r=n(13),o=n(40),i=n(27),a=Object.defineProperty;e.f=n(4)?Object.defineProperty:function(t,e,n){if(r(t),e=i(e,!0),r(n),o)try{return a(t,e,n)}catch(t){}if("get"in n||"set"in n)throw TypeError("Accessors not supported!");return"value"in n&&(t[e]=n.value),t}},function(t,e,n){var r=n(5),o=n(12);t.exports=n(4)?function(t,e,n){return r.f(t,e,o(1,n))}:function(t,e,n){return t[e]=n,t}},function(t,e){t.exports=function(t){try{return!!t()}catch(t){return!0}}},function(t,e){t.exports=function(t){return"object"==typeof t?null!==t:"function"==typeof t}},function(t,e){e.f={}.propertyIsEnumerable},function(t,e){var n=0,r=Math.random();t.exports=function(t){return"Symbol(".concat(void 0===t?"":t,")_",(++n+r).toString(36))}},function(t,e,n){var r=n(38),o=n(22);t.exports=Object.keys||function(t){return r(t,o)}},function(t,e){t.exports=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}}},function(t,e,n){var r=n(8);t.exports=function(t){if(!r(t))throw TypeError(t+" is not an object!");return t}},function(t,e){var n=t.exports={version:"2.5.4"};"number"==typeof __e&&(__e=n)},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=a(n(75)),o=a(n(66)),i=a(n(30));function a(t){return t&&t.__esModule?t:{default:t}}var s,u,c,f,l=new Event("keydown"),p=new Event("keyup"),d=new Event("keypress"),h=new Event("input"),v=!1;e.default={name:"FocusKeyboard",props:{keyboardWidth:{type:[String,Number],default:"100%"},theme:{type:String,default:""},keyboard:{type:[Object,String],default:function(){return"us_international"}}},data:function(){return{show:!1,shift:!1,capslock:!1,ctrl:!1,alt:!1,input:null}},computed:{layout:function(){return"object"===(0,o.default)(this.keyboard)&&this.keyboard.hasOwnProperty("default")?this.keyboard:"string"==typeof this.keyboard&&i.default.hasOwnProperty(this.keyboard)?i.default[this.keyboard]:i.default[i.default.default]},buttons:function(){var t=this,e={},n=function(n){var r=t.layout[n];"_metaKeys"!==n&&(e[n]=[],r.forEach(function(t,r){e[n].push(t.split(" "))}))};for(var r in this.layout)n(r);return e},currentButtons:function(){var t="default";return this.shift?(t="shifted",this.capslock?t="shifted_capsed":this.alt&&(t="shifted_alted")):this.capslock?t="capsed":this.alt&&(t="alted"),this.buttons[t]}},methods:{getIconByMetaKey:function(t){if(["{backspace}","{shiftl}","{shiftr}","{enter}","{capslock}","{tab}"].indexOf(t)>-1){var e=t.replace(/({|})/g,"");return/(shift(l|r))/.test(e)?"shift":e}return null},getClass:function(t){return this.capslock&&"{capslock}"===t||this.ctrl&&/ctrl(l|r)/g.test(t)||this.alt&&/alt(gr)?/g.test(t)||this.shift&&/shift(l|r)?/g.test(t)?"active":""},getStyle:function(t){var e=t.replace(/({|})/g,""),n=i.default._metaKeys;return this.layout.hasOwnProperty("_metaKeys")&&(n=(0,r.default)({},n,this.layout._metaKeys)),n.hasOwnProperty(e)?{flex:n[e].width}:{}},open:function(){this.show=!0},close:function(){this.show=!1,this.input=null,clearInterval(v),v=!1},moveCursor:function(t,e){this.input&&(this.input.selectionStart=t||0,this.input.selectionEnd=e||t||0)},isInput:function(t){var e=t.getAttribute("contenteditable");return["INPUT","TEXTAREA"].indexOf(t.nodeName)>-1||!!e},isSupportedType:function(t){var e=t.getAttribute("contenteditable"),n="TEXTAREA"===t.nodeName;return["text","password","search","tel","url","email","number"].indexOf(t.type)>-1||!!e||n},backspace:function(t){var e=(this.input.value||this.input.innerHTML).split("");return e.splice(t.start,t.end-t.start),e.join("")},setMetaKey:function(t){switch(t){case"{capslock}":this.capslock=!this.capslock;break;case"{shiftl}":case"{shiftr}":this.shift=!this.shift;break;case"{ctrll}":case"{ctrlr}":this.ctrl=!this.ctrl;break;case"{alt}":case"{altgr}":this.alt=!this.alt}},setKey:function(t,e){var n=(this.input.value||this.input.innerHTML).split("");return n.splice(t.start,0,e),n.join("")},ctrlCombination:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";if("string"!=typeof t)return!1;switch(t.toLowerCase()){case"a":this.input.select();break;case"z":document.execCommand("undo",!1,null);break;case"y":document.execCommand("redo",!1,null);break;case"c":document.execCommand("copy",!1,null);break;case"x":document.execCommand("cut",!1,null)}this.ctrl=!1},setValue:function(t,e){var n="";switch(t){case"{backspace}":e.start===e.end&&(e.start-=1),n=this.backspace(e);break;case"{space}":n=this.setKey(e," ");break;case"{tab}":n=this.setKey(e,"\t");break;case"{enter}":"TEXTAREA"===this.input.nodeName?n=this.setKey(e,"\n"):this.input.form&&this.input.form.submit();break;default:n=this.setKey(e,t)}if("{enter}"===t&&"TEXTAREA"!==this.input.nodeName)return!1;!!this.input.getAttribute("contenteditable")?this.input.innerHTML=n:this.input.value=n},buttonPress:function(t){if(this.input){var e=this.getCursor();["{shiftl}","{shiftr}","{capslock}","{ctrll}","{ctrlr}","{alt}","{altgr}"].indexOf(t)>-1?this.setMetaKey(t):this.ctrl?this.ctrlCombination(t):(this.shift=!1,this.ctrl=!1,this.alt=!1,this.setValue(t,e),this.triggerEvents(),this.input.focus(),"{backspace}"===t?this.moveCursor(e.start):this.moveCursor(e.start+1,e.end+1))}},triggerEvents:function(){this.input.dispatchEvent(h),this.input.dispatchEvent(l),this.input.dispatchEvent(d),this.input.dispatchEvent(p)},pressAndHold:(s=function(t,e){var n=this;"{backspace}"===e&&("mouseup"===t.type||"touchend"===t.type?(clearInterval(v),v=!1):v||(v=setInterval(function(){n.buttonPress(e)},30)))},u=150,f=void 0,function(){var t=this,e=arguments,n=c&&!f;clearTimeout(f),f=setTimeout(function(){f=null,c||s.apply(t,e)},u),n&&s.apply(t,e)}),getCursor:function(){if(this.isSupportedType(this.input)){var t={start:this.input.selectionStart||0,end:this.input.selectionEnd||0};return t.end0?r:n)(t)}},function(t,e){t.exports=function(t){if(void 0==t)throw TypeError("Can't call method on "+t);return t}},function(t,e,n){var r=n(8);t.exports=function(t,e){if(!r(t))return t;var n,o;if(e&&"function"==typeof(n=t.toString)&&!r(o=n.call(t)))return o;if("function"==typeof(n=t.valueOf)&&!r(o=n.call(t)))return o;if(!e&&"function"==typeof(n=t.toString)&&!r(o=n.call(t)))return o;throw TypeError("Can't convert object to primitive value")}},function(t,e,n){var r=n(0),o=n(14),i=n(72),a=n(6),s=n(1),u=function(t,e,n){var c,f,l,p=t&u.F,d=t&u.G,h=t&u.S,v=t&u.P,y=t&u.B,b=t&u.W,m=d?o:o[e]||(o[e]={}),g=m.prototype,x=d?r:h?r[e]:(r[e]||{}).prototype;for(c in d&&(n=e),n)(f=!p&&x&&void 0!==x[c])&&s(m,c)||(l=f?x[c]:n[c],m[c]=d&&"function"!=typeof x[c]?n[c]:y&&f?i(l,r):b&&x[c]==l?function(t){var e=function(e,n,r){if(this instanceof t){switch(arguments.length){case 0:return new t;case 1:return new t(e);case 2:return new t(e,n)}return new t(e,n,r)}return t.apply(this,arguments)};return e.prototype=t.prototype,e}(l):v&&"function"==typeof l?i(Function.call,l):l,v&&((m.virtual||(m.virtual={}))[c]=l,t&u.R&&g&&!g[c]&&a(g,c,l)))};u.F=1,u.G=2,u.S=4,u.P=8,u.B=16,u.W=32,u.U=64,u.R=128,t.exports=u},function(t,e,n){"use strict";n.d(e,"a",function(){return r}),n.d(e,"b",function(){return o});var r=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("transition",{attrs:{name:"slideUp"}},[t.show?n("div",{staticClass:"KeyboardComponent",class:t.theme,on:{click:function(e){t.input.focus()},contextmenu:function(t){t.preventDefault()}}},[n("div",{staticClass:"buttons",style:{width:t.keyboardWidth}},t._l(t.currentButtons,function(e,r){return n("div",{key:r,staticClass:"key-row"},t._l(e,function(e,r){return n("button",{key:r,class:t.getClass(e),style:t.getStyle(e),attrs:{type:"button"},on:{mousedown:function(n){t.pressAndHold(n,e)},mouseup:function(n){t.pressAndHold(n,e)},click:function(n){n.preventDefault(),t.buttonPress(e)},touchstart:function(n){t.pressAndHold(n,e)},touchend:function(n){t.pressAndHold(n,e)}}},[n("div",{staticClass:"text"},[t.getIconByMetaKey(e)?n("svg",{staticClass:"icon"},[n("use",{attrs:{"xlink:href":"static/icons.svg#icon-"+t.getIconByMetaKey(e)}})]):/(ctrl(l|r))/g.test(e)?[t._v("Ctrl")]:"{alt}"===e?[t._v("Alt")]:"{altgr}"===e?[t._v("Alt Gr")]:/(space|empty)/g.test(e)?[t._v(" ")]:[t._v(t._s(e))]],2)])}))}))]):t._e()])},o=[];r._withStripped=!0},function(t,e){t.exports={default:"us_international",_metaKeys:{backspace:{width:110},tab:{width:80},enter:{width:80},caps:{width:110},shiftl:{width:80},shiftr:{width:135},ctrll:{width:105},ctrlr:{width:105},alt:{width:80},altgr:{width:80},space:{width:435}},us_international:{default:["§ 1 2 3 4 5 6 7 8 9 0 - = {backspace}","{tab} q w e r t y u i o p [ ] {enter}","{capslock} a s d f g h j k l ; ' \\","{shiftl} ` z x c v b n m , . / {shiftr}","{ctrll} {alt} {space} {altgr} {ctrlr}"],shifted:["± ! @ # $ % ˆ & * ( ) _ + {backspace}","{tab} Q W E R T Y U I O P { } {enter}",'{capslock} A S D F G H J K L : " |',"{shiftl} ˜ Z X C V B N M < > ? {shiftr}","{ctrll} {alt} {space} {altgr} {ctrlr}"],capsed:["§ 1 2 3 4 5 6 7 8 9 0 - = {backspace}","{tab} Q W E R T Y U I O P [ ] {enter}","{capslock} A S D F G H J K L ; ' \\","{shiftl} ` Z X C V B N M , . / {shiftr}","{ctrll} {alt} {space} {altgr} {ctrlr}"],alted:["§ ¡ ™ £ ¢ ∞ § ¶ • ª º – ≠ {backspace}","{tab} œ ∑ ´ ® † ¥ ¨ ˆ ø π “ ‘ {enter}","{capslock} å ß ∂ ƒ © ˙ ∆ ˚ ¬ … æ «","{shiftl} ` Ω ≈ ç √ ∫ ˜ µ ≤ ≥ ÷ {shiftr}","{ctrll} {alt} {space} {altgr} {ctrlr}"],shifted_capsed:["± ! @ # $ % ˆ & * ( ) _ + {backspace}","{tab} q w e r t y u i o p { } {enter}",'{capslock} a s d f g h j k l : " |',"{shiftl} ˜ z x c v b n m < > ? {shiftr}","{ctrll} {alt} {space} {altgr} {ctrlr}"],shifted_alted:["± ⁄ € ‹ › fi fl ‡ ° · ‚ — ± {backspace}","{tab} Œ „ ´ ‰ ˇ Á ¨ i Ø ∏ ” ’ {enter}","{capslock} Å Í Î Ï ˝ Ó Ô Ò Ú Æ »","{shiftl} ` ¸ ˛ Ç ◊ ı ˜  ¯ ˘ ¿ {shiftr}","{ctrll} {alt} {space} {altgr} {ctrlr}"]},turkish_qwerty_pc:{_metaKeys:{backspace:{width:110},tab:{width:80},enter:{width:80},caps:{width:110},shiftl:{width:80},shiftr:{width:135},ctrll:{width:105},ctrlr:{width:105},alt:{width:80},altgr:{width:80},space:{width:435}},default:['" 1 2 3 4 5 6 7 8 9 0 * - {backspace}',"{tab} q w e r t y u ı o p ğ ü {enter}","{capslock} a s d f g h j k l ş i ,","{shiftl} < z x c v b n m ö ç . {shiftr}","{ctrll} {alt} {space} {altgr} {ctrlr}"],shifted:["é ! ' ^ + % & / ( ) = ? _ {backspace}","{tab} Q W E R T Y U I O P Ğ Ü {enter}","{capslock} A S D F G H J K L Ş İ ;","{shiftl} > Z X C V B N M Ö Ç : {shiftr}","{ctrll} {alt} {space} {altgr} {ctrlr}"],capsed:['" 1 2 3 4 5 6 7 8 9 0 * - {backspace}',"{tab} Q W E R T Y U I O P Ğ Ü {enter}","{capslock} A S D F G H J K L Ş İ ,","{shiftl} < Z X C V B N M Ö Ç . {shiftr}","{ctrll} {alt} {space} {altgr} {ctrlr}"],alted:["< > £ # $ ½ § { [ ] } \\ | {backspace}","{tab} @ ∑ € ® ₺ ¥ ü i ö π ¨ ~ {enter}","{capslock} æ ß ∂ ƒ ğ ^ ∆ ¨ ¬ ´ æ `","{shiftl} | ≈ ç √ ∫ ~ µ ≤ ≥ . {shiftr}","{ctrll} {alt} {space} {altgr} {ctrlr}"],shifted_capsed:["é ! ' ^ + % & / ( ) = ? _ {backspace}","{tab} q w e r t y u ı o p ğ ü {enter}","{capslock} a s d f g h j k l ş i ;","{shiftl} > z x c v b n m ö ç : {shiftr}","{ctrll} {alt} {space} {altgr} {ctrlr}"],shifted_alted:['" · € © ‚ ˚ ˙ ‡ ° Ø ø — ± {backspace}',"{tab} Œ „ ‰ Â Ê Á Ü ı Ö ∏ ” ’ {enter}","{capslock} Æ Ş Î Ï Ğ Ó Ô Ò Ú Æ »","{shiftl} Ÿ Û Ù Ç ◊ ß ˆ ˜ ¯ ˘ ¿ {shiftr}","{ctrll} {alt} {space} {altgr} {ctrlr}"]},numeric:{_metaKeys:{enter:{width:50}},default:["{backspace}","7 8 9","6 5 4","3 2 1","0 . {enter}"]}}},function(t,e,n){var r=n(38),o=n(22).concat("length","prototype");e.f=Object.getOwnPropertyNames||function(t){return r(t,o)}},function(t,e,n){var r=n(13),o=n(60),i=n(22),a=n(24)("IE_PROTO"),s=function(){},u=function(){var t,e=n(39)("iframe"),r=i.length;for(e.style.display="none",n(59).appendChild(e),e.src="javascript:",(t=e.contentWindow.document).open(),t.write(" 417 | 418 | 542 | -------------------------------------------------------------------------------- /src/layout.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | default: 'us_international', 3 | _metaKeys: { 4 | backspace: { width: 110 }, 5 | tab: { width: 80 }, 6 | enter: { width: 80 }, 7 | caps: { width: 110 }, 8 | shiftl: { width: 80 }, 9 | shiftr: { width: 135 }, 10 | ctrll: { width: 105 }, 11 | ctrlr: { width: 105 }, 12 | alt: { width: 80 }, 13 | altgr: { width: 80 }, 14 | space: { width: 435 } 15 | }, 16 | us_international: { 17 | default: [ 18 | '§ 1 2 3 4 5 6 7 8 9 0 - = {backspace}', 19 | '{tab} q w e r t y u i o p [ ] {enter}', 20 | `{capslock} a s d f g h j k l ; ' \\`, 21 | '{shiftl} ` z x c v b n m , . / {shiftr}', 22 | '{ctrll} {alt} {space} {altgr} {ctrlr}' 23 | ], 24 | shifted: [ 25 | '± ! @ # $ % ˆ & * ( ) _ + {backspace}', 26 | '{tab} Q W E R T Y U I O P { } {enter}', 27 | '{capslock} A S D F G H J K L : " |', 28 | '{shiftl} ˜ Z X C V B N M < > ? {shiftr}', 29 | '{ctrll} {alt} {space} {altgr} {ctrlr}' 30 | ], 31 | capsed: [ 32 | '§ 1 2 3 4 5 6 7 8 9 0 - = {backspace}', 33 | '{tab} Q W E R T Y U I O P [ ] {enter}', 34 | `{capslock} A S D F G H J K L ; ' \\`, 35 | '{shiftl} ` Z X C V B N M , . / {shiftr}', 36 | '{ctrll} {alt} {space} {altgr} {ctrlr}' 37 | ], 38 | alted: [ 39 | '§ ¡ ™ £ ¢ ∞ § ¶ • ª º – ≠ {backspace}', 40 | '{tab} œ ∑ ´ ® † ¥ ¨ ˆ ø π “ ‘ {enter}', 41 | `{capslock} å ß ∂ ƒ © ˙ ∆ ˚ ¬ … æ «`, 42 | '{shiftl} ` Ω ≈ ç √ ∫ ˜ µ ≤ ≥ ÷ {shiftr}', 43 | '{ctrll} {alt} {space} {altgr} {ctrlr}' 44 | ], 45 | shifted_capsed: [ 46 | '± ! @ # $ % ˆ & * ( ) _ + {backspace}', 47 | '{tab} q w e r t y u i o p { } {enter}', 48 | '{capslock} a s d f g h j k l : " |', 49 | '{shiftl} ˜ z x c v b n m < > ? {shiftr}', 50 | '{ctrll} {alt} {space} {altgr} {ctrlr}' 51 | ], 52 | shifted_alted: [ 53 | '± ⁄ € ‹ › fi fl ‡ ° · ‚ — ± {backspace}', 54 | '{tab} Œ „ ´ ‰ ˇ Á ¨ i Ø ∏ ” ’ {enter}', 55 | `{capslock} Å Í Î Ï ˝ Ó Ô Ò Ú Æ »`, 56 | '{shiftl} ` ¸ ˛ Ç ◊ ı ˜  ¯ ˘ ¿ {shiftr}', 57 | '{ctrll} {alt} {space} {altgr} {ctrlr}' 58 | ] 59 | }, 60 | turkish_qwerty_pc: { 61 | _metaKeys: { 62 | backspace: { width: 110 }, 63 | tab: { width: 80 }, 64 | enter: { width: 80 }, 65 | caps: { width: 110 }, 66 | shiftl: { width: 80 }, 67 | shiftr: { width: 135 }, 68 | ctrll: { width: 105 }, 69 | ctrlr: { width: 105 }, 70 | alt: { width: 80 }, 71 | altgr: { width: 80 }, 72 | space: { width: 435 } 73 | }, 74 | default: [ 75 | '" 1 2 3 4 5 6 7 8 9 0 * - {backspace}', 76 | '{tab} q w e r t y u ı o p ğ ü {enter}', 77 | '{capslock} a s d f g h j k l ş i ,', 78 | '{shiftl} < z x c v b n m ö ç . {shiftr}', 79 | '{ctrll} {alt} {space} {altgr} {ctrlr}' 80 | ], 81 | shifted: [ 82 | "é ! ' ^ + % & / ( ) = ? _ {backspace}", 83 | '{tab} Q W E R T Y U I O P Ğ Ü {enter}', 84 | '{capslock} A S D F G H J K L Ş İ ;', 85 | '{shiftl} > Z X C V B N M Ö Ç : {shiftr}', 86 | '{ctrll} {alt} {space} {altgr} {ctrlr}' 87 | ], 88 | capsed: [ 89 | '" 1 2 3 4 5 6 7 8 9 0 * - {backspace}', 90 | '{tab} Q W E R T Y U I O P Ğ Ü {enter}', 91 | '{capslock} A S D F G H J K L Ş İ ,', 92 | '{shiftl} < Z X C V B N M Ö Ç . {shiftr}', 93 | '{ctrll} {alt} {space} {altgr} {ctrlr}' 94 | ], 95 | alted: [ 96 | `< > £ # $ ½ § { [ ] } \\ | {backspace}`, 97 | '{tab} @ ∑ € ® ₺ ¥ ü i ö π ¨ ~ {enter}', 98 | '{capslock} æ ß ∂ ƒ ğ ^ ∆ ¨ ¬ ´ æ `', 99 | '{shiftl} | ≈ ç √ ∫ ~ µ ≤ ≥ . {shiftr}', 100 | '{ctrll} {alt} {space} {altgr} {ctrlr}' 101 | ], 102 | shifted_capsed: [ 103 | "é ! ' ^ + % & / ( ) = ? _ {backspace}", 104 | '{tab} q w e r t y u ı o p ğ ü {enter}', 105 | '{capslock} a s d f g h j k l ş i ;', 106 | '{shiftl} > z x c v b n m ö ç : {shiftr}', 107 | '{ctrll} {alt} {space} {altgr} {ctrlr}' 108 | ], 109 | shifted_alted: [ 110 | '" · € © ‚ ˚ ˙ ‡ ° Ø ø — ± {backspace}', 111 | '{tab} Œ „ ‰ Â Ê Á Ü ı Ö ∏ ” ’ {enter}', 112 | '{capslock} Æ Ş Î Ï Ğ Ó Ô Ò Ú Æ »', 113 | '{shiftl} Ÿ Û Ù Ç ◊ ß ˆ ˜ ¯ ˘ ¿ {shiftr}', 114 | '{ctrll} {alt} {space} {altgr} {ctrlr}' 115 | ] 116 | }, 117 | numeric: { 118 | _metaKeys: { 119 | enter: { width: 50 } 120 | }, 121 | default: [ 122 | '{backspace}', 123 | '7 8 9', 124 | '6 5 4', 125 | '3 2 1', 126 | '0 . {enter}' 127 | ] 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /static/icons.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /utils.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | 3 | exports.assetsPath = function(_path) { 4 | var assetsSubDirectory = 5 | process.env.NODE_ENV === 'production' 6 | ? config.build.assetsSubDirectory 7 | : config.dev.assetsSubDirectory 8 | return path.posix.join(assetsSubDirectory, _path) 9 | } 10 | -------------------------------------------------------------------------------- /webpack.build.conf.js: -------------------------------------------------------------------------------- 1 | var ora = require('ora') 2 | var path = require('path') 3 | var chalk = require('chalk') 4 | var webpack = require('webpack') 5 | var CopyWebpackPlugin = require('copy-webpack-plugin') 6 | var package = require('./package.json') 7 | 8 | var banner = ` 9 | /** 10 | * vue-focus-keyboard v${package.version} 11 | * @description ${package.description} 12 | * https://github.com/SinanMtl/vue-rate 13 | * Released under the MIT License. 14 | */ 15 | ` 16 | 17 | var config = { 18 | entry: './dev/index.js', 19 | output: { 20 | path: path.resolve(__dirname, './dist'), 21 | publicPath: '/dist/', 22 | filename: 'vue-focus-keyboard.js', 23 | library: 'VueFocusKeyboard', 24 | libraryTarget: 'umd', 25 | umdNamedDefine: true 26 | }, 27 | optimization: { 28 | minimize: true 29 | }, 30 | mode: 'production', 31 | devtool: false, 32 | plugins: [ 33 | new webpack.DefinePlugin({ 34 | 'process.env': { 35 | NODE_ENV: JSON.stringify('production') 36 | } 37 | }), 38 | new webpack.BannerPlugin({ banner, raw: true, entryOnly: true }), 39 | // copy custom static assets 40 | new CopyWebpackPlugin([ 41 | { 42 | from: path.resolve(__dirname, 'static'), 43 | to: path.resolve(__dirname, 'dist/static'), 44 | ignore: ['.*'] 45 | } 46 | ]) 47 | ], 48 | module: { 49 | rules: [ 50 | { 51 | test: /\.js$/, 52 | loader: 'babel-loader', 53 | include: [path.resolve(__dirname, './dev')] 54 | }, 55 | { 56 | test: /\.vue$/, 57 | loader: 'vue-loader' 58 | }, 59 | { 60 | test: /\.svg$/, 61 | loader: 'url-loader', 62 | options: { 63 | limit: 10000, 64 | name: path.posix.join('static/', '[name].[hash:7].[ext]') 65 | } 66 | } 67 | ] 68 | }, 69 | resolve: { 70 | alias: { 71 | vue$: 'vue/dist/vue.common.js' 72 | } 73 | } 74 | } 75 | 76 | var spinner = ora('building for production...') 77 | spinner.start() 78 | 79 | webpack(config, function(err, stats) { 80 | spinner.stop() 81 | if (err) throw err 82 | process.stdout.write( 83 | stats.toString({ 84 | colors: true, 85 | modules: false, 86 | children: false, 87 | chunks: false, 88 | chunkModules: false 89 | }) + '\n\n' 90 | ) 91 | 92 | console.log(chalk.cyan(' Build complete.\n')) 93 | console.log( 94 | chalk.yellow( 95 | ' Tip: built files are meant to be served over an HTTP server.\n' + 96 | " Opening index.html over file:// won't work.\n" 97 | ) 98 | ) 99 | }) 100 | -------------------------------------------------------------------------------- /webpack.demo.js: -------------------------------------------------------------------------------- 1 | var ora = require('ora') 2 | var path = require('path') 3 | var chalk = require('chalk') 4 | var webpack = require('webpack') 5 | var HtmlWebpackPlugin = require('html-webpack-plugin') 6 | var CopyWebpackPlugin = require('copy-webpack-plugin') 7 | var version = require('./package.json').version 8 | 9 | function resolve(dir) { 10 | return path.join(__dirname, '..', dir) 11 | } 12 | 13 | var config = { 14 | entry: { 15 | app: './dev/main.js' 16 | }, 17 | output: { 18 | path: path.resolve(__dirname, './demo'), 19 | filename: '[name].js', 20 | publicPath: './' 21 | }, 22 | optimization: { 23 | minimize: true 24 | }, 25 | mode: 'production', 26 | devtool: false, 27 | plugins: [ 28 | new webpack.DefinePlugin({ 29 | 'process.env': { 30 | NODE_ENV: JSON.stringify('production') 31 | } 32 | }), 33 | // copy custom static assets 34 | new CopyWebpackPlugin([ 35 | { 36 | from: path.resolve(__dirname, 'static'), 37 | to: path.resolve(__dirname, 'demo/static'), 38 | ignore: ['.*'] 39 | } 40 | ]), 41 | new HtmlWebpackPlugin({ 42 | filename: './index.html', 43 | template: 'index.html', 44 | inject: true, 45 | minify: { 46 | removeComments: true, 47 | collapseWhitespace: true, 48 | removeAttributeQuotes: true 49 | // more options: 50 | // https://github.com/kangax/html-minifier#options-quick-reference 51 | }, 52 | // necessary to consistently work with multiple chunks via CommonsChunkPlugin 53 | chunksSortMode: 'dependency' 54 | }) 55 | ], 56 | module: { 57 | rules: [ 58 | { 59 | test: /\.js$/, 60 | loader: 'babel-loader', 61 | include: [path.resolve(__dirname, './dev')] 62 | }, 63 | { 64 | test: /\.vue$/, 65 | loader: 'vue-loader' 66 | }, 67 | { 68 | test: /\.svg$/, 69 | loader: 'url-loader', 70 | options: { 71 | limit: 10000, 72 | name: path.posix.join('static/', '[name].[hash:7].[ext]') 73 | } 74 | } 75 | ] 76 | }, 77 | resolve: { 78 | extensions: ['.js', '.vue', '.json'], 79 | alias: { 80 | vue$: 'vue/dist/vue.common.js', 81 | '@': path.resolve(__dirname, './dev') 82 | } 83 | } 84 | } 85 | 86 | var spinner = ora('building for production...') 87 | spinner.start() 88 | 89 | webpack(config, function(err, stats) { 90 | spinner.stop() 91 | if (err) throw err 92 | process.stdout.write( 93 | stats.toString({ 94 | colors: true, 95 | modules: false, 96 | children: false, 97 | chunks: false, 98 | chunkModules: false 99 | }) + '\n\n' 100 | ) 101 | 102 | console.log(chalk.cyan(' Build complete.\n')) 103 | console.log( 104 | chalk.yellow( 105 | ' Tip: built files are meant to be served over an HTTP server.\n' + 106 | " Opening index.html over file:// won't work.\n" 107 | ) 108 | ) 109 | }) 110 | -------------------------------------------------------------------------------- /webpack.dev.conf.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var webpack = require('webpack') 3 | var HtmlWebpackPlugin = require('html-webpack-plugin') 4 | var FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') 5 | 6 | function resolve(dir) { 7 | return path.join(__dirname, './', dir) 8 | } 9 | const HOST = process.env.HOST 10 | const PORT = process.env.PORT && Number(process.env.PORT) 11 | 12 | module.exports = { 13 | devtool: '#cheap-module-eval-source-map', 14 | entry: { 15 | app: path.resolve(__dirname, './dev/main.js') 16 | }, 17 | output: { 18 | path: path.resolve(__dirname, './dist'), 19 | filename: '[name].js', 20 | publicPath: '/' 21 | }, 22 | resolve: { 23 | extensions: ['.js', '.vue', '.json'], 24 | alias: { 25 | vue$: 'vue/dist/vue.esm.js', 26 | '@': path.resolve(__dirname, './dev') 27 | } 28 | }, 29 | module: { 30 | rules: [ 31 | { 32 | test: /\.js$/, 33 | loader: 'babel-loader', 34 | include: [ 35 | path.resolve(__dirname, './dev'), 36 | path.resolve(__dirname, './test') 37 | ] 38 | }, 39 | { 40 | test: /\.vue$/, 41 | loader: 'vue-loader' 42 | }, 43 | { 44 | test: /\.svg$/, 45 | loader: 'url-loader', 46 | options: { 47 | limit: 10000 48 | } 49 | } 50 | ] 51 | }, 52 | devServer: { 53 | clientLogLevel: 'warning', 54 | historyApiFallback: true, 55 | hot: true, 56 | compress: true, 57 | host: HOST || '0.0.0.0', 58 | port: PORT || 9000, 59 | open: true, 60 | overlay: { warnings: false, errors: true }, 61 | publicPath: '/', 62 | proxy: {}, 63 | quiet: true, // necessary for FriendlyErrorsPlugin 64 | watchContentBase: true, 65 | watchOptions: { 66 | poll: false, 67 | ignored: /node_modules/ 68 | } 69 | }, 70 | mode: 'development', 71 | plugins: [ 72 | new webpack.DefinePlugin({ 73 | 'process.env': { 74 | NODE_ENV: JSON.stringify('development') 75 | } 76 | }), 77 | new webpack.ProvidePlugin({ 78 | Vue: 'vue/dist/vue' 79 | }), 80 | new webpack.HotModuleReplacementPlugin(), 81 | new webpack.NamedModulesPlugin(), 82 | new HtmlWebpackPlugin({ 83 | filename: 'index.html', 84 | template: 'index.html', 85 | inject: true 86 | }), 87 | new FriendlyErrorsPlugin() 88 | ] 89 | } 90 | --------------------------------------------------------------------------------