├── README.md
├── demo
├── css
│ ├── demo.css
│ └── keypad-numeric.css
├── index.html
├── js
│ ├── DemoController.js
│ └── app.js
├── lib
│ ├── angular.js
│ └── jquery.2.0.2.js
└── partials
│ └── keypad
│ └── numeric.html
└── src
├── ngDraggable
└── ngDraggable.js
└── ngKeypad
├── ngKey.js
├── ngKeypad.js
└── ngKeypadInput.js
/README.md:
--------------------------------------------------------------------------------
1 | # ng-keypad
2 |
3 |
4 | ng-keypad is a simple set of directive that allow you to create a custom keypad to provide on screen user input functionnality. Mobile and desktop compatible. You can view a demo here : [View Demo](http://dev.tommyrochette.com/ng-keypad/demo/).
5 |
6 | Keypad design by [Courtny Cotten](http://dribbble.com/court)
7 |
8 |
9 | ## Usage
10 |
11 | ```
12 | //Basic usage
13 |
14 | // Insert key pad template here.
15 |
16 |
17 | //Combined with ng-include
18 |
21 | ```
22 |
23 | ## Options
24 |
25 | - `data-ng-keypad` *REQUIRED* Specify ID for the keypad instance. You can have multiple keypads in the same application.
26 | - `data-auto-close` Set that to true and the keypad will automatically close when user click outside of it.
27 | - `data-ng-draggable` Use for the ng-draggable directive. Allowing the keypad to be moved on screen.
28 |
29 | ## ng-key
30 |
31 | ### Usage
32 |
33 | ```
34 | //Basic usage - This dispatch Keypad.KEY_PRESSED event
35 |
36 |
37 | //Custom or modifier key - This dispatch Keypad.MODIFIER_KEY_PRESSED event. Those events need to be handled by your application.
38 |
39 | ```
40 |
41 |
42 | ## ng-keypad-input
43 |
44 | ### Usage
45 |
46 |
47 | ```
48 | //Basic usage
49 |
50 |
51 | //It can also be used on a tag to prevent keyboard to show up on mobile devices
52 |
53 | ```
54 |
55 | ### Options
56 |
57 | - `data-ng-model` *REQUIRED* Instance of ng-model that will key updated by the keypad.
58 | - `data-ng-keypad-input` *REQUIRED* Specify ID of the ng-keypad instance you want to open when user focus or click on the field.
59 | - `data-ng-keypad-restrict` Restrict character or input value using regular expression.
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/demo/css/demo.css:
--------------------------------------------------------------------------------
1 | html, body{
2 | width:100%;
3 | height:100%;
4 | font-family: Helvetica, Arial, sans-serif;
5 | padding:10px;
6 | }
7 |
8 | .numpad{
9 | left:350px;
10 | top:200px;
11 | }
12 |
13 | hr{
14 | margin-top:40px;
15 | }
16 |
17 |
18 | .clear{
19 | display: block;
20 | clear: both;
21 | height: 1px;
22 | }
23 |
24 | .helpers button{
25 | width:auto;
26 | padding:10px;
27 |
28 | }
29 |
30 | .values{
31 | margin-top:40px;
32 | line-height:20px;
33 | }
34 |
35 |
36 |
37 | .closed{
38 | display:none;
39 | }
40 |
41 | .pressed{
42 | background:#666;
43 | }
44 |
45 | .input{
46 | display: block;
47 | border:2px solid #666;
48 | max-width: 100px;
49 | padding:5px 10px;
50 | height:18px;
51 | text-decoration: none;
52 | color:#000;
53 | }
54 |
55 | .focus{
56 | border:2px solid #FF0000;
57 | }
58 |
--------------------------------------------------------------------------------
/demo/css/keypad-numeric.css:
--------------------------------------------------------------------------------
1 | .numpad {
2 | position: relative;
3 | height: 300px;
4 | width: 215px;
5 | padding: 13px;
6 | z-index: 10000;
7 | background: #666;
8 | -webkit-border-radius: 10px;
9 | -moz-border-radius: 10px;
10 | -ms-border-radius: 10px;
11 | -o-border-radius: 10px;
12 | border-radius: 10px;
13 | background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #999999), color-stop(100%, #666666));
14 | background-image: -webkit-linear-gradient(#999999, #666666);
15 | background-image: -moz-linear-gradient(#999999, #666666);
16 | background-image: -o-linear-gradient(#999999, #666666);
17 | background-image: linear-gradient(#999999, #666666);
18 | -webkit-box-shadow: rgba(0, 0, 0, 0.4) 0 0 25px;
19 | -moz-box-shadow: rgba(0, 0, 0, 0.4) 0 0 25px;
20 | box-shadow: rgba(0, 0, 0, 0.4) 0 0 25px;
21 | }
22 |
23 | .numpad[disabled] button {
24 | opacity: .5;
25 | }
26 |
27 | .numpad .button-wrapper {
28 | width: 200px;
29 | overflow: auto;
30 | }
31 |
32 | .numpad .button-wrapper button {
33 | -webkit-border-radius: 5px;
34 | -moz-border-radius: 5px;
35 | -ms-border-radius: 5px;
36 | -o-border-radius: 5px;
37 | border-radius: 5px;
38 | background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #e5e5e5));
39 | background-image: -webkit-linear-gradient(#ffffff, #e5e5e5);
40 | background-image: -moz-linear-gradient(#ffffff, #e5e5e5);
41 | background-image: -o-linear-gradient(#ffffff, #e5e5e5);
42 | background-image: linear-gradient(#ffffff, #e5e5e5);
43 | -webkit-box-shadow: rgba(0, 0, 0, 0.3) 0px 3px 10px;
44 | -moz-box-shadow: rgba(0, 0, 0, 0.3) 0px 3px 10px;
45 | box-shadow: rgba(0, 0, 0, 0.3) 0px 3px 10px;
46 | box-sizing: border-box;
47 | display: block;
48 | float: left;
49 | width: 51px;
50 | height: 51px;
51 | padding: 10px 0px;
52 | margin: 0 10px 10px 0;
53 | border: none;
54 | font-size: 20px;
55 | }
56 |
57 | .numpad .button-wrapper .smaller {
58 | font-size: 14px;
59 | }
60 |
61 | .numpad .button-wrapper .button-wide {
62 | width: 82px;
63 | }
64 |
65 | .numpad .button-wrapper .pressed {
66 | background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #dadada), color-stop(100%, #bebebe));
67 | background-image: -webkit-linear-gradient(#dadada, #bebebe);
68 | background-image: -moz-linear-gradient(#dadada, #bebebe);
69 | background-image: -o-linear-gradient(#dadada, #bebebe);
70 | background-image: linear-gradient(#dadada, #bebebe);
71 | -webkit-box-shadow: rgba(0, 0, 0, 0.3) 0px 0px 4px;
72 | -moz-box-shadow: rgba(0, 0, 0, 0.3) 0px 0px 4px;
73 | box-shadow: rgba(0, 0, 0, 0.3) 0px 0px 4px;
74 | }
75 |
76 | .numpad .drag-indicator {
77 | position: absolute;
78 | width: 24px;
79 | height: 80px;
80 | top: 115px;
81 | left: 208px;
82 | }
83 |
84 | .numpad .drag-indicator > span {
85 | display: block;
86 | height: 80px;
87 | width: 0px;
88 | border-left: 1px solid #666666;
89 | border-right: 1px solid #999999;
90 | float: left;
91 | margin-right: 3px;
92 | }
93 |
94 | .numpad .close {
95 | -webkit-border-radius: 15px;
96 | -moz-border-radius: 15px;
97 | -ms-border-radius: 15px;
98 | -o-border-radius: 15px;
99 | border-radius: 15px;
100 | filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=30);
101 | opacity: 0.3;
102 | -webkit-box-shadow: black 0px 1px 2px inset;
103 | -moz-box-shadow: black 0px 1px 2px inset;
104 | box-shadow: black 0px 1px 2px inset;
105 | position: absolute;
106 | box-sizing: border-box;
107 | width: 30px;
108 | height: 30px;
109 | top: 10px;
110 | left: 198px;
111 | background: #fff;
112 | border: none;
113 | padding-top: 4px;
114 | }
115 |
116 | .numpad .close:before {
117 | font-family: Arial;
118 | content: "X";
119 | filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=60);
120 | opacity: 0.6;
121 | font-weight: bold;
122 | color: #000;
123 | font-size: 16px;
124 | }
125 |
--------------------------------------------------------------------------------
/demo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Tablet Numpad component test sandbox
5 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
ngKeypad Actions
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
32 |
33 |
34 |
35 |
36 |
ngKeypad Events
37 | KEY_PRESSED event generated string : {{listenedString}}
38 |
39 |
ngKeypadInput
40 |
43 |
44 |
ngKeypadRestrict
45 |
48 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/demo/js/DemoController.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @overview
3 | * @copyright 2013 Tommy Rochette http://trochette.github.com/
4 | * @author Tommy Rochette
5 | * @version 0.0.1
6 | *
7 | * @license MIT
8 | */
9 | (function(){
10 | var DemoController = function DemoController($scope) {
11 | var self = this;
12 | var selectedInputIndex = 0;
13 |
14 | /**
15 | * This example show how to toggle locked/unlocked
16 | * state of the keypad.
17 | */
18 | $scope.toggleKeypadLock = function(){
19 | toggleLock();
20 | //Toggling KeyPad Locking state, second argument is Keypad ID
21 | $scope.$emit(Keypad.TOGGLE_LOCKING, "numeric");
22 | }
23 |
24 |
25 |
26 | /**
27 | * This example show how to toggle open/close
28 | * state of the keypad.
29 | */
30 | $scope.toggleKeypadOpening = function(){
31 | //Toggling KeyPad Locking state, second argument is Keypad ID
32 | $scope.$emit(Keypad.TOGGLE_OPENING, "numeric");
33 | }
34 |
35 |
36 | /**
37 | * This example show how to toggle open/close
38 | * state of the keypad and set a position at the same time.
39 | */
40 | $scope.toggleKeypadPosition = function(){
41 | var params = {
42 | position:{
43 | x:600,
44 | y:70
45 | }
46 | };
47 | //Toggling KeyPad Locking state, second argument is Keypad ID, third is the param object.
48 | $scope.$emit(Keypad.TOGGLE_OPENING, "numeric",params);
49 | }
50 |
51 |
52 | /**
53 | * This example show how to listen for the KEY_PRESSED event thrown
54 | * by the keypad and do what you need to do with it.
55 | */
56 | $scope.listenedString = "";
57 |
58 | $scope.$on(Keypad.KEY_PRESSED, function(event,data){
59 | $scope.listenedString += data;
60 | $scope.$digest();
61 | });
62 |
63 |
64 |
65 |
66 |
67 | //DEMO APPLICATION CODE NOT WORTH READING
68 |
69 | $scope.opened = false;
70 | $scope.openLabel = "Open Keypad"
71 |
72 | $scope.$on(Keypad.OPENED, function(event,id){
73 | $scope.openLabel = "Close Keypad";
74 | $scope.opened = true;
75 |
76 | if(!$scope.$$phase){
77 | $scope.$apply();
78 | }
79 | });
80 |
81 | $scope.$on(Keypad.CLOSED, function(event,id){
82 | $scope.openLabel = "Open Keypad";
83 | $scope.opened = false;
84 |
85 | if(!$scope.$$phase){
86 | $scope.$apply();
87 | }
88 | });
89 |
90 |
91 | $scope.$on(Keypad.MODIFIER_KEY_PRESSED, function(event,key,id){
92 | var focusedInput = $('a[data-ng-keypad-input]:focus'),
93 | inputs = $('a[data-ng-keypad-input]'),
94 | foundIndex = findInputInInputs(focusedInput,inputs),
95 | index = 0;
96 |
97 | switch(key){
98 | case "PREVIOUS":
99 | if(!focusedInput.length){
100 | index = inputs.length-1;
101 | }else{
102 | if(foundIndex===0){
103 | index = inputs.length-1;
104 | }else{
105 | index = foundIndex-1;
106 | }
107 | }
108 | inputs.eq(index).focus();
109 | break;
110 | case "NEXT":
111 | if(focusedInput.length){
112 | if(foundIndex===inputs.length-1){
113 | index = 0;
114 | }else{
115 | index = foundIndex+1;
116 | }
117 | }
118 | inputs.eq(index).focus();
119 | break;
120 | }
121 | });
122 |
123 |
124 | function findInputInInputs(input,inputs){
125 | var foundIndex = 0;
126 | inputs.each(function(index){
127 | if($(this).is(input.eq(0))){
128 | foundIndex = index;
129 | }
130 | });
131 |
132 | return foundIndex;
133 | }
134 |
135 |
136 | $scope.locked = true;
137 | $scope.lockLabel = "Lock Keypad"
138 |
139 | function toggleLock(){
140 | $scope.locked = !$scope.locked;
141 |
142 | if($scope.locked){
143 | $scope.lockLabel = "Lock Keypad"
144 | }else{
145 | $scope.lockLabel = "Unlock Keypad"
146 | }
147 | }
148 | }
149 |
150 | window.DemoController = DemoController;
151 | })();
--------------------------------------------------------------------------------
/demo/js/app.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @overview
3 | * @copyright 2013 Tommy Rochette http://trochette.github.com/
4 | * @author Tommy Rochette
5 | * @version 0.0.1
6 | *
7 | * @license MIT
8 | */
9 | (function(){
10 | 'use strict';
11 | angular.module('app',['ngKeypad','ngDraggable']);
12 | })();
13 |
14 |
--------------------------------------------------------------------------------
/demo/partials/keypad/numeric.html:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/src/ngDraggable/ngDraggable.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Draggable directive, allow to drag any element
3 | * targeted by this directive.
4 | *
5 | * Use of Namespace.js
6 | *
7 | * @author Tommy Rochette http://trochette.github.com/
8 | * @version 0.0.1
9 | * @license MIT
10 | */
11 | (function () {
12 | "use strict";
13 |
14 | /**
15 | * @Class
16 | */
17 | var Draggable = function($scope,$element,$attrs){
18 |
19 | var offset = {},
20 | body = $('body'),
21 | isDragging = false;
22 |
23 | /**
24 | * Initialize Draggable to default settings
25 | */
26 | function init(){
27 | $element.css("position","absolute");
28 |
29 | if ("ontouchstart" in document.documentElement){
30 | $element.bind('touchstart.ngDraggable',startDrag);
31 | body.bind('touchend.ngDraggable',stopDrag);
32 | body.bind('touchmove.ngDraggable',drag);
33 | }else{
34 | $element.bind('mousedown.ngDraggable',startDrag);
35 | body.bind('mouseup.ngDraggable',stopDrag);
36 | body.bind('mousemove.ngDraggable',drag);
37 | }
38 |
39 | $scope.$on('destroy',destroy);
40 | };
41 |
42 |
43 | /**
44 | * Triggered when a user start moving his mouse
45 | * or his finger on mobile application.
46 | *
47 | * @param event
48 | */
49 | function drag(event){
50 | if(isDragging){
51 | var position = {};
52 |
53 | if(event.type==="mousemove"){
54 | position.x = Math.floor(event.pageX-offset.x);
55 | position.y = Math.floor(event.pageY-offset.y);
56 |
57 | }else{
58 | var orig = event.originalEvent;
59 | position.x = Math.floor(orig.changedTouches[0].pageX - offset.x);
60 | position.y = Math.floor(orig.changedTouches[0].pageY - offset.y);
61 | }
62 |
63 | $element.css("left",position.x);
64 | $element.css("top",position.y);
65 | }
66 | }
67 |
68 |
69 | /**
70 | * Triggered when a user click or start touching the element.
71 | *
72 | * @param event
73 | */
74 | function startDrag(event){
75 | isDragging = true;
76 |
77 | if(event.type==="mousedown"){
78 | var localoffsetX = $element.offset().left,
79 | localoffsetY = $element.offset().top,
80 | localx = Math.floor(event.pageX-localoffsetX),
81 | localy = Math.floor(event.pageY-localoffsetY);
82 | offset.x = localx;
83 | offset.y = localy;
84 | }else{
85 | var orig = event.originalEvent;
86 | var pos = $(this).position();
87 | offset.x = Math.floor(orig.changedTouches[0].pageX - pos.left);
88 | offset.y = Math.floor(orig.changedTouches[0].pageY - pos.top);
89 | }
90 |
91 | $scope.$emit("$startDrag");
92 | event.preventDefault();
93 | }
94 |
95 |
96 | /**
97 | * Triggered when a user remove his finger from the screen.
98 | *
99 | * @param event
100 | */
101 | function stopDrag(event){
102 | isDragging = false;
103 | offset = {};
104 | $scope.$emit("$stopDrag");
105 | }
106 |
107 |
108 | /**
109 | * Cleanup all listeners before destroying this directive.
110 | */
111 | function destroy(){
112 | $element.unbind('mousedown.ngDraggable',startDrag);
113 | $element.unbind('mousestart.ngDraggable',startDrag);
114 | body.unbind('mouseup.ngDraggable',stopDrag);
115 | body.unbind('touchend.ngDraggable',stopDrag);
116 | body.unbind('mousemove.ngDraggable',drag);
117 | body.unbind('touchmove.ngDraggable',drag);
118 | }
119 |
120 |
121 | init();
122 | };
123 |
124 | angular.module('ngDraggable',[])
125 | .directive('ngDraggable',function () {
126 | return {
127 | restrict: 'A',
128 | link: function ($scope, $element, $attrs) {
129 | new Draggable($scope, $element, $attrs);
130 | }
131 | }
132 | });
133 | })();
--------------------------------------------------------------------------------
/src/ngKeypad/ngKey.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @overview
3 | * @copyright 2013 Tommy Rochette http://trochette.github.com/
4 | * @author Tommy Rochette
5 | * @version 0.0.1
6 | *
7 | * @license MIT
8 | */
9 | (function () {
10 | "use strict";
11 |
12 | /**
13 | * @Class
14 | */
15 | window.Key = function ($scope, $element, $attrs) {
16 |
17 | var body = $('body');
18 |
19 | /**
20 | * Initialize Key to default settings
21 | */
22 | function init() {
23 | if ("ontouchstart" in document.documentElement) {
24 | $element.bind('touchstart.ngKey', keyPressed);
25 | body.bind('touchend.ngKey', keyDepressed);
26 | } else {
27 | $element.bind('mousedown.ngKey', keyPressed);
28 | body.bind('mouseup.ngKey', keyDepressed);
29 | }
30 |
31 | $scope.$on('destroy', destroy);
32 | };
33 |
34 |
35 | /**
36 | * Triggered when a user press on this element
37 | *
38 | * @param event
39 | */
40 | function keyPressed(event) {
41 | $element.addClass('pressed');
42 | event.preventDefault();
43 | event.stopImmediatePropagation();
44 | $scope.$emit(Key.PRESSED, $attrs.ngKey);
45 | }
46 |
47 |
48 | /**
49 | * Triggered when a user stop pressing this element
50 | *
51 | * @param event
52 | */
53 | function keyDepressed(event) {
54 | $element.removeClass('pressed');
55 | }
56 |
57 | /**
58 | * Cleanup all listeners before destroying this directive.
59 | */
60 | function destroy() {
61 | $element.bind('touchstart.ngKey', keyPressed);
62 | body.bind('touchend.ngKey', keyDepressed);
63 | $element.bind('mousedown.ngKey', keyPressed);
64 | body.bind('mouseup.ngKey', keyDepressed);
65 | }
66 |
67 |
68 | init();
69 | };
70 |
71 | /**
72 | * @Event
73 | */
74 | Key.PRESSED = "Key.PRESSED";
75 |
76 | angular.module('ngKeypad', [])
77 | .directive('ngKey', function () {
78 | return {
79 | restrict: 'A',
80 | link: function ($scope, $element, $attrs) {
81 | new Key($scope, $element, $attrs);
82 | }
83 | };
84 | });
85 | })();
86 |
--------------------------------------------------------------------------------
/src/ngKeypad/ngKeypad.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Keypad directive, display a keypad on the screen. Template for it
3 | * can be include inside the node containing the keypad attribute.
4 | *
5 | * Use of Namespace.js
6 | *
7 | * @author tommy.rochette[followed by the usual sign]universalmind.com
8 | */
9 |
10 | (function () {
11 | "use strict";
12 |
13 |
14 | /**
15 | * @Class
16 | */
17 | window.Keypad = function ($scope, $element, $attrs, $rootScope) {
18 |
19 | var locked = false,
20 | opened = false,
21 | padId = $attrs.ngKeypad,
22 | body = $('body');
23 |
24 | /**
25 | * Initialize Keypad to default settings
26 | * based on attributes.
27 | */
28 | function init() {
29 | $scope.$on(Key.PRESSED, handleKeyPressed);
30 | $rootScope.$on(Keypad.TOGGLE_LOCKING, handleLockingToggle);
31 | $rootScope.$on(Keypad.TOGGLE_OPENING, handleOpeningToggle);
32 | $rootScope.$on(Keypad.OPEN, handleOpeningToggle);
33 | $rootScope.$on(Keypad.CLOSE, handleOpeningToggle);
34 | $element.addClass("closed");
35 |
36 | initScope();
37 | };
38 |
39 | /**
40 | * Initialize scope variables
41 | */
42 | function initScope() {
43 | $scope.close = function () {
44 | close();
45 | }
46 | }
47 |
48 |
49 | /**
50 | * Triggered when a user press any key on the pad
51 | * check current status then dispatch KEY_PRESSED event.
52 | *
53 | * @param event
54 | * @param key
55 | */
56 | function handleKeyPressed(event, key) {
57 | if (!locked) {
58 | if (key.indexOf('[') === -1 && key.indexOf(']') === -1) {
59 | $scope.$emit(Keypad.KEY_PRESSED, key, padId);
60 | } else {
61 | $scope.$emit(Keypad.MODIFIER_KEY_PRESSED, key.substring(1, key.length - 1), padId);
62 | }
63 | }
64 | }
65 |
66 |
67 | /**
68 | * Triggered when a user press any key on the pad
69 | * check current status then dispatch KEY_PRESSED event.
70 | *
71 | * @param event
72 | * @param key
73 | */
74 | function handleLockingToggle(event, id) {
75 | if (padId === id || !id) {
76 | locked = !locked;
77 |
78 | if (locked) {
79 | $element.attr("disabled", "disabled");
80 | } else {
81 | $element.removeAttr("disabled");
82 | }
83 | }
84 | }
85 |
86 |
87 | /**
88 | * Triggered when a user toggle keypad opening.
89 | *
90 | * @param event
91 | * @param key
92 | */
93 | function handleOpeningToggle(event, id, params) {
94 | if (padId === id || !id) {
95 | switch (event.name) {
96 | case Keypad.CLOSE:
97 | opened = false;
98 | break;
99 | case Keypad.OPEN:
100 | opened = true;
101 | break;
102 | default:
103 | opened = !opened;
104 | break;
105 | }
106 |
107 | if (!opened) {
108 | close();
109 | } else {
110 | open();
111 | applyOptions(params);
112 | }
113 | }
114 | }
115 |
116 | function applyOptions(params) {
117 | if (params && params.position) {
118 | $element.css("top", params.position.y);
119 | $element.css("left", params.position.x);
120 | }
121 | }
122 |
123 |
124 | /**
125 | * Open current pad
126 | */
127 | function open() {
128 | $element.removeClass("closed");
129 | $scope.$emit(Keypad.OPENED, padId);
130 | autoClose();
131 | applyOptions();
132 | }
133 |
134 |
135 | /**
136 | * Close current pad
137 | */
138 | function close() {
139 | opened = false;
140 | $scope.$emit(Keypad.CLOSED, padId);
141 | body.off("click.keypad");
142 | $element.addClass("closed");
143 | }
144 |
145 |
146 | /**
147 | * Check if the attribute auto-close is set then
148 | * add event for automatic closing.
149 | *
150 | * @param event
151 | * @param key
152 | */
153 | function autoClose() {
154 | if ($attrs.autoClose) {
155 | //Timeout use to break the event flow.
156 | setTimeout(function () {
157 | body.on("click.keypad", function () {
158 | opened = !opened;
159 | close();
160 | if (!$scope.$$phase) {
161 | $scope.$apply();
162 | }
163 | });
164 | $element.on("click.keypad", cancelEvent);
165 | }, 1);
166 | }
167 | }
168 |
169 |
170 | /**
171 | * Dummy function to cancel event propagation.
172 | *
173 | * @param event
174 | */
175 | function cancelEvent(event) {
176 | event.stopPropagation();
177 | }
178 |
179 |
180 | init();
181 | };
182 |
183 |
184 | /**
185 | * @Events
186 | */
187 | Keypad.KEY_PRESSED = "Keypad.KEY_PRESSED";
188 | Keypad.MODIFIER_KEY_PRESSED = "Keypad.MODIFIER_KEY_PRESSED";
189 | Keypad.TOGGLE_LOCKING = "Keypad.TOGGLE_LOCKING";
190 | Keypad.TOGGLE_OPENING = "Keypad.TOGGLE_OPENING";
191 | Keypad.OPENED = "Keypad.OPENED";
192 | Keypad.CLOSED = "Keypad.CLOSED";
193 | Keypad.OPEN = "Keypad.OPEN";
194 | Keypad.CLOSE = "Keypad.CLOSE";
195 |
196 |
197 | angular.module('ngKeypad')
198 | .directive('ngKeypad', ['$rootScope', function ($rootScope) {
199 |
200 | return {
201 | restrict: 'A',
202 | link: function ($scope, $element, $attrs) {
203 | new Keypad($scope, $element, $attrs, $rootScope);
204 | }
205 | }
206 | }]);
207 | })();
--------------------------------------------------------------------------------
/src/ngKeypad/ngKeypadInput.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @overview
3 | * @copyright 2013 Tommy Rochette http://trochette.github.com/
4 | * @author Tommy Rochette
5 | * @version 0.0.1
6 | *
7 | * @license MIT
8 | */
9 | (function () {
10 | "use strict";
11 |
12 | /**
13 | * @Class
14 | */
15 | window.KeypadInput = function ($scope, $element, $attrs, controller) {
16 |
17 | var self = this;
18 |
19 | this.modifierKeyListener = null;
20 | this.keyListener = null;
21 | this.element = $element;
22 | this.active = false;
23 | this.model = null;
24 | this.padId = $attrs.ngKeypadInput;
25 |
26 |
27 | /**
28 | * Initialize Key to default settings
29 | */
30 | function init() {
31 | $element.on('click.ngKeypadInput', handleElementSelected);
32 | $element.on('focus.ngKeypadInput', handleElementSelected);
33 | $element.on('blur.ngKeypadInput', handleElementBlur);
34 |
35 | if (!$attrs.ngModel) {
36 | throw new Error("KeypadInput requires the use of ng-model on this element", $element);
37 | }
38 |
39 | $scope.$on('destroy', destroy);
40 | };
41 |
42 |
43 | /**
44 | * Triggered when a user select a keypad input field.
45 | * Set listeners and add focused class.
46 | *
47 | * @param event
48 | */
49 | function handleElementSelected(event) {
50 | event.stopPropagation();
51 |
52 | if (!self.active) {
53 | clearSelectedElement();
54 |
55 | self.active = true;
56 | self.keyListener = $scope.$on(Keypad.KEY_PRESSED, handleKeyPressed);
57 | self.closeListener = $scope.$on(Keypad.CLOSED, handleKeypadClosed);
58 | self.modifierKeyListener = $scope.$on(Keypad.MODIFIER_KEY_PRESSED, handleModifierKeyPressed);
59 |
60 | $element.addClass("focus");
61 | $element.focus();
62 |
63 | $scope.$emit(Keypad.OPEN, $attrs.ngKeypadInput);
64 |
65 | KeypadInput.selectedInput = self;
66 | }
67 | };
68 |
69 |
70 | /**
71 | * Clear old reference of the selected element
72 | */
73 | function clearSelectedElement() {
74 | if (KeypadInput.selectedInput) {
75 | KeypadInput.selectedInput.active = false;
76 | KeypadInput.selectedInput.keyListener();
77 | KeypadInput.selectedInput.element.removeClass("focus");
78 | KeypadInput.selectedInput = null;
79 | }
80 | }
81 |
82 |
83 | /**
84 | * Triggered when keypad is closing to clear
85 | * the current input being edited.
86 | *
87 | * @param event
88 | * @param padId
89 | */
90 |
91 | function handleKeypadClosed(event, padId) {
92 | if (self.padId === padId && self.active) {
93 | clearSelectedElement();
94 | }
95 | }
96 |
97 |
98 | /**
99 | * Trigerred when the input loose focus to
100 | * clear selected element.
101 | *
102 | * @param event
103 | */
104 | function handleElementBlur(event) {
105 | clearSelectedElement();
106 | }
107 |
108 |
109 | /**
110 | * Event triggered from ngKeyPad called only if the current
111 | * input is selected. Works with ngModel.
112 | *
113 | * @param event
114 | * @param key
115 | * @param padId
116 | */
117 | function handleKeyPressed(event, key, padId) {
118 | if (self.padId === padId && self.active) {
119 | var value = controller.$viewValue;
120 |
121 |
122 | if (!value) {
123 | value = "";
124 | }
125 |
126 | if (!isRestricted(value, key)) {
127 | value += key;
128 | }
129 |
130 | controller.$setViewValue(value);
131 | $scope.$apply();
132 | }
133 | };
134 |
135 |
136 | /**
137 | * Triggered when a modifier key is pressed
138 | *
139 | * @param event
140 | * @param key
141 | * @param padId
142 | */
143 | function handleModifierKeyPressed(event, key, padId) {
144 | if (self.padId === padId && self.active) {
145 | if (key === "CLEAR") {
146 | controller.$setViewValue("");
147 | $scope.$apply();
148 | }
149 | }
150 | };
151 |
152 |
153 | /**
154 | * Check if the input is retricted by a RegExp.
155 | * data-ng-keypad-restric is used to save the string
156 | * corresponding to the RegExp
157 | *
158 | * @param value
159 | * @param key
160 | */
161 | function isRestricted(value, key) {
162 | var restricted = false,
163 | regExp = new RegExp($attrs.ngKeypadRestrict, 'gi');
164 |
165 | if ($attrs.ngKeypadRestrict) {
166 | if (!key.match(regExp)) {
167 | restricted = true;
168 | } else if (!String(value + key).match(regExp, 'gi')) {
169 | restricted = true;
170 | }
171 | }
172 |
173 | return restricted;
174 | }
175 |
176 |
177 | /**
178 | * Cleanup all listeners before destroying this directive.
179 | */
180 | function destroy() {
181 | $element.off('click.ngKeypadInput');
182 | $element.on('focus.ngKeypadInput', this.handleElementSelected);
183 | };
184 |
185 |
186 | init();
187 | };
188 |
189 |
190 | KeypadInput.selectedInput = null;
191 |
192 |
193 | angular.module('ngKeypad')
194 | .directive('ngKeypadInput', function () {
195 | return {
196 | restrict: 'A',
197 | require: '^ngModel',
198 | link: function ($scope, $element, $attrs, controller) {
199 | new KeypadInput($scope, $element, $attrs, controller);
200 | }
201 | }
202 | });
203 | })();
--------------------------------------------------------------------------------