├── .gitignore ├── .jshintrc ├── .ruby-version ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Dockerfile ├── Gruntfile.js ├── ISSUE_TEMPLATE.md ├── LICENSE ├── README.md ├── bower.json ├── component.json ├── composer.json ├── deploy.rb ├── dist ├── jquery.mask.js └── jquery.mask.min.js ├── docker-compose.yml ├── package-lock.json ├── package.js ├── package.json ├── src └── jquery.mask.js └── test ├── jquery.mask.test.js ├── qunit.css ├── qunit.js ├── sinon-1.10.3.js ├── sinon-qunit-1.0.0.js ├── sinon-qunit.js ├── test-for-jquery-1.11.1.html ├── test-for-jquery-1.7.2.html ├── test-for-jquery-1.8.3.html ├── test-for-jquery-1.9.1.html ├── test-for-jquery-2.1.1.html ├── test-for-jquery-3.0.0.html ├── test-for-zepto.html └── zepto ├── data.js ├── event.js └── zepto.min.js /.gitignore: -------------------------------------------------------------------------------- 1 | index.html 2 | css/* 3 | img/* 4 | js/* 5 | node_modules/ 6 | bower_components/ 7 | .sparkleshare 8 | libpeerconnection.log 9 | .versions 10 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "bitwise": true, 3 | "browser": true, 4 | "camelcase": true, 5 | "curly": true, 6 | "eqeqeq": true, 7 | "esnext": true, 8 | "expr": true, 9 | "globals": { 10 | "console": false, 11 | "define": false, 12 | "document": false, 13 | "expect": false, 14 | "module": false, 15 | "require": false, 16 | "window": false 17 | }, 18 | "immed": true, 19 | "indent": 4, 20 | "latedef": false, 21 | "maxcomplexity": 15, 22 | "newcap": true, 23 | "noarg": true, 24 | "node": true, 25 | "noempty": true, 26 | "nonstandard": true, 27 | "quotmark": "single", 28 | "regexp": true, 29 | "smarttabs": true, 30 | "strict": false, 31 | "trailing": true, 32 | "undef": true, 33 | "unused": true 34 | } 35 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.0.0p598 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "8.9.3" 4 | before_script: 5 | - npm install -g grunt-cli 6 | - npm install 7 | script: grunt test 8 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | == v1.14.16 (Jul/31 2019 16:32 +0100 by Igor Escobar) == 2 | 3 | Bugfixes: 4 | 5 | * Fixed oldVal being updated prior to caret position calculation 6 | * Solve loading JQuery as module on Meteor 7 | 8 | == v1.14.15 (Mar/08 2018 22:59 +0000 by Igor Escobar) == 9 | 10 | Bugfixes: 11 | 12 | * rolling back change to fix caret positioning. it didn’t worked on some devices 13 | 14 | == v1.14.14 (Mar/02 2018 16:55 +0000 by Igor Escobar) == 15 | 16 | Bugfixes: 17 | 18 | * fixing mask positioning delays 19 | * unmask: also removing place holder if added on the first place. 20 | * unmask: unsetting maxlength if we set it in the first place 21 | 22 | == v1.14.13 (Dec/11 2017 18:59 +0000 by Igor Escobar) == 23 | 24 | Bugfixes: 25 | 26 | * fixes caret issue explained on #636 27 | * fixing use strict issue 28 | 29 | == v1.14.12 (Oct/04 2017 09:57 +0100 by Igor Escobar) == 30 | 31 | Bugfixes: 32 | 33 | * bug fixing on caret positioning on some devices 34 | 35 | == v1.14.11 (May/30 2017 21:53 +0100 by Igor Escobar) == 36 | 37 | Bugfixes: 38 | 39 | * fixing a lot of caret positioning issues. Thanks to @onuradsay 40 | 41 | == v1.14.10 (Feb/13 2017 14:18 +0000 by Igor Escobar) == 42 | 43 | Bugfixes: 44 | 45 | * fixing exception when oValue in undefined 46 | 47 | == v1.14.9 (Jan/25 2017 11:17 +0000 by Igor Escobar) == 48 | 49 | Bugfixes: 50 | 51 | * don’t use input event when using samsung browser or old chrome versions 52 | 53 | == v1.14.8 (Dec/26 2016 13:18 +0000 by Igor Escobar) == 54 | 55 | Bugfixes: 56 | 57 | * fixing caret on android with chrome 28 58 | 59 | == v1.14.7 (Dec/25 2016 03:51 +0000 by Igor Escobar) == 60 | 61 | Bugfixes: 62 | 63 | * improving caret positioning when cursor is on the middle 64 | 65 | == v1.14.6 (Dec/24 2016 17:14 +0000 by Igor Escobar) == 66 | 67 | Bugfixes: 68 | 69 | * fix caret positioning with multiple mask chars 70 | 71 | == v1.14.5 (Dec/24 2016 14:42 +0000 by Igor Escobar) == 72 | 73 | Changes: 74 | 75 | * fixing reserved word 76 | 77 | == v1.14.4 (Dec/24 2016 14:38 +0000 by Igor Escobar) == 78 | 79 | Bugfixes: 80 | 81 | * fixing android cursor positioning (special thanks to @felipejunges and @fernandobandeira) 82 | 83 | == v1.14.3 (Nov/28 2016 11:53 +0000 by Igor Escobar) == 84 | 85 | Bugfixes: 86 | 87 | * fixing caret positioning on safari 88 | 89 | == v1.14.2 (Nov/27 2016 20:04 +0000 by Igor Escobar) == 90 | 91 | Bugfixes: 92 | 93 | * apply auto maxlength in case the mask doesn't have recursive pattern 94 | 95 | == v1.14.1 (Nov/27 2016 19:20 +0000 by Igor Escobar) == 96 | 97 | Bugfixes: 98 | 99 | * Fix input value mangling when inserting before a static mask character 100 | * fixing caret position issue 101 | 102 | == v1.14.0 (Apr/03 2016 17:52 +0100 by Igor Escobar) == 103 | 104 | Bugfixes: 105 | 106 | * Fix cursor jumping while editing in non-IE browsers. Thanks to @archwyrm 107 | 108 | Features: 109 | 110 | * adding masked function for better angular use 111 | 112 | == v1.13.9 (Mar/20 2016 16:17 +0000 by Igor Escobar) == 113 | 114 | Changes: 115 | 116 | * giving the opportunity to pass watchInputs locally 117 | 118 | == v1.13.8 (Mar/06 2016 23:25 +0000 by Igor Escobar) == 119 | 120 | Changes: 121 | 122 | * adding support for meteor 123 | 124 | == v1.13.7 (Mar/06 2016 22:46 +0000 by Igor Escobar) == 125 | 126 | Bugfixes: 127 | 128 | * fixing onChange behaviour 129 | 130 | == v1.13.6 (Mar/06 2016 22:14 +0000 by Igor Escobar) == 131 | 132 | Bugfixes: 133 | 134 | * fixing deploy procedure 135 | 136 | == v1.13.5 (Mar/06 2016 22:01 +0000 by Igor Escobar) == 137 | 138 | Changes: 139 | 140 | * adding clearIfNotMatch to globalOptions 141 | 142 | Bugfixes: 143 | 144 | * fixing some bugs when using non-input elements 145 | * fixing mobile issues at #348. 146 | * using input event when supported 147 | 148 | == v1.13.4 (Aug/07 2015 14:21 +0100 by Igor Escobar) == 149 | 150 | Bugfixes: 151 | 152 | * Add check to ensure that there are input elements before using them 153 | 154 | == v1.13.3 (Jul/16 2015 16:11 +0100 by Igor Escobar) == 155 | 156 | Changes: 157 | 158 | * adding main property to package.json 159 | 160 | == v1.13.2 (Jul/16 2015 16:06 +0100 by Igor Escobar) == 161 | 162 | Bugfixes: 163 | 164 | * change event wasnt being triggered in some cases 165 | 166 | == v1.13.1 (Jul/07 2015 15:38 +0100 by Igor Escobar) == 167 | 168 | Bugfixes: 169 | 170 | * destroying input event too 171 | 172 | == v1.13.0 (Jul/07 2015 15:26 +0100 by Igor Escobar) == 173 | 174 | Changes: 175 | 176 | * removing the autocomplete default. 177 | 178 | Bugfixes: 179 | 180 | * fixing bower file thanks to @lazyants 181 | 182 | Features: 183 | 184 | * prevent glitch when invalid chars. 185 | * turning off autocomplete when browsers doesn't support oninput event. 186 | 187 | == v1.12.0 (Jul/07 2015 11:37 +0100 by Igor Escobar) == 188 | 189 | Features: 190 | 191 | * giving an alternative to the autocomplete/autofill problem. 192 | 193 | == v1.11.4 (Feb/26 2015 22:11 +0000 by Igor Escobar) == 194 | 195 | Changes: 196 | 197 | * grunt, jshint and better applyDataMask. Thanks to @lagden 198 | * automated deploy to npm 199 | 200 | == v1.11.3 (Jan/28 2015 15:41 +0000 by Igor Escobar) == 201 | 202 | Changes: 203 | 204 | * Added commonjs module definition 205 | 206 | == v1.11.2 (Dec/26 2014 15:36 +0000 by Igor Escobar) == 207 | 208 | Bugfixes: 209 | 210 | * unreachable code 211 | 212 | == v1.11.1 (Dec/26 2014 15:34 +0000 by Igor Escobar) == 213 | 214 | Bugfixes: 215 | 216 | * unreachable code 217 | 218 | == v1.11.0 (Dec/26 2014 15:33 +0000 by Igor Escobar) == 219 | 220 | Features: 221 | 222 | * implementing selectOnFocus and data-mask-selectonfocus option 223 | * adding public method called: .applyDataMask in case you want to decide whether to apply masks in data-mask fields 224 | 225 | == v1.10.13 (Nov/19 2014 16:06 +0000 by Igor Escobar) == 226 | 227 | Bugfixes: 228 | 229 | * fixing bug with watchInputs feature when mask is used as a function and not a string. 230 | 231 | == v1.10.12 (Nov/06 2014 13:08 +0000 by Igor Escobar) == 232 | 233 | Changes: 234 | 235 | * making a few improvements to make selection, copy events easier 236 | 237 | == v1.10.11 (Nov/06 2014 11:26 +0000 by Igor Escobar) == 238 | 239 | Bugfixes: 240 | 241 | * we need to revaluate dataMask flags everytime 242 | 243 | == v1.10.10 (Nov/06 2014 10:41 +0000 by Igor Escobar) == 244 | 245 | Bugfixes: 246 | 247 | * fixing dynamically data-mask added elements 248 | 249 | == v1.10.9 (Nov/05 2014 10:52 +0000 by Igor Escobar) == 250 | 251 | Bugfixes: 252 | 253 | * data-mask wasnt working 254 | 255 | == v1.10.8 (Nov/01 2014 13:49 +0000 by Igor Escobar) == 256 | 257 | Changes: 258 | 259 | * we dont need to seek for data-mask every time 260 | 261 | == v1.10.7 (Nov/01 2014 13:18 +0000 by Igor Escobar) == 262 | 263 | Changes: 264 | 265 | * little optimization 266 | 267 | == v1.10.6 (Oct/28 2014 13:59 +0000 by Igor Escobar) == 268 | 269 | Bugfixes: 270 | 271 | * fixing weird cursor problems in weird cases. 272 | * dynamically added inputs wasnt working 273 | 274 | == v1.10.5 (Oct/23 2014 11:41 +0100 by Igor Escobar) == 275 | 276 | Bugfixes: 277 | 278 | * fixing weird cursor problems in weird cases. 279 | 280 | == v1.10.4 (Oct/23 2014 11:02 +0100 by Igor Escobar) == 281 | 282 | Bugfixes: 283 | 284 | * fixing on the fly mask change feature. 285 | 286 | == v1.10.3 (Oct/22 2014 09:50 +0100 by Igor Escobar) == 287 | 288 | Bugfixes: 289 | 290 | * fixing unmask method. 291 | 292 | == v1.10.2 (Oct/20 2014 16:38 +0100 by Igor Escobar) == 293 | 294 | Bugfixes: 295 | 296 | * onChange event fired at the wrong time when the field already has a value. 297 | 298 | == v1.10.1 (Oct/20 2014 16:08 +0100 by Igor Escobar) == 299 | 300 | Bugfixes: 301 | 302 | * fixing onChange event behaviour 303 | 304 | == v1.10.0 (Oct/20 2014 10:56 +0100 by Igor Escobar) == 305 | 306 | Features: 307 | 308 | * adding a way to change global settings like translation object and the byPassKeys object. 309 | 310 | == v1.9.2 (Oct/20 2014 10:08 +0100 by Igor Escobar) == 311 | 312 | Bugfixes: 313 | 314 | * fixing fallback digits implementation. Thanks @A1rPun 315 | 316 | == v1.9.1 (Oct/18 2014 12:27 +0100 by Igor Escobar) == 317 | 318 | Bugfixes: 319 | 320 | * cant convert circular json exception 321 | 322 | == v1.9.0 (Oct/18 2014 12:07 +0100 by Igor Escobar) == 323 | 324 | Features: 325 | 326 | * adding onInvalid callback 327 | 328 | == v1.8.0 (Oct/17 2014 11:35 +0100 by Igor Escobar) == 329 | 330 | Changes: 331 | 332 | * removing automatic maxlength support 333 | * making a few optimizations to make it faster and retro compatible with other libraries 334 | * creating globalOptions to make it more fast and flexible 335 | 336 | Bugfixes: 337 | 338 | * fixing issue #196 339 | 340 | Features: 341 | 342 | * adding the fallback translation option 343 | 344 | == v1.7.8 (Oct/15 2014 10:55 +0100 by Igor Escobar) == 345 | 346 | Bugfixes: 347 | 348 | * change event may experience issues 349 | * avoid maximum call stack trace error 350 | 351 | == v1.7.7 (Sep/10 2014 22:31 +0100 by Igor Escobar) == 352 | 353 | Bugfixes: 354 | 355 | * fixing clojure compile issue 356 | 357 | == v1.7.6 (Sep/10 2014 22:14 +0100 by Igor Escobar) == 358 | 359 | Bugfixes: 360 | 361 | * fixing clearifnotmatch in masks with literal digits 362 | 363 | == v1.7.5 (Sep/09 2014 15:43 +0100 by Igor Escobar) == 364 | 365 | Bugfixes: 366 | 367 | * fixing paste inside of empty fields. 368 | 369 | == v1.7.4 (Aug/11 2014 14:53 +0100 by Igor Escobar) == 370 | 371 | Changes: 372 | 373 | * smaller and reliable code 374 | 375 | == v1.7.3 (Aug/11 2014 11:28 +0100 by Igor Escobar) == 376 | 377 | Bugfixes: 378 | 379 | * fixing issue #185 380 | 381 | == v1.7.2 (Aug/08 2014 11:11 +0100 by Igor Escobar) == 382 | 383 | Changes: 384 | 385 | * smaller code 386 | 387 | Bugfixes: 388 | 389 | * fixing remove bug 390 | 391 | == v1.7.1 (Aug/08 2014 00:55 +0100 by Igor Escobar) == 392 | 393 | Changes: 394 | 395 | * upgrading zepto, smaller syntax and fixing build 396 | 397 | == v1.7.0 (Aug/07 2014 23:56 +0100 by Igor Escobar) == 398 | 399 | Features: 400 | 401 | * applying masks to dynamically added elements. (html/javascript notation) 402 | 403 | == v1.6.5 (Jun/30 2014 10:24 +0100 by Igor Escobar) == 404 | 405 | Bugfixes: 406 | 407 | * fixing clearIfNotMatch feature in cases of optional and recursive digits 408 | 409 | == v1.6.4 (May/08 2014 23:54 +0100 by Igor Escobar) == 410 | 411 | Changes: 412 | 413 | * testing some deployment stunts 414 | 415 | == v1.6.3 (May/08 2014 23:51 +0100 by Igor Escobar) == 416 | 417 | Changes: 418 | 419 | * testing some deployment stunts 420 | 421 | == v1.6.2 (May/08 2014 23:45 +0100 by Igor Escobar) == 422 | 423 | Bugfixes: 424 | 425 | * fuckin typo 426 | 427 | == v1.6.1 (May/08 2014 23:39 +0100 by Igor Escobar) == 428 | 429 | Bugfixes: 430 | 431 | * fixing autofocus bug 432 | 433 | == v1.6.0 (May/07 2014 21:13 +0100 by Igor Escobar) == 434 | 435 | Bugfixes: 436 | 437 | * fixing autofocus bug 438 | 439 | Features: 440 | 441 | * adding support to the clearIfNotMatch option 442 | * HTML5 placeholder support 443 | 444 | == v1.5.7 (May/01 2014 18:37 +0100 by Igor Escobar) == 445 | 446 | Changes: 447 | 448 | * some cleanup and stuff 449 | 450 | == v1.5.6 (May/01 2014 18:30 +0100 by Igor Escobar) == 451 | 452 | Bugfixes: 453 | 454 | * Bug in calculating difference between mask characters between old and new field values 455 | * Fix stack limit exceeded 456 | 457 | == v1.5.5 (Apr/27 2014 13:47 +0100 by Igor Escobar) == 458 | 459 | Changes: 460 | 461 | * UMD (Universal Module Definition) patterns for JavaScript modules 462 | 463 | Bugfixes: 464 | 465 | * caret position correction 466 | * 114 - Fix onChange Event error 467 | 468 | == v1.5.4 (Feb/09 2014 12:02 +0000 by Igor Escobar) == 469 | 470 | Changes: 471 | 472 | * optmizing code 473 | 474 | == v1.5.3 (Feb/08 2014 14:59 +0000 by Igor Escobar) == 475 | 476 | Bugfixes: 477 | 478 | * fixing ctrl a bug 479 | 480 | == v1.5.2 (Dec/20 2013 16:35 +0000 by Igor Escobar) == 481 | 482 | Changes: 483 | 484 | * smaller source code 485 | 486 | == v1.5.1 (Dec/18 2013 22:34 +0000 by Igor Escobar) == 487 | 488 | Changes: 489 | 490 | * fixing some code climate problems 491 | 492 | == v1.5.0 (Dec/18 2013 22:10 +0000 by Igor Escobar) == 493 | 494 | Bugfixes: 495 | 496 | * fixing getCleanVal() 497 | 498 | Features: 499 | 500 | * new public method called cleanVal 501 | 502 | == v1.4.2 (Dec/16 2013 15:48 +0000 by Igor Escobar) == 503 | 504 | Bugfixes: 505 | 506 | * Dirty fix for masks not completing with a literal 507 | 508 | == v1.4.1 (Dec/09 2013 21:23 +0000 by Igor Escobar) == 509 | 510 | Changes: 511 | 512 | * revising ignored keys 513 | 514 | == v1.4.0 (Nov/28 2013 18:06 +0000 by Igor Escobar) == 515 | 516 | Features: 517 | 518 | * caret positioning implementation 519 | 520 | == v1.3.1 (Oct/08 2013 20:38 +0100 by Igor Escobar) == 521 | 522 | Changes: 523 | 524 | * adding more keys to ignore list to make the char navigation smoothly 525 | 526 | Bugfixes: 527 | 528 | * Sounds like 'options' has disappeared for some reason 529 | 530 | == v1.3.0 (Sep/13 2013 10:37 +0100 by Igor Escobar) == 531 | 532 | Features: 533 | 534 | * creating the maxlength option 535 | 536 | == v1.2.0 (Sep/07 2013 12:07 +0100 by Igor Escobar) == 537 | 538 | Features: 539 | 540 | * adding the possibility to put recursive digits inside masks 541 | 542 | == v1.1.3 (Sep/04 2013 21:21 +0100 by Igor Escobar) == 543 | 544 | Bugfixes: 545 | 546 | * fixing late masking 547 | 548 | == v1.1.2 (Aug/26 2013 15:08 +0100 by Igor Escobar) == 549 | 550 | Bugfixes: 551 | 552 | * fixing mask on div,span etc 553 | 554 | == v1.1.1 (Aug/26 2013 14:42 +0100 by Igor Escobar) == 555 | 556 | Bugfixes: 557 | 558 | * better callback handling 559 | 560 | == v1.1.0 (Aug/24 2013 15:59 +0100 by Igor Escobar) == 561 | 562 | Features: 563 | 564 | * adding onchange support 565 | 566 | == v1.0.3 (Aug/23 2013 23:10 +0100 by Igor Escobar) == 567 | 568 | Changes: 569 | 570 | * optimizations to mask on non html fields 571 | 572 | == v1.0.2 (Aug/23 2013 22:46 +0100 by Igor Escobar) == 573 | 574 | Bugfixes: 575 | 576 | * adding remask method do improve callback performance 577 | 578 | == v1.0.1 (Aug/23 2013 22:01 +0100 by Igor Escobar) == 579 | 580 | Changes: 581 | 582 | * normal releases again 583 | 584 | == v1.0.0 (Aug/23 2013 21:59 +0100 by Igor Escobar) == 585 | 586 | Features: 587 | 588 | * huge refactoring focusing no reduce source code weight and bugfixing 589 | 590 | == v0.11.5 (Aug/20 2013 17:11 +0100 by Igor Escobar) == 591 | 592 | Bugfixes: 593 | 594 | * bug fixing when mask range is bigger than 2 digits. 595 | 596 | == v0.11.4 (Aug/19 2013 10:24 +0100 by Igor Escobar) == 597 | 598 | Changes: 599 | 600 | * adding de delete key to byPassKeys 601 | 602 | == v0.11.3 (Aug/18 2013 00:48 +0100 by Igor Escobar) == 603 | 604 | Bugfixes: 605 | 606 | * fixing zepto compatibily 607 | 608 | == v0.11.2 (Aug/17 2013 18:39 +0100 by Igor Escobar) == 609 | 610 | Bugfixes: 611 | 612 | * jmask iterate all items 613 | 614 | == v0.11.1 (Aug/17 2013 18:32 +0100 by Igor Escobar) == 615 | 616 | Changes: 617 | 618 | * a little bit smaller source code 619 | 620 | == v0.11.0 (Aug/16 2013 21:27 +0100 by Igor Escobar) == 621 | 622 | Bugfixes: 623 | 624 | * Altered "ignored keys" hook to run events (i.e. onKeyPress) afterwards. Otherwise, we miss key triggered events when the user deletes the entire text box, etc. 625 | 626 | Features: 627 | 628 | * adding support to method getCleanVal 629 | 630 | == v0.10.1 (Jul/26 2013 09:35 +0100 by ) == 631 | 632 | 633 | 634 | == v0.10.0 (Jul/19 2013 23:07 +0100 by Igor Escobar) == 635 | 636 | Features: 637 | 638 | * adding data-mask support 639 | 640 | == v0.9.1 (Jul/19 2013 22:35 +0100 by Igor Escobar) == 641 | 642 | Changes: 643 | 644 | * jQuery-Mask-Plugin is now available at bower.io 645 | 646 | Bugfixes: 647 | 648 | * fixing addEventListener on IE7 649 | 650 | == v0.9.0 (Apr/24 2013 07:44 +0100 by Igor Escobar) == 651 | 652 | Features: 653 | 654 | * Adding compatibility with Zepto.js 655 | 656 | == v0.8.0 (Apr/07 2013 18:39 +0100 by Igor Escobar) == 657 | 658 | Features: 659 | 660 | * applying masks anything != than input :) 661 | * implementing the possibility of range chars ex: A{1,3} 662 | 663 | == v0.7.11 (Apr/05 2013 22:12 +0100 by Igor Escobar) == 664 | 665 | Changes: 666 | 667 | * now when you type a wrong char, the plugin will make your text fit inside of the mask instead of lose your data. 668 | 669 | == v0.7.10 (Apr/04 2013 22:14 +0100 by Igor Escobar) == 670 | 671 | Changes: 672 | 673 | * changing yui-compressor to clojure-compiler 674 | 675 | == v0.7.9 (Apr/04 2013 22:04 +0100 by Igor Escobar) == 676 | 677 | Changes: 678 | 679 | * refactoring and implementation of optional mask digits 680 | 681 | Bugfixes: 682 | 683 | * fixing maxlength and adding a smarter mask removal. issue #18 684 | 685 | == v0.7.8 (Mar/30 2013 00:48 +0000 by Igor Escobar) == 686 | 687 | Changes: 688 | 689 | * a few changes to get the code smallest possible. 690 | * removing unnecessary methods and making code smaller. 691 | 692 | == v0.7.7 (Mar/29 2013 12:38 +0000 by Igor Escobar) == 693 | 694 | Bugfixes: 695 | 696 | * fixing copy and paste problem related on issue #15 697 | 698 | == v0.7.6 (Mar/29 2013 00:28 +0000 by Igor Escobar) == 699 | 700 | Bugfixes: 701 | 702 | * correcting mask formatationg problem related on issue #16 703 | 704 | == v0.7.5 (Mar/03 2013 20:56 +0000 by Igor Escobar) == 705 | 706 | Changes: 707 | 708 | * generating .gz file on deploy 709 | 710 | == v0.7.4 (Mar/03 2013 20:38 +0000 by Igor Escobar) == 711 | 712 | Changes: 713 | 714 | * changing minifier jsmin to yui compressor. 715 | 716 | == v0.7.3 (Mar/02 2013 01:12 +0000 by Igor Escobar) == 717 | 718 | Bugfixes: 719 | 720 | * bug fixing when typed wrong data type on mixing masks. 721 | 722 | == v0.7.2 (Feb/24 2013 22:02 +0000 by Igor Escobar) == 723 | 724 | Bugfixes: 725 | 726 | * fuckin stupid comma. 727 | 728 | == v0.7.1 (Feb/24 2013 21:57 +0000 by Igor Escobar) == 729 | 730 | Changes: 731 | 732 | * testing the private method maskToRegex 733 | * a little bit of changes to make the code more testable 734 | 735 | == v0.7.0 (Feb/12 2013 00:30 +0000 by Igor Escobar) == 736 | 737 | Features: 738 | 739 | * Now you can decide for jquery mask plugin how to interpret 0 to 9, A and S and even teach him how to reconize patterns. 740 | 741 | == v0.6.3 (Feb/11 2013 12:20 +0000 by Igor Escobar) == 742 | 743 | Bugfixes: 744 | 745 | * When the user paste a text and the last char is valid sanitize may fail 746 | 747 | == v0.6.2 (Feb/11 2013 00:02 +0000 by Igor Escobar) == 748 | 749 | Bugfixes: 750 | 751 | * allowing the user type the same character as the mask without erasing it. 752 | 753 | == v0.6.1 (Jan/20 2013 23:57 +0000 by Igor Escobar) == 754 | 755 | Changes: 756 | 757 | * changing the way ta deployment occurs to correct jquery plugins deployments. 758 | 759 | == v0.6.0 (Jan/18 2013 17:19 +0000 by Igor Escobar) == 760 | 761 | Changes: 762 | 763 | * Now pushing jQuery Mask Plugin to jQuery Plugins Repository 764 | 765 | == v0.5.4 (Jan/17 2013 23:06 +0000 by Igor Escobar) == 766 | 767 | Changes: 768 | 769 | * upgrading jquery plugins manifest file 770 | 771 | == v0.5.3 (Jan/17 2013 22:48 +0000 by Igor Escobar) == 772 | 773 | Bugfixes: 774 | 775 | * correctly generating jmask version inside of jquery mask source 776 | 777 | == v0.5.2 (Jan/17 2013 22:43 +0000 by Igor Escobar) == 778 | 779 | Changes: 780 | 781 | * Now pushing to jQuery Plugin Repository 782 | 783 | == v0.5.1 (Jan/07 2013 23:33 +0000 by Igor Escobar) == 784 | 785 | Changes: 786 | 787 | * improving the deploy process with the new stepup's upgrade. 788 | 789 | == v0.5.0 (Oct/27 2012 13:40 +0100 by Igor Escobar) == 790 | 791 | Bugfixes: 792 | 793 | * Bug fixes on OnSupport method with Firefox. 794 | 795 | Features: 796 | 797 | * the first parameter of the .mask() function, now accepts a string or a anonymous function 798 | 799 | == v0.4.7 (Aug/06 2012 22:56 +0100 by Igor Escobar) == 800 | 801 | Changes: 802 | 803 | * Nothing big, just class refactoring 804 | 805 | == v0.4.6 (Aug/06 2012 01:25 +0100 by Igor Escobar) == 806 | 807 | Changes: 808 | 809 | - better OOP design 810 | - implementing the jquery data object on each mask field 811 | - implementing the public method .remove to disable and remove the mask 812 | 813 | == v0.4.5 (Aug/04 2012 01:31 +0100 by Igor Escobar) == 814 | 815 | Changes: 816 | 817 | - improving support to complex jquery selectors 818 | - performance improvement. 819 | - callback handling improvement 820 | 821 | == v0.4.4 (Jun/03 2012 21:01 +0100 by Igor Escobar) == 822 | 823 | Bugfixes: 824 | 825 | * Bug fixes on Internet Explorer 8. 826 | 827 | == v0.4.3 (Mar/19 2012 21:52 +0000 by Igor Escobar) == 828 | 829 | Bugfixes: 830 | 831 | * Corrigindo bug para mascaras com + 832 | 833 | == v0.4.2 (Mar/18 2012 15:28 +0000 by Igor Escobar) == 834 | 835 | Bugfixes: 836 | 837 | * Mascara não pararecia no firefox 838 | 839 | == v0.4.1 (Mar/18 2012 15:01 +0000 by Igor Escobar) == 840 | 841 | Bugfixes: 842 | 843 | * Corrigindo tim das macaras. 844 | 845 | == v0.4.0 (Mar/18 2012 14:51 +0000 by Igor Escobar) == 846 | 847 | Features: 848 | 849 | * Implementado mascara reversa para moeda/cpf/rg/etc. 850 | * Nova engine. 851 | 852 | == v0.3.0 (Mar/14 2012 10:14 +0000 by Igor Escobar) == 853 | 854 | Changes: 855 | 856 | * License and comments up to date. 857 | 858 | Features: 859 | 860 | * On-the-fly mask change. 861 | * onComplete and onKeyPress new callbacks. 862 | 863 | == v0.2.5 (Mar/13 2012 22:55 +0000 by Igor Escobar) == 864 | 865 | Bugfixes: 866 | 867 | - Corrigindo ctrl+v com mascara errada. - Cortando dados que exceder a mascara no ctrl+v ou se segurar alguma tecla. - Refatorando algumas partes do código. 868 | 869 | == v0.2.4 (Mar/13 2012 11:06 +0000 by Igor Escobar) == 870 | 871 | Changes: 872 | 873 | * Codigo refatorado, otimizado, validação mais precisa e efetiva. 874 | 875 | == v0.2.3 (Mar/13 2012 01:01 +0000 by Igor Escobar) == 876 | 877 | Changes: 878 | 879 | * Melhorando expressoes regulares. 880 | 881 | == v0.2.2 (Mar/13 2012 00:50 +0000 by Igor Escobar) == 882 | 883 | Bugfixes: 884 | 885 | * Corrindo regex de validação 886 | 887 | == v0.2.1 (Mar/13 2012 00:41 +0000 by Igor Escobar) == 888 | 889 | Bugfixes: 890 | 891 | * Corrigida validação alphanumerica. 892 | 893 | == v0.2.0 (Mar/13 2012 00:24 +0000 by Igor Escobar) == 894 | 895 | Features: 896 | 897 | - Input Data Type Validation. 898 | - Automatic MaxLength (When are not defined). 899 | - Live Event Implemented for Ajax-based Apps. 900 | - Mixed mask with validation. 901 | * S for string digit 902 | * A for alphanumeric digit 903 | * 0 to 9 for numeric digit. 904 | 905 | == v0.1.1 (Mar/10 2012 14:05 +0000 by Igor Escobar) == 906 | 907 | Bugfixes: 908 | 909 | * Implementando Crossbrowser event handling. 910 | 911 | == v0.1.0 (Mar/10 2012 13:10 +0000 by Igor Escobar) == 912 | 913 | Features: 914 | 915 | * Implementando mascaras com espaço para data e hora 916 | 917 | == v0.0.1 (Mar/10 2012 04:42 +0000 by Igor Escobar) == 918 | 919 | Changes: 920 | 921 | * Refatorando o codigo para suportar multiplas instancias 922 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ### Have you take a look into our docs? 2 | https://igorescobar.github.io/jQuery-Mask-Plugin/ 3 | 4 | ### Want to contribute? Make sure you read this first 5 | https://github.com/igorescobar/jQuery-Mask-Plugin#contributing 6 | 7 | Is this plugin helping you out? Buy me a beer and cheers! :beer: 8 | 9 | :bowtie: https://www.paypal.me/igorcescobar 10 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM phusion/baseimage:0.9.17 2 | 3 | # Use baseimage-docker's init system. 4 | CMD ["/sbin/my_init"] 5 | 6 | # Java 8 for Google's clojure compiler 7 | RUN \ 8 | echo oracle-java8-installer shared/accepted-oracle-license-v1-1 select true | debconf-set-selections && \ 9 | echo "deb http://dl.bintray.com/sbt/debian /" | tee -a /etc/apt/sources.list.d/sbt.list && \ 10 | add-apt-repository -y ppa:webupd8team/java && \ 11 | apt-get update && \ 12 | apt-get install -y oracle-java8-installer git unzip ruby-full && \ 13 | rm -rf /var/lib/apt/lists/* && \ 14 | rm -rf /var/cache/oracle-jdk8-installer 15 | 16 | # Define commonly used JAVA_HOME variable 17 | ENV JAVA_HOME /usr/lib/jvm/java-8-oracle 18 | 19 | RUN mkdir /app 20 | RUN mkdir /app/clojure-compiler 21 | 22 | # Clojure compiler 23 | RUN \ 24 | curl -O http://dl.google.com/closure-compiler/compiler-latest.zip && \ 25 | unzip compiler-latest.zip -d /app/clojure-compiler && \ 26 | chmod a+x /app/clojure-compiler && \ 27 | rm compiler-latest.zip 28 | 29 | RUN gem install bundler pry step-up --no-rdoc --no-ri 30 | 31 | # Install Node.js 32 | RUN curl --silent --location https://deb.nodesource.com/setup_0.12 | sudo bash - 33 | RUN apt-get install --yes nodejs 34 | 35 | RUN npm install -g grunt-cli 36 | WORKDIR /app/jquery-mask-plugin 37 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | require('load-grunt-tasks')(grunt); 3 | require('time-grunt')(grunt); 4 | 5 | grunt.initConfig({ 6 | jshint: { 7 | options: { 8 | jshintrc: '.jshintrc', 9 | reporter: require('jshint-stylish') 10 | }, 11 | all: ['src/{,*/}*.js'] 12 | }, 13 | connect: { 14 | server: { 15 | options: { 16 | port: 9001, 17 | base: './' 18 | } 19 | } 20 | }, 21 | qunit: { 22 | all: { 23 | options: { 24 | urls: [ 25 | 'http://localhost:9001/test/test-for-jquery-1.11.1.html', 26 | 'http://localhost:9001/test/test-for-jquery-1.7.2.html', 27 | 'http://localhost:9001/test/test-for-jquery-1.8.3.html', 28 | 'http://localhost:9001/test/test-for-jquery-1.9.1.html', 29 | 'http://localhost:9001/test/test-for-jquery-2.1.1.html', 30 | 'http://localhost:9001/test/test-for-jquery-3.0.0.html', 31 | 'http://localhost:9001/test/test-for-zepto.html' 32 | ] 33 | } 34 | } 35 | } 36 | }); 37 | 38 | // A convenient task alias. 39 | grunt.registerTask('test', ['jshint', 'connect', 'qunit']); 40 | grunt.registerTask('default', ['test']); 41 | 42 | }; 43 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Have you take a look into our docs? 2 | https://igorescobar.github.io/jQuery-Mask-Plugin/ 3 | 4 | ### Make sure your read this before opening a new issue: 5 | https://github.com/igorescobar/jQuery-Mask-Plugin#problems-or-questions 6 | 7 | #### Device 8 | [...] 9 | 10 | #### Browser (and version)? 11 | [...] 12 | 13 | #### Functional `jsfiddle` exemplifying your problem: 14 | You can use this one as exemple: http://jsfiddle.net/igorescobar/6pco4om7/ 15 | 16 | #### Describe de problem depth: 17 | [...] 18 | 19 | 20 | Is this plugin helping you out? Buy me a beer and cheers! :beer: 21 | 22 | :bowtie: https://www.paypal.me/igorcescobar 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Created by Igor Escobar on 2012-03-10. Please report any bug at http://blog.igorescobar.com 2 | 3 | Copyright (c) 2012 Igor Escobar http://blog.igorescobar.com 4 | 5 | The MIT License (http://www.opensource.org/licenses/mit-license.php) 6 | 7 | Permission is hereby granted, free of charge, to any person 8 | obtaining a copy of this software and associated documentation 9 | files (the "Software"), to deal in the Software without 10 | restriction, including without limitation the rights to use, 11 | copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the 13 | Software is furnished to do so, subject to the following 14 | conditions: 15 | 16 | The above copyright notice and this permission notice shall be 17 | included in all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 21 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 | OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jQuery Mask Plugin 2 | A jQuery Plugin to make masks on form fields and HTML elements. 3 | 4 | [![Build Status](https://travis-ci.org/igorescobar/jQuery-Mask-Plugin.png)](https://travis-ci.org/igorescobar/jQuery-Mask-Plugin) 5 | [![Code Climate](https://codeclimate.com/github/igorescobar/jQuery-Mask-Plugin.png)](https://codeclimate.com/github/igorescobar/jQuery-Mask-Plugin) 6 | [![jsDelivr Hits](https://data.jsdelivr.com/v1/package/npm/jquery-mask-plugin/badge?style=rounded)](https://www.jsdelivr.com/package/npm/jquery-mask-plugin) 7 | [![CDNJS](https://img.shields.io/cdnjs/v/jquery.mask.svg)](https://cdnjs.com/libraries/jquery.mask) 8 | 9 | # Documentation, Demos & Usage Examples 10 | https://igorescobar.github.io/jQuery-Mask-Plugin/ 11 | 12 | ## Features 13 | 14 | * Lightweight (~2kb minified, ~1kb gziped). 15 | * Built-in support for dynamically added elements. 16 | * Masks on any HTML element (no need to server-side mask anymore!)! 17 | * HTML notation support (data-mask, data-mask-recursive, data-mask-clearifnotmatch). 18 | * String/Numeric/Alpha/Mixed masks. 19 | * Reverse mask support for masks on numeric fields. 20 | * Sanitization. 21 | * Optional digits. 22 | * Recursive Digits. 23 | * Fallback Digits. 24 | * Advanced mask initialization. 25 | * Advanced Callbacks. 26 | * On-the-fly mask change. 27 | * Mask removal. 28 | * Full customization. 29 | * Compatibility with React/UMD/Zepto.js/Angular.JS. 30 | * HTML5 placeholder support. 31 | * Clear the field if it not matches support. 32 | 33 | ## Want to buy me a beer? :heart_eyes: 34 | http://paypal.me/igorcescobar 35 | 36 | ## Install it via Package Managers 37 | ### Bower 38 | `bower install jquery-mask-plugin` 39 | ### NPM 40 | `npm i jquery-mask-plugin` 41 | ### Meteor 42 | `meteor add igorescobar:jquery-mask-plugin` 43 | ### Packagist/Composer 44 | `composer require igorescobar/jquery-mask-plugin` 45 | 46 | ## CDNs 47 | ### CDNjs 48 | https://cdnjs.com/libraries/jquery.mask 49 | ### JSDelivr 50 | http://www.jsdelivr.com/projects/jquery.mask 51 | 52 | ## RubyGems 53 | ```ruby 54 | gem 'jquery_mask_rails' # more details at http://bit.ly/jquery-mask-gem 55 | ``` 56 | 57 | ## Tutorials 58 | ### English 59 | * [Masks with jQuery Mask Plugin](http://bit.ly/masks-with-jquery-mask-plugin) 60 | * [Using jQuery Mask Plugin With Zepto.js](http://bit.ly/using-jquery-mask-plugin-with-zeptojs) 61 | 62 | ### Portuguese 63 | * [Mascaras com JQuery Mask Plugin](http://bit.ly/mascaras-com-jquery-mask-plugin) 64 | * [Mascara Javascript para os novos telefones de São Paulo](http://bit.ly/mascara-javascript-para-os-novos-telefones-de-sao-paulo) 65 | 66 | ### Fun (or not) facts 67 | * [I’ve had the chance to troll Donald Trump. But I didn’t.](http://www.igorescobar.com/blog/2016/08/21/ive-the-chance-to-troll-donald-trump-but-i-didnt/) 68 | 69 | ## Compatibility 70 | jQuery Mask Plugin has been tested with jQuery 1.7+ on all major browsers: 71 | 72 | * Firefox 2+ (Win, Mac, Linux); 73 | * IE7+ (Win); 74 | * Chrome 6+ (Win, Mac, Linux, Android, iPhone); 75 | * Safari 3.2+ (Win, Mac, iPhone); 76 | * Opera 8+ (Win, Mac, Linux, Android, iPhone). 77 | * Android Default Browser v4+ 78 | 79 | ## Typescript support 80 | Definition can be found [here](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/jquery-mask-plugin). 81 | 82 | To install, open terminal and navigate to your working directory. 83 | 84 | ### Typescript 1.x users 85 | * Install [typings](https://github.com/typings/typings) by running `npm install typings --global`. 86 | * Then install the definition by running `typings install dt~jquery-mask-plugin --global --save`. 87 | ### Typescript 2.x users 88 | * Use npm `npm install --save-dev @types/jquery-mask-plugin`. 89 | 90 | For configuration options and troubleshooting refer to these repositories: 91 | * [Typings](https://github.com/typings/typings) 92 | * [DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped) 93 | * [Typescript](https://github.com/Microsoft/TypeScript) 94 | 95 | 96 | ## Problems or Questions? 97 | Before opening a new [issue](https://github.com/igorescobar/jQuery-Mask-Plugin/issues) take a look on those frequently asked questions: 98 | #### [How to integrate with React.js?](https://github.com/igorescobar/jQuery-Mask-Plugin/issues/498) 99 | #### [How to integrate with Angular.js?](https://github.com/igorescobar/jQuery-Mask-Plugin/issues/499) 100 | #### [How to integrate with Vue.js?](https://github.com/ankurk91/vue-jquery-mask) 101 | #### [Problems with old versions of Android keyboard](https://github.com/igorescobar/jQuery-Mask-Plugin/issues/135) 102 | #### [Negative numbers, or currency related problems](https://github.com/igorescobar/jQuery-Mask-Plugin/issues/436#issuecomment-253176511) 103 | #### [Prefix or sufix on the Mask](https://github.com/igorescobar/jQuery-Mask-Plugin/issues/166) 104 | #### [Add validation?](https://github.com/igorescobar/jQuery-Mask-Plugin/issues/387#issuecomment-192998092) 105 | #### [Field type number, email not working?](https://github.com/igorescobar/jQuery-Mask-Plugin/issues/450#issuecomment-253225719) 106 | #### [Want to keep the placeholder as the user types?](https://github.com/igorescobar/jQuery-Mask-Plugin/issues/633#issuecomment-350819224) 107 | #### [E-mail mask?](https://github.com/igorescobar/jQuery-Mask-Plugin/issues/582) 108 | 109 | ## Bugs? 110 | Did you read our [docs](https://igorescobar.github.io/jQuery-Mask-Plugin/docs.html)? Yes? Cool! So now... make sure that you have a *functional* [jsfiddle](http://jsfiddle.net/) exemplifying your problem and open an [issue](https://github.com/igorescobar/jQuery-Mask-Plugin/issues) for us. Don't know how to do it? Use this [fiddle example](http://jsfiddle.net/igorescobar/6pco4om7/). 111 | 112 | ## Contributing 113 | * **Bug Reporting**: Yes! You can contribute opening [issues](https://github.com/igorescobar/jQuery-Mask-Plugin/issues)! 114 | * **Documenting**: Do you think that something in our [docs](https://github.com/igorescobar/jQuery-Mask-Plugin/tree/gh-pages) should be better? Do you have a cool idea to increase the awesomeness? Summit your pull request with your idea! 115 | * **Bug Fixing**: No time to lose? Fix it and help others! Write some [tests](https://github.com/igorescobar/jQuery-Mask-Plugin/tree/master/test) to make sure that everything are working propertly. 116 | * **Improving**: Open an [issue](https://github.com/igorescobar/jQuery-Mask-Plugin/issues) and lets discuss it. Just to make sure that you're on the right track. 117 | * **Sharing**: Yes! Have we saved some of your time? Are you enjoying our mask plugin? Sharing is caring! Tweet it! Facebook it! Linkedin It(?!) :D 118 | * **Donating**: Hey, now that you don't need to worry about masks again... buy me a coffee, beer or a PlayStation 4 (Xbox One also accepted!) :o) 119 | 120 | ### Unit Tests 121 | We use [QUnit](http://qunitjs.com/) and [GruntJS](http://gruntjs.com/). To run our test suit is just run: ```grunt test``` in your console or you can open those ```test-for*.html``` files inside of our ```test/``` folder. 122 | 123 | In case you're familiar with [Docker](https://www.docker.com/) here is how you can use it: 124 | ```bash 125 | docker build -t jquery-mask . 126 | CONTAINER_ID=$(docker run -d -v $PWD:/app/jquery-mask-plugin jquery-mask) 127 | docker exec $CONTAINER_ID sh -c "npm install" 128 | docker exec -it $CONTAINER_ID /bin/bash 129 | grunt test 130 | ``` 131 | 132 | ## Contributors 133 | * [Igor Lima](https://github.com/igorlima) 134 | * [Mark Simmons](https://github.com/Markipelago) 135 | * [Gabriel Schammah](https://github.com/gschammah) 136 | * [Marcelo Manzan](https://github.com/kawamanza) 137 | * [See the full list](https://github.com/igorescobar/jQuery-Mask-Plugin/graphs/contributors) 138 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-mask-plugin", 3 | "version": "1.14.16", 4 | "main": "dist/jquery.mask.js", 5 | "ignore": [ 6 | "deploy.rb", 7 | "jquery.mask.json", 8 | "Gruntfile.js", 9 | "test/*", 10 | ".*" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jQuery-Mask-Plugin", 3 | "description": "A jQuery Plugin to make masks on form fields and HTML elements.", 4 | "version": "1.14.16", 5 | "keywords": ["javascript", "mask", "form"], 6 | "scripts": [ 7 | "dist/jquery.mask.js" 8 | ], 9 | "main": "dist/jquery.mask.js" 10 | } 11 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "igorescobar/jquery-mask-plugin", 3 | "type": "library", 4 | "description": "A jQuery Plugin to make masks on form fields and html elements.", 5 | "keywords": ["jquery", "mask", "plugin"], 6 | "homepage": "https://github.com/igorescobar/jQuery-Mask-Plugin", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "Igor Escobar", 11 | "email": "blog@igorescobar.com", 12 | "homepage": "https://about.me/igorescobar", 13 | "role": "Developer" 14 | } 15 | ], 16 | "support": { 17 | "issues": "https://github.com/igorescobar/jQuery-Mask-Plugin/issues" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /deploy.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'zlib' 3 | 4 | JMASK_FILE = 'src/jquery.mask.js' 5 | JMASK_MIN_FILE = 'dist/jquery.mask.min.js' 6 | GHPAGES_JMASK_MIN_FILE = 'js/jquery.mask.min.js' 7 | JMASK_VERSION = `stepup version --next-release`.delete("\n") 8 | BOWER_MANIFEST_FILE = 'bower.json' 9 | NPM_MANIFEST_FILE = 'package.json' 10 | METEOR_MANIFEST_FILE = 'package.js' 11 | COMPONENT_MANIFEST_FILE = 'component.json' 12 | 13 | abort("No notes, do deal.") if JMASK_VERSION.empty? 14 | 15 | puts '# PUTTING NEW VERSION INSIDE OF JQUERY MASK FILE' 16 | unversioned_jmask_file = File.open(JMASK_FILE, 'rb') { |file| file.read } 17 | File.open(JMASK_FILE, 'w') do |file| 18 | file.write(unversioned_jmask_file.gsub(/\* @version: (v[0-9.+]+)/, "\* @version: #{JMASK_VERSION}")) 19 | end 20 | 21 | puts '# COPYING NEW JMASK FILE TO DIST/' 22 | `yes | cp #{JMASK_FILE} dist/` 23 | 24 | [BOWER_MANIFEST_FILE, NPM_MANIFEST_FILE, COMPONENT_MANIFEST_FILE, METEOR_MANIFEST_FILE].each { |manifest_name| 25 | puts "# UPGRADING #{manifest_name} " 26 | manifest_file = File.open(manifest_name, 'rb') { |file| file.read } 27 | File.open(manifest_name, 'w') do |file| 28 | file.write(manifest_file.gsub(/"version": "([0-9.+]+)"/, "\"version\": \"#{JMASK_VERSION.gsub("v", "")}\"")) 29 | end 30 | } 31 | 32 | puts '# GENERATING MIN FILE' 33 | jquery_mask_min_file = nil 34 | File.open(JMASK_FILE, 'r') do |file| 35 | minFile = File.open(JMASK_MIN_FILE, 'w') 36 | minFile.puts("// jQuery Mask Plugin #{JMASK_VERSION}") 37 | minFile.puts("// github.com/igorescobar/jQuery-Mask-Plugin") 38 | jquery_mask_min_file = `java -jar ../clojure-compiler/compiler.jar --js src/jquery.mask.js --charset UTF-8` 39 | minFile.puts(jquery_mask_min_file) 40 | minFile.close 41 | end 42 | 43 | puts '# GENERATING A NEW COMMIT WITH VERSIONED FILEs' 44 | `git commit -am 'generating jquery mask files #{JMASK_VERSION}'` 45 | 46 | puts '# PUSHING CHANGES TO REMOTE' 47 | `git pull --rebase && git push` 48 | 49 | puts '# CREATING NEW VERSION' 50 | `stepup version create --no-editor` 51 | 52 | puts '# UPGRATING CHANGELOG' 53 | `stepup changelog --format=wiki > CHANGELOG.md` 54 | `git commit -am "upgrading changelog"` 55 | `git push` 56 | 57 | puts '# UPGRADING gh-pages' 58 | `git checkout gh-pages` 59 | `git pull origin gh-pages` 60 | 61 | minFile = File.open(GHPAGES_JMASK_MIN_FILE, 'w') 62 | minFile.puts("// jQuery Mask Plugin #{JMASK_VERSION}") 63 | minFile.puts("// github.com/igorescobar/jQuery-Mask-Plugin") 64 | minFile.puts(jquery_mask_min_file) 65 | minFile.close 66 | 67 | `git commit -am "upgrading plugin file"` 68 | `git push` 69 | `git checkout master` 70 | 71 | puts '# PUBLISHING NPM PACKAGE' 72 | `npm publish` 73 | 74 | puts '# PUBLISHING METEOR PACKAGE' 75 | `meteor publish` 76 | 77 | puts '# DONE!' 78 | -------------------------------------------------------------------------------- /dist/jquery.mask.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jquery.mask.js 3 | * @version: v1.14.16 4 | * @author: Igor Escobar 5 | * 6 | * Created by Igor Escobar on 2012-03-10. Please report any bug at github.com/igorescobar/jQuery-Mask-Plugin 7 | * 8 | * Copyright (c) 2012 Igor Escobar http://igorescobar.com 9 | * 10 | * The MIT License (http://www.opensource.org/licenses/mit-license.php) 11 | * 12 | * Permission is hereby granted, free of charge, to any person 13 | * obtaining a copy of this software and associated documentation 14 | * files (the "Software"), to deal in the Software without 15 | * restriction, including without limitation the rights to use, 16 | * copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | * copies of the Software, and to permit persons to whom the 18 | * Software is furnished to do so, subject to the following 19 | * conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 26 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 28 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 29 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 30 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 31 | * OTHER DEALINGS IN THE SOFTWARE. 32 | */ 33 | 34 | /* jshint laxbreak: true */ 35 | /* jshint maxcomplexity:17 */ 36 | /* global define */ 37 | 38 | // UMD (Universal Module Definition) patterns for JavaScript modules that work everywhere. 39 | // https://github.com/umdjs/umd/blob/master/templates/jqueryPlugin.js 40 | (function (factory, jQuery, Zepto) { 41 | 42 | if (typeof define === 'function' && define.amd) { 43 | define(['jquery'], factory); 44 | } else if (typeof exports === 'object' && typeof Meteor === 'undefined') { 45 | module.exports = factory(require('jquery')); 46 | } else { 47 | factory(jQuery || Zepto); 48 | } 49 | 50 | }(function ($) { 51 | 'use strict'; 52 | 53 | var Mask = function (el, mask, options) { 54 | 55 | var p = { 56 | invalid: [], 57 | getCaret: function () { 58 | try { 59 | var sel, 60 | pos = 0, 61 | ctrl = el.get(0), 62 | dSel = document.selection, 63 | cSelStart = ctrl.selectionStart; 64 | 65 | // IE Support 66 | if (dSel && navigator.appVersion.indexOf('MSIE 10') === -1) { 67 | sel = dSel.createRange(); 68 | sel.moveStart('character', -p.val().length); 69 | pos = sel.text.length; 70 | } 71 | // Firefox support 72 | else if (cSelStart || cSelStart === '0') { 73 | pos = cSelStart; 74 | } 75 | 76 | return pos; 77 | } catch (e) {} 78 | }, 79 | setCaret: function(pos) { 80 | try { 81 | if (el.is(':focus')) { 82 | var range, ctrl = el.get(0); 83 | 84 | // Firefox, WebKit, etc.. 85 | if (ctrl.setSelectionRange) { 86 | ctrl.setSelectionRange(pos, pos); 87 | } else { // IE 88 | range = ctrl.createTextRange(); 89 | range.collapse(true); 90 | range.moveEnd('character', pos); 91 | range.moveStart('character', pos); 92 | range.select(); 93 | } 94 | } 95 | } catch (e) {} 96 | }, 97 | events: function() { 98 | el 99 | .on('keydown.mask', function(e) { 100 | el.data('mask-keycode', e.keyCode || e.which); 101 | el.data('mask-previus-value', el.val()); 102 | el.data('mask-previus-caret-pos', p.getCaret()); 103 | p.maskDigitPosMapOld = p.maskDigitPosMap; 104 | }) 105 | .on($.jMaskGlobals.useInput ? 'input.mask' : 'keyup.mask', p.behaviour) 106 | .on('paste.mask drop.mask', function() { 107 | setTimeout(function() { 108 | el.keydown().keyup(); 109 | }, 100); 110 | }) 111 | .on('change.mask', function(){ 112 | el.data('changed', true); 113 | }) 114 | .on('blur.mask', function(){ 115 | if (oldValue !== p.val() && !el.data('changed')) { 116 | el.trigger('change'); 117 | } 118 | el.data('changed', false); 119 | }) 120 | // it's very important that this callback remains in this position 121 | // otherwhise oldValue it's going to work buggy 122 | .on('blur.mask', function() { 123 | oldValue = p.val(); 124 | }) 125 | // select all text on focus 126 | .on('focus.mask', function (e) { 127 | if (options.selectOnFocus === true) { 128 | $(e.target).select(); 129 | } 130 | }) 131 | // clear the value if it not complete the mask 132 | .on('focusout.mask', function() { 133 | if (options.clearIfNotMatch && !regexMask.test(p.val())) { 134 | p.val(''); 135 | } 136 | }); 137 | }, 138 | getRegexMask: function() { 139 | var maskChunks = [], translation, pattern, optional, recursive, oRecursive, r; 140 | 141 | for (var i = 0; i < mask.length; i++) { 142 | translation = jMask.translation[mask.charAt(i)]; 143 | 144 | if (translation) { 145 | 146 | pattern = translation.pattern.toString().replace(/.{1}$|^.{1}/g, ''); 147 | optional = translation.optional; 148 | recursive = translation.recursive; 149 | 150 | if (recursive) { 151 | maskChunks.push(mask.charAt(i)); 152 | oRecursive = {digit: mask.charAt(i), pattern: pattern}; 153 | } else { 154 | maskChunks.push(!optional && !recursive ? pattern : (pattern + '?')); 155 | } 156 | 157 | } else { 158 | maskChunks.push(mask.charAt(i).replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')); 159 | } 160 | } 161 | 162 | r = maskChunks.join(''); 163 | 164 | if (oRecursive) { 165 | r = r.replace(new RegExp('(' + oRecursive.digit + '(.*' + oRecursive.digit + ')?)'), '($1)?') 166 | .replace(new RegExp(oRecursive.digit, 'g'), oRecursive.pattern); 167 | } 168 | 169 | return new RegExp(r); 170 | }, 171 | destroyEvents: function() { 172 | el.off(['input', 'keydown', 'keyup', 'paste', 'drop', 'blur', 'focusout', ''].join('.mask ')); 173 | }, 174 | val: function(v) { 175 | var isInput = el.is('input'), 176 | method = isInput ? 'val' : 'text', 177 | r; 178 | 179 | if (arguments.length > 0) { 180 | if (el[method]() !== v) { 181 | el[method](v); 182 | } 183 | r = el; 184 | } else { 185 | r = el[method](); 186 | } 187 | 188 | return r; 189 | }, 190 | calculateCaretPosition: function(oldVal) { 191 | var newVal = p.getMasked(), 192 | caretPosNew = p.getCaret(); 193 | if (oldVal !== newVal) { 194 | var caretPosOld = el.data('mask-previus-caret-pos') || 0, 195 | newValL = newVal.length, 196 | oldValL = oldVal.length, 197 | maskDigitsBeforeCaret = 0, 198 | maskDigitsAfterCaret = 0, 199 | maskDigitsBeforeCaretAll = 0, 200 | maskDigitsBeforeCaretAllOld = 0, 201 | i = 0; 202 | 203 | for (i = caretPosNew; i < newValL; i++) { 204 | if (!p.maskDigitPosMap[i]) { 205 | break; 206 | } 207 | maskDigitsAfterCaret++; 208 | } 209 | 210 | for (i = caretPosNew - 1; i >= 0; i--) { 211 | if (!p.maskDigitPosMap[i]) { 212 | break; 213 | } 214 | maskDigitsBeforeCaret++; 215 | } 216 | 217 | for (i = caretPosNew - 1; i >= 0; i--) { 218 | if (p.maskDigitPosMap[i]) { 219 | maskDigitsBeforeCaretAll++; 220 | } 221 | } 222 | 223 | for (i = caretPosOld - 1; i >= 0; i--) { 224 | if (p.maskDigitPosMapOld[i]) { 225 | maskDigitsBeforeCaretAllOld++; 226 | } 227 | } 228 | 229 | // if the cursor is at the end keep it there 230 | if (caretPosNew > oldValL) { 231 | caretPosNew = newValL * 10; 232 | } else if (caretPosOld >= caretPosNew && caretPosOld !== oldValL) { 233 | if (!p.maskDigitPosMapOld[caretPosNew]) { 234 | var caretPos = caretPosNew; 235 | caretPosNew -= maskDigitsBeforeCaretAllOld - maskDigitsBeforeCaretAll; 236 | caretPosNew -= maskDigitsBeforeCaret; 237 | if (p.maskDigitPosMap[caretPosNew]) { 238 | caretPosNew = caretPos; 239 | } 240 | } 241 | } 242 | else if (caretPosNew > caretPosOld) { 243 | caretPosNew += maskDigitsBeforeCaretAll - maskDigitsBeforeCaretAllOld; 244 | caretPosNew += maskDigitsAfterCaret; 245 | } 246 | } 247 | return caretPosNew; 248 | }, 249 | behaviour: function(e) { 250 | e = e || window.event; 251 | p.invalid = []; 252 | 253 | var keyCode = el.data('mask-keycode'); 254 | 255 | if ($.inArray(keyCode, jMask.byPassKeys) === -1) { 256 | var newVal = p.getMasked(), 257 | caretPos = p.getCaret(), 258 | oldVal = el.data('mask-previus-value') || ''; 259 | 260 | // this is a compensation to devices/browsers that don't compensate 261 | // caret positioning the right way 262 | setTimeout(function() { 263 | p.setCaret(p.calculateCaretPosition(oldVal)); 264 | }, $.jMaskGlobals.keyStrokeCompensation); 265 | 266 | p.val(newVal); 267 | p.setCaret(caretPos); 268 | return p.callbacks(e); 269 | } 270 | }, 271 | getMasked: function(skipMaskChars, val) { 272 | var buf = [], 273 | value = val === undefined ? p.val() : val + '', 274 | m = 0, maskLen = mask.length, 275 | v = 0, valLen = value.length, 276 | offset = 1, addMethod = 'push', 277 | resetPos = -1, 278 | maskDigitCount = 0, 279 | maskDigitPosArr = [], 280 | lastMaskChar, 281 | check; 282 | 283 | if (options.reverse) { 284 | addMethod = 'unshift'; 285 | offset = -1; 286 | lastMaskChar = 0; 287 | m = maskLen - 1; 288 | v = valLen - 1; 289 | check = function () { 290 | return m > -1 && v > -1; 291 | }; 292 | } else { 293 | lastMaskChar = maskLen - 1; 294 | check = function () { 295 | return m < maskLen && v < valLen; 296 | }; 297 | } 298 | 299 | var lastUntranslatedMaskChar; 300 | while (check()) { 301 | var maskDigit = mask.charAt(m), 302 | valDigit = value.charAt(v), 303 | translation = jMask.translation[maskDigit]; 304 | 305 | if (translation) { 306 | if (valDigit.match(translation.pattern)) { 307 | buf[addMethod](valDigit); 308 | if (translation.recursive) { 309 | if (resetPos === -1) { 310 | resetPos = m; 311 | } else if (m === lastMaskChar && m !== resetPos) { 312 | m = resetPos - offset; 313 | } 314 | 315 | if (lastMaskChar === resetPos) { 316 | m -= offset; 317 | } 318 | } 319 | m += offset; 320 | } else if (valDigit === lastUntranslatedMaskChar) { 321 | // matched the last untranslated (raw) mask character that we encountered 322 | // likely an insert offset the mask character from the last entry; fall 323 | // through and only increment v 324 | maskDigitCount--; 325 | lastUntranslatedMaskChar = undefined; 326 | } else if (translation.optional) { 327 | m += offset; 328 | v -= offset; 329 | } else if (translation.fallback) { 330 | buf[addMethod](translation.fallback); 331 | m += offset; 332 | v -= offset; 333 | } else { 334 | p.invalid.push({p: v, v: valDigit, e: translation.pattern}); 335 | } 336 | v += offset; 337 | } else { 338 | if (!skipMaskChars) { 339 | buf[addMethod](maskDigit); 340 | } 341 | 342 | if (valDigit === maskDigit) { 343 | maskDigitPosArr.push(v); 344 | v += offset; 345 | } else { 346 | lastUntranslatedMaskChar = maskDigit; 347 | maskDigitPosArr.push(v + maskDigitCount); 348 | maskDigitCount++; 349 | } 350 | 351 | m += offset; 352 | } 353 | } 354 | 355 | var lastMaskCharDigit = mask.charAt(lastMaskChar); 356 | if (maskLen === valLen + 1 && !jMask.translation[lastMaskCharDigit]) { 357 | buf.push(lastMaskCharDigit); 358 | } 359 | 360 | var newVal = buf.join(''); 361 | p.mapMaskdigitPositions(newVal, maskDigitPosArr, valLen); 362 | return newVal; 363 | }, 364 | mapMaskdigitPositions: function(newVal, maskDigitPosArr, valLen) { 365 | var maskDiff = options.reverse ? newVal.length - valLen : 0; 366 | p.maskDigitPosMap = {}; 367 | for (var i = 0; i < maskDigitPosArr.length; i++) { 368 | p.maskDigitPosMap[maskDigitPosArr[i] + maskDiff] = 1; 369 | } 370 | }, 371 | callbacks: function (e) { 372 | var val = p.val(), 373 | changed = val !== oldValue, 374 | defaultArgs = [val, e, el, options], 375 | callback = function(name, criteria, args) { 376 | if (typeof options[name] === 'function' && criteria) { 377 | options[name].apply(this, args); 378 | } 379 | }; 380 | 381 | callback('onChange', changed === true, defaultArgs); 382 | callback('onKeyPress', changed === true, defaultArgs); 383 | callback('onComplete', val.length === mask.length, defaultArgs); 384 | callback('onInvalid', p.invalid.length > 0, [val, e, el, p.invalid, options]); 385 | } 386 | }; 387 | 388 | el = $(el); 389 | var jMask = this, oldValue = p.val(), regexMask; 390 | 391 | mask = typeof mask === 'function' ? mask(p.val(), undefined, el, options) : mask; 392 | 393 | // public methods 394 | jMask.mask = mask; 395 | jMask.options = options; 396 | jMask.remove = function() { 397 | var caret = p.getCaret(); 398 | if (jMask.options.placeholder) { 399 | el.removeAttr('placeholder'); 400 | } 401 | if (el.data('mask-maxlength')) { 402 | el.removeAttr('maxlength'); 403 | } 404 | p.destroyEvents(); 405 | p.val(jMask.getCleanVal()); 406 | p.setCaret(caret); 407 | return el; 408 | }; 409 | 410 | // get value without mask 411 | jMask.getCleanVal = function() { 412 | return p.getMasked(true); 413 | }; 414 | 415 | // get masked value without the value being in the input or element 416 | jMask.getMaskedVal = function(val) { 417 | return p.getMasked(false, val); 418 | }; 419 | 420 | jMask.init = function(onlyMask) { 421 | onlyMask = onlyMask || false; 422 | options = options || {}; 423 | 424 | jMask.clearIfNotMatch = $.jMaskGlobals.clearIfNotMatch; 425 | jMask.byPassKeys = $.jMaskGlobals.byPassKeys; 426 | jMask.translation = $.extend({}, $.jMaskGlobals.translation, options.translation); 427 | 428 | jMask = $.extend(true, {}, jMask, options); 429 | 430 | regexMask = p.getRegexMask(); 431 | 432 | if (onlyMask) { 433 | p.events(); 434 | p.val(p.getMasked()); 435 | } else { 436 | if (options.placeholder) { 437 | el.attr('placeholder' , options.placeholder); 438 | } 439 | 440 | // this is necessary, otherwise if the user submit the form 441 | // and then press the "back" button, the autocomplete will erase 442 | // the data. Works fine on IE9+, FF, Opera, Safari. 443 | if (el.data('mask')) { 444 | el.attr('autocomplete', 'off'); 445 | } 446 | 447 | // detect if is necessary let the user type freely. 448 | // for is a lot faster than forEach. 449 | for (var i = 0, maxlength = true; i < mask.length; i++) { 450 | var translation = jMask.translation[mask.charAt(i)]; 451 | if (translation && translation.recursive) { 452 | maxlength = false; 453 | break; 454 | } 455 | } 456 | 457 | if (maxlength) { 458 | el.attr('maxlength', mask.length).data('mask-maxlength', true); 459 | } 460 | 461 | p.destroyEvents(); 462 | p.events(); 463 | 464 | var caret = p.getCaret(); 465 | p.val(p.getMasked()); 466 | p.setCaret(caret); 467 | } 468 | }; 469 | 470 | jMask.init(!el.is('input')); 471 | }; 472 | 473 | $.maskWatchers = {}; 474 | var HTMLAttributes = function () { 475 | var input = $(this), 476 | options = {}, 477 | prefix = 'data-mask-', 478 | mask = input.attr('data-mask'); 479 | 480 | if (input.attr(prefix + 'reverse')) { 481 | options.reverse = true; 482 | } 483 | 484 | if (input.attr(prefix + 'clearifnotmatch')) { 485 | options.clearIfNotMatch = true; 486 | } 487 | 488 | if (input.attr(prefix + 'selectonfocus') === 'true') { 489 | options.selectOnFocus = true; 490 | } 491 | 492 | if (notSameMaskObject(input, mask, options)) { 493 | return input.data('mask', new Mask(this, mask, options)); 494 | } 495 | }, 496 | notSameMaskObject = function(field, mask, options) { 497 | options = options || {}; 498 | var maskObject = $(field).data('mask'), 499 | stringify = JSON.stringify, 500 | value = $(field).val() || $(field).text(); 501 | try { 502 | if (typeof mask === 'function') { 503 | mask = mask(value); 504 | } 505 | return typeof maskObject !== 'object' || stringify(maskObject.options) !== stringify(options) || maskObject.mask !== mask; 506 | } catch (e) {} 507 | }, 508 | eventSupported = function(eventName) { 509 | var el = document.createElement('div'), isSupported; 510 | 511 | eventName = 'on' + eventName; 512 | isSupported = (eventName in el); 513 | 514 | if ( !isSupported ) { 515 | el.setAttribute(eventName, 'return;'); 516 | isSupported = typeof el[eventName] === 'function'; 517 | } 518 | el = null; 519 | 520 | return isSupported; 521 | }; 522 | 523 | $.fn.mask = function(mask, options) { 524 | options = options || {}; 525 | var selector = this.selector, 526 | globals = $.jMaskGlobals, 527 | interval = globals.watchInterval, 528 | watchInputs = options.watchInputs || globals.watchInputs, 529 | maskFunction = function() { 530 | if (notSameMaskObject(this, mask, options)) { 531 | return $(this).data('mask', new Mask(this, mask, options)); 532 | } 533 | }; 534 | 535 | $(this).each(maskFunction); 536 | 537 | if (selector && selector !== '' && watchInputs) { 538 | clearInterval($.maskWatchers[selector]); 539 | $.maskWatchers[selector] = setInterval(function(){ 540 | $(document).find(selector).each(maskFunction); 541 | }, interval); 542 | } 543 | return this; 544 | }; 545 | 546 | $.fn.masked = function(val) { 547 | return this.data('mask').getMaskedVal(val); 548 | }; 549 | 550 | $.fn.unmask = function() { 551 | clearInterval($.maskWatchers[this.selector]); 552 | delete $.maskWatchers[this.selector]; 553 | return this.each(function() { 554 | var dataMask = $(this).data('mask'); 555 | if (dataMask) { 556 | dataMask.remove().removeData('mask'); 557 | } 558 | }); 559 | }; 560 | 561 | $.fn.cleanVal = function() { 562 | return this.data('mask').getCleanVal(); 563 | }; 564 | 565 | $.applyDataMask = function(selector) { 566 | selector = selector || $.jMaskGlobals.maskElements; 567 | var $selector = (selector instanceof $) ? selector : $(selector); 568 | $selector.filter($.jMaskGlobals.dataMaskAttr).each(HTMLAttributes); 569 | }; 570 | 571 | var globals = { 572 | maskElements: 'input,td,span,div', 573 | dataMaskAttr: '*[data-mask]', 574 | dataMask: true, 575 | watchInterval: 300, 576 | watchInputs: true, 577 | keyStrokeCompensation: 10, 578 | // old versions of chrome dont work great with input event 579 | useInput: !/Chrome\/[2-4][0-9]|SamsungBrowser/.test(window.navigator.userAgent) && eventSupported('input'), 580 | watchDataMask: false, 581 | byPassKeys: [9, 16, 17, 18, 36, 37, 38, 39, 40, 91], 582 | translation: { 583 | '0': {pattern: /\d/}, 584 | '9': {pattern: /\d/, optional: true}, 585 | '#': {pattern: /\d/, recursive: true}, 586 | 'A': {pattern: /[a-zA-Z0-9]/}, 587 | 'S': {pattern: /[a-zA-Z]/} 588 | } 589 | }; 590 | 591 | $.jMaskGlobals = $.jMaskGlobals || {}; 592 | globals = $.jMaskGlobals = $.extend(true, {}, globals, $.jMaskGlobals); 593 | 594 | // looking for inputs with data-mask attribute 595 | if (globals.dataMask) { 596 | $.applyDataMask(); 597 | } 598 | 599 | setInterval(function() { 600 | if ($.jMaskGlobals.watchDataMask) { 601 | $.applyDataMask(); 602 | } 603 | }, globals.watchInterval); 604 | }, window.jQuery, window.Zepto)); 605 | -------------------------------------------------------------------------------- /dist/jquery.mask.min.js: -------------------------------------------------------------------------------- 1 | // jQuery Mask Plugin v1.14.16 2 | // github.com/igorescobar/jQuery-Mask-Plugin 3 | var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.findInternal=function(a,n,f){a instanceof String&&(a=String(a));for(var p=a.length,k=0;kg?h=10*d:e>=h&&e!==g?c.maskDigitPosMapOld[h]||(e=h,h=h-(k-l)-a,c.maskDigitPosMap[h]&&(h=e)):h>e&&(h=h+(l-k)+f)}return h},behaviour:function(d){d= 11 | d||window.event;c.invalid=[];var e=b.data("mask-keycode");if(-1===a.inArray(e,l.byPassKeys)){e=c.getMasked();var h=c.getCaret(),g=b.data("mask-previus-value")||"";setTimeout(function(){c.setCaret(c.calculateCaretPosition(g))},a.jMaskGlobals.keyStrokeCompensation);c.val(e);c.setCaret(h);return c.callbacks(d)}},getMasked:function(a,b){var h=[],f=void 0===b?c.val():b+"",g=0,k=d.length,n=0,p=f.length,m=1,r="push",u=-1,w=0;b=[];if(e.reverse){r="unshift";m=-1;var x=0;g=k-1;n=p-1;var A=function(){return-1< 12 | g&&-1", 6 | "homepage": "http://igorescobar.github.io/jQuery-Mask-Plugin/", 7 | "main": "./dist/jquery.mask.js", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/igorescobar/jQuery-Mask-Plugin" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/igorescobar/jQuery-Mask-Plugin/issues" 14 | }, 15 | "keywords": [ 16 | "mask", 17 | "masks", 18 | "form", 19 | "input", 20 | "jquery-plugin" 21 | ], 22 | "license": "MIT", 23 | "devDependencies": { 24 | "grunt": "^1.0.4", 25 | "grunt-contrib-connect": "*", 26 | "grunt-contrib-jshint": "^2.1.0", 27 | "grunt-contrib-qunit": "^3.1.0", 28 | "grunt-contrib-uglify": "*", 29 | "jshint-stylish": "^1.0.0", 30 | "load-grunt-tasks": "^3.1.0", 31 | "request": "2.88.0", 32 | "time-grunt": "^1.0.0" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/jquery.mask.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jquery.mask.js 3 | * @version: v1.14.16 4 | * @author: Igor Escobar 5 | * 6 | * Created by Igor Escobar on 2012-03-10. Please report any bug at github.com/igorescobar/jQuery-Mask-Plugin 7 | * 8 | * Copyright (c) 2012 Igor Escobar http://igorescobar.com 9 | * 10 | * The MIT License (http://www.opensource.org/licenses/mit-license.php) 11 | * 12 | * Permission is hereby granted, free of charge, to any person 13 | * obtaining a copy of this software and associated documentation 14 | * files (the "Software"), to deal in the Software without 15 | * restriction, including without limitation the rights to use, 16 | * copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | * copies of the Software, and to permit persons to whom the 18 | * Software is furnished to do so, subject to the following 19 | * conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 26 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 28 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 29 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 30 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 31 | * OTHER DEALINGS IN THE SOFTWARE. 32 | */ 33 | 34 | /* jshint laxbreak: true */ 35 | /* jshint maxcomplexity:17 */ 36 | /* global define */ 37 | 38 | // UMD (Universal Module Definition) patterns for JavaScript modules that work everywhere. 39 | // https://github.com/umdjs/umd/blob/master/templates/jqueryPlugin.js 40 | (function (factory, jQuery, Zepto) { 41 | 42 | if (typeof define === 'function' && define.amd) { 43 | define(['jquery'], factory); 44 | } else if (typeof exports === 'object' && typeof Meteor === 'undefined') { 45 | module.exports = factory(require('jquery')); 46 | } else { 47 | factory(jQuery || Zepto); 48 | } 49 | 50 | }(function ($) { 51 | 'use strict'; 52 | 53 | var Mask = function (el, mask, options) { 54 | 55 | var p = { 56 | invalid: [], 57 | getCaret: function () { 58 | try { 59 | var sel, 60 | pos = 0, 61 | ctrl = el.get(0), 62 | dSel = document.selection, 63 | cSelStart = ctrl.selectionStart; 64 | 65 | // IE Support 66 | if (dSel && navigator.appVersion.indexOf('MSIE 10') === -1) { 67 | sel = dSel.createRange(); 68 | sel.moveStart('character', -p.val().length); 69 | pos = sel.text.length; 70 | } 71 | // Firefox support 72 | else if (cSelStart || cSelStart === '0') { 73 | pos = cSelStart; 74 | } 75 | 76 | return pos; 77 | } catch (e) {} 78 | }, 79 | setCaret: function(pos) { 80 | try { 81 | if (el.is(':focus')) { 82 | var range, ctrl = el.get(0); 83 | 84 | // Firefox, WebKit, etc.. 85 | if (ctrl.setSelectionRange) { 86 | ctrl.setSelectionRange(pos, pos); 87 | } else { // IE 88 | range = ctrl.createTextRange(); 89 | range.collapse(true); 90 | range.moveEnd('character', pos); 91 | range.moveStart('character', pos); 92 | range.select(); 93 | } 94 | } 95 | } catch (e) {} 96 | }, 97 | events: function() { 98 | el 99 | .on('keydown.mask', function(e) { 100 | el.data('mask-keycode', e.keyCode || e.which); 101 | el.data('mask-previus-value', el.val()); 102 | el.data('mask-previus-caret-pos', p.getCaret()); 103 | p.maskDigitPosMapOld = p.maskDigitPosMap; 104 | }) 105 | .on($.jMaskGlobals.useInput ? 'input.mask' : 'keyup.mask', p.behaviour) 106 | .on('paste.mask drop.mask', function() { 107 | setTimeout(function() { 108 | el.keydown().keyup(); 109 | }, 100); 110 | }) 111 | .on('change.mask', function(){ 112 | el.data('changed', true); 113 | }) 114 | .on('blur.mask', function(){ 115 | if (oldValue !== p.val() && !el.data('changed')) { 116 | el.trigger('change'); 117 | } 118 | el.data('changed', false); 119 | }) 120 | // it's very important that this callback remains in this position 121 | // otherwhise oldValue it's going to work buggy 122 | .on('blur.mask', function() { 123 | oldValue = p.val(); 124 | }) 125 | // select all text on focus 126 | .on('focus.mask', function (e) { 127 | if (options.selectOnFocus === true) { 128 | $(e.target).select(); 129 | } 130 | }) 131 | // clear the value if it not complete the mask 132 | .on('focusout.mask', function() { 133 | if (options.clearIfNotMatch && !regexMask.test(p.val())) { 134 | p.val(''); 135 | } 136 | }); 137 | }, 138 | getRegexMask: function() { 139 | var maskChunks = [], translation, pattern, optional, recursive, oRecursive, r; 140 | 141 | for (var i = 0; i < mask.length; i++) { 142 | translation = jMask.translation[mask.charAt(i)]; 143 | 144 | if (translation) { 145 | 146 | pattern = translation.pattern.toString().replace(/.{1}$|^.{1}/g, ''); 147 | optional = translation.optional; 148 | recursive = translation.recursive; 149 | 150 | if (recursive) { 151 | maskChunks.push(mask.charAt(i)); 152 | oRecursive = {digit: mask.charAt(i), pattern: pattern}; 153 | } else { 154 | maskChunks.push(!optional && !recursive ? pattern : (pattern + '?')); 155 | } 156 | 157 | } else { 158 | maskChunks.push(mask.charAt(i).replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')); 159 | } 160 | } 161 | 162 | r = maskChunks.join(''); 163 | 164 | if (oRecursive) { 165 | r = r.replace(new RegExp('(' + oRecursive.digit + '(.*' + oRecursive.digit + ')?)'), '($1)?') 166 | .replace(new RegExp(oRecursive.digit, 'g'), oRecursive.pattern); 167 | } 168 | 169 | return new RegExp(r); 170 | }, 171 | destroyEvents: function() { 172 | el.off(['input', 'keydown', 'keyup', 'paste', 'drop', 'blur', 'focusout', ''].join('.mask ')); 173 | }, 174 | val: function(v) { 175 | var isInput = el.is('input'), 176 | method = isInput ? 'val' : 'text', 177 | r; 178 | 179 | if (arguments.length > 0) { 180 | if (el[method]() !== v) { 181 | el[method](v); 182 | } 183 | r = el; 184 | } else { 185 | r = el[method](); 186 | } 187 | 188 | return r; 189 | }, 190 | calculateCaretPosition: function(oldVal) { 191 | var newVal = p.getMasked(), 192 | caretPosNew = p.getCaret(); 193 | if (oldVal !== newVal) { 194 | var caretPosOld = el.data('mask-previus-caret-pos') || 0, 195 | newValL = newVal.length, 196 | oldValL = oldVal.length, 197 | maskDigitsBeforeCaret = 0, 198 | maskDigitsAfterCaret = 0, 199 | maskDigitsBeforeCaretAll = 0, 200 | maskDigitsBeforeCaretAllOld = 0, 201 | i = 0; 202 | 203 | for (i = caretPosNew; i < newValL; i++) { 204 | if (!p.maskDigitPosMap[i]) { 205 | break; 206 | } 207 | maskDigitsAfterCaret++; 208 | } 209 | 210 | for (i = caretPosNew - 1; i >= 0; i--) { 211 | if (!p.maskDigitPosMap[i]) { 212 | break; 213 | } 214 | maskDigitsBeforeCaret++; 215 | } 216 | 217 | for (i = caretPosNew - 1; i >= 0; i--) { 218 | if (p.maskDigitPosMap[i]) { 219 | maskDigitsBeforeCaretAll++; 220 | } 221 | } 222 | 223 | for (i = caretPosOld - 1; i >= 0; i--) { 224 | if (p.maskDigitPosMapOld[i]) { 225 | maskDigitsBeforeCaretAllOld++; 226 | } 227 | } 228 | 229 | // if the cursor is at the end keep it there 230 | if (caretPosNew > oldValL) { 231 | caretPosNew = newValL * 10; 232 | } else if (caretPosOld >= caretPosNew && caretPosOld !== oldValL) { 233 | if (!p.maskDigitPosMapOld[caretPosNew]) { 234 | var caretPos = caretPosNew; 235 | caretPosNew -= maskDigitsBeforeCaretAllOld - maskDigitsBeforeCaretAll; 236 | caretPosNew -= maskDigitsBeforeCaret; 237 | if (p.maskDigitPosMap[caretPosNew]) { 238 | caretPosNew = caretPos; 239 | } 240 | } 241 | } 242 | else if (caretPosNew > caretPosOld) { 243 | caretPosNew += maskDigitsBeforeCaretAll - maskDigitsBeforeCaretAllOld; 244 | caretPosNew += maskDigitsAfterCaret; 245 | } 246 | } 247 | return caretPosNew; 248 | }, 249 | behaviour: function(e) { 250 | e = e || window.event; 251 | p.invalid = []; 252 | 253 | var keyCode = el.data('mask-keycode'); 254 | 255 | if ($.inArray(keyCode, jMask.byPassKeys) === -1) { 256 | var newVal = p.getMasked(), 257 | caretPos = p.getCaret(), 258 | oldVal = el.data('mask-previus-value') || ''; 259 | 260 | // this is a compensation to devices/browsers that don't compensate 261 | // caret positioning the right way 262 | setTimeout(function() { 263 | p.setCaret(p.calculateCaretPosition(oldVal)); 264 | }, $.jMaskGlobals.keyStrokeCompensation); 265 | 266 | p.val(newVal); 267 | p.setCaret(caretPos); 268 | return p.callbacks(e); 269 | } 270 | }, 271 | getMasked: function(skipMaskChars, val) { 272 | var buf = [], 273 | value = val === undefined ? p.val() : val + '', 274 | m = 0, maskLen = mask.length, 275 | v = 0, valLen = value.length, 276 | offset = 1, addMethod = 'push', 277 | resetPos = -1, 278 | maskDigitCount = 0, 279 | maskDigitPosArr = [], 280 | lastMaskChar, 281 | check; 282 | 283 | if (options.reverse) { 284 | addMethod = 'unshift'; 285 | offset = -1; 286 | lastMaskChar = 0; 287 | m = maskLen - 1; 288 | v = valLen - 1; 289 | check = function () { 290 | return m > -1 && v > -1; 291 | }; 292 | } else { 293 | lastMaskChar = maskLen - 1; 294 | check = function () { 295 | return m < maskLen && v < valLen; 296 | }; 297 | } 298 | 299 | var lastUntranslatedMaskChar; 300 | while (check()) { 301 | var maskDigit = mask.charAt(m), 302 | valDigit = value.charAt(v), 303 | translation = jMask.translation[maskDigit]; 304 | 305 | if (translation) { 306 | if (valDigit.match(translation.pattern)) { 307 | buf[addMethod](valDigit); 308 | if (translation.recursive) { 309 | if (resetPos === -1) { 310 | resetPos = m; 311 | } else if (m === lastMaskChar && m !== resetPos) { 312 | m = resetPos - offset; 313 | } 314 | 315 | if (lastMaskChar === resetPos) { 316 | m -= offset; 317 | } 318 | } 319 | m += offset; 320 | } else if (valDigit === lastUntranslatedMaskChar) { 321 | // matched the last untranslated (raw) mask character that we encountered 322 | // likely an insert offset the mask character from the last entry; fall 323 | // through and only increment v 324 | maskDigitCount--; 325 | lastUntranslatedMaskChar = undefined; 326 | } else if (translation.optional) { 327 | m += offset; 328 | v -= offset; 329 | } else if (translation.fallback) { 330 | buf[addMethod](translation.fallback); 331 | m += offset; 332 | v -= offset; 333 | } else { 334 | p.invalid.push({p: v, v: valDigit, e: translation.pattern}); 335 | } 336 | v += offset; 337 | } else { 338 | if (!skipMaskChars) { 339 | buf[addMethod](maskDigit); 340 | } 341 | 342 | if (valDigit === maskDigit) { 343 | maskDigitPosArr.push(v); 344 | v += offset; 345 | } else { 346 | lastUntranslatedMaskChar = maskDigit; 347 | maskDigitPosArr.push(v + maskDigitCount); 348 | maskDigitCount++; 349 | } 350 | 351 | m += offset; 352 | } 353 | } 354 | 355 | var lastMaskCharDigit = mask.charAt(lastMaskChar); 356 | if (maskLen === valLen + 1 && !jMask.translation[lastMaskCharDigit]) { 357 | buf.push(lastMaskCharDigit); 358 | } 359 | 360 | var newVal = buf.join(''); 361 | p.mapMaskdigitPositions(newVal, maskDigitPosArr, valLen); 362 | return newVal; 363 | }, 364 | mapMaskdigitPositions: function(newVal, maskDigitPosArr, valLen) { 365 | var maskDiff = options.reverse ? newVal.length - valLen : 0; 366 | p.maskDigitPosMap = {}; 367 | for (var i = 0; i < maskDigitPosArr.length; i++) { 368 | p.maskDigitPosMap[maskDigitPosArr[i] + maskDiff] = 1; 369 | } 370 | }, 371 | callbacks: function (e) { 372 | var val = p.val(), 373 | changed = val !== oldValue, 374 | defaultArgs = [val, e, el, options], 375 | callback = function(name, criteria, args) { 376 | if (typeof options[name] === 'function' && criteria) { 377 | options[name].apply(this, args); 378 | } 379 | }; 380 | 381 | callback('onChange', changed === true, defaultArgs); 382 | callback('onKeyPress', changed === true, defaultArgs); 383 | callback('onComplete', val.length === mask.length, defaultArgs); 384 | callback('onInvalid', p.invalid.length > 0, [val, e, el, p.invalid, options]); 385 | } 386 | }; 387 | 388 | el = $(el); 389 | var jMask = this, oldValue = p.val(), regexMask; 390 | 391 | mask = typeof mask === 'function' ? mask(p.val(), undefined, el, options) : mask; 392 | 393 | // public methods 394 | jMask.mask = mask; 395 | jMask.options = options; 396 | jMask.remove = function() { 397 | var caret = p.getCaret(); 398 | if (jMask.options.placeholder) { 399 | el.removeAttr('placeholder'); 400 | } 401 | if (el.data('mask-maxlength')) { 402 | el.removeAttr('maxlength'); 403 | } 404 | p.destroyEvents(); 405 | p.val(jMask.getCleanVal()); 406 | p.setCaret(caret); 407 | return el; 408 | }; 409 | 410 | // get value without mask 411 | jMask.getCleanVal = function() { 412 | return p.getMasked(true); 413 | }; 414 | 415 | // get masked value without the value being in the input or element 416 | jMask.getMaskedVal = function(val) { 417 | return p.getMasked(false, val); 418 | }; 419 | 420 | jMask.init = function(onlyMask) { 421 | onlyMask = onlyMask || false; 422 | options = options || {}; 423 | 424 | jMask.clearIfNotMatch = $.jMaskGlobals.clearIfNotMatch; 425 | jMask.byPassKeys = $.jMaskGlobals.byPassKeys; 426 | jMask.translation = $.extend({}, $.jMaskGlobals.translation, options.translation); 427 | 428 | jMask = $.extend(true, {}, jMask, options); 429 | 430 | regexMask = p.getRegexMask(); 431 | 432 | if (onlyMask) { 433 | p.events(); 434 | p.val(p.getMasked()); 435 | } else { 436 | if (options.placeholder) { 437 | el.attr('placeholder' , options.placeholder); 438 | } 439 | 440 | // this is necessary, otherwise if the user submit the form 441 | // and then press the "back" button, the autocomplete will erase 442 | // the data. Works fine on IE9+, FF, Opera, Safari. 443 | if (el.data('mask')) { 444 | el.attr('autocomplete', 'off'); 445 | } 446 | 447 | // detect if is necessary let the user type freely. 448 | // for is a lot faster than forEach. 449 | for (var i = 0, maxlength = true; i < mask.length; i++) { 450 | var translation = jMask.translation[mask.charAt(i)]; 451 | if (translation && translation.recursive) { 452 | maxlength = false; 453 | break; 454 | } 455 | } 456 | 457 | if (maxlength) { 458 | el.attr('maxlength', mask.length).data('mask-maxlength', true); 459 | } 460 | 461 | p.destroyEvents(); 462 | p.events(); 463 | 464 | var caret = p.getCaret(); 465 | p.val(p.getMasked()); 466 | p.setCaret(caret); 467 | } 468 | }; 469 | 470 | jMask.init(!el.is('input')); 471 | }; 472 | 473 | $.maskWatchers = {}; 474 | var HTMLAttributes = function () { 475 | var input = $(this), 476 | options = {}, 477 | prefix = 'data-mask-', 478 | mask = input.attr('data-mask'); 479 | 480 | if (input.attr(prefix + 'reverse')) { 481 | options.reverse = true; 482 | } 483 | 484 | if (input.attr(prefix + 'clearifnotmatch')) { 485 | options.clearIfNotMatch = true; 486 | } 487 | 488 | if (input.attr(prefix + 'selectonfocus') === 'true') { 489 | options.selectOnFocus = true; 490 | } 491 | 492 | if (notSameMaskObject(input, mask, options)) { 493 | return input.data('mask', new Mask(this, mask, options)); 494 | } 495 | }, 496 | notSameMaskObject = function(field, mask, options) { 497 | options = options || {}; 498 | var maskObject = $(field).data('mask'), 499 | stringify = JSON.stringify, 500 | value = $(field).val() || $(field).text(); 501 | try { 502 | if (typeof mask === 'function') { 503 | mask = mask(value); 504 | } 505 | return typeof maskObject !== 'object' || stringify(maskObject.options) !== stringify(options) || maskObject.mask !== mask; 506 | } catch (e) {} 507 | }, 508 | eventSupported = function(eventName) { 509 | var el = document.createElement('div'), isSupported; 510 | 511 | eventName = 'on' + eventName; 512 | isSupported = (eventName in el); 513 | 514 | if ( !isSupported ) { 515 | el.setAttribute(eventName, 'return;'); 516 | isSupported = typeof el[eventName] === 'function'; 517 | } 518 | el = null; 519 | 520 | return isSupported; 521 | }; 522 | 523 | $.fn.mask = function(mask, options) { 524 | options = options || {}; 525 | var selector = this.selector, 526 | globals = $.jMaskGlobals, 527 | interval = globals.watchInterval, 528 | watchInputs = options.watchInputs || globals.watchInputs, 529 | maskFunction = function() { 530 | if (notSameMaskObject(this, mask, options)) { 531 | return $(this).data('mask', new Mask(this, mask, options)); 532 | } 533 | }; 534 | 535 | $(this).each(maskFunction); 536 | 537 | if (selector && selector !== '' && watchInputs) { 538 | clearInterval($.maskWatchers[selector]); 539 | $.maskWatchers[selector] = setInterval(function(){ 540 | $(document).find(selector).each(maskFunction); 541 | }, interval); 542 | } 543 | return this; 544 | }; 545 | 546 | $.fn.masked = function(val) { 547 | return this.data('mask').getMaskedVal(val); 548 | }; 549 | 550 | $.fn.unmask = function() { 551 | clearInterval($.maskWatchers[this.selector]); 552 | delete $.maskWatchers[this.selector]; 553 | return this.each(function() { 554 | var dataMask = $(this).data('mask'); 555 | if (dataMask) { 556 | dataMask.remove().removeData('mask'); 557 | } 558 | }); 559 | }; 560 | 561 | $.fn.cleanVal = function() { 562 | return this.data('mask').getCleanVal(); 563 | }; 564 | 565 | $.applyDataMask = function(selector) { 566 | selector = selector || $.jMaskGlobals.maskElements; 567 | var $selector = (selector instanceof $) ? selector : $(selector); 568 | $selector.filter($.jMaskGlobals.dataMaskAttr).each(HTMLAttributes); 569 | }; 570 | 571 | var globals = { 572 | maskElements: 'input,td,span,div', 573 | dataMaskAttr: '[data-mask]', 574 | dataMask: true, 575 | watchInterval: 300, 576 | watchInputs: true, 577 | keyStrokeCompensation: 10, 578 | // old versions of chrome dont work great with input event 579 | useInput: !/Chrome\/[2-4][0-9]|SamsungBrowser/.test(window.navigator.userAgent) && eventSupported('input'), 580 | watchDataMask: false, 581 | byPassKeys: [9, 16, 17, 18, 36, 37, 38, 39, 40, 91], 582 | translation: { 583 | '0': {pattern: /\d/}, 584 | '9': {pattern: /\d/, optional: true}, 585 | '#': {pattern: /\d/, recursive: true}, 586 | 'A': {pattern: /[a-zA-Z0-9]/}, 587 | 'S': {pattern: /[a-zA-Z]/} 588 | } 589 | }; 590 | 591 | $.jMaskGlobals = $.jMaskGlobals || {}; 592 | globals = $.jMaskGlobals = $.extend(true, {}, globals, $.jMaskGlobals); 593 | 594 | // looking for inputs with data-mask attribute 595 | if (globals.dataMask) { 596 | $.applyDataMask(); 597 | } 598 | 599 | setInterval(function() { 600 | if ($.jMaskGlobals.watchDataMask) { 601 | $.applyDataMask(); 602 | } 603 | }, globals.watchInterval); 604 | }, window.jQuery, window.Zepto)); 605 | -------------------------------------------------------------------------------- /test/jquery.mask.test.js: -------------------------------------------------------------------------------- 1 | var QUNIT = true; 2 | $(document).ready(function(){ 3 | 4 | var testfield = $('.simple-field'), 5 | testfieldDataMask = $('.simple-field-data-mask'), 6 | testfieldDataMaskWithReverse = $('.simple-field-data-mask-reverse'), 7 | testfieldDataMaskWithClearIfNotMatch = $('.simple-field-data-mask-clearifnotmatch'), 8 | testfieldDataMaskWithClearIfNotMatchAndOptionalMask = $('.simple-field-data-mask-clearifnotmatch-and-optional-mask'), 9 | testdiv = $('.simple-div'), 10 | typeTest = function (typedValue, obj) { 11 | obj = typeof obj === "undefined" ? testfield : obj; 12 | 13 | return obj.keydown().val(typedValue).trigger('input').val(); 14 | }, 15 | typeDivTest = function(typedValue){ 16 | return testdiv.keydown().text(typedValue).trigger('input').text(); 17 | }; 18 | 19 | module('Setting Up'); 20 | test("test if the mask method exists after plugin insertion", function() { 21 | equal( typeof testfield.mask , "function" , "mask function should exists" ); 22 | }); 23 | 24 | module('Simple Masks'); 25 | test("Masks with only numbers.", function(){ 26 | testfield.mask('000000'); 27 | 28 | equal( typeTest("1."), "1"); 29 | equal( typeTest('1éáa2aaaaqwo'), "12"); 30 | equal( typeTest('1234567'), "123456"); 31 | 32 | }); 33 | 34 | test("When I change the mask on-the-fly things should work normally", function(){ 35 | testfield.mask('0000.0000'); 36 | 37 | equal( typeTest("1."), "1"); 38 | equal( typeTest('1éáa2aaaaqwo'), "12"); 39 | equal( typeTest('1234567'), "1234.567"); 40 | 41 | // changing on-the-fly 42 | testfield.mask('0.000.000'); 43 | 44 | equal( typeTest("1."), "1."); 45 | equal( typeTest('1éáa2aaaaqwo'), "1.2"); 46 | equal( typeTest('1234567'), "1.234.567"); 47 | 48 | }); 49 | 50 | test("When I change the mask on-the-fly with onChange callback things should work normally", function(){ 51 | 52 | var masks = ['0000.00009', '0.0000.0000']; 53 | var SPphoneMask = function(phone){ 54 | return phone.length <= 9 ? masks[0] : masks[1]; 55 | }; 56 | 57 | testfield.mask(SPphoneMask, {onChange: function(phone, e, currentField, options){ 58 | $(currentField).mask(SPphoneMask(phone), options); 59 | }}); 60 | 61 | equal( typeTest("1"), "1"); 62 | equal( typeTest("12"), "12"); 63 | equal( typeTest("123"), "123"); 64 | equal( typeTest("1234"), "1234"); 65 | equal( typeTest("12345"), "1234.5"); 66 | equal( typeTest("123456"), "1234.56"); 67 | equal( typeTest("1234567"), "1234.567"); 68 | equal( typeTest("12345678"), "1234.5678"); 69 | equal( typeTest("123456789"), "1.2345.6789"); 70 | 71 | }); 72 | 73 | test("When I change the mask on-the-fly with onKeyPress callback things should work normally", function(){ 74 | 75 | var masks = ['0000.00009', '0.0000.0000']; 76 | var SPphoneMask = function(phone){ 77 | return phone.length <= 9 ? masks[0] : masks[1]; 78 | }; 79 | 80 | testfield.mask(SPphoneMask, {onKeyPress: function(phone, e, currentField, options){ 81 | $(currentField).mask(SPphoneMask(phone), options); 82 | }}); 83 | 84 | equal( typeTest("1"), "1"); 85 | equal( typeTest("12"), "12"); 86 | equal( typeTest("123"), "123"); 87 | equal( typeTest("1234"), "1234"); 88 | equal( typeTest("12345"), "1234.5"); 89 | equal( typeTest("123456"), "1234.56"); 90 | equal( typeTest("1234567"), "1234.567"); 91 | equal( typeTest("12345678"), "1234.5678"); 92 | equal( typeTest("123456789"), "1.2345.6789"); 93 | 94 | }); 95 | 96 | test("#onInvalid callback. should call when invalid", function(){ 97 | testfield.mask('00/00/0000', {onInvalid: function(val, e, el, invalid, options){ 98 | equal(val, "1") 99 | equal(typeof e, "object") 100 | equal(typeof el, "object") 101 | equal(invalid.length, 1); 102 | equal(invalid[0]["e"], "/\\d/"); 103 | equal(invalid[0]["p"], 1); 104 | equal(invalid[0]["v"], "a"); 105 | equal(typeof options.onInvalid, "function"); 106 | }}); 107 | 108 | equal( typeTest("1a") , "1"); 109 | }); 110 | 111 | test("#onInvalid callback. should not call when valid", function(){ 112 | var callback = sinon.spy(); 113 | testfield.mask('00/00/0000', {onInvalid: callback}); 114 | 115 | equal( typeTest("11") , "11"); 116 | equal(callback.called, false) 117 | }); 118 | 119 | test('When I typed a char thats the same as the mask char', function(){ 120 | testfield.unmask(); 121 | testfield.mask('00/00/0000'); 122 | 123 | equal( typeTest("00/"), "00/"); 124 | equal( typeTest("00a"), "00/"); 125 | equal( typeTest("00a00/00"), "00/00/00"); 126 | equal( typeTest("0a/00/00"), "00/00/0"); 127 | equal( typeTest("0a/0a/00"), "00/00"); 128 | 129 | }); 130 | 131 | test('When I typed exactly the same as the mask', function(){ 132 | testfield.mask('00/00/0000'); 133 | equal( typeTest("00"), "00"); 134 | equal( typeTest("00/"), "00/"); 135 | equal( typeTest("aa/"), ""); 136 | equal( typeTest("00/0"), "00/0"); 137 | equal( typeTest("00/00"), "00/00"); 138 | equal( typeTest("00/00/0"), "00/00/0"); 139 | equal( typeTest("00/00/00"), "00/00/00"); 140 | }); 141 | 142 | test("Testing masks with a literal on the last char", function () { 143 | testfield.mask("(99)"); 144 | 145 | equal( typeTest("(99"), "(99)"); 146 | }); 147 | 148 | 149 | module('Masks with numbers and especial characters'); 150 | 151 | test("Masks with numbers and special characters.", function(){ 152 | testfield.mask('(000) 000-0000'); 153 | 154 | equal( typeTest("1"), "(1"); 155 | equal( typeTest('12'), "(12"); 156 | equal( typeTest('123'), "(123"); 157 | equal( typeTest('1234'), "(123) 4"); 158 | equal( typeTest('12345'), "(123) 45"); 159 | equal( typeTest('(123) 456'), "(123) 456"); 160 | equal( typeTest('(123) 4567'), "(123) 456-7"); 161 | 162 | }); 163 | 164 | test("Testing masks with a annonymous function", function(){ 165 | testfield.mask(function(){ 166 | return "(123) 456-7899" 167 | }); 168 | 169 | equal( typeTest("1"), "(1"); 170 | equal( typeTest('12'), "(12"); 171 | equal( typeTest('123'), "(123"); 172 | equal( typeTest('1234'), "(123) 4"); 173 | equal( typeTest('12345'), "(123) 45"); 174 | equal( typeTest('123456'), "(123) 456"); 175 | equal( typeTest('1234567'), "(123) 456-7"); 176 | 177 | }); 178 | 179 | test("Masks with numbers, strings e special characters", function(){ 180 | testfield.mask('(999) A99-SSSS'); 181 | 182 | equal( typeTest("(1"), "(1"); 183 | equal( typeTest('(12'), "(12"); 184 | equal( typeTest('(123'), "(123"); 185 | equal( typeTest('(123) 4'), "(123) 4"); 186 | equal( typeTest('(123) A'), "(123) A"); 187 | equal( typeTest('123.'), "(123) "); 188 | equal( typeTest('(123) 45'), "(123) 45"); 189 | equal( typeTest('(123) 456'), "(123) 456"); 190 | equal( typeTest('(123) 456-A'), "(123) 456-A"); 191 | equal( typeTest('(123) 456-AB'), "(123) 456-AB"); 192 | equal( typeTest('(123) 456-ABC'), "(123) 456-ABC"); 193 | equal( typeTest('(123) 456-ABCD'), "(123) 456-ABCD"); 194 | equal( typeTest('(123) 456-ABCDE'), "(123) 456-ABCD"); 195 | equal( typeTest('(123) 456-ABCD1'), "(123) 456-ABCD"); 196 | 197 | }); 198 | 199 | test("Masks with numbers, strings e special characters #2 ", function(){ 200 | testfield.mask('AAA 000-S0S'); 201 | 202 | equal( typeTest("1"), "1"); 203 | equal( typeTest('12'), "12"); 204 | equal( typeTest('123'), "123"); 205 | equal( typeTest('123 4'), "123 4"); 206 | equal( typeTest('123 45'), "123 45"); 207 | equal( typeTest('123 456'), "123 456"); 208 | equal( typeTest('123 456-7'), "123 456-"); 209 | 210 | }); 211 | 212 | module("Testing Reversible Masks"); 213 | 214 | test("Testing a CPF Mask", function(){ 215 | testfield.mask('000.000.000-00', {reverse: true}); 216 | 217 | equal( typeTest("1"), "1"); 218 | equal( typeTest("12"), "12"); 219 | equal( typeTest("123"), "1-23"); 220 | equal( typeTest("12-34"), "12-34"); 221 | equal( typeTest("123-45"), "123-45"); 222 | equal( typeTest("1.234-56"), "1.234-56"); 223 | equal( typeTest("12.345-67"), "12.345-67"); 224 | equal( typeTest("123.456-78"), "123.456-78"); 225 | equal( typeTest("1.234.567-89"), "1.234.567-89"); 226 | equal( typeTest("12.345.678-90"), "12.345.678-90"); 227 | equal( typeTest("123.456.789-00"), "123.456.789-00"); 228 | equal( typeTest("123.456.789-00"), "123.456.789-00"); 229 | 230 | equal( typeTest("123.456.789a00"), "123.456.789-00"); 231 | equal( typeTest("123-a5"), "12-35"); 232 | 233 | equal( typeTest("1"), "1"); 234 | equal( typeTest("12"), "12"); 235 | equal( typeTest("1-23"), "1-23"); 236 | equal( typeTest("12-34"), "12-34"); 237 | equal( typeTest("12-345"), "123-45"); 238 | equal( typeTest("1.234-56"), "1.234-56"); 239 | equal( typeTest("12.345-67"), "12.345-67"); 240 | equal( typeTest("123.456-78"), "123.456-78"); 241 | equal( typeTest("1.234.567-89"), "1.234.567-89"); 242 | equal( typeTest("12.345.678-90"), "12.345.678-90"); 243 | equal( typeTest("123.456.789-00"), "123.456.789-00"); 244 | equal( typeTest("123.456.789-00"), "123.456.789-00"); 245 | equal( typeTest("123.456.789a00"), "123.456.789-00"); 246 | }); 247 | 248 | test("Testing Reverse numbers with recursive mask", function(){ 249 | testfield.mask("#.##0,00", {reverse: true}); 250 | 251 | equal(typeTest(""), ""); 252 | equal(typeTest("1"), "1"); 253 | equal(typeTest("12"), "12"); 254 | equal(typeTest("123"), "1,23"); 255 | equal(typeTest("1,234"), "12,34"); 256 | equal(typeTest("12,345"), "123,45"); 257 | equal(typeTest("123,456"), "1.234,56"); 258 | equal(typeTest("1.234,567"), "12.345,67"); 259 | equal(typeTest("12.345,678"), "123.456,78"); 260 | equal(typeTest("123.456,789"), "1.234.567,89"); 261 | equal(typeTest("1.234.567,890"), "12.345.678,90"); 262 | equal(typeTest("12.345.678,901"), "123.456.789,01"); 263 | equal(typeTest("123.456.789,012"), "1.234.567.890,12"); 264 | equal(typeTest("1.234.567.890,1"), "123.456.789,01"); 265 | equal(typeTest("123.456.789,0"), "12.345.678,90"); 266 | equal(typeTest("12.345.678,9"), "1.234.567,89"); 267 | equal(typeTest("1.234.567,8"), "123.456,78"); 268 | }); 269 | 270 | test("Testing numbers with recursive mask", function(){ 271 | testfield.mask("0#.#"); 272 | 273 | equal(typeTest(""), ""); 274 | equal(typeTest("1"), "1"); 275 | equal(typeTest("12"), "12"); 276 | equal(typeTest("12."), "12."); 277 | equal(typeTest("12.3"), "12.3"); 278 | equal(typeTest("12.34"), "12.34"); 279 | equal(typeTest("12.345"), "12.34.5"); 280 | equal(typeTest("12.34.5."), "12.34.5"); 281 | equal(typeTest("12.34.56"), "12.34.56"); 282 | equal(typeTest("12.34.567"), "12.34.56.7"); 283 | equal(typeTest("12.34.56."), "12.34.56."); 284 | equal(typeTest("12.34.56"), "12.34.56"); 285 | equal(typeTest("12.34.5"), "12.34.5"); 286 | }); 287 | 288 | test("Testing numbers with recursive mask with one #", function(){ 289 | testfield.mask("0#", {}); 290 | 291 | equal(typeTest(""), ""); 292 | equal(typeTest("1"), "1"); 293 | equal(typeTest("12"), "12"); 294 | equal(typeTest("123"), "123"); 295 | equal(typeTest("1234"), "1234"); 296 | equal(typeTest("12345"), "12345"); 297 | equal(typeTest("12345"), "12345"); 298 | equal(typeTest("123456"), "123456"); 299 | equal(typeTest("1234567"), "1234567"); 300 | equal(typeTest("123456."), "123456"); 301 | equal(typeTest("123456"), "123456"); 302 | equal(typeTest("12345"), "12345"); 303 | }); 304 | test("Testing numbers with recursive mask with one # and reverse", function(){ 305 | testfield.mask("#0", {reverse: true}); 306 | 307 | equal(typeTest(""), ""); 308 | equal(typeTest("1"), "1"); 309 | equal(typeTest("12"), "12"); 310 | equal(typeTest("123"), "123"); 311 | equal(typeTest("1234"), "1234"); 312 | equal(typeTest("12345"), "12345"); 313 | equal(typeTest("12345"), "12345"); 314 | equal(typeTest("123456"), "123456"); 315 | equal(typeTest("1234567"), "1234567"); 316 | equal(typeTest("123456."), "123456"); 317 | equal(typeTest("123456"), "123456"); 318 | equal(typeTest("12345"), "12345"); 319 | }); 320 | test("Testing reversible masks with a literal on the last char", function () { 321 | testfield.mask("(99)"); 322 | 323 | equal( typeTest("(99"), "(99)"); 324 | }); 325 | 326 | module('Removing mask'); 327 | 328 | test("when I get the unmasked value", function(){ 329 | testfield.mask('(00) 0000-0000', { placeholder: '(__) ____-____' }); 330 | 331 | equal(typeTest("1299999999"), "(12) 9999-9999"); 332 | testfield.unmask() 333 | equal(testfield.val(), "1299999999"); 334 | 335 | if (window.Zepto) { 336 | equal(testfield.attr('placeholder'), ''); 337 | equal(testfield.attr('maxlength'), null); 338 | } else { 339 | equal(testfield.attr('placeholder'), undefined); 340 | equal(testfield.attr('maxlength'), undefined); 341 | } 342 | }); 343 | 344 | module('Getting Unmasked Value'); 345 | 346 | test("when I get the unmasked value", function(){ 347 | testfield.mask('(00) 0000-0000'); 348 | 349 | equal( typeTest("1299999999"), "(12) 9999-9999"); 350 | equal( testfield.cleanVal(), "1299999999"); 351 | }); 352 | 353 | test("when I get the unmasked value with recursive mask", function(){ 354 | testfield.mask('#.##0,00', {reverse:true}); 355 | 356 | equal( typeTest("123123123123123123", testfield), "1.231.231.231.231.231,23"); 357 | equal( testfield.cleanVal(), "123123123123123123"); 358 | }); 359 | 360 | module('Masking a value programmatically'); 361 | 362 | test("when I get the masked value programmatically", function(){ 363 | testfield.mask('(00) 0000-0000'); 364 | typeTest("1299999999", testfield); 365 | equal( testfield.masked("3488888888"), "(34) 8888-8888"); 366 | }); 367 | 368 | module('personalized settings') 369 | 370 | test("when adding more itens to the table translation",function(){ 371 | testfield.mask('00/00/0000', {'translation': {0: {pattern: /[0-9*]/}}}); 372 | 373 | equal( typeTest('12/34/5678'), '12/34/5678'); 374 | equal( typeTest('**/34/5678'), '**/34/5678'); 375 | }); 376 | 377 | test("when adding more itens to the table translation #2",function(){ 378 | testfield.mask('00/YY/0000', {'translation': {'Y': {pattern: /[0-9*]/}}}); 379 | 380 | equal( typeTest('12/34/5678'), '12/34/5678'); 381 | equal( typeTest('12/**/5678'), '12/**/5678'); 382 | }); 383 | 384 | test("when adding more itens to the table translation #3",function(){ 385 | var old_translation = $.jMaskGlobals.translation 386 | $.jMaskGlobals.translation = { 387 | '1': {pattern: /\d/}, 388 | '9': {pattern: /\d/, optional: true}, 389 | '#': {pattern: /\d/, recursive: true}, 390 | 'A': {pattern: /[a-zA-Z0-9]/}, 391 | 'S': {pattern: /[a-zA-Z]/} 392 | }; 393 | 394 | testfield.mask('00/11/1111'); 395 | 396 | equal( typeTest('12/12/5678'), '00/12/1256'); 397 | 398 | testfield.mask('11/00/1111'); 399 | equal( typeTest('12/12/5678'), '12/00/1256'); 400 | 401 | $.jMaskGlobals.translation = old_translation; 402 | }); 403 | 404 | test("when adding more itens to the table translation #fallback",function(){ 405 | testfield.mask('zz/z0/0000', {'translation': {'z': {pattern: /[0-9*]/, fallback: '*'}}}); 406 | 407 | equal( typeTest('12/:4/5678'), '12/*4/5678'); 408 | equal( typeTest('::/:4/5678'), '**/*4/5678'); 409 | }); 410 | 411 | test("test the translation #fallback #1" , function(){ 412 | testfield.mask('00t00', {'translation': {'t': {pattern: /[:,.]/, fallback: ':'}}}); 413 | 414 | equal( typeTest('1'), '1'); 415 | equal( typeTest('13'), '13'); 416 | equal( typeTest('137'), '13:7'); 417 | equal( typeTest('1337'), '13:37'); 418 | equal( typeTest('13z00'), '13:00'); 419 | }); 420 | 421 | test("test the translation #fallback #2" , function(){ 422 | testfield.mask('00/t0/t0', {'translation': {'t': {pattern: /[:,.*]/, fallback: '*'}}}); 423 | 424 | equal( typeTest('1'), '1'); 425 | equal( typeTest('13'), '13'); 426 | equal( typeTest('13/'), '13/'); 427 | equal( typeTest('13/a'), '13/*'); 428 | equal( typeTest('13/a1z1'), '13/*1/*1'); 429 | }); 430 | 431 | test("test the translation #fallback #3" , function(){ 432 | testfield.mask('tt/00/00', {'translation': {'t': {pattern: /[:,.*]/, fallback: '*'}}}); 433 | 434 | equal( typeTest('*'), '*'); 435 | equal( typeTest('13'), '**/13'); 436 | equal( typeTest('13/'), '**/13/'); 437 | equal( typeTest('13/a'), '**/13/'); 438 | equal( typeTest('13/a1z1'), '**/13/11'); 439 | }); 440 | 441 | test("when adding opcional chars",function(){ 442 | testfield.mask('099.099.099.099'); 443 | 444 | equal( typeTest('0.0.0.0'), '0.0.0.0'); 445 | equal( typeTest('00.00.00.00'), '00.00.00.00'); 446 | equal( typeTest('00.000.00.000'), '00.000.00.000'); 447 | equal( typeTest('000.00.000.00'), '000.00.000.00'); 448 | equal( typeTest('000.000.000.000'), '000.000.000.000'); 449 | equal( typeTest('000000000000'), '000.000.000.000'); 450 | equal( typeTest('0'), '0'); 451 | equal( typeTest('00'), '00'); 452 | equal( typeTest('00.'), '00.'); 453 | equal( typeTest('00.0'), '00.0'); 454 | equal( typeTest('00.00'), '00.00'); 455 | equal( typeTest('00.00.'), '00.00.'); 456 | equal( typeTest('00.00.000'), '00.00.000'); 457 | equal( typeTest('00.00.000.'), '00.00.000.'); 458 | equal( typeTest('00.00.000.0'), '00.00.000.0'); 459 | equal( typeTest('00..'), '00.'); 460 | }); 461 | 462 | test("when aplying mask on a element different than a form field",function(){ 463 | testdiv.mask('000.000.000-00', {reverse: true}); 464 | 465 | equal( typeDivTest('12312312312'), '123.123.123-12'); 466 | equal( typeDivTest('123.123.123-12'), '123.123.123-12'); 467 | equal( typeDivTest('123.123a123-12'), '123.123.123-12'); 468 | equal( typeDivTest('191'), '1-91'); 469 | 470 | testdiv.mask('00/00/0000'); 471 | equal( typeDivTest('000000'), '00/00/00'); 472 | equal( typeDivTest('00000000'), '00/00/0000'); 473 | equal( typeDivTest('00/00/0000'), '00/00/0000'); 474 | equal( typeDivTest('0a/00/0000'), '00/00/000'); 475 | 476 | }); 477 | 478 | module('Testing data-mask attribute support'); 479 | 480 | test("Testing data-mask attribute", function(){ 481 | equal( typeTest("00/", testfieldDataMask), "00/"); 482 | equal( typeTest("00a", testfieldDataMask), "00/"); 483 | equal( typeTest("00a00/00", testfieldDataMask), "00/00/00"); 484 | equal( typeTest("0a/00/00", testfieldDataMask), "00/00/0"); 485 | equal( typeTest("0a/0a/00", testfieldDataMask), "00/00"); 486 | equal( typeTest("00000000", testfieldDataMask), "00/00/0000"); 487 | }); 488 | 489 | test("Testing data-mask-reverse attribute", function(){ 490 | equal( typeTest("0000", testfieldDataMaskWithReverse), "00,00"); 491 | equal( typeTest("000000", testfieldDataMaskWithReverse), "0.000,00"); 492 | equal( typeTest("0000000000", testfieldDataMaskWithReverse), "00.000.000,00"); 493 | }); 494 | 495 | module('Event fire test'); 496 | 497 | // TODO: need to understand why zepto.js isnt calling change event! 498 | if(!window.Zepto) { 499 | test('onChange Test', 2, function(){ 500 | testfield.unmask(); 501 | 502 | var callback = sinon.spy(); 503 | var mock = sinon.mock() 504 | var typeAndBlur = function(typedValue){ 505 | testfield.trigger('keydown'); 506 | testfield.val(typedValue); 507 | testfield.trigger('input'); 508 | testfield.trigger("blur"); 509 | }; 510 | 511 | testfield.mask('000.(000).000/0-0'); 512 | 513 | testfield.on("change", callback); 514 | 515 | typeAndBlur(""); 516 | typeAndBlur("1"); 517 | typeAndBlur("12"); 518 | typeAndBlur("123"); 519 | typeAndBlur("1234"); 520 | typeAndBlur("12345"); 521 | typeAndBlur("123456"); 522 | typeAndBlur("1234567"); 523 | typeAndBlur("12345678"); 524 | typeAndBlur("123456789"); 525 | typeAndBlur("123456789"); 526 | typeAndBlur("1234567891"); 527 | typeAndBlur("12345678912"); 528 | 529 | equal(testfield.val(), "123.(456).789/1-2" ); 530 | equal(true, sinon.match(11).or(12).test(callback.callCount)) 531 | 532 | testfield.off("change"); 533 | testfield.unmask(); 534 | 535 | }); 536 | } 537 | 538 | 539 | test('onDrop Test', function(){ 540 | ok(true, "todo"); 541 | }); 542 | 543 | module('testing Remove If Not Match Feature'); 544 | 545 | test('test when clearifnotmatch javascript notation', 4, function(){ 546 | var typeAndFocusOut = function(typedValue){ 547 | testfield.keydown().val(typedValue).trigger('input'); 548 | testfield.trigger("focusout"); 549 | }; 550 | 551 | testfield.mask('000'); 552 | 553 | typeAndFocusOut("1"); 554 | equal( testfield.val(), "1" ); 555 | 556 | testfield.mask('000', {clearIfNotMatch: true}); 557 | 558 | typeAndFocusOut("1"); 559 | equal( testfield.val(), "" ); 560 | 561 | typeAndFocusOut("12"); 562 | equal( testfield.val(), "" ); 563 | 564 | typeAndFocusOut("123"); 565 | equal( testfield.val(), "123" ); 566 | }); 567 | 568 | test('test when clearifnotmatch javascript notation #2', 4, function(){ 569 | var typeAndFocusOut = function(typedValue){ 570 | testfield.keydown().val(typedValue).trigger('input'); 571 | testfield.trigger("focusout"); 572 | }; 573 | 574 | testfield.mask('7 (000) 000-0000'); 575 | 576 | typeAndFocusOut("1"); 577 | equal( testfield.val(), "7 (1" ); 578 | 579 | testfield.mask('7 (000) 000-0000', {clearIfNotMatch: true}); 580 | 581 | typeAndFocusOut("1"); 582 | equal( testfield.val(), "" ); 583 | 584 | typeAndFocusOut("12"); 585 | equal( testfield.val(), "" ); 586 | 587 | typeAndFocusOut("7 (123) 123-1234"); 588 | equal( testfield.val(), "7 (123) 123-1234" ); 589 | }); 590 | 591 | test('test when clearifnotmatch is HTML notation', 3, function(){ 592 | var typeAndFocusOut = function(typedValue){ 593 | testfieldDataMaskWithClearIfNotMatch.keydown().val(typedValue).trigger('input'); 594 | testfieldDataMaskWithClearIfNotMatch.trigger("focusout"); 595 | }; 596 | 597 | typeAndFocusOut("1"); 598 | equal( testfieldDataMaskWithClearIfNotMatch.val(), "" ); 599 | 600 | typeAndFocusOut("12"); 601 | equal( testfieldDataMaskWithClearIfNotMatch.val(), "" ); 602 | 603 | typeAndFocusOut("123"); 604 | equal( testfieldDataMaskWithClearIfNotMatch.val(), "123" ); 605 | }); 606 | 607 | module('testing Remove If Not Match Feature'); 608 | 609 | test('test when clearifnotmatch javascript notation', 1, function(){ 610 | 611 | testfield.mask('000', {placeholder: '___'}); 612 | equal( testfield.attr('placeholder'), "___" ); 613 | 614 | }); 615 | 616 | test('test when clearifnotmatch with optional mask', 9, function(){ 617 | // html notation 618 | var typeAndBlur = function(field, typedValue){ 619 | field.keydown().val(typedValue).trigger('input'); 620 | field.trigger("focusout"); 621 | }; 622 | 623 | typeAndBlur(testfieldDataMaskWithClearIfNotMatchAndOptionalMask, "1"); 624 | equal( testfieldDataMaskWithClearIfNotMatchAndOptionalMask.val(), "" ); 625 | 626 | typeAndBlur(testfieldDataMaskWithClearIfNotMatchAndOptionalMask, "12"); 627 | equal( testfieldDataMaskWithClearIfNotMatchAndOptionalMask.val(), "12" ); 628 | 629 | typeAndBlur(testfieldDataMaskWithClearIfNotMatchAndOptionalMask, "123"); 630 | equal( testfieldDataMaskWithClearIfNotMatchAndOptionalMask.val(), "123" ); 631 | 632 | // javascript notation 633 | testfield.mask('099.099.099.099', {clearIfNotMatch: true}); 634 | 635 | typeAndBlur(testfield, "1"); 636 | equal( testfield.val(), "" ); 637 | 638 | typeAndBlur(testfield, "123."); 639 | equal( testfield.val(), "" ); 640 | 641 | typeAndBlur(testfield, "123.0"); 642 | equal( testfield.val(), "" ); 643 | 644 | typeAndBlur(testfield, "123.01.000."); 645 | equal( testfield.val(), "" ); 646 | 647 | typeAndBlur(testfield, "123.01.000.123"); 648 | equal( testfield.val(), "123.01.000.123" ); 649 | 650 | typeAndBlur(testfield, "0.0.0.0"); 651 | equal( testfield.val(), "0.0.0.0" ); 652 | }); 653 | 654 | test('test when clearifnotmatch with recursive mask', 4, function(){ 655 | // html notation 656 | var typeAndBlur = function(field, typedValue){ 657 | field.keydown().val(typedValue).trigger('input'); 658 | field.trigger("focusout"); 659 | }; 660 | 661 | // javascript notation 662 | testfield.mask('#.##0,00', {clearIfNotMatch: true, reverse: true}); 663 | 664 | typeAndBlur(testfield, "0"); 665 | equal( testfield.val(), "" ); 666 | 667 | typeAndBlur(testfield, "00"); 668 | equal( testfield.val(), "" ); 669 | 670 | typeAndBlur(testfield, "0,00"); 671 | equal( testfield.val(), "0,00" ); 672 | 673 | typeAndBlur(testfield, "1.000,00"); 674 | equal( testfield.val(), "1.000,00" ); 675 | 676 | }); 677 | 678 | module('dynamically loaded elements') 679 | test('#non-inputs', function(){ 680 | expect(5); 681 | 682 | var $container = $('#container-dy-non-inputs'); 683 | var clock = this.clock; 684 | var ticker; 685 | var tester; 686 | var c = 0; 687 | 688 | function write() { 689 | 690 | if (c >= 5) { 691 | clearInterval(ticker); 692 | clearInterval(tester); 693 | return; 694 | } 695 | 696 | c++; 697 | 698 | $container.append('
' + c + c + c + c + '
'); 699 | clock.tick(1000); 700 | }; 701 | 702 | function testIt() { 703 | 704 | var cs = $container.find('.c'); 705 | $.each(cs, function(k, field){ 706 | var t = k + 1; 707 | equal($(field).text(), '' + t + t + ':' + t + t); 708 | t++; 709 | }); 710 | }; 711 | 712 | ticker = setInterval(write, 1000); 713 | 714 | write(); 715 | $('.c', $container).mask('00:00'); 716 | testIt() 717 | }); 718 | }); 719 | -------------------------------------------------------------------------------- /test/qunit.css: -------------------------------------------------------------------------------- 1 | /** 2 | * QUnit v1.11.0 - A JavaScript Unit Testing Framework 3 | * 4 | * http://qunitjs.com 5 | * 6 | * Copyright 2012 jQuery Foundation and other contributors 7 | * Released under the MIT license. 8 | * http://jquery.org/license 9 | */ 10 | 11 | /** Font Family and Sizes */ 12 | 13 | #qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult { 14 | font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif; 15 | } 16 | 17 | #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; } 18 | #qunit-tests { font-size: smaller; } 19 | 20 | 21 | /** Resets */ 22 | 23 | #qunit-tests, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter { 24 | margin: 0; 25 | padding: 0; 26 | } 27 | 28 | 29 | /** Header */ 30 | 31 | #qunit-header { 32 | padding: 0.5em 0 0.5em 1em; 33 | 34 | color: #8699a4; 35 | background-color: #0d3349; 36 | 37 | font-size: 1.5em; 38 | line-height: 1em; 39 | font-weight: normal; 40 | 41 | border-radius: 5px 5px 0 0; 42 | -moz-border-radius: 5px 5px 0 0; 43 | -webkit-border-top-right-radius: 5px; 44 | -webkit-border-top-left-radius: 5px; 45 | } 46 | 47 | #qunit-header a { 48 | text-decoration: none; 49 | color: #c2ccd1; 50 | } 51 | 52 | #qunit-header a:hover, 53 | #qunit-header a:focus { 54 | color: #fff; 55 | } 56 | 57 | #qunit-testrunner-toolbar label { 58 | display: inline-block; 59 | padding: 0 .5em 0 .1em; 60 | } 61 | 62 | #qunit-banner { 63 | height: 5px; 64 | } 65 | 66 | #qunit-testrunner-toolbar { 67 | padding: 0.5em 0 0.5em 2em; 68 | color: #5E740B; 69 | background-color: #eee; 70 | overflow: hidden; 71 | } 72 | 73 | #qunit-userAgent { 74 | padding: 0.5em 0 0.5em 2.5em; 75 | background-color: #2b81af; 76 | color: #fff; 77 | text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; 78 | } 79 | 80 | #qunit-modulefilter-container { 81 | float: right; 82 | } 83 | 84 | /** Tests: Pass/Fail */ 85 | 86 | #qunit-tests { 87 | list-style-position: inside; 88 | } 89 | 90 | #qunit-tests li { 91 | padding: 0.4em 0.5em 0.4em 2.5em; 92 | border-bottom: 1px solid #fff; 93 | list-style-position: inside; 94 | } 95 | 96 | #qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running { 97 | display: none; 98 | } 99 | 100 | #qunit-tests li strong { 101 | cursor: pointer; 102 | } 103 | 104 | #qunit-tests li a { 105 | padding: 0.5em; 106 | color: #c2ccd1; 107 | text-decoration: none; 108 | } 109 | #qunit-tests li a:hover, 110 | #qunit-tests li a:focus { 111 | color: #000; 112 | } 113 | 114 | #qunit-tests li .runtime { 115 | float: right; 116 | font-size: smaller; 117 | } 118 | 119 | .qunit-assert-list { 120 | margin-top: 0.5em; 121 | padding: 0.5em; 122 | 123 | background-color: #fff; 124 | 125 | border-radius: 5px; 126 | -moz-border-radius: 5px; 127 | -webkit-border-radius: 5px; 128 | } 129 | 130 | .qunit-collapsed { 131 | display: none; 132 | } 133 | 134 | #qunit-tests table { 135 | border-collapse: collapse; 136 | margin-top: .2em; 137 | } 138 | 139 | #qunit-tests th { 140 | text-align: right; 141 | vertical-align: top; 142 | padding: 0 .5em 0 0; 143 | } 144 | 145 | #qunit-tests td { 146 | vertical-align: top; 147 | } 148 | 149 | #qunit-tests pre { 150 | margin: 0; 151 | white-space: pre-wrap; 152 | word-wrap: break-word; 153 | } 154 | 155 | #qunit-tests del { 156 | background-color: #e0f2be; 157 | color: #374e0c; 158 | text-decoration: none; 159 | } 160 | 161 | #qunit-tests ins { 162 | background-color: #ffcaca; 163 | color: #500; 164 | text-decoration: none; 165 | } 166 | 167 | /*** Test Counts */ 168 | 169 | #qunit-tests b.counts { color: black; } 170 | #qunit-tests b.passed { color: #5E740B; } 171 | #qunit-tests b.failed { color: #710909; } 172 | 173 | #qunit-tests li li { 174 | padding: 5px; 175 | background-color: #fff; 176 | border-bottom: none; 177 | list-style-position: inside; 178 | } 179 | 180 | /*** Passing Styles */ 181 | 182 | #qunit-tests li li.pass { 183 | color: #3c510c; 184 | background-color: #fff; 185 | border-left: 10px solid #C6E746; 186 | } 187 | 188 | #qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; } 189 | #qunit-tests .pass .test-name { color: #366097; } 190 | 191 | #qunit-tests .pass .test-actual, 192 | #qunit-tests .pass .test-expected { color: #999999; } 193 | 194 | #qunit-banner.qunit-pass { background-color: #C6E746; } 195 | 196 | /*** Failing Styles */ 197 | 198 | #qunit-tests li li.fail { 199 | color: #710909; 200 | background-color: #fff; 201 | border-left: 10px solid #EE5757; 202 | white-space: pre; 203 | } 204 | 205 | #qunit-tests > li:last-child { 206 | border-radius: 0 0 5px 5px; 207 | -moz-border-radius: 0 0 5px 5px; 208 | -webkit-border-bottom-right-radius: 5px; 209 | -webkit-border-bottom-left-radius: 5px; 210 | } 211 | 212 | #qunit-tests .fail { color: #000000; background-color: #EE5757; } 213 | #qunit-tests .fail .test-name, 214 | #qunit-tests .fail .module-name { color: #000000; } 215 | 216 | #qunit-tests .fail .test-actual { color: #EE5757; } 217 | #qunit-tests .fail .test-expected { color: green; } 218 | 219 | #qunit-banner.qunit-fail { background-color: #EE5757; } 220 | 221 | 222 | /** Result */ 223 | 224 | #qunit-testresult { 225 | padding: 0.5em 0.5em 0.5em 2.5em; 226 | 227 | color: #2b81af; 228 | background-color: #D2E0E6; 229 | 230 | border-bottom: 1px solid white; 231 | } 232 | #qunit-testresult .module-name { 233 | font-weight: bold; 234 | } 235 | 236 | /** Fixture */ 237 | 238 | #qunit-fixture { 239 | position: absolute; 240 | top: -10000px; 241 | left: -10000px; 242 | width: 1000px; 243 | height: 1000px; 244 | } -------------------------------------------------------------------------------- /test/sinon-qunit-1.0.0.js: -------------------------------------------------------------------------------- 1 | /** 2 | * sinon-qunit 1.0.0, 2010/12/09 3 | * 4 | * @author Christian Johansen (christian@cjohansen.no) 5 | * 6 | * (The BSD License) 7 | * 8 | * Copyright (c) 2010-2011, Christian Johansen, christian@cjohansen.no 9 | * All rights reserved. 10 | * 11 | * Redistribution and use in source and binary forms, with or without modification, 12 | * are permitted provided that the following conditions are met: 13 | * 14 | * * Redistributions of source code must retain the above copyright notice, 15 | * this list of conditions and the following disclaimer. 16 | * * Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * * Neither the name of Christian Johansen nor the names of his contributors 20 | * may be used to endorse or promote products derived from this software 21 | * without specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 24 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 25 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 26 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 27 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 29 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 30 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 31 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 32 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | */ 34 | /*global sinon, QUnit, test*/ 35 | sinon.assert.fail = function (msg) { 36 | QUnit.ok(false, msg); 37 | }; 38 | 39 | sinon.assert.pass = function (assertion) { 40 | QUnit.ok(true, assertion); 41 | }; 42 | 43 | sinon.config = { 44 | injectIntoThis: true, 45 | injectInto: null, 46 | properties: ["spy", "stub", "mock", "clock", "sandbox"], 47 | useFakeTimers: true, 48 | useFakeServer: false 49 | }; 50 | 51 | (function (global) { 52 | var qTest = QUnit.test; 53 | 54 | QUnit.test = global.test = function (testName, expected, callback, async) { 55 | if (arguments.length === 2) { 56 | callback = expected; 57 | expected = null; 58 | } 59 | 60 | return qTest(testName, expected, sinon.test(callback), async); 61 | }; 62 | }(this)); 63 | -------------------------------------------------------------------------------- /test/sinon-qunit.js: -------------------------------------------------------------------------------- 1 | /** 2 | * sinon-qunit 1.0.0, 2010/12/09 3 | * 4 | * @author Christian Johansen (christian@cjohansen.no) 5 | * 6 | * (The BSD License) 7 | * 8 | * Copyright (c) 2010-2011, Christian Johansen, christian@cjohansen.no 9 | * All rights reserved. 10 | * 11 | * Redistribution and use in source and binary forms, with or without modification, 12 | * are permitted provided that the following conditions are met: 13 | * 14 | * * Redistributions of source code must retain the above copyright notice, 15 | * this list of conditions and the following disclaimer. 16 | * * Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * * Neither the name of Christian Johansen nor the names of his contributors 20 | * may be used to endorse or promote products derived from this software 21 | * without specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 24 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 25 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 26 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 27 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 29 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 30 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 31 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 32 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | */ 34 | /*global sinon, QUnit, test*/ 35 | sinon.assert.fail = function (msg) { 36 | QUnit.ok(false, msg); 37 | }; 38 | 39 | sinon.assert.pass = function (assertion) { 40 | QUnit.ok(true, assertion); 41 | }; 42 | 43 | sinon.config = { 44 | injectIntoThis: true, 45 | injectInto: null, 46 | properties: ["spy", "stub", "mock", "clock", "sandbox"], 47 | useFakeTimers: true, 48 | useFakeServer: false 49 | }; 50 | 51 | (function (global) { 52 | var qTest = QUnit.test; 53 | 54 | QUnit.test = global.test = function (testName, expected, callback, async) { 55 | if (arguments.length === 2) { 56 | callback = expected; 57 | expected = null; 58 | } 59 | 60 | return qTest(testName, expected, sinon.test(callback), async); 61 | }; 62 | }(this)); 63 | -------------------------------------------------------------------------------- /test/test-for-jquery-1.11.1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | jQuery-Mask-Plugin UnitTesting 4 | 5 | 6 | 7 | 8 |

