├── .gitignore ├── LICENSE ├── README.md ├── example ├── example.html └── example.js ├── package-lock.json ├── package.json └── src └── card.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 27 | node_modules 28 | 29 | bower_components 30 | 31 | tags 32 | .idea 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Sergey Gavruk 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 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | angular-card 2 | ============ 3 | 4 | Angular directive for card https://github.com/jessepollak/card 5 | 6 | ![screen shot 2014-12-20 at 4 37 52 pm](https://cloud.githubusercontent.com/assets/507195/5514998/9a252c80-8866-11e4-81ad-dea3dc5e6870.png) 7 | 8 | ## Demo 9 | 10 | http://jessepollak.github.io/card/ 11 | 12 | ## Installation 13 | 14 | ### npm 15 | `npm install angular-card` 16 | 17 | ## Usage 18 | 19 | ### `name` is required for form and inputs (you can use any unique name) 20 | ### `width` is optional, it can be set on the element or the options object (defaults to 350) 21 | 22 | ```html 23 |
31 | 32 |
33 | 34 | 35 | 36 |
37 |
38 | 39 | 40 | 41 | 42 | 43 | 44 |
45 | 46 |
47 | ``` 48 | 49 | ```js 50 | angular.module('app', ['gavruk.card']) 51 | .controller('ExampleCtrl', ['$scope', function($scope) { 52 | 53 | $scope.card = { 54 | name: 'Mike Brown', 55 | number: '5555 4444 3333 1111', 56 | expiry: '11 / 2020', 57 | cvc: '123' 58 | }; 59 | 60 | $scope.cardPlaceholders = { 61 | name: 'Your Full Name', 62 | number: 'xxxx xxxx xxxx xxxx', 63 | expiry: 'MM/YY', 64 | cvc: 'xxx' 65 | }; 66 | 67 | $scope.cardMessages = { 68 | validDate: 'valid\nthru', 69 | monthYear: 'MM/YYYY', 70 | }; 71 | 72 | $scope.cardOptions = { 73 | debug: false, 74 | formatting: true, 75 | width: 500 //optional 76 | }; 77 | 78 | }]); 79 | ``` 80 | 81 | #### Using multiple fields for card expiry 82 | 83 | Simply use 2 input fields for the expiry, and pass either `month`, or `year` to the directive. 84 | 85 | ```js 86 | 87 | 88 | ``` 89 | -------------------------------------------------------------------------------- /example/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Angular-Card Example 7 | 8 | 9 | 10 |
19 | 20 |
21 | 22 | 23 | 24 |
25 |
26 | 27 | 28 | 29 | 30 | 31 | 32 |
33 | 34 |
35 | 36 |
37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /example/example.js: -------------------------------------------------------------------------------- 1 | angular.module('app', ['gavruk.card']) 2 | 3 | .controller('ExampleCtrl', ['$scope', function($scope) { 4 | 5 | var card1 = { 6 | name: 'Mike Brown', 7 | number: '5555 4444 3333 1111', 8 | expiry: '11 / 2020', 9 | cvc: '123' 10 | }; 11 | var card2 = { 12 | name: 'Bill Smith', 13 | number: '4321 4321 4321 4321', 14 | expiry: '02 / 2018', 15 | cvc: '591' 16 | }; 17 | 18 | var selectedCard = 1; 19 | $scope.card = card1; 20 | 21 | $scope.changeCard = function() { 22 | if (selectedCard == 1) { 23 | $scope.card = card2; 24 | selectedCard = 2; 25 | } else { 26 | $scope.card = card1; 27 | selectedCard = 1; 28 | } 29 | }; 30 | 31 | $scope.clear = function() { 32 | $scope.card = {}; 33 | }; 34 | 35 | 36 | $scope.cardPlaceholders = { 37 | name: 'Your Full Name', 38 | number: 'xxxx xxxx xxxx xxxx', 39 | expiry: 'MM/YY', 40 | cvc: 'xxx' 41 | }; 42 | 43 | $scope.cardMessages = { 44 | validDate: 'valid\nthru', 45 | monthYear: 'MM/YYYY', 46 | }; 47 | 48 | $scope.cardOptions = { 49 | debug: false, 50 | formatting: true 51 | }; 52 | 53 | }]); 54 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-card", 3 | "version": "0.3.12", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "angular": { 8 | "version": "1.6.6", 9 | "resolved": "https://registry.npmjs.org/angular/-/angular-1.6.6.tgz", 10 | "integrity": "sha1-/Vo8+0N844LYVO4BEgeXl4Uny2Q=", 11 | "dev": true 12 | }, 13 | "card": { 14 | "version": "2.4.0", 15 | "resolved": "https://registry.npmjs.org/card/-/card-2.4.0.tgz", 16 | "integrity": "sha1-fBATHFROZ9q/3oJgizDdClOLEvo=", 17 | "requires": { 18 | "node.extend": "1.1.6", 19 | "payment": "2.3.0", 20 | "qj": "2.0.0" 21 | } 22 | }, 23 | "is": { 24 | "version": "3.2.1", 25 | "resolved": "https://registry.npmjs.org/is/-/is-3.2.1.tgz", 26 | "integrity": "sha1-0Kwq1V63sL7JJqUmb2xmKqqD3KU=" 27 | }, 28 | "node.extend": { 29 | "version": "1.1.6", 30 | "resolved": "https://registry.npmjs.org/node.extend/-/node.extend-1.1.6.tgz", 31 | "integrity": "sha1-p7iCyC1sk6SGOlUEvV3o7IYli5Y=", 32 | "requires": { 33 | "is": "3.2.1" 34 | } 35 | }, 36 | "payment": { 37 | "version": "2.3.0", 38 | "resolved": "https://registry.npmjs.org/payment/-/payment-2.3.0.tgz", 39 | "integrity": "sha1-sqlenDBSRZZH2jkmuLZsVT2TCY8=", 40 | "requires": { 41 | "qj": "2.0.0" 42 | } 43 | }, 44 | "qj": { 45 | "version": "2.0.0", 46 | "resolved": "https://registry.npmjs.org/qj/-/qj-2.0.0.tgz", 47 | "integrity": "sha1-BU3Tt1zgGHKNI6BgXwMN/aC9FYo=" 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-card", 3 | "version": "0.3.13", 4 | "description": "Angular directive for card https://github.com/jessepollak/card", 5 | "main": "src/card.js", 6 | "directories": { 7 | "example": "example" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://gavruk@github.com/gavruk/angular-card.git" 15 | }, 16 | "keywords": [ 17 | "angular", 18 | "credit", 19 | "card" 20 | ], 21 | "author": "Sergey Gavruk ", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/gavruk/angular-card/issues" 25 | }, 26 | "homepage": "https://github.com/gavruk/angular-card#readme", 27 | "dependencies": { 28 | "card": "^2.4.0" 29 | }, 30 | "peerDependencies": { 31 | "angular": "^1.4.8" 32 | }, 33 | "devDependencies": { 34 | "angular": "^1.4.8" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/card.js: -------------------------------------------------------------------------------- 1 | var hasRequire = window && window.angular ? false : typeof require === 'function'; 2 | 3 | (function (window, document, Card, angular, undefined) { 4 | 'use strict'; 5 | angular 6 | .module('gavruk.card', []) 7 | 8 | .controller('CardCtrl', ['$scope', function ($scope) { 9 | }]) 10 | 11 | .directive('card', ['$timeout', function ($timeout) { 12 | return { 13 | restrict: 'A', 14 | scope: { 15 | cardContainer: '@', // required 16 | width: '@', 17 | placeholders: '=', 18 | options: '=', 19 | messages: '=', 20 | }, 21 | controller: 'CardCtrl', 22 | link: function (scope, element, attributes, cardCtrl) { 23 | var defaultPlaceholders = { 24 | number: '•••• •••• •••• ••••', 25 | name: 'Full Name', 26 | expiry: '••/••', 27 | cvc: '•••' 28 | }; 29 | var defaultMessages = { 30 | validDate: 'valid\nthru', 31 | monthYear: 'month/year', 32 | }; 33 | var defaultOptions = { 34 | debug: false, 35 | formatting: true 36 | }; 37 | 38 | var placeholders = angular.extend(defaultPlaceholders, scope.placeholders); 39 | var messages = angular.extend(defaultMessages, scope.messages); 40 | var options = angular.extend(defaultOptions, scope.options); 41 | 42 | var opts = { 43 | form: '[name="' + attributes.name + '"]', 44 | 45 | // a selector or jQuery object for the container 46 | // where you want the card to appear 47 | container: scope.cardContainer, // *required* 48 | 49 | formSelectors: {}, 50 | 51 | width: options.width, 52 | 53 | // Strings for translation - optional 54 | messages: { 55 | validDate: messages.validDate, 56 | monthYear: messages.monthYear 57 | }, 58 | 59 | // Default placeholders for rendered fields - options 60 | placeholders: { 61 | number: placeholders.number, 62 | name: placeholders.name, 63 | expiry: placeholders.expiry, 64 | cvc: placeholders.cvc 65 | }, 66 | 67 | formatting: options.formatting, // optional - default true 68 | debug: options.debug // if true, will log helpful messages for setting up Card 69 | }; 70 | 71 | opts.width = opts.width || scope.width || 350; 72 | 73 | if (cardCtrl.numberInput && cardCtrl.numberInput.length > 0) { 74 | opts.formSelectors.numberInput = 'input[name="' + cardCtrl.numberInput[0].name + '"]'; 75 | } 76 | if (angular.isDefined(cardCtrl.expiryInput.combined)) { 77 | opts.formSelectors.expiryInput = 'input[name="' + cardCtrl.expiryInput.combined[0].name + '"]'; 78 | } else if (angular.isDefined(cardCtrl.expiryInput.month) && angular.isDefined(cardCtrl.expiryInput.year)) { 79 | opts.formSelectors.expiryInput = 'input[name="' + cardCtrl.expiryInput.month[0].name + '"], input[name="' + cardCtrl.expiryInput.year[0].name + '"]'; 80 | } 81 | if (cardCtrl.cvcInput && cardCtrl.cvcInput.length > 0) { 82 | opts.formSelectors.cvcInput = 'input[name="' + cardCtrl.cvcInput[0].name + '"]'; 83 | } 84 | if (cardCtrl.nameInput && cardCtrl.nameInput.length > 0) { 85 | opts.formSelectors.nameInput = 'input[name="' + cardCtrl.nameInput[0].name + '"]'; 86 | } 87 | 88 | //Don't initialize card until angular has had a chance to update the DOM with any interpolated bindings 89 | $timeout(angular.noop) 90 | .then(function () { 91 | new Card(opts); 92 | }); 93 | } 94 | }; 95 | }]) 96 | 97 | .directive('cardNumber', ['$compile', function ($compile) { 98 | return { 99 | restrict: 'A', 100 | scope: { 101 | ngModel: '=' 102 | }, 103 | require: [ 104 | '^card', 105 | 'ngModel' 106 | ], 107 | link: function (scope, element, attributes, ctrls) { 108 | var cardCtrl = ctrls[0]; 109 | cardCtrl.numberInput = element; 110 | scope.$watch('ngModel', function (newVal, oldVal) { 111 | if (!oldVal && !newVal) { 112 | return; 113 | } 114 | if (oldVal === newVal && !newVal) { 115 | return; 116 | } 117 | 118 | var evt = document.createEvent('HTMLEvents'); 119 | evt.initEvent('keyup', false, true); 120 | element[0].dispatchEvent(evt); 121 | }); 122 | } 123 | }; 124 | }]) 125 | 126 | .directive('cardName', ['$compile', function ($compile) { 127 | return { 128 | restrict: 'A', 129 | scope: { 130 | ngModel: '=' 131 | }, 132 | require: [ 133 | '^card', 134 | 'ngModel' 135 | ], 136 | link: function (scope, element, attributes, ctrls) { 137 | var cardCtrl = ctrls[0]; 138 | cardCtrl.nameInput = element; 139 | scope.$watch('ngModel', function (newVal, oldVal) { 140 | if (!oldVal && !newVal) { 141 | return; 142 | } 143 | if (oldVal === newVal && !newVal) { 144 | return; 145 | } 146 | 147 | var evt = document.createEvent('HTMLEvents'); 148 | evt.initEvent('keyup', false, true); 149 | element[0].dispatchEvent(evt); 150 | }); 151 | } 152 | }; 153 | }]) 154 | 155 | .directive('cardExpiry', ['$compile', function ($compile) { 156 | return { 157 | restrict: 'A', 158 | scope: { 159 | ngModel: '=', 160 | type: '@cardExpiry' 161 | }, 162 | require: [ 163 | '^card', 164 | 'ngModel' 165 | ], 166 | link: function (scope, element, attributes, ctrls) { 167 | var cardCtrl = ctrls[0]; 168 | var expiryType = scope.type || 'combined'; 169 | if (angular.isUndefined(cardCtrl.expiryInput)) { 170 | cardCtrl.expiryInput = {}; 171 | } 172 | cardCtrl.expiryInput[expiryType] = element; 173 | scope.$watch('ngModel', function (newVal, oldVal) { 174 | if (!oldVal && !newVal) { 175 | return; 176 | } 177 | if (oldVal === newVal && !newVal) { 178 | return; 179 | } 180 | 181 | var evt = document.createEvent('HTMLEvents'); 182 | evt.initEvent('keyup', false, true); 183 | element[0].dispatchEvent(evt); 184 | }); 185 | } 186 | }; 187 | }]) 188 | 189 | .directive('cardCvc', ['$compile', function ($compile) { 190 | return { 191 | restrict: 'A', 192 | scope: { 193 | ngModel: '=' 194 | }, 195 | require: [ 196 | '^card', 197 | 'ngModel' 198 | ], 199 | link: function (scope, element, attributes, ctrls) { 200 | var cardCtrl = ctrls[0]; 201 | cardCtrl.cvcInput = element; 202 | scope.$watch('ngModel', function (newVal, oldVal) { 203 | if (!oldVal && !newVal) { 204 | return; 205 | } 206 | if (oldVal === newVal && !newVal) { 207 | return; 208 | } 209 | 210 | var evt = document.createEvent('HTMLEvents'); 211 | evt.initEvent('keyup', false, true); 212 | element[0].dispatchEvent(evt); 213 | }); 214 | } 215 | }; 216 | }]); 217 | 218 | })(window, window.document, hasRequire ? require('card') : window.Card, hasRequire ? require('angular') : window.angular); 219 | 220 | if(typeof module !== 'undefined') { 221 | module.exports = 'gavruk.card'; 222 | } 223 | --------------------------------------------------------------------------------