├── .gitignore ├── Gruntfile.js ├── README.md ├── bower.json ├── css └── angular-on-screen-keyboard.less ├── demo1.html ├── demo2.html ├── demo3.html ├── dist ├── angular-on-screen-keyboard.min.css └── angular-on-screen-keyboard.min.js ├── index.html ├── js └── angular-on-screen-keyboard.js ├── package.json └── templates └── angular-on-screen-keyboard.html /.gitignore: -------------------------------------------------------------------------------- 1 | /jshint-report.html 2 | 3 | node_modules 4 | .idea -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function (grunt) { 2 | 3 | grunt.loadNpmTasks('grunt-contrib-jshint'); 4 | grunt.loadNpmTasks("grunt-contrib-uglify"); 5 | grunt.loadNpmTasks("grunt-contrib-less"); 6 | grunt.loadNpmTasks("grunt-contrib-copy"); 7 | grunt.loadNpmTasks("grunt-contrib-watch"); 8 | grunt.loadNpmTasks("grunt-contrib-clean"); 9 | grunt.loadNpmTasks("grunt-contrib-concat"); 10 | grunt.loadNpmTasks('grunt-browser-sync'); 11 | grunt.loadNpmTasks("grunt-notify"); 12 | grunt.loadNpmTasks('grunt-ng-annotate'); 13 | grunt.loadNpmTasks('grunt-angular-templates'); 14 | 15 | require('time-grunt')(grunt); 16 | 17 | grunt.initConfig({ 18 | pkg: grunt.file.readJSON("package.json"), 19 | jshint: { 20 | files: ['Gruntfile.js', 'app/src/js/**/*.js'], 21 | options: { 22 | undef: false, 23 | unused: false, 24 | nonstandard: true, 25 | sub: true, 26 | reporter: require('jshint-html-reporter'), 27 | reporterOutput: 'jshint-report.html', 28 | force: false, 29 | } 30 | }, 31 | ngAnnotate: { 32 | options: { 33 | singleQuotes: true 34 | }, 35 | app: { 36 | files: { 37 | 'dist/angular-on-screen-keyboard.min.js': ['js/angular-on-screen-keyboard.js'] 38 | } 39 | } 40 | }, 41 | uglify: { 42 | main: { 43 | src: [ 44 | "dist/angular-on-screen-keyboard.min.js" 45 | ], 46 | dest: "dist/angular-on-screen-keyboard.min.js" 47 | } 48 | }, 49 | ngtemplates: { 50 | app: { 51 | src: 'templates/*.html', 52 | dest: 'dist/templates.js', 53 | cwd: '', 54 | options: { 55 | module: 'onScreenKeyboard', 56 | prefix: '/', 57 | htmlmin: { 58 | collapseBooleanAttributes: true, 59 | collapseWhitespace: true, 60 | removeAttributeQuotes: true, 61 | removeComments: true, // Only if you don't use comment directives! 62 | removeEmptyAttributes: true, 63 | removeRedundantAttributes: true, 64 | removeScriptTypeAttributes: true, 65 | removeStyleLinkTypeAttributes: true 66 | } 67 | } 68 | } 69 | }, 70 | less: { 71 | options: { 72 | ieCompat: true, 73 | report: 'min' 74 | }, 75 | production: { 76 | options: { 77 | compress: true 78 | }, 79 | files: { 80 | "dist/angular-on-screen-keyboard.min.css": [ 81 | "css/*.less" 82 | ] 83 | } 84 | } 85 | }, 86 | concat: { 87 | app: { 88 | options: { 89 | separator: ';\n', 90 | sourceMap: false 91 | }, 92 | files: [ 93 | {'dist/angular-on-screen-keyboard.min.js': ['dist/*.js']} 94 | ] 95 | } 96 | }, 97 | clean: ['dist/templates.js'], 98 | watch: { 99 | files: [ 100 | "demo1.html", 101 | "demo2.html", 102 | "demo3.html", 103 | "Gruntfile.js", 104 | "css/*.less", 105 | "js/*.js", 106 | "templates/*.html" 107 | ], 108 | tasks: [ 109 | 'pack' 110 | ], 111 | options: { 112 | spawn: true, 113 | interrupt: true, 114 | livereload: true 115 | } 116 | }, 117 | browserSync: { 118 | app: { 119 | bsFiles: { 120 | src : [ 121 | 'dist/*.*' 122 | ] 123 | }, 124 | options: { 125 | watchTask: true, 126 | server: './' 127 | } 128 | } 129 | } 130 | }); 131 | grunt.registerTask('pack', [ 132 | 'jshint', 133 | 'less', 134 | 'ngAnnotate', 135 | 'uglify', 136 | 'ngtemplates', 137 | 'concat', 138 | 'clean', 139 | ]); 140 | 141 | grunt.registerTask('default', [ 142 | 'pack', 143 | 'browserSync', 144 | 'watch' 145 | ]); 146 | }; 147 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Angular-On-Screen-Keyboard 2 | =================== 3 | 4 | An on screen keyboard that's always present. Fully customizable layout and easy to use API. 5 | Perfect for applications on touch devices as an alternative to the default on screen keyboard. 6 | 7 | Web site: [https://github.com/GreenfieldVentures/angular-on-screen-keyboard](https://github.com/GreenfieldVentures/angular-on-screen-keyboard) 8 | 9 | License: MIT License 10 | 11 | Simple demos: [http://rawgit.com/GreenfieldVentures/angular-on-screen-keyboard/master/index.html](http://rawgit.com/GreenfieldVentures/angular-on-screen-keyboard/master/index.html) 12 | 13 | 14 | Installation 15 | ------------ 16 | 17 | bower install angular-on-screen-keyboard 18 | 19 | or 20 | 21 | npm install angular-on-screen-keyboard 22 | 23 | 24 | Usage 25 | ----- 26 | 27 | 1. Include the minified js and css files in your markup 28 | 29 | 2. Register the module in your angular app 30 | ```js 31 | angular.module('myAngularApp', ['onScreenKeyboard']); 32 | ``` 33 | 34 | 3. Add the ```html ``` tag in your markup 35 | 36 | API (optional) 37 | -------------- 38 | * `rows` - The plugin comes with a predefined keyboard. It can easily be overridden by supplying an arrays of keys on the rows attribute. 39 | * A regular key is specified as the character it represents, i.e. 'i', 'p', '5', etc. 40 | * Keys can also be specified in more detail by passing an object instead of a char. Examples can be seen in [Demo 2] and [Demo 3] 41 | * `colspan` - Specifies how wide, in number of keys, the key will be. Default: 1 42 | * `text` - The text on the key and thereby the value the pressed key will send to the currently selected input field 43 | * `upperCase / lowerCase` - As an alternative to the `text` property, you can manually specify the characters to use for upper & lower case. For regular letters in the array passed as single characters upper and lower casing is handled automatically 44 | * `type` - Specifies the type of key. The value will be set as an additional class name on the key's element, though some reserved names will give the key extra powers such as 'margin', 'erase' and 'shift 45 | * To specify an empty space instead of a key, for layout purposes, by adding an object with the type set to margin: ``` {type: 'margin'}``` in the array 46 | * `uppercase-all-words` - By specifying this attribute the keyboard will be set to uppercase before entering the first letter of every new word 47 | 48 | [Demo 2]: http://rawgit.com/GreenfieldVentures/angular-on-screen-keyboard/master/demo2.html 49 | [Demo 3]: http://rawgit.com/GreenfieldVentures/angular-on-screen-keyboard/master/demo3.html 50 | 51 | DEPENDENCIES 52 | ----------- 53 | * ngSanitize 54 | 55 | POLYFILLS 56 | --------- 57 | FireFox seems to lack proper support for focusin. This polyfill might help 58 | https://gist.github.com/nuxodin/9250e56a3ce6c0446efa 59 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-on-screen-keyboard", 3 | "version": "0.9.7", 4 | "main": ["dist/angular-on-screen-keyboard.min.js", "dist/angular-on-screen-keyboard.min.css"], 5 | "dependencies": { 6 | "angular": ">= 1.2.0", 7 | "angular-sanitize": ">= 1.5.0" 8 | }, 9 | "authors": [ 10 | "Jacob Blecher " 11 | ], 12 | "ignore": [ 13 | "**/.*", 14 | "node_modules", 15 | "js", 16 | "css", 17 | "templates", 18 | ".gitignore", 19 | ".git", 20 | ".idea", 21 | "Gruntfile.js", 22 | "package.json", 23 | "*.html" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /css/angular-on-screen-keyboard.less: -------------------------------------------------------------------------------- 1 | .keyboard { 2 | -webkit-touch-callout: none; 3 | -webkit-user-select: none; 4 | -khtml-user-select: none; 5 | -moz-user-select: none; 6 | -ms-user-select: none; 7 | user-select: none; 8 | 9 | table { 10 | border-spacing: 10px; 11 | border-collapse: separate; 12 | background-color: #F1F1F1; 13 | 14 | td{ 15 | touch-action: none; 16 | } 17 | } 18 | 19 | .button { 20 | background-color: #fff; 21 | box-shadow: 0px 1px 3px rgba(0,0,0,0.12), 0px 1px 2px rgba(0,0,0,0.24); 22 | width: 60px; 23 | height: 60px; 24 | text-align: center; 25 | font-family: "arial"; 26 | cursor: pointer; 27 | color: #000; 28 | font-size: 22px; 29 | 30 | &:hover{ 31 | background-color: #fafafa; 32 | } 33 | &:active { 34 | background-color: #999; 35 | color: #fff; 36 | } 37 | } 38 | 39 | .margin { 40 | width: 40px; 41 | height: 50px; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /demo1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | angular-on-screen-keyboard Demo 1 6 | 7 | 8 | 9 | 10 | 11 | 12 |
Enter some text:
13 | 14 |
15 |
16 | 17 | 18 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /demo2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | angular-on-screen-keyboard Demo 2 6 | 7 | 8 | 9 | 10 | 11 | 12 |
Enter some numbers:
13 | 14 |
15 |
16 | 17 | 18 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /demo3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | angular-on-screen-keyboard Demo 3 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
Enter your full name:
14 | 15 |
16 |
17 |
18 | 19 |
20 |
21 | 35 | 36 | -------------------------------------------------------------------------------- /dist/angular-on-screen-keyboard.min.css: -------------------------------------------------------------------------------- 1 | .keyboard{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.keyboard table{border-spacing:10px;border-collapse:separate;background-color:#F1F1F1}.keyboard table td{touch-action:none}.keyboard .button{background-color:#fff;box-shadow:0 1px 3px rgba(0,0,0,0.12),0 1px 2px rgba(0,0,0,0.24);width:60px;height:60px;text-align:center;font-family:"arial";cursor:pointer;color:#000;font-size:22px}.keyboard .button:hover{background-color:#fafafa}.keyboard .button:active{background-color:#999;color:#fff}.keyboard .margin{width:40px;height:50px} -------------------------------------------------------------------------------- /dist/angular-on-screen-keyboard.min.js: -------------------------------------------------------------------------------- 1 | angular.module("onScreenKeyboard",["ngSanitize"]).directive("onScreenKeyboard",["$timeout","$document",function(a,b){"use strict";return{restrict:"E",bindToController:!0,controllerAs:"ctrl",scope:{rows:"=?",uppercaseAllWords:"@"},controller:["$sce",function(a){var b=this;b.rows||(b.rows=[["1","2","3","4","5","6","7","8","9","0",{type:"erase",colspan:2,text:"⇐"}],["q","w","e","r","t","y","u","i","o","p","@"],["a","s","d","f","g","h","j","k","l","-","_",{type:"margin"}],[{type:"shift",upperCase:"⇓",lowerCase:"⇑"},"z","x","c","v","b","n","m",",",".",{type:"shift",upperCase:"⇓",lowerCase:"⇑"}],[{type:"margin"},{type:"space",colspan:9,text:" "}]]),b.getText=function(c){if("margin"===c.type)return"";var d="";return d=c.text?c.text:c.lowerCase&&!b.isUpperCase?c.lowerCase:c.upperCase&&b.isUpperCase?c.upperCase:b.isUpperCase?c.toUpperCase():c.toLowerCase(),d&&d.indexOf("&")>-1?a.trustAsHtml(d):d}}],link:function(c,d,e){var f=c.ctrl;f.isUpperCase=!1,f.lastInputCtrl=null,f.startPos=null,f.endPos=null,f.printKeyStroke=function(a,b){if(f.lastInputCtrl){if(f.startPos=f.lastInputCtrl.selectionStart,f.endPos=f.lastInputCtrl.selectionEnd,"erase"===a.type)return void f.eraseKeyStroke();if("shift"===a.type)return void(f.isUpperCase=!f.isUpperCase);var c=angular.element(b.target||b.srcElement).text(),d=angular.element(f.lastInputCtrl),e=d.val(),g=e.substring(0,f.startPos),h=e.substring(f.endPos,e.length);d.val(g+c+h),d.triggerHandler("change"),f.startPos+=c.length,f.endPos+=c.length,f.lastInputCtrl.selectionStart=f.startPos,f.lastInputCtrl.selectionEnd=f.startPos,f.setKeyboardLayout(),f.refocus()}},f.refocus=function(){f.lastInputCtrl.focus()},f.eraseKeyStroke=function(){if(f.lastInputCtrl){var a=f.startPos!==f.endPos,b=angular.element(f.lastInputCtrl),c=b.val(),d=c.substring(0,a?f.startPos:f.startPos-1),e=c.substring(f.endPos,c.length);b.val(d+e),b.triggerHandler("change"),a?f.endPos=f.startPos:(f.startPos--,f.endPos--),f.lastInputCtrl.selectionStart=f.startPos,f.lastInputCtrl.selectionEnd=f.startPos,f.setKeyboardLayout(),f.refocus()}},f.setKeyboardLayout=function(){return f.lastInputCtrl?void(f.lastInputCtrl.className&&f.isUpperCase?f.isUpperCase=!1:0===angular.element(f.lastInputCtrl).val().length?f.isUpperCase=!0:" "!==angular.element(f.lastInputCtrl).val().slice(-1)||f.isUpperCase||void 0===e.uppercaseAllWords?f.isUpperCase=!1:f.isUpperCase=!0):void(f.isUpperCase=!0)};var g=function(a){var b=a.target||a.srcElement;"INPUT"!==b.tagName&&"TEXTAREA"!==b.tagName||(f.lastInputCtrl=b,f.setKeyboardLayout())},h=function(){f.lastInputCtrl&&(f.startPos=f.lastInputCtrl.selectionStart,f.endPos=f.lastInputCtrl.selectionEnd,f.setKeyboardLayout(),c.$digest())};b.bind("focusin",g),b.bind("keyup",h),c.$on("$destroy",function(){b.unbind("focusin",g),b.unbind("keyup",h)}),d.bind("contextmenu",function(a){return a.preventDefault(),!1}),a(function(){f.isUpperCase=!0},0)},templateUrl:"/templates/angular-on-screen-keyboard.html"}}]);; 2 | angular.module('onScreenKeyboard').run(['$templateCache', function($templateCache) { 3 | 'use strict'; 4 | 5 | $templateCache.put('/templates/angular-on-screen-keyboard.html', 6 | "
" 7 | ); 8 | 9 | }]); 10 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |

angular-on-screen-keyboard

7 |
8 | Demo 1: With the default keyboard 9 |
10 |
11 | Demo 2: With an overridden mini-numpad keyboard 12 |
13 |
14 | Demo 3: With overridden keyboard that suggests uppercase for every word 15 |
16 | 17 | -------------------------------------------------------------------------------- /js/angular-on-screen-keyboard.js: -------------------------------------------------------------------------------- 1 | /* global angular:false */ 2 | angular.module('onScreenKeyboard', ['ngSanitize']) 3 | .directive('onScreenKeyboard', function ($timeout, $document) { 4 | 'use strict'; 5 | 6 | return { 7 | restrict: 'E', 8 | bindToController: true, 9 | controllerAs: 'ctrl', 10 | scope: { 11 | rows : '=?', 12 | uppercaseAllWords : '@', 13 | }, 14 | controller: function($sce){ 15 | var ctrl = this; 16 | 17 | if (!ctrl.rows){ 18 | ctrl.rows = [ 19 | ['1', '2', '3', '4','5','6','7','8', '9', '0', {type: 'erase', colspan: 2, text: '⇐'}], 20 | ['q','w','e','r','t','y','u','i','o','p','@'], 21 | ['a','s','d','f','g','h','j','k','l','-','_', {type: 'margin'}], 22 | [{type: 'shift', upperCase: '⇓', lowerCase: '⇑'}, 'z','x','c','v','b','n','m',',', '.',{type: 'shift', upperCase: '⇓', lowerCase: '⇑'}], 23 | [{type: 'margin'}, {type: 'space', colspan: 9, text: ' '}] 24 | ]; 25 | } 26 | 27 | ctrl.getText = function(key){ 28 | if (key.type === 'margin') 29 | return ''; 30 | 31 | var val = ''; 32 | 33 | if (key.text) 34 | val = key.text; 35 | else if (key.lowerCase && !ctrl.isUpperCase) 36 | val = key.lowerCase; 37 | else if (key.upperCase && ctrl.isUpperCase) 38 | val = key.upperCase; 39 | else { 40 | val = ctrl.isUpperCase ? key.toUpperCase() : key.toLowerCase(); 41 | } 42 | 43 | if (val && val.indexOf('&') > -1) 44 | return $sce.trustAsHtml(val); 45 | 46 | return val; 47 | }; 48 | }, 49 | link: function (scope, element, attr) { 50 | var ctrl = scope.ctrl; 51 | ctrl.isUpperCase = false; 52 | ctrl.lastInputCtrl = null; 53 | ctrl.startPos = null; 54 | ctrl.endPos = null; 55 | 56 | ctrl.printKeyStroke = function(key, event){ 57 | if (!ctrl.lastInputCtrl) 58 | return; 59 | 60 | ctrl.startPos = ctrl.lastInputCtrl.selectionStart; 61 | ctrl.endPos = ctrl.lastInputCtrl.selectionEnd; 62 | 63 | if (key.type === 'erase'){ 64 | ctrl.eraseKeyStroke(); 65 | return; 66 | } else if (key.type === 'shift'){ 67 | ctrl.isUpperCase = !ctrl.isUpperCase; 68 | return; 69 | } 70 | 71 | var htmlKeyVal = angular.element(event.target || event.srcElement).text(); 72 | var lastInputCtrl = angular.element(ctrl.lastInputCtrl); 73 | var val = lastInputCtrl.val(); 74 | var pre = val.substring(0, ctrl.startPos); 75 | var post = val.substring(ctrl.endPos, val.length); 76 | lastInputCtrl.val(pre + htmlKeyVal + post); 77 | lastInputCtrl.triggerHandler('change'); 78 | 79 | ctrl.startPos += htmlKeyVal.length; 80 | ctrl.endPos += htmlKeyVal.length; 81 | ctrl.lastInputCtrl.selectionStart = ctrl.startPos; 82 | ctrl.lastInputCtrl.selectionEnd = ctrl.startPos; 83 | ctrl.setKeyboardLayout(); 84 | ctrl.refocus(); 85 | }; 86 | 87 | ctrl.refocus = function () { 88 | ctrl.lastInputCtrl.focus(); 89 | }; 90 | 91 | ctrl.eraseKeyStroke = function () { 92 | if (!ctrl.lastInputCtrl) 93 | return; 94 | 95 | var hasSel = ctrl.startPos !== ctrl.endPos; 96 | 97 | var lastInputCtrl = angular.element(ctrl.lastInputCtrl); 98 | var val = lastInputCtrl.val(); 99 | var pre = val.substring(0, hasSel ? ctrl.startPos : ctrl.startPos - 1); 100 | var post = val.substring(ctrl.endPos, val.length); 101 | 102 | lastInputCtrl.val(pre + post); 103 | lastInputCtrl.triggerHandler('change'); 104 | 105 | if (hasSel) { 106 | ctrl.endPos = ctrl.startPos; 107 | } 108 | else { 109 | ctrl.startPos--; 110 | ctrl.endPos--; 111 | } 112 | ctrl.lastInputCtrl.selectionStart = ctrl.startPos; 113 | ctrl.lastInputCtrl.selectionEnd = ctrl.startPos; 114 | ctrl.setKeyboardLayout(); 115 | ctrl.refocus(); 116 | }; 117 | 118 | ctrl.setKeyboardLayout = function () { 119 | if (!ctrl.lastInputCtrl){ 120 | ctrl.isUpperCase = true; 121 | return; 122 | } 123 | else if (ctrl.lastInputCtrl.className && ctrl.isUpperCase) 124 | ctrl.isUpperCase = false; 125 | else if (angular.element(ctrl.lastInputCtrl).val().length === 0) { 126 | ctrl.isUpperCase = true; 127 | } 128 | else if (angular.element(ctrl.lastInputCtrl).val().slice(-1) === ' ' && !ctrl.isUpperCase && attr.uppercaseAllWords !== undefined) 129 | ctrl.isUpperCase = true; 130 | else{ 131 | ctrl.isUpperCase = false; 132 | } 133 | }; 134 | 135 | var focusin = function(event){ 136 | var e = event.target || event.srcElement; 137 | 138 | if (e.tagName === 'INPUT' || e.tagName === 'TEXTAREA') { 139 | ctrl.lastInputCtrl = e; 140 | ctrl.setKeyboardLayout(); 141 | } 142 | }; 143 | 144 | var keyup = function(){ 145 | if (!ctrl.lastInputCtrl) 146 | return; 147 | 148 | ctrl.startPos = ctrl.lastInputCtrl.selectionStart; 149 | ctrl.endPos = ctrl.lastInputCtrl.selectionEnd; 150 | 151 | ctrl.setKeyboardLayout(); 152 | scope.$digest(); 153 | }; 154 | 155 | $document.bind('focusin', focusin); 156 | $document.bind('keyup', keyup); 157 | 158 | scope.$on("$destroy", function() { 159 | $document.unbind('focusin', focusin); 160 | $document.unbind('keyup', keyup); 161 | }); 162 | 163 | element.bind('contextmenu', function (event) { 164 | event.preventDefault(); 165 | return false; 166 | }); 167 | 168 | $timeout(function(){ 169 | ctrl.isUpperCase = true; 170 | },0); 171 | }, 172 | templateUrl: '/templates/angular-on-screen-keyboard.html' 173 | }; 174 | }); 175 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-on-screen-keyboard", 3 | "version": "0.9.8", 4 | "description": "Always present 'On screen keyboard' for angular", 5 | "author": "Greenfield Ventures AB", 6 | "homepage": "https://github.com/GreenfieldVentures/angular-on-screen-keyboard", 7 | "main": "dist/angular-on-screen-keyboard.min.js", 8 | "style": "dist/angular-on-screen-keyboard.min.css", 9 | "keywords": [ 10 | "directive", 11 | "angular", 12 | "keyboard", 13 | "numpad", 14 | "onscreen" 15 | ], 16 | "repository": { 17 | "type": "git", 18 | "url": "git://github.com/GreenfieldVentures/angular-on-screen-keyboard.git" 19 | }, 20 | "license": "MIT", 21 | "devDependencies": { 22 | "grunt": "~0.4.5", 23 | "grunt-angular-templates": "^1.0.3", 24 | "grunt-browser-sync": "^2.2.0", 25 | "grunt-contrib-clean": "^0.6.0", 26 | "grunt-contrib-concat": "^0.5.1", 27 | "grunt-contrib-copy": "~0.5.0", 28 | "grunt-contrib-jshint": "~0.10.0", 29 | "grunt-contrib-less": "^1.0.1", 30 | "grunt-contrib-uglify": "~0.5.0", 31 | "grunt-contrib-watch": "^0.6.1", 32 | "grunt-ng-annotate": "^1.0.1", 33 | "grunt-notify": "^0.4.1", 34 | "jshint-html-reporter": "^0.2.2", 35 | "less-plugin-autoprefix": "^1.5.1", 36 | "time-grunt": "^1.0.0" 37 | }, 38 | "dependencies": { 39 | "angular": "^1.5.0", 40 | "angular-sanitize": "^1.5.0" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /templates/angular-on-screen-keyboard.html: -------------------------------------------------------------------------------- 1 | 
2 | 3 | 4 | 10 | 11 |
9 |
12 |
13 | --------------------------------------------------------------------------------