jQuery-Mask-Plugin QUnit Tests

9 |

10 |
11 |

12 |
    13 |
    test markup, will be hidden
    14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
    23 |
    24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /test/test-for-jquery-1.7.2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | jQuery-Mask-Plugin UnitTesting 4 | 5 | 6 | 7 | 8 |

    jQuery-Mask-Plugin QUnit Tests

    9 |

    10 |
    11 |

    12 |
      13 |
      test markup, will be hidden
      14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
      23 |
      24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /test/test-for-jquery-1.8.3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | jQuery-Mask-Plugin UnitTesting 4 | 5 | 6 | 7 | 8 |

      jQuery-Mask-Plugin QUnit Tests

      9 |

      10 |
      11 |

      12 |
        13 |
        test markup, will be hidden
        14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
        23 |
        24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /test/test-for-jquery-1.9.1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | jQuery-Mask-Plugin UnitTesting 4 | 5 | 6 | 7 | 8 |

        jQuery-Mask-Plugin QUnit Tests

        9 |

        10 |
        11 |

        12 |
          13 |
          test markup, will be hidden
          14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
          23 |
          24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /test/test-for-jquery-2.1.1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | jQuery-Mask-Plugin UnitTesting 4 | 5 | 6 | 7 | 8 |

          jQuery-Mask-Plugin QUnit Tests

          9 |

          10 |
          11 |

          12 |
            13 |
            test markup, will be hidden
            14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
            23 |
            24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /test/test-for-jquery-3.0.0.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | jQuery-Mask-Plugin UnitTesting 4 | 5 | 6 | 7 | 8 |

            jQuery-Mask-Plugin QUnit Tests

            9 |

            10 |
            11 |

            12 |
              13 |
              test markup, will be hidden
              14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
              23 |
              24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /test/test-for-zepto.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Zepto-Mask-Plugin UnitTesting 4 | 5 | 6 | 7 | 8 |

              Zepto-Mask-Plugin QUnit Tests

              9 |

              10 |
              11 |

              12 |
                13 |
                test markup, will be hidden
                14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
                23 |
                24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /test/zepto/data.js: -------------------------------------------------------------------------------- 1 | // Zepto.js 2 | // (c) 2010-2014 Thomas Fuchs 3 | // Zepto.js may be freely distributed under the MIT license. 4 | 5 | // The following code is heavily inspired by jQuery's $.fn.data() 6 | 7 | ;(function($){ 8 | var data = {}, dataAttr = $.fn.data, camelize = $.camelCase, 9 | exp = $.expando = 'Zepto' + (+new Date()), emptyArray = [] 10 | 11 | // Get value from node: 12 | // 1. first try key as given, 13 | // 2. then try camelized key, 14 | // 3. fall back to reading "data-*" attribute. 15 | function getData(node, name) { 16 | var id = node[exp], store = id && data[id] 17 | if (name === undefined) return store || setData(node) 18 | else { 19 | if (store) { 20 | if (name in store) return store[name] 21 | var camelName = camelize(name) 22 | if (camelName in store) return store[camelName] 23 | } 24 | return dataAttr.call($(node), name) 25 | } 26 | } 27 | 28 | // Store value under camelized key on node 29 | function setData(node, name, value) { 30 | var id = node[exp] || (node[exp] = ++$.uuid), 31 | store = data[id] || (data[id] = attributeData(node)) 32 | if (name !== undefined) store[camelize(name)] = value 33 | return store 34 | } 35 | 36 | // Read all "data-*" attributes from a node 37 | function attributeData(node) { 38 | var store = {} 39 | $.each(node.attributes || emptyArray, function(i, attr){ 40 | if (attr.name.indexOf('data-') == 0) 41 | store[camelize(attr.name.replace('data-', ''))] = 42 | $.zepto.deserializeValue(attr.value) 43 | }) 44 | return store 45 | } 46 | 47 | $.fn.data = function(name, value) { 48 | return value === undefined ? 49 | // set multiple values via object 50 | $.isPlainObject(name) ? 51 | this.each(function(i, node){ 52 | $.each(name, function(key, value){ setData(node, key, value) }) 53 | }) : 54 | // get value from first element 55 | (0 in this ? getData(this[0], name) : undefined) : 56 | // set value on all elements 57 | this.each(function(){ setData(this, name, value) }) 58 | } 59 | 60 | $.fn.removeData = function(names) { 61 | if (typeof names == 'string') names = names.split(/\s+/) 62 | return this.each(function(){ 63 | var id = this[exp], store = id && data[id] 64 | if (store) $.each(names || store, function(key){ 65 | delete store[names ? camelize(this) : key] 66 | }) 67 | }) 68 | } 69 | 70 | // Generate extended `remove` and `empty` functions 71 | ;['remove', 'empty'].forEach(function(methodName){ 72 | var origFn = $.fn[methodName] 73 | $.fn[methodName] = function() { 74 | var elements = this.find('*') 75 | if (methodName === 'remove') elements = elements.add(this) 76 | elements.removeData() 77 | return origFn.call(this) 78 | } 79 | }) 80 | })(Zepto) 81 | -------------------------------------------------------------------------------- /test/zepto/event.js: -------------------------------------------------------------------------------- 1 | // Zepto.js 2 | // (c) 2010-2014 Thomas Fuchs 3 | // Zepto.js may be freely distributed under the MIT license. 4 | 5 | ;(function($){ 6 | var _zid = 1, undefined, 7 | slice = Array.prototype.slice, 8 | isFunction = $.isFunction, 9 | isString = function(obj){ return typeof obj == 'string' }, 10 | handlers = {}, 11 | specialEvents={}, 12 | focusinSupported = 'onfocusin' in window, 13 | focus = { focus: 'focusin', blur: 'focusout' }, 14 | hover = { mouseenter: 'mouseover', mouseleave: 'mouseout' } 15 | 16 | specialEvents.click = specialEvents.mousedown = specialEvents.mouseup = specialEvents.mousemove = 'MouseEvents' 17 | 18 | function zid(element) { 19 | return element._zid || (element._zid = _zid++) 20 | } 21 | function findHandlers(element, event, fn, selector) { 22 | event = parse(event) 23 | if (event.ns) var matcher = matcherFor(event.ns) 24 | return (handlers[zid(element)] || []).filter(function(handler) { 25 | return handler 26 | && (!event.e || handler.e == event.e) 27 | && (!event.ns || matcher.test(handler.ns)) 28 | && (!fn || zid(handler.fn) === zid(fn)) 29 | && (!selector || handler.sel == selector) 30 | }) 31 | } 32 | function parse(event) { 33 | var parts = ('' + event).split('.') 34 | return {e: parts[0], ns: parts.slice(1).sort().join(' ')} 35 | } 36 | function matcherFor(ns) { 37 | return new RegExp('(?:^| )' + ns.replace(' ', ' .* ?') + '(?: |$)') 38 | } 39 | 40 | function eventCapture(handler, captureSetting) { 41 | return handler.del && 42 | (!focusinSupported && (handler.e in focus)) || 43 | !!captureSetting 44 | } 45 | 46 | function realEvent(type) { 47 | return hover[type] || (focusinSupported && focus[type]) || type 48 | } 49 | 50 | function add(element, events, fn, data, selector, delegator, capture){ 51 | var id = zid(element), set = (handlers[id] || (handlers[id] = [])) 52 | events.split(/\s/).forEach(function(event){ 53 | if (event == 'ready') return $(document).ready(fn) 54 | var handler = parse(event) 55 | handler.fn = fn 56 | handler.sel = selector 57 | // emulate mouseenter, mouseleave 58 | if (handler.e in hover) fn = function(e){ 59 | var related = e.relatedTarget 60 | if (!related || (related !== this && !$.contains(this, related))) 61 | return handler.fn.apply(this, arguments) 62 | } 63 | handler.del = delegator 64 | var callback = delegator || fn 65 | handler.proxy = function(e){ 66 | e = compatible(e) 67 | if (e.isImmediatePropagationStopped()) return 68 | e.data = data 69 | var result = callback.apply(element, e._args == undefined ? [e] : [e].concat(e._args)) 70 | if (result === false) e.preventDefault(), e.stopPropagation() 71 | return result 72 | } 73 | handler.i = set.length 74 | set.push(handler) 75 | if ('addEventListener' in element) 76 | element.addEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture)) 77 | }) 78 | } 79 | function remove(element, events, fn, selector, capture){ 80 | var id = zid(element) 81 | ;(events || '').split(/\s/).forEach(function(event){ 82 | findHandlers(element, event, fn, selector).forEach(function(handler){ 83 | delete handlers[id][handler.i] 84 | if ('removeEventListener' in element) 85 | element.removeEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture)) 86 | }) 87 | }) 88 | } 89 | 90 | $.event = { add: add, remove: remove } 91 | 92 | $.proxy = function(fn, context) { 93 | var args = (2 in arguments) && slice.call(arguments, 2) 94 | if (isFunction(fn)) { 95 | var proxyFn = function(){ return fn.apply(context, args ? args.concat(slice.call(arguments)) : arguments) } 96 | proxyFn._zid = zid(fn) 97 | return proxyFn 98 | } else if (isString(context)) { 99 | if (args) { 100 | args.unshift(fn[context], fn) 101 | return $.proxy.apply(null, args) 102 | } else { 103 | return $.proxy(fn[context], fn) 104 | } 105 | } else { 106 | throw new TypeError("expected function") 107 | } 108 | } 109 | 110 | $.fn.bind = function(event, data, callback){ 111 | return this.on(event, data, callback) 112 | } 113 | $.fn.unbind = function(event, callback){ 114 | return this.off(event, callback) 115 | } 116 | $.fn.one = function(event, selector, data, callback){ 117 | return this.on(event, selector, data, callback, 1) 118 | } 119 | 120 | var returnTrue = function(){return true}, 121 | returnFalse = function(){return false}, 122 | ignoreProperties = /^([A-Z]|returnValue$|layer[XY]$)/, 123 | eventMethods = { 124 | preventDefault: 'isDefaultPrevented', 125 | stopImmediatePropagation: 'isImmediatePropagationStopped', 126 | stopPropagation: 'isPropagationStopped' 127 | } 128 | 129 | function compatible(event, source) { 130 | if (source || !event.isDefaultPrevented) { 131 | source || (source = event) 132 | 133 | $.each(eventMethods, function(name, predicate) { 134 | var sourceMethod = source[name] 135 | event[name] = function(){ 136 | this[predicate] = returnTrue 137 | return sourceMethod && sourceMethod.apply(source, arguments) 138 | } 139 | event[predicate] = returnFalse 140 | }) 141 | 142 | if (source.defaultPrevented !== undefined ? source.defaultPrevented : 143 | 'returnValue' in source ? source.returnValue === false : 144 | source.getPreventDefault && source.getPreventDefault()) 145 | event.isDefaultPrevented = returnTrue 146 | } 147 | return event 148 | } 149 | 150 | function createProxy(event) { 151 | var key, proxy = { originalEvent: event } 152 | for (key in event) 153 | if (!ignoreProperties.test(key) && event[key] !== undefined) proxy[key] = event[key] 154 | 155 | return compatible(proxy, event) 156 | } 157 | 158 | $.fn.delegate = function(selector, event, callback){ 159 | return this.on(event, selector, callback) 160 | } 161 | $.fn.undelegate = function(selector, event, callback){ 162 | return this.off(event, selector, callback) 163 | } 164 | 165 | $.fn.live = function(event, callback){ 166 | $(document.body).delegate(this.selector, event, callback) 167 | return this 168 | } 169 | $.fn.die = function(event, callback){ 170 | $(document.body).undelegate(this.selector, event, callback) 171 | return this 172 | } 173 | 174 | $.fn.on = function(event, selector, data, callback, one){ 175 | var autoRemove, delegator, $this = this 176 | if (event && !isString(event)) { 177 | $.each(event, function(type, fn){ 178 | $this.on(type, selector, data, fn, one) 179 | }) 180 | return $this 181 | } 182 | 183 | if (!isString(selector) && !isFunction(callback) && callback !== false) 184 | callback = data, data = selector, selector = undefined 185 | if (isFunction(data) || data === false) 186 | callback = data, data = undefined 187 | 188 | if (callback === false) callback = returnFalse 189 | 190 | return $this.each(function(_, element){ 191 | if (one) autoRemove = function(e){ 192 | remove(element, e.type, callback) 193 | return callback.apply(this, arguments) 194 | } 195 | 196 | if (selector) delegator = function(e){ 197 | var evt, match = $(e.target).closest(selector, element).get(0) 198 | if (match && match !== element) { 199 | evt = $.extend(createProxy(e), {currentTarget: match, liveFired: element}) 200 | return (autoRemove || callback).apply(match, [evt].concat(slice.call(arguments, 1))) 201 | } 202 | } 203 | 204 | add(element, event, callback, data, selector, delegator || autoRemove) 205 | }) 206 | } 207 | $.fn.off = function(event, selector, callback){ 208 | var $this = this 209 | if (event && !isString(event)) { 210 | $.each(event, function(type, fn){ 211 | $this.off(type, selector, fn) 212 | }) 213 | return $this 214 | } 215 | 216 | if (!isString(selector) && !isFunction(callback) && callback !== false) 217 | callback = selector, selector = undefined 218 | 219 | if (callback === false) callback = returnFalse 220 | 221 | return $this.each(function(){ 222 | remove(this, event, callback, selector) 223 | }) 224 | } 225 | 226 | $.fn.trigger = function(event, args){ 227 | event = (isString(event) || $.isPlainObject(event)) ? $.Event(event) : compatible(event) 228 | event._args = args 229 | return this.each(function(){ 230 | // items in the collection might not be DOM elements 231 | if('dispatchEvent' in this) this.dispatchEvent(event) 232 | else $(this).triggerHandler(event, args) 233 | }) 234 | } 235 | 236 | // triggers event handlers on current element just as if an event occurred, 237 | // doesn't trigger an actual event, doesn't bubble 238 | $.fn.triggerHandler = function(event, args){ 239 | var e, result 240 | this.each(function(i, element){ 241 | e = createProxy(isString(event) ? $.Event(event) : event) 242 | e._args = args 243 | e.target = element 244 | $.each(findHandlers(element, event.type || event), function(i, handler){ 245 | result = handler.proxy(e) 246 | if (e.isImmediatePropagationStopped()) return false 247 | }) 248 | }) 249 | return result 250 | } 251 | 252 | // shortcut methods for `.bind(event, fn)` for each event type 253 | ;('focusin focusout load resize scroll unload click dblclick '+ 254 | 'mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave '+ 255 | 'change select keydown keypress keyup error').split(' ').forEach(function(event) { 256 | $.fn[event] = function(callback) { 257 | return callback ? 258 | this.bind(event, callback) : 259 | this.trigger(event) 260 | } 261 | }) 262 | 263 | ;['focus', 'blur'].forEach(function(name) { 264 | $.fn[name] = function(callback) { 265 | if (callback) this.bind(name, callback) 266 | else this.each(function(){ 267 | try { this[name]() } 268 | catch(e) {} 269 | }) 270 | return this 271 | } 272 | }) 273 | 274 | $.Event = function(type, props) { 275 | if (!isString(type)) props = type, type = props.type 276 | var event = document.createEvent(specialEvents[type] || 'Events'), bubbles = true 277 | if (props) for (var name in props) (name == 'bubbles') ? (bubbles = !!props[name]) : (event[name] = props[name]) 278 | event.initEvent(type, bubbles, true) 279 | return compatible(event) 280 | } 281 | 282 | })(Zepto) 283 | -------------------------------------------------------------------------------- /test/zepto/zepto.min.js: -------------------------------------------------------------------------------- 1 | /* Zepto v1.1.4 - zepto event ajax form ie - zeptojs.com/license */ 2 | 3 | var Zepto = (function() { 4 | var undefined, key, $, classList, emptyArray = [], slice = emptyArray.slice, filter = emptyArray.filter, 5 | document = window.document, 6 | elementDisplay = {}, classCache = {}, 7 | cssNumber = { 'column-count': 1, 'columns': 1, 'font-weight': 1, 'line-height': 1,'opacity': 1, 'z-index': 1, 'zoom': 1 }, 8 | fragmentRE = /^\s*<(\w+|!)[^>]*>/, 9 | singleTagRE = /^<(\w+)\s*\/?>(?:<\/\1>|)$/, 10 | tagExpanderRE = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig, 11 | rootNodeRE = /^(?:body|html)$/i, 12 | capitalRE = /([A-Z])/g, 13 | 14 | // special attributes that should be get/set via method calls 15 | methodAttributes = ['val', 'css', 'html', 'text', 'data', 'width', 'height', 'offset'], 16 | 17 | adjacencyOperators = [ 'after', 'prepend', 'before', 'append' ], 18 | table = document.createElement('table'), 19 | tableRow = document.createElement('tr'), 20 | containers = { 21 | 'tr': document.createElement('tbody'), 22 | 'tbody': table, 'thead': table, 'tfoot': table, 23 | 'td': tableRow, 'th': tableRow, 24 | '*': document.createElement('div') 25 | }, 26 | readyRE = /complete|loaded|interactive/, 27 | simpleSelectorRE = /^[\w-]*$/, 28 | class2type = {}, 29 | toString = class2type.toString, 30 | zepto = {}, 31 | camelize, uniq, 32 | tempParent = document.createElement('div'), 33 | propMap = { 34 | 'tabindex': 'tabIndex', 35 | 'readonly': 'readOnly', 36 | 'for': 'htmlFor', 37 | 'class': 'className', 38 | 'maxlength': 'maxLength', 39 | 'cellspacing': 'cellSpacing', 40 | 'cellpadding': 'cellPadding', 41 | 'rowspan': 'rowSpan', 42 | 'colspan': 'colSpan', 43 | 'usemap': 'useMap', 44 | 'frameborder': 'frameBorder', 45 | 'contenteditable': 'contentEditable' 46 | }, 47 | isArray = Array.isArray || 48 | function(object){ return object instanceof Array } 49 | 50 | zepto.matches = function(element, selector) { 51 | if (!selector || !element || element.nodeType !== 1) return false 52 | var matchesSelector = element.webkitMatchesSelector || element.mozMatchesSelector || 53 | element.oMatchesSelector || element.matchesSelector 54 | if (matchesSelector) return matchesSelector.call(element, selector) 55 | // fall back to performing a selector: 56 | var match, parent = element.parentNode, temp = !parent 57 | if (temp) (parent = tempParent).appendChild(element) 58 | match = ~zepto.qsa(parent, selector).indexOf(element) 59 | temp && tempParent.removeChild(element) 60 | return match 61 | } 62 | 63 | function type(obj) { 64 | return obj == null ? String(obj) : 65 | class2type[toString.call(obj)] || "object" 66 | } 67 | 68 | function isFunction(value) { return type(value) == "function" } 69 | function isWindow(obj) { return obj != null && obj == obj.window } 70 | function isDocument(obj) { return obj != null && obj.nodeType == obj.DOCUMENT_NODE } 71 | function isObject(obj) { return type(obj) == "object" } 72 | function isPlainObject(obj) { 73 | return isObject(obj) && !isWindow(obj) && Object.getPrototypeOf(obj) == Object.prototype 74 | } 75 | function likeArray(obj) { return typeof obj.length == 'number' } 76 | 77 | function compact(array) { return filter.call(array, function(item){ return item != null }) } 78 | function flatten(array) { return array.length > 0 ? $.fn.concat.apply([], array) : array } 79 | camelize = function(str){ return str.replace(/-+(.)?/g, function(match, chr){ return chr ? chr.toUpperCase() : '' }) } 80 | function dasherize(str) { 81 | return str.replace(/::/g, '/') 82 | .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2') 83 | .replace(/([a-z\d])([A-Z])/g, '$1_$2') 84 | .replace(/_/g, '-') 85 | .toLowerCase() 86 | } 87 | uniq = function(array){ return filter.call(array, function(item, idx){ return array.indexOf(item) == idx }) } 88 | 89 | function classRE(name) { 90 | return name in classCache ? 91 | classCache[name] : (classCache[name] = new RegExp('(^|\\s)' + name + '(\\s|$)')) 92 | } 93 | 94 | function maybeAddPx(name, value) { 95 | return (typeof value == "number" && !cssNumber[dasherize(name)]) ? value + "px" : value 96 | } 97 | 98 | function defaultDisplay(nodeName) { 99 | var element, display 100 | if (!elementDisplay[nodeName]) { 101 | element = document.createElement(nodeName) 102 | document.body.appendChild(element) 103 | display = getComputedStyle(element, '').getPropertyValue("display") 104 | element.parentNode.removeChild(element) 105 | display == "none" && (display = "block") 106 | elementDisplay[nodeName] = display 107 | } 108 | return elementDisplay[nodeName] 109 | } 110 | 111 | function children(element) { 112 | return 'children' in element ? 113 | slice.call(element.children) : 114 | $.map(element.childNodes, function(node){ if (node.nodeType == 1) return node }) 115 | } 116 | 117 | // `$.zepto.fragment` takes a html string and an optional tag name 118 | // to generate DOM nodes nodes from the given html string. 119 | // The generated DOM nodes are returned as an array. 120 | // This function can be overriden in plugins for example to make 121 | // it compatible with browsers that don't support the DOM fully. 122 | zepto.fragment = function(html, name, properties) { 123 | var dom, nodes, container 124 | 125 | // A special case optimization for a single tag 126 | if (singleTagRE.test(html)) dom = $(document.createElement(RegExp.$1)) 127 | 128 | if (!dom) { 129 | if (html.replace) html = html.replace(tagExpanderRE, "<$1>") 130 | if (name === undefined) name = fragmentRE.test(html) && RegExp.$1 131 | if (!(name in containers)) name = '*' 132 | 133 | container = containers[name] 134 | container.innerHTML = '' + html 135 | dom = $.each(slice.call(container.childNodes), function(){ 136 | container.removeChild(this) 137 | }) 138 | } 139 | 140 | if (isPlainObject(properties)) { 141 | nodes = $(dom) 142 | $.each(properties, function(key, value) { 143 | if (methodAttributes.indexOf(key) > -1) nodes[key](value) 144 | else nodes.attr(key, value) 145 | }) 146 | } 147 | 148 | return dom 149 | } 150 | 151 | // `$.zepto.Z` swaps out the prototype of the given `dom` array 152 | // of nodes with `$.fn` and thus supplying all the Zepto functions 153 | // to the array. Note that `__proto__` is not supported on Internet 154 | // Explorer. This method can be overriden in plugins. 155 | zepto.Z = function(dom, selector) { 156 | dom = dom || [] 157 | dom.__proto__ = $.fn 158 | dom.selector = selector || '' 159 | return dom 160 | } 161 | 162 | // `$.zepto.isZ` should return `true` if the given object is a Zepto 163 | // collection. This method can be overriden in plugins. 164 | zepto.isZ = function(object) { 165 | return object instanceof zepto.Z 166 | } 167 | 168 | // `$.zepto.init` is Zepto's counterpart to jQuery's `$.fn.init` and 169 | // takes a CSS selector and an optional context (and handles various 170 | // special cases). 171 | // This method can be overriden in plugins. 172 | zepto.init = function(selector, context) { 173 | var dom 174 | // If nothing given, return an empty Zepto collection 175 | if (!selector) return zepto.Z() 176 | // Optimize for string selectors 177 | else if (typeof selector == 'string') { 178 | selector = selector.trim() 179 | // If it's a html fragment, create nodes from it 180 | // Note: In both Chrome 21 and Firefox 15, DOM error 12 181 | // is thrown if the fragment doesn't begin with < 182 | if (selector[0] == '<' && fragmentRE.test(selector)) 183 | dom = zepto.fragment(selector, RegExp.$1, context), selector = null 184 | // If there's a context, create a collection on that context first, and select 185 | // nodes from there 186 | else if (context !== undefined) return $(context).find(selector) 187 | // If it's a CSS selector, use it to select nodes. 188 | else dom = zepto.qsa(document, selector) 189 | } 190 | // If a function is given, call it when the DOM is ready 191 | else if (isFunction(selector)) return $(document).ready(selector) 192 | // If a Zepto collection is given, just return it 193 | else if (zepto.isZ(selector)) return selector 194 | else { 195 | // normalize array if an array of nodes is given 196 | if (isArray(selector)) dom = compact(selector) 197 | // Wrap DOM nodes. 198 | else if (isObject(selector)) 199 | dom = [selector], selector = null 200 | // If it's a html fragment, create nodes from it 201 | else if (fragmentRE.test(selector)) 202 | dom = zepto.fragment(selector.trim(), RegExp.$1, context), selector = null 203 | // If there's a context, create a collection on that context first, and select 204 | // nodes from there 205 | else if (context !== undefined) return $(context).find(selector) 206 | // And last but no least, if it's a CSS selector, use it to select nodes. 207 | else dom = zepto.qsa(document, selector) 208 | } 209 | // create a new Zepto collection from the nodes found 210 | return zepto.Z(dom, selector) 211 | } 212 | 213 | // `$` will be the base `Zepto` object. When calling this 214 | // function just call `$.zepto.init, which makes the implementation 215 | // details of selecting nodes and creating Zepto collections 216 | // patchable in plugins. 217 | $ = function(selector, context){ 218 | return zepto.init(selector, context) 219 | } 220 | 221 | function extend(target, source, deep) { 222 | for (key in source) 223 | if (deep && (isPlainObject(source[key]) || isArray(source[key]))) { 224 | if (isPlainObject(source[key]) && !isPlainObject(target[key])) 225 | target[key] = {} 226 | if (isArray(source[key]) && !isArray(target[key])) 227 | target[key] = [] 228 | extend(target[key], source[key], deep) 229 | } 230 | else if (source[key] !== undefined) target[key] = source[key] 231 | } 232 | 233 | // Copy all but undefined properties from one or more 234 | // objects to the `target` object. 235 | $.extend = function(target){ 236 | var deep, args = slice.call(arguments, 1) 237 | if (typeof target == 'boolean') { 238 | deep = target 239 | target = args.shift() 240 | } 241 | args.forEach(function(arg){ extend(target, arg, deep) }) 242 | return target 243 | } 244 | 245 | // `$.zepto.qsa` is Zepto's CSS selector implementation which 246 | // uses `document.querySelectorAll` and optimizes for some special cases, like `#id`. 247 | // This method can be overriden in plugins. 248 | zepto.qsa = function(element, selector){ 249 | var found, 250 | maybeID = selector[0] == '#', 251 | maybeClass = !maybeID && selector[0] == '.', 252 | nameOnly = maybeID || maybeClass ? selector.slice(1) : selector, // Ensure that a 1 char tag name still gets checked 253 | isSimple = simpleSelectorRE.test(nameOnly) 254 | return (isDocument(element) && isSimple && maybeID) ? 255 | ( (found = element.getElementById(nameOnly)) ? [found] : [] ) : 256 | (element.nodeType !== 1 && element.nodeType !== 9) ? [] : 257 | slice.call( 258 | isSimple && !maybeID ? 259 | maybeClass ? element.getElementsByClassName(nameOnly) : // If it's simple, it could be a class 260 | element.getElementsByTagName(selector) : // Or a tag 261 | element.querySelectorAll(selector) // Or it's not simple, and we need to query all 262 | ) 263 | } 264 | 265 | function filtered(nodes, selector) { 266 | return selector == null ? $(nodes) : $(nodes).filter(selector) 267 | } 268 | 269 | $.contains = document.documentElement.contains ? 270 | function(parent, node) { 271 | return parent !== node && parent.contains(node) 272 | } : 273 | function(parent, node) { 274 | while (node && (node = node.parentNode)) 275 | if (node === parent) return true 276 | return false 277 | } 278 | 279 | function funcArg(context, arg, idx, payload) { 280 | return isFunction(arg) ? arg.call(context, idx, payload) : arg 281 | } 282 | 283 | function setAttribute(node, name, value) { 284 | value == null ? node.removeAttribute(name) : node.setAttribute(name, value) 285 | } 286 | 287 | // access className property while respecting SVGAnimatedString 288 | function className(node, value){ 289 | var klass = node.className, 290 | svg = klass && klass.baseVal !== undefined 291 | 292 | if (value === undefined) return svg ? klass.baseVal : klass 293 | svg ? (klass.baseVal = value) : (node.className = value) 294 | } 295 | 296 | // "true" => true 297 | // "false" => false 298 | // "null" => null 299 | // "42" => 42 300 | // "42.5" => 42.5 301 | // "08" => "08" 302 | // JSON => parse if valid 303 | // String => self 304 | function deserializeValue(value) { 305 | var num 306 | try { 307 | return value ? 308 | value == "true" || 309 | ( value == "false" ? false : 310 | value == "null" ? null : 311 | !/^0/.test(value) && !isNaN(num = Number(value)) ? num : 312 | /^[\[\{]/.test(value) ? $.parseJSON(value) : 313 | value ) 314 | : value 315 | } catch(e) { 316 | return value 317 | } 318 | } 319 | 320 | $.type = type 321 | $.isFunction = isFunction 322 | $.isWindow = isWindow 323 | $.isArray = isArray 324 | $.isPlainObject = isPlainObject 325 | 326 | $.isEmptyObject = function(obj) { 327 | var name 328 | for (name in obj) return false 329 | return true 330 | } 331 | 332 | $.inArray = function(elem, array, i){ 333 | return emptyArray.indexOf.call(array, elem, i) 334 | } 335 | 336 | $.camelCase = camelize 337 | $.trim = function(str) { 338 | return str == null ? "" : String.prototype.trim.call(str) 339 | } 340 | 341 | // plugin compatibility 342 | $.uuid = 0 343 | $.support = { } 344 | $.expr = { } 345 | 346 | $.map = function(elements, callback){ 347 | var value, values = [], i, key 348 | if (likeArray(elements)) 349 | for (i = 0; i < elements.length; i++) { 350 | value = callback(elements[i], i) 351 | if (value != null) values.push(value) 352 | } 353 | else 354 | for (key in elements) { 355 | value = callback(elements[key], key) 356 | if (value != null) values.push(value) 357 | } 358 | return flatten(values) 359 | } 360 | 361 | $.each = function(elements, callback){ 362 | var i, key 363 | if (likeArray(elements)) { 364 | for (i = 0; i < elements.length; i++) 365 | if (callback.call(elements[i], i, elements[i]) === false) return elements 366 | } else { 367 | for (key in elements) 368 | if (callback.call(elements[key], key, elements[key]) === false) return elements 369 | } 370 | 371 | return elements 372 | } 373 | 374 | $.grep = function(elements, callback){ 375 | return filter.call(elements, callback) 376 | } 377 | 378 | if (window.JSON) $.parseJSON = JSON.parse 379 | 380 | // Populate the class2type map 381 | $.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { 382 | class2type[ "[object " + name + "]" ] = name.toLowerCase() 383 | }) 384 | 385 | // Define methods that will be available on all 386 | // Zepto collections 387 | $.fn = { 388 | // Because a collection acts like an array 389 | // copy over these useful array functions. 390 | forEach: emptyArray.forEach, 391 | reduce: emptyArray.reduce, 392 | push: emptyArray.push, 393 | sort: emptyArray.sort, 394 | indexOf: emptyArray.indexOf, 395 | concat: emptyArray.concat, 396 | 397 | // `map` and `slice` in the jQuery API work differently 398 | // from their array counterparts 399 | map: function(fn){ 400 | return $($.map(this, function(el, i){ return fn.call(el, i, el) })) 401 | }, 402 | slice: function(){ 403 | return $(slice.apply(this, arguments)) 404 | }, 405 | 406 | ready: function(callback){ 407 | // need to check if document.body exists for IE as that browser reports 408 | // document ready when it hasn't yet created the body element 409 | if (readyRE.test(document.readyState) && document.body) callback($) 410 | else document.addEventListener('DOMContentLoaded', function(){ callback($) }, false) 411 | return this 412 | }, 413 | get: function(idx){ 414 | return idx === undefined ? slice.call(this) : this[idx >= 0 ? idx : idx + this.length] 415 | }, 416 | toArray: function(){ return this.get() }, 417 | size: function(){ 418 | return this.length 419 | }, 420 | remove: function(){ 421 | return this.each(function(){ 422 | if (this.parentNode != null) 423 | this.parentNode.removeChild(this) 424 | }) 425 | }, 426 | each: function(callback){ 427 | emptyArray.every.call(this, function(el, idx){ 428 | return callback.call(el, idx, el) !== false 429 | }) 430 | return this 431 | }, 432 | filter: function(selector){ 433 | if (isFunction(selector)) return this.not(this.not(selector)) 434 | return $(filter.call(this, function(element){ 435 | return zepto.matches(element, selector) 436 | })) 437 | }, 438 | add: function(selector,context){ 439 | return $(uniq(this.concat($(selector,context)))) 440 | }, 441 | is: function(selector){ 442 | return this.length > 0 && zepto.matches(this[0], selector) 443 | }, 444 | not: function(selector){ 445 | var nodes=[] 446 | if (isFunction(selector) && selector.call !== undefined) 447 | this.each(function(idx){ 448 | if (!selector.call(this,idx)) nodes.push(this) 449 | }) 450 | else { 451 | var excludes = typeof selector == 'string' ? this.filter(selector) : 452 | (likeArray(selector) && isFunction(selector.item)) ? slice.call(selector) : $(selector) 453 | this.forEach(function(el){ 454 | if (excludes.indexOf(el) < 0) nodes.push(el) 455 | }) 456 | } 457 | return $(nodes) 458 | }, 459 | has: function(selector){ 460 | return this.filter(function(){ 461 | return isObject(selector) ? 462 | $.contains(this, selector) : 463 | $(this).find(selector).size() 464 | }) 465 | }, 466 | eq: function(idx){ 467 | return idx === -1 ? this.slice(idx) : this.slice(idx, + idx + 1) 468 | }, 469 | first: function(){ 470 | var el = this[0] 471 | return el && !isObject(el) ? el : $(el) 472 | }, 473 | last: function(){ 474 | var el = this[this.length - 1] 475 | return el && !isObject(el) ? el : $(el) 476 | }, 477 | find: function(selector){ 478 | var result, $this = this 479 | if (!selector) result = [] 480 | else if (typeof selector == 'object') 481 | result = $(selector).filter(function(){ 482 | var node = this 483 | return emptyArray.some.call($this, function(parent){ 484 | return $.contains(parent, node) 485 | }) 486 | }) 487 | else if (this.length == 1) result = $(zepto.qsa(this[0], selector)) 488 | else result = this.map(function(){ return zepto.qsa(this, selector) }) 489 | return result 490 | }, 491 | closest: function(selector, context){ 492 | var node = this[0], collection = false 493 | if (typeof selector == 'object') collection = $(selector) 494 | while (node && !(collection ? collection.indexOf(node) >= 0 : zepto.matches(node, selector))) 495 | node = node !== context && !isDocument(node) && node.parentNode 496 | return $(node) 497 | }, 498 | parents: function(selector){ 499 | var ancestors = [], nodes = this 500 | while (nodes.length > 0) 501 | nodes = $.map(nodes, function(node){ 502 | if ((node = node.parentNode) && !isDocument(node) && ancestors.indexOf(node) < 0) { 503 | ancestors.push(node) 504 | return node 505 | } 506 | }) 507 | return filtered(ancestors, selector) 508 | }, 509 | parent: function(selector){ 510 | return filtered(uniq(this.pluck('parentNode')), selector) 511 | }, 512 | children: function(selector){ 513 | return filtered(this.map(function(){ return children(this) }), selector) 514 | }, 515 | contents: function() { 516 | return this.map(function() { return slice.call(this.childNodes) }) 517 | }, 518 | siblings: function(selector){ 519 | return filtered(this.map(function(i, el){ 520 | return filter.call(children(el.parentNode), function(child){ return child!==el }) 521 | }), selector) 522 | }, 523 | empty: function(){ 524 | return this.each(function(){ this.innerHTML = '' }) 525 | }, 526 | // `pluck` is borrowed from Prototype.js 527 | pluck: function(property){ 528 | return $.map(this, function(el){ return el[property] }) 529 | }, 530 | show: function(){ 531 | return this.each(function(){ 532 | this.style.display == "none" && (this.style.display = '') 533 | if (getComputedStyle(this, '').getPropertyValue("display") == "none") 534 | this.style.display = defaultDisplay(this.nodeName) 535 | }) 536 | }, 537 | replaceWith: function(newContent){ 538 | return this.before(newContent).remove() 539 | }, 540 | wrap: function(structure){ 541 | var func = isFunction(structure) 542 | if (this[0] && !func) 543 | var dom = $(structure).get(0), 544 | clone = dom.parentNode || this.length > 1 545 | 546 | return this.each(function(index){ 547 | $(this).wrapAll( 548 | func ? structure.call(this, index) : 549 | clone ? dom.cloneNode(true) : dom 550 | ) 551 | }) 552 | }, 553 | wrapAll: function(structure){ 554 | if (this[0]) { 555 | $(this[0]).before(structure = $(structure)) 556 | var children 557 | // drill down to the inmost element 558 | while ((children = structure.children()).length) structure = children.first() 559 | $(structure).append(this) 560 | } 561 | return this 562 | }, 563 | wrapInner: function(structure){ 564 | var func = isFunction(structure) 565 | return this.each(function(index){ 566 | var self = $(this), contents = self.contents(), 567 | dom = func ? structure.call(this, index) : structure 568 | contents.length ? contents.wrapAll(dom) : self.append(dom) 569 | }) 570 | }, 571 | unwrap: function(){ 572 | this.parent().each(function(){ 573 | $(this).replaceWith($(this).children()) 574 | }) 575 | return this 576 | }, 577 | clone: function(){ 578 | return this.map(function(){ return this.cloneNode(true) }) 579 | }, 580 | hide: function(){ 581 | return this.css("display", "none") 582 | }, 583 | toggle: function(setting){ 584 | return this.each(function(){ 585 | var el = $(this) 586 | ;(setting === undefined ? el.css("display") == "none" : setting) ? el.show() : el.hide() 587 | }) 588 | }, 589 | prev: function(selector){ return $(this.pluck('previousElementSibling')).filter(selector || '*') }, 590 | next: function(selector){ return $(this.pluck('nextElementSibling')).filter(selector || '*') }, 591 | html: function(html){ 592 | return 0 in arguments ? 593 | this.each(function(idx){ 594 | var originHtml = this.innerHTML 595 | $(this).empty().append( funcArg(this, html, idx, originHtml) ) 596 | }) : 597 | (0 in this ? this[0].innerHTML : null) 598 | }, 599 | text: function(text){ 600 | return 0 in arguments ? 601 | this.each(function(idx){ 602 | var newText = funcArg(this, text, idx, this.textContent) 603 | this.textContent = newText == null ? '' : ''+newText 604 | }) : 605 | (0 in this ? this[0].textContent : null) 606 | }, 607 | attr: function(name, value){ 608 | var result 609 | return (typeof name == 'string' && !(1 in arguments)) ? 610 | (!this.length || this[0].nodeType !== 1 ? undefined : 611 | (!(result = this[0].getAttribute(name)) && name in this[0]) ? this[0][name] : result 612 | ) : 613 | this.each(function(idx){ 614 | if (this.nodeType !== 1) return 615 | if (isObject(name)) for (key in name) setAttribute(this, key, name[key]) 616 | else setAttribute(this, name, funcArg(this, value, idx, this.getAttribute(name))) 617 | }) 618 | }, 619 | removeAttr: function(name){ 620 | return this.each(function(){ this.nodeType === 1 && setAttribute(this, name) }) 621 | }, 622 | prop: function(name, value){ 623 | name = propMap[name] || name 624 | return (1 in arguments) ? 625 | this.each(function(idx){ 626 | this[name] = funcArg(this, value, idx, this[name]) 627 | }) : 628 | (this[0] && this[0][name]) 629 | }, 630 | data: function(name, value){ 631 | var attrName = 'data-' + name.replace(capitalRE, '-$1').toLowerCase() 632 | 633 | var data = (1 in arguments) ? 634 | this.attr(attrName, value) : 635 | this.attr(attrName) 636 | 637 | return data !== null ? deserializeValue(data) : undefined 638 | }, 639 | val: function(value){ 640 | return 0 in arguments ? 641 | this.each(function(idx){ 642 | this.value = funcArg(this, value, idx, this.value) 643 | }) : 644 | (this[0] && (this[0].multiple ? 645 | $(this[0]).find('option').filter(function(){ return this.selected }).pluck('value') : 646 | this[0].value) 647 | ) 648 | }, 649 | offset: function(coordinates){ 650 | if (coordinates) return this.each(function(index){ 651 | var $this = $(this), 652 | coords = funcArg(this, coordinates, index, $this.offset()), 653 | parentOffset = $this.offsetParent().offset(), 654 | props = { 655 | top: coords.top - parentOffset.top, 656 | left: coords.left - parentOffset.left 657 | } 658 | 659 | if ($this.css('position') == 'static') props['position'] = 'relative' 660 | $this.css(props) 661 | }) 662 | if (!this.length) return null 663 | var obj = this[0].getBoundingClientRect() 664 | return { 665 | left: obj.left + window.pageXOffset, 666 | top: obj.top + window.pageYOffset, 667 | width: Math.round(obj.width), 668 | height: Math.round(obj.height) 669 | } 670 | }, 671 | css: function(property, value){ 672 | if (arguments.length < 2) { 673 | var element = this[0], computedStyle = getComputedStyle(element, '') 674 | if(!element) return 675 | if (typeof property == 'string') 676 | return element.style[camelize(property)] || computedStyle.getPropertyValue(property) 677 | else if (isArray(property)) { 678 | var props = {} 679 | $.each(isArray(property) ? property: [property], function(_, prop){ 680 | props[prop] = (element.style[camelize(prop)] || computedStyle.getPropertyValue(prop)) 681 | }) 682 | return props 683 | } 684 | } 685 | 686 | var css = '' 687 | if (type(property) == 'string') { 688 | if (!value && value !== 0) 689 | this.each(function(){ this.style.removeProperty(dasherize(property)) }) 690 | else 691 | css = dasherize(property) + ":" + maybeAddPx(property, value) 692 | } else { 693 | for (key in property) 694 | if (!property[key] && property[key] !== 0) 695 | this.each(function(){ this.style.removeProperty(dasherize(key)) }) 696 | else 697 | css += dasherize(key) + ':' + maybeAddPx(key, property[key]) + ';' 698 | } 699 | 700 | return this.each(function(){ this.style.cssText += ';' + css }) 701 | }, 702 | index: function(element){ 703 | return element ? this.indexOf($(element)[0]) : this.parent().children().indexOf(this[0]) 704 | }, 705 | hasClass: function(name){ 706 | if (!name) return false 707 | return emptyArray.some.call(this, function(el){ 708 | return this.test(className(el)) 709 | }, classRE(name)) 710 | }, 711 | addClass: function(name){ 712 | if (!name) return this 713 | return this.each(function(idx){ 714 | classList = [] 715 | var cls = className(this), newName = funcArg(this, name, idx, cls) 716 | newName.split(/\s+/g).forEach(function(klass){ 717 | if (!$(this).hasClass(klass)) classList.push(klass) 718 | }, this) 719 | classList.length && className(this, cls + (cls ? " " : "") + classList.join(" ")) 720 | }) 721 | }, 722 | removeClass: function(name){ 723 | return this.each(function(idx){ 724 | if (name === undefined) return className(this, '') 725 | classList = className(this) 726 | funcArg(this, name, idx, classList).split(/\s+/g).forEach(function(klass){ 727 | classList = classList.replace(classRE(klass), " ") 728 | }) 729 | className(this, classList.trim()) 730 | }) 731 | }, 732 | toggleClass: function(name, when){ 733 | if (!name) return this 734 | return this.each(function(idx){ 735 | var $this = $(this), names = funcArg(this, name, idx, className(this)) 736 | names.split(/\s+/g).forEach(function(klass){ 737 | (when === undefined ? !$this.hasClass(klass) : when) ? 738 | $this.addClass(klass) : $this.removeClass(klass) 739 | }) 740 | }) 741 | }, 742 | scrollTop: function(value){ 743 | if (!this.length) return 744 | var hasScrollTop = 'scrollTop' in this[0] 745 | if (value === undefined) return hasScrollTop ? this[0].scrollTop : this[0].pageYOffset 746 | return this.each(hasScrollTop ? 747 | function(){ this.scrollTop = value } : 748 | function(){ this.scrollTo(this.scrollX, value) }) 749 | }, 750 | scrollLeft: function(value){ 751 | if (!this.length) return 752 | var hasScrollLeft = 'scrollLeft' in this[0] 753 | if (value === undefined) return hasScrollLeft ? this[0].scrollLeft : this[0].pageXOffset 754 | return this.each(hasScrollLeft ? 755 | function(){ this.scrollLeft = value } : 756 | function(){ this.scrollTo(value, this.scrollY) }) 757 | }, 758 | position: function() { 759 | if (!this.length) return 760 | 761 | var elem = this[0], 762 | // Get *real* offsetParent 763 | offsetParent = this.offsetParent(), 764 | // Get correct offsets 765 | offset = this.offset(), 766 | parentOffset = rootNodeRE.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset() 767 | 768 | // Subtract element margins 769 | // note: when an element has margin: auto the offsetLeft and marginLeft 770 | // are the same in Safari causing offset.left to incorrectly be 0 771 | offset.top -= parseFloat( $(elem).css('margin-top') ) || 0 772 | offset.left -= parseFloat( $(elem).css('margin-left') ) || 0 773 | 774 | // Add offsetParent borders 775 | parentOffset.top += parseFloat( $(offsetParent[0]).css('border-top-width') ) || 0 776 | parentOffset.left += parseFloat( $(offsetParent[0]).css('border-left-width') ) || 0 777 | 778 | // Subtract the two offsets 779 | return { 780 | top: offset.top - parentOffset.top, 781 | left: offset.left - parentOffset.left 782 | } 783 | }, 784 | offsetParent: function() { 785 | return this.map(function(){ 786 | var parent = this.offsetParent || document.body 787 | while (parent && !rootNodeRE.test(parent.nodeName) && $(parent).css("position") == "static") 788 | parent = parent.offsetParent 789 | return parent 790 | }) 791 | } 792 | } 793 | 794 | // for now 795 | $.fn.detach = $.fn.remove 796 | 797 | // Generate the `width` and `height` functions 798 | ;['width', 'height'].forEach(function(dimension){ 799 | var dimensionProperty = 800 | dimension.replace(/./, function(m){ return m[0].toUpperCase() }) 801 | 802 | $.fn[dimension] = function(value){ 803 | var offset, el = this[0] 804 | if (value === undefined) return isWindow(el) ? el['inner' + dimensionProperty] : 805 | isDocument(el) ? el.documentElement['scroll' + dimensionProperty] : 806 | (offset = this.offset()) && offset[dimension] 807 | else return this.each(function(idx){ 808 | el = $(this) 809 | el.css(dimension, funcArg(this, value, idx, el[dimension]())) 810 | }) 811 | } 812 | }) 813 | 814 | function traverseNode(node, fun) { 815 | fun(node) 816 | for (var i = 0, len = node.childNodes.length; i < len; i++) 817 | traverseNode(node.childNodes[i], fun) 818 | } 819 | 820 | // Generate the `after`, `prepend`, `before`, `append`, 821 | // `insertAfter`, `insertBefore`, `appendTo`, and `prependTo` methods. 822 | adjacencyOperators.forEach(function(operator, operatorIndex) { 823 | var inside = operatorIndex % 2 //=> prepend, append 824 | 825 | $.fn[operator] = function(){ 826 | // arguments can be nodes, arrays of nodes, Zepto objects and HTML strings 827 | var argType, nodes = $.map(arguments, function(arg) { 828 | argType = type(arg) 829 | return argType == "object" || argType == "array" || arg == null ? 830 | arg : zepto.fragment(arg) 831 | }), 832 | parent, copyByClone = this.length > 1 833 | if (nodes.length < 1) return this 834 | 835 | return this.each(function(_, target){ 836 | parent = inside ? target : target.parentNode 837 | 838 | // convert all methods to a "before" operation 839 | target = operatorIndex == 0 ? target.nextSibling : 840 | operatorIndex == 1 ? target.firstChild : 841 | operatorIndex == 2 ? target : 842 | null 843 | 844 | var parentInDocument = $.contains(document.documentElement, parent) 845 | 846 | nodes.forEach(function(node){ 847 | if (copyByClone) node = node.cloneNode(true) 848 | else if (!parent) return $(node).remove() 849 | 850 | parent.insertBefore(node, target) 851 | if (parentInDocument) traverseNode(node, function(el){ 852 | if (el.nodeName != null && el.nodeName.toUpperCase() === 'SCRIPT' && 853 | (!el.type || el.type === 'text/javascript') && !el.src) 854 | window['eval'].call(window, el.innerHTML) 855 | }) 856 | }) 857 | }) 858 | } 859 | 860 | // after => insertAfter 861 | // prepend => prependTo 862 | // before => insertBefore 863 | // append => appendTo 864 | $.fn[inside ? operator+'To' : 'insert'+(operatorIndex ? 'Before' : 'After')] = function(html){ 865 | $(html)[operator](this) 866 | return this 867 | } 868 | }) 869 | 870 | zepto.Z.prototype = $.fn 871 | 872 | // Export internal API functions in the `$.zepto` namespace 873 | zepto.uniq = uniq 874 | zepto.deserializeValue = deserializeValue 875 | $.zepto = zepto 876 | 877 | return $ 878 | })() 879 | 880 | window.Zepto = Zepto 881 | window.$ === undefined && (window.$ = Zepto) 882 | 883 | ;(function($){ 884 | var _zid = 1, undefined, 885 | slice = Array.prototype.slice, 886 | isFunction = $.isFunction, 887 | isString = function(obj){ return typeof obj == 'string' }, 888 | handlers = {}, 889 | specialEvents={}, 890 | focusinSupported = 'onfocusin' in window, 891 | focus = { focus: 'focusin', blur: 'focusout' }, 892 | hover = { mouseenter: 'mouseover', mouseleave: 'mouseout' } 893 | 894 | specialEvents.click = specialEvents.mousedown = specialEvents.mouseup = specialEvents.mousemove = 'MouseEvents' 895 | 896 | function zid(element) { 897 | return element._zid || (element._zid = _zid++) 898 | } 899 | function findHandlers(element, event, fn, selector) { 900 | event = parse(event) 901 | if (event.ns) var matcher = matcherFor(event.ns) 902 | return (handlers[zid(element)] || []).filter(function(handler) { 903 | return handler 904 | && (!event.e || handler.e == event.e) 905 | && (!event.ns || matcher.test(handler.ns)) 906 | && (!fn || zid(handler.fn) === zid(fn)) 907 | && (!selector || handler.sel == selector) 908 | }) 909 | } 910 | function parse(event) { 911 | var parts = ('' + event).split('.') 912 | return {e: parts[0], ns: parts.slice(1).sort().join(' ')} 913 | } 914 | function matcherFor(ns) { 915 | return new RegExp('(?:^| )' + ns.replace(' ', ' .* ?') + '(?: |$)') 916 | } 917 | 918 | function eventCapture(handler, captureSetting) { 919 | return handler.del && 920 | (!focusinSupported && (handler.e in focus)) || 921 | !!captureSetting 922 | } 923 | 924 | function realEvent(type) { 925 | return hover[type] || (focusinSupported && focus[type]) || type 926 | } 927 | 928 | function add(element, events, fn, data, selector, delegator, capture){ 929 | var id = zid(element), set = (handlers[id] || (handlers[id] = [])) 930 | events.split(/\s/).forEach(function(event){ 931 | if (event == 'ready') return $(document).ready(fn) 932 | var handler = parse(event) 933 | handler.fn = fn 934 | handler.sel = selector 935 | // emulate mouseenter, mouseleave 936 | if (handler.e in hover) fn = function(e){ 937 | var related = e.relatedTarget 938 | if (!related || (related !== this && !$.contains(this, related))) 939 | return handler.fn.apply(this, arguments) 940 | } 941 | handler.del = delegator 942 | var callback = delegator || fn 943 | handler.proxy = function(e){ 944 | e = compatible(e) 945 | if (e.isImmediatePropagationStopped()) return 946 | e.data = data 947 | var result = callback.apply(element, e._args == undefined ? [e] : [e].concat(e._args)) 948 | if (result === false) e.preventDefault(), e.stopPropagation() 949 | return result 950 | } 951 | handler.i = set.length 952 | set.push(handler) 953 | if ('addEventListener' in element) 954 | element.addEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture)) 955 | }) 956 | } 957 | function remove(element, events, fn, selector, capture){ 958 | var id = zid(element) 959 | ;(events || '').split(/\s/).forEach(function(event){ 960 | findHandlers(element, event, fn, selector).forEach(function(handler){ 961 | delete handlers[id][handler.i] 962 | if ('removeEventListener' in element) 963 | element.removeEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture)) 964 | }) 965 | }) 966 | } 967 | 968 | $.event = { add: add, remove: remove } 969 | 970 | $.proxy = function(fn, context) { 971 | var args = (2 in arguments) && slice.call(arguments, 2) 972 | if (isFunction(fn)) { 973 | var proxyFn = function(){ return fn.apply(context, args ? args.concat(slice.call(arguments)) : arguments) } 974 | proxyFn._zid = zid(fn) 975 | return proxyFn 976 | } else if (isString(context)) { 977 | if (args) { 978 | args.unshift(fn[context], fn) 979 | return $.proxy.apply(null, args) 980 | } else { 981 | return $.proxy(fn[context], fn) 982 | } 983 | } else { 984 | throw new TypeError("expected function") 985 | } 986 | } 987 | 988 | $.fn.bind = function(event, data, callback){ 989 | return this.on(event, data, callback) 990 | } 991 | $.fn.unbind = function(event, callback){ 992 | return this.off(event, callback) 993 | } 994 | $.fn.one = function(event, selector, data, callback){ 995 | return this.on(event, selector, data, callback, 1) 996 | } 997 | 998 | var returnTrue = function(){return true}, 999 | returnFalse = function(){return false}, 1000 | ignoreProperties = /^([A-Z]|returnValue$|layer[XY]$)/, 1001 | eventMethods = { 1002 | preventDefault: 'isDefaultPrevented', 1003 | stopImmediatePropagation: 'isImmediatePropagationStopped', 1004 | stopPropagation: 'isPropagationStopped' 1005 | } 1006 | 1007 | function compatible(event, source) { 1008 | if (source || !event.isDefaultPrevented) { 1009 | source || (source = event) 1010 | 1011 | $.each(eventMethods, function(name, predicate) { 1012 | var sourceMethod = source[name] 1013 | event[name] = function(){ 1014 | this[predicate] = returnTrue 1015 | return sourceMethod && sourceMethod.apply(source, arguments) 1016 | } 1017 | event[predicate] = returnFalse 1018 | }) 1019 | 1020 | if (source.defaultPrevented !== undefined ? source.defaultPrevented : 1021 | 'returnValue' in source ? source.returnValue === false : 1022 | source.getPreventDefault && source.getPreventDefault()) 1023 | event.isDefaultPrevented = returnTrue 1024 | } 1025 | return event 1026 | } 1027 | 1028 | function createProxy(event) { 1029 | var key, proxy = { originalEvent: event } 1030 | for (key in event) 1031 | if (!ignoreProperties.test(key) && event[key] !== undefined) proxy[key] = event[key] 1032 | 1033 | return compatible(proxy, event) 1034 | } 1035 | 1036 | $.fn.delegate = function(selector, event, callback){ 1037 | return this.on(event, selector, callback) 1038 | } 1039 | $.fn.undelegate = function(selector, event, callback){ 1040 | return this.off(event, selector, callback) 1041 | } 1042 | 1043 | $.fn.live = function(event, callback){ 1044 | $(document.body).delegate(this.selector, event, callback) 1045 | return this 1046 | } 1047 | $.fn.die = function(event, callback){ 1048 | $(document.body).undelegate(this.selector, event, callback) 1049 | return this 1050 | } 1051 | 1052 | $.fn.on = function(event, selector, data, callback, one){ 1053 | var autoRemove, delegator, $this = this 1054 | if (event && !isString(event)) { 1055 | $.each(event, function(type, fn){ 1056 | $this.on(type, selector, data, fn, one) 1057 | }) 1058 | return $this 1059 | } 1060 | 1061 | if (!isString(selector) && !isFunction(callback) && callback !== false) 1062 | callback = data, data = selector, selector = undefined 1063 | if (isFunction(data) || data === false) 1064 | callback = data, data = undefined 1065 | 1066 | if (callback === false) callback = returnFalse 1067 | 1068 | return $this.each(function(_, element){ 1069 | if (one) autoRemove = function(e){ 1070 | remove(element, e.type, callback) 1071 | return callback.apply(this, arguments) 1072 | } 1073 | 1074 | if (selector) delegator = function(e){ 1075 | var evt, match = $(e.target).closest(selector, element).get(0) 1076 | if (match && match !== element) { 1077 | evt = $.extend(createProxy(e), {currentTarget: match, liveFired: element}) 1078 | return (autoRemove || callback).apply(match, [evt].concat(slice.call(arguments, 1))) 1079 | } 1080 | } 1081 | 1082 | add(element, event, callback, data, selector, delegator || autoRemove) 1083 | }) 1084 | } 1085 | $.fn.off = function(event, selector, callback){ 1086 | var $this = this 1087 | if (event && !isString(event)) { 1088 | $.each(event, function(type, fn){ 1089 | $this.off(type, selector, fn) 1090 | }) 1091 | return $this 1092 | } 1093 | 1094 | if (!isString(selector) && !isFunction(callback) && callback !== false) 1095 | callback = selector, selector = undefined 1096 | 1097 | if (callback === false) callback = returnFalse 1098 | 1099 | return $this.each(function(){ 1100 | remove(this, event, callback, selector) 1101 | }) 1102 | } 1103 | 1104 | $.fn.trigger = function(event, args){ 1105 | event = (isString(event) || $.isPlainObject(event)) ? $.Event(event) : compatible(event) 1106 | event._args = args 1107 | return this.each(function(){ 1108 | // items in the collection might not be DOM elements 1109 | if('dispatchEvent' in this) this.dispatchEvent(event) 1110 | else $(this).triggerHandler(event, args) 1111 | }) 1112 | } 1113 | 1114 | // triggers event handlers on current element just as if an event occurred, 1115 | // doesn't trigger an actual event, doesn't bubble 1116 | $.fn.triggerHandler = function(event, args){ 1117 | var e, result 1118 | this.each(function(i, element){ 1119 | e = createProxy(isString(event) ? $.Event(event) : event) 1120 | e._args = args 1121 | e.target = element 1122 | $.each(findHandlers(element, event.type || event), function(i, handler){ 1123 | result = handler.proxy(e) 1124 | if (e.isImmediatePropagationStopped()) return false 1125 | }) 1126 | }) 1127 | return result 1128 | } 1129 | 1130 | // shortcut methods for `.bind(event, fn)` for each event type 1131 | ;('focusin focusout load resize scroll unload click dblclick '+ 1132 | 'mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave '+ 1133 | 'change select keydown keypress keyup error').split(' ').forEach(function(event) { 1134 | $.fn[event] = function(callback) { 1135 | return callback ? 1136 | this.bind(event, callback) : 1137 | this.trigger(event) 1138 | } 1139 | }) 1140 | 1141 | ;['focus', 'blur'].forEach(function(name) { 1142 | $.fn[name] = function(callback) { 1143 | if (callback) this.bind(name, callback) 1144 | else this.each(function(){ 1145 | try { this[name]() } 1146 | catch(e) {} 1147 | }) 1148 | return this 1149 | } 1150 | }) 1151 | 1152 | $.Event = function(type, props) { 1153 | if (!isString(type)) props = type, type = props.type 1154 | var event = document.createEvent(specialEvents[type] || 'Events'), bubbles = true 1155 | if (props) for (var name in props) (name == 'bubbles') ? (bubbles = !!props[name]) : (event[name] = props[name]) 1156 | event.initEvent(type, bubbles, true) 1157 | return compatible(event) 1158 | } 1159 | 1160 | })(Zepto) 1161 | 1162 | ;(function($){ 1163 | var jsonpID = 0, 1164 | document = window.document, 1165 | key, 1166 | name, 1167 | rscript = /)<[^<]*)*<\/script>/gi, 1168 | scriptTypeRE = /^(?:text|application)\/javascript/i, 1169 | xmlTypeRE = /^(?:text|application)\/xml/i, 1170 | jsonType = 'application/json', 1171 | htmlType = 'text/html', 1172 | blankRE = /^\s*$/ 1173 | 1174 | // trigger a custom event and return false if it was cancelled 1175 | function triggerAndReturn(context, eventName, data) { 1176 | var event = $.Event(eventName) 1177 | $(context).trigger(event, data) 1178 | return !event.isDefaultPrevented() 1179 | } 1180 | 1181 | // trigger an Ajax "global" event 1182 | function triggerGlobal(settings, context, eventName, data) { 1183 | if (settings.global) return triggerAndReturn(context || document, eventName, data) 1184 | } 1185 | 1186 | // Number of active Ajax requests 1187 | $.active = 0 1188 | 1189 | function ajaxStart(settings) { 1190 | if (settings.global && $.active++ === 0) triggerGlobal(settings, null, 'ajaxStart') 1191 | } 1192 | function ajaxStop(settings) { 1193 | if (settings.global && !(--$.active)) triggerGlobal(settings, null, 'ajaxStop') 1194 | } 1195 | 1196 | // triggers an extra global event "ajaxBeforeSend" that's like "ajaxSend" but cancelable 1197 | function ajaxBeforeSend(xhr, settings) { 1198 | var context = settings.context 1199 | if (settings.beforeSend.call(context, xhr, settings) === false || 1200 | triggerGlobal(settings, context, 'ajaxBeforeSend', [xhr, settings]) === false) 1201 | return false 1202 | 1203 | triggerGlobal(settings, context, 'ajaxSend', [xhr, settings]) 1204 | } 1205 | function ajaxSuccess(data, xhr, settings, deferred) { 1206 | var context = settings.context, status = 'success' 1207 | settings.success.call(context, data, status, xhr) 1208 | if (deferred) deferred.resolveWith(context, [data, status, xhr]) 1209 | triggerGlobal(settings, context, 'ajaxSuccess', [xhr, settings, data]) 1210 | ajaxComplete(status, xhr, settings) 1211 | } 1212 | // type: "timeout", "error", "abort", "parsererror" 1213 | function ajaxError(error, type, xhr, settings, deferred) { 1214 | var context = settings.context 1215 | settings.error.call(context, xhr, type, error) 1216 | if (deferred) deferred.rejectWith(context, [xhr, type, error]) 1217 | triggerGlobal(settings, context, 'ajaxError', [xhr, settings, error || type]) 1218 | ajaxComplete(type, xhr, settings) 1219 | } 1220 | // status: "success", "notmodified", "error", "timeout", "abort", "parsererror" 1221 | function ajaxComplete(status, xhr, settings) { 1222 | var context = settings.context 1223 | settings.complete.call(context, xhr, status) 1224 | triggerGlobal(settings, context, 'ajaxComplete', [xhr, settings]) 1225 | ajaxStop(settings) 1226 | } 1227 | 1228 | // Empty function, used as default callback 1229 | function empty() {} 1230 | 1231 | $.ajaxJSONP = function(options, deferred){ 1232 | if (!('type' in options)) return $.ajax(options) 1233 | 1234 | var _callbackName = options.jsonpCallback, 1235 | callbackName = ($.isFunction(_callbackName) ? 1236 | _callbackName() : _callbackName) || ('jsonp' + (++jsonpID)), 1237 | script = document.createElement('script'), 1238 | originalCallback = window[callbackName], 1239 | responseData, 1240 | abort = function(errorType) { 1241 | $(script).triggerHandler('error', errorType || 'abort') 1242 | }, 1243 | xhr = { abort: abort }, abortTimeout 1244 | 1245 | if (deferred) deferred.promise(xhr) 1246 | 1247 | $(script).on('load error', function(e, errorType){ 1248 | clearTimeout(abortTimeout) 1249 | $(script).off().remove() 1250 | 1251 | if (e.type == 'error' || !responseData) { 1252 | ajaxError(null, errorType || 'error', xhr, options, deferred) 1253 | } else { 1254 | ajaxSuccess(responseData[0], xhr, options, deferred) 1255 | } 1256 | 1257 | window[callbackName] = originalCallback 1258 | if (responseData && $.isFunction(originalCallback)) 1259 | originalCallback(responseData[0]) 1260 | 1261 | originalCallback = responseData = undefined 1262 | }) 1263 | 1264 | if (ajaxBeforeSend(xhr, options) === false) { 1265 | abort('abort') 1266 | return xhr 1267 | } 1268 | 1269 | window[callbackName] = function(){ 1270 | responseData = arguments 1271 | } 1272 | 1273 | script.src = options.url.replace(/\?(.+)=\?/, '?$1=' + callbackName) 1274 | document.head.appendChild(script) 1275 | 1276 | if (options.timeout > 0) abortTimeout = setTimeout(function(){ 1277 | abort('timeout') 1278 | }, options.timeout) 1279 | 1280 | return xhr 1281 | } 1282 | 1283 | $.ajaxSettings = { 1284 | // Default type of request 1285 | type: 'GET', 1286 | // Callback that is executed before request 1287 | beforeSend: empty, 1288 | // Callback that is executed if the request succeeds 1289 | success: empty, 1290 | // Callback that is executed the the server drops error 1291 | error: empty, 1292 | // Callback that is executed on request complete (both: error and success) 1293 | complete: empty, 1294 | // The context for the callbacks 1295 | context: null, 1296 | // Whether to trigger "global" Ajax events 1297 | global: true, 1298 | // Transport 1299 | xhr: function () { 1300 | return new window.XMLHttpRequest() 1301 | }, 1302 | // MIME types mapping 1303 | // IIS returns Javascript as "application/x-javascript" 1304 | accepts: { 1305 | script: 'text/javascript, application/javascript, application/x-javascript', 1306 | json: jsonType, 1307 | xml: 'application/xml, text/xml', 1308 | html: htmlType, 1309 | text: 'text/plain' 1310 | }, 1311 | // Whether the request is to another domain 1312 | crossDomain: false, 1313 | // Default timeout 1314 | timeout: 0, 1315 | // Whether data should be serialized to string 1316 | processData: true, 1317 | // Whether the browser should be allowed to cache GET responses 1318 | cache: true 1319 | } 1320 | 1321 | function mimeToDataType(mime) { 1322 | if (mime) mime = mime.split(';', 2)[0] 1323 | return mime && ( mime == htmlType ? 'html' : 1324 | mime == jsonType ? 'json' : 1325 | scriptTypeRE.test(mime) ? 'script' : 1326 | xmlTypeRE.test(mime) && 'xml' ) || 'text' 1327 | } 1328 | 1329 | function appendQuery(url, query) { 1330 | if (query == '') return url 1331 | return (url + '&' + query).replace(/[&?]{1,2}/, '?') 1332 | } 1333 | 1334 | // serialize payload and append it to the URL for GET requests 1335 | function serializeData(options) { 1336 | if (options.processData && options.data && $.type(options.data) != "string") 1337 | options.data = $.param(options.data, options.traditional) 1338 | if (options.data && (!options.type || options.type.toUpperCase() == 'GET')) 1339 | options.url = appendQuery(options.url, options.data), options.data = undefined 1340 | } 1341 | 1342 | $.ajax = function(options){ 1343 | var settings = $.extend({}, options || {}), 1344 | deferred = $.Deferred && $.Deferred() 1345 | for (key in $.ajaxSettings) if (settings[key] === undefined) settings[key] = $.ajaxSettings[key] 1346 | 1347 | ajaxStart(settings) 1348 | 1349 | if (!settings.crossDomain) settings.crossDomain = /^([\w-]+:)?\/\/([^\/]+)/.test(settings.url) && 1350 | RegExp.$2 != window.location.host 1351 | 1352 | if (!settings.url) settings.url = window.location.toString() 1353 | serializeData(settings) 1354 | 1355 | var dataType = settings.dataType, hasPlaceholder = /\?.+=\?/.test(settings.url) 1356 | if (hasPlaceholder) dataType = 'jsonp' 1357 | 1358 | if (settings.cache === false || ( 1359 | (!options || options.cache !== true) && 1360 | ('script' == dataType || 'jsonp' == dataType) 1361 | )) 1362 | settings.url = appendQuery(settings.url, '_=' + Date.now()) 1363 | 1364 | if ('jsonp' == dataType) { 1365 | if (!hasPlaceholder) 1366 | settings.url = appendQuery(settings.url, 1367 | settings.jsonp ? (settings.jsonp + '=?') : settings.jsonp === false ? '' : 'callback=?') 1368 | return $.ajaxJSONP(settings, deferred) 1369 | } 1370 | 1371 | var mime = settings.accepts[dataType], 1372 | headers = { }, 1373 | setHeader = function(name, value) { headers[name.toLowerCase()] = [name, value] }, 1374 | protocol = /^([\w-]+:)\/\//.test(settings.url) ? RegExp.$1 : window.location.protocol, 1375 | xhr = settings.xhr(), 1376 | nativeSetHeader = xhr.setRequestHeader, 1377 | abortTimeout 1378 | 1379 | if (deferred) deferred.promise(xhr) 1380 | 1381 | if (!settings.crossDomain) setHeader('X-Requested-With', 'XMLHttpRequest') 1382 | setHeader('Accept', mime || '*/*') 1383 | if (mime = settings.mimeType || mime) { 1384 | if (mime.indexOf(',') > -1) mime = mime.split(',', 2)[0] 1385 | xhr.overrideMimeType && xhr.overrideMimeType(mime) 1386 | } 1387 | if (settings.contentType || (settings.contentType !== false && settings.data && settings.type.toUpperCase() != 'GET')) 1388 | setHeader('Content-Type', settings.contentType || 'application/x-www-form-urlencoded') 1389 | 1390 | if (settings.headers) for (name in settings.headers) setHeader(name, settings.headers[name]) 1391 | xhr.setRequestHeader = setHeader 1392 | 1393 | xhr.onreadystatechange = function(){ 1394 | if (xhr.readyState == 4) { 1395 | xhr.onreadystatechange = empty 1396 | clearTimeout(abortTimeout) 1397 | var result, error = false 1398 | if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304 || (xhr.status == 0 && protocol == 'file:')) { 1399 | dataType = dataType || mimeToDataType(settings.mimeType || xhr.getResponseHeader('content-type')) 1400 | result = xhr.responseText 1401 | 1402 | try { 1403 | // http://perfectionkills.com/global-eval-what-are-the-options/ 1404 | if (dataType == 'script') (1,eval)(result) 1405 | else if (dataType == 'xml') result = xhr.responseXML 1406 | else if (dataType == 'json') result = blankRE.test(result) ? null : $.parseJSON(result) 1407 | } catch (e) { error = e } 1408 | 1409 | if (error) ajaxError(error, 'parsererror', xhr, settings, deferred) 1410 | else ajaxSuccess(result, xhr, settings, deferred) 1411 | } else { 1412 | ajaxError(xhr.statusText || null, xhr.status ? 'error' : 'abort', xhr, settings, deferred) 1413 | } 1414 | } 1415 | } 1416 | 1417 | if (ajaxBeforeSend(xhr, settings) === false) { 1418 | xhr.abort() 1419 | ajaxError(null, 'abort', xhr, settings, deferred) 1420 | return xhr 1421 | } 1422 | 1423 | if (settings.xhrFields) for (name in settings.xhrFields) xhr[name] = settings.xhrFields[name] 1424 | 1425 | var async = 'async' in settings ? settings.async : true 1426 | xhr.open(settings.type, settings.url, async, settings.username, settings.password) 1427 | 1428 | for (name in headers) nativeSetHeader.apply(xhr, headers[name]) 1429 | 1430 | if (settings.timeout > 0) abortTimeout = setTimeout(function(){ 1431 | xhr.onreadystatechange = empty 1432 | xhr.abort() 1433 | ajaxError(null, 'timeout', xhr, settings, deferred) 1434 | }, settings.timeout) 1435 | 1436 | // avoid sending empty string (#319) 1437 | xhr.send(settings.data ? settings.data : null) 1438 | return xhr 1439 | } 1440 | 1441 | // handle optional data/success arguments 1442 | function parseArguments(url, data, success, dataType) { 1443 | if ($.isFunction(data)) dataType = success, success = data, data = undefined 1444 | if (!$.isFunction(success)) dataType = success, success = undefined 1445 | return { 1446 | url: url 1447 | , data: data 1448 | , success: success 1449 | , dataType: dataType 1450 | } 1451 | } 1452 | 1453 | $.get = function(/* url, data, success, dataType */){ 1454 | return $.ajax(parseArguments.apply(null, arguments)) 1455 | } 1456 | 1457 | $.post = function(/* url, data, success, dataType */){ 1458 | var options = parseArguments.apply(null, arguments) 1459 | options.type = 'POST' 1460 | return $.ajax(options) 1461 | } 1462 | 1463 | $.getJSON = function(/* url, data, success */){ 1464 | var options = parseArguments.apply(null, arguments) 1465 | options.dataType = 'json' 1466 | return $.ajax(options) 1467 | } 1468 | 1469 | $.fn.load = function(url, data, success){ 1470 | if (!this.length) return this 1471 | var self = this, parts = url.split(/\s/), selector, 1472 | options = parseArguments(url, data, success), 1473 | callback = options.success 1474 | if (parts.length > 1) options.url = parts[0], selector = parts[1] 1475 | options.success = function(response){ 1476 | self.html(selector ? 1477 | $('
                ').html(response.replace(rscript, "")).find(selector) 1478 | : response) 1479 | callback && callback.apply(self, arguments) 1480 | } 1481 | $.ajax(options) 1482 | return this 1483 | } 1484 | 1485 | var escape = encodeURIComponent 1486 | 1487 | function serialize(params, obj, traditional, scope){ 1488 | var type, array = $.isArray(obj), hash = $.isPlainObject(obj) 1489 | $.each(obj, function(key, value) { 1490 | type = $.type(value) 1491 | if (scope) key = traditional ? scope : 1492 | scope + '[' + (hash || type == 'object' || type == 'array' ? key : '') + ']' 1493 | // handle data in serializeArray() format 1494 | if (!scope && array) params.add(value.name, value.value) 1495 | // recurse into nested objects 1496 | else if (type == "array" || (!traditional && type == "object")) 1497 | serialize(params, value, traditional, key) 1498 | else params.add(key, value) 1499 | }) 1500 | } 1501 | 1502 | $.param = function(obj, traditional){ 1503 | var params = [] 1504 | params.add = function(k, v){ this.push(escape(k) + '=' + escape(v)) } 1505 | serialize(params, obj, traditional) 1506 | return params.join('&').replace(/%20/g, '+') 1507 | } 1508 | })(Zepto) 1509 | 1510 | ;(function($){ 1511 | $.fn.serializeArray = function() { 1512 | var result = [], el 1513 | $([].slice.call(this.get(0).elements)).each(function(){ 1514 | el = $(this) 1515 | var type = el.attr('type') 1516 | if (this.nodeName.toLowerCase() != 'fieldset' && 1517 | !this.disabled && type != 'submit' && type != 'reset' && type != 'button' && 1518 | ((type != 'radio' && type != 'checkbox') || this.checked)) 1519 | result.push({ 1520 | name: el.attr('name'), 1521 | value: el.val() 1522 | }) 1523 | }) 1524 | return result 1525 | } 1526 | 1527 | $.fn.serialize = function(){ 1528 | var result = [] 1529 | this.serializeArray().forEach(function(elm){ 1530 | result.push(encodeURIComponent(elm.name) + '=' + encodeURIComponent(elm.value)) 1531 | }) 1532 | return result.join('&') 1533 | } 1534 | 1535 | $.fn.submit = function(callback) { 1536 | if (callback) this.bind('submit', callback) 1537 | else if (this.length) { 1538 | var event = $.Event('submit') 1539 | this.eq(0).trigger(event) 1540 | if (!event.isDefaultPrevented()) this.get(0).submit() 1541 | } 1542 | return this 1543 | } 1544 | 1545 | })(Zepto) 1546 | 1547 | ;(function($){ 1548 | // __proto__ doesn't exist on IE<11, so redefine 1549 | // the Z function to use object extension instead 1550 | if (!('__proto__' in {})) { 1551 | $.extend($.zepto, { 1552 | Z: function(dom, selector){ 1553 | dom = dom || [] 1554 | $.extend(dom, $.fn) 1555 | dom.selector = selector || '' 1556 | dom.__Z = true 1557 | return dom 1558 | }, 1559 | // this is a kludge but works 1560 | isZ: function(object){ 1561 | return $.type(object) === 'array' && '__Z' in object 1562 | } 1563 | }) 1564 | } 1565 | 1566 | // getComputedStyle shouldn't freak out when called 1567 | // without a valid element as argument 1568 | try { 1569 | getComputedStyle(undefined) 1570 | } catch(e) { 1571 | var nativeGetComputedStyle = getComputedStyle; 1572 | window.getComputedStyle = function(element){ 1573 | try { 1574 | return nativeGetComputedStyle(element) 1575 | } catch(e) { 1576 | return null 1577 | } 1578 | } 1579 | } 1580 | })(Zepto) 1581 | --------------------------------------------------------------------------------