1/** 2 * tFormer.js - empower your forms 3 * http://tjrus.com/tFormer 4 * (c) 2013 Vasiliy Zubach (aka TjRus) - http://tjrus.com/ 5 * tFormer may be freely distributed under the MIT license. 6 */ 7 8(function ( window, document, undefined ){ 9 10 // button types 11 var BUTTON_TYPES = ['button', 'submit']; 12 // all field types that can be empowered with tFormer 13 var FIELD_TYPES = ['text', 'password', 'select-one', 'checkbox', 'textarea', 'range', 'number', 'email', 'hidden', 'file', 'date', 'datetime-local', 'search', 'tel', 'time', 'url', 'month', 'week']; 14 // fields that has only 'change' event 15 var HAS_CHANGE_EVENT = ['checkbox', 'select-one', 'select', 'file']; 16 // fields that has also 'change' event 17 var CHANGE_ALSO = ['number', 'date', 'datetime-local', 'time', 'month', 'week', 'range']; 18 19 // tFormer options 20 var TF_OPTIONS = ['timeout', 'requestTimeout', 'errorClass', 'disabledClass', 'processingClass', 'validateEvent', 'submitButtonControl', 'submitButton', 'submit', 'before', 'onerror', 'onvalid', 'eventBefore', 'eventError', 'eventValid']; 21 var FIELD_OPTIONS = ['timeout', 'requestTimeout', 'request', 'validClass', 'errorClass', 'disabledClass', 'processingClass', 'rules', 'validateEvent', 'before', 'onerror', 'onvalid', 'own', 'eventBefore', 'eventError', 'eventValid']; 22 var BUTTON_OPTIONS = ['disabledClass', 'processingClass']; 23 24 25 var EMPOWERED = 'empowered'; 26 27 var defaults = { 28 errorClass: 'error', 29 processingClass: 'processing', 30 disabledClass: 'disabled', 31 validClass: 'valid', 32 33 eventBefore: 'tFormer:before', 34 eventError: 'tFormer:error', 35 eventValid: 'tFormer:valid', 36 37 timeout: 0, 38 requestTimeout: 2000, 39 40 validateEvent: 'input keyup', 41 42 fields: {}, 43 buttons: {}, 44 45 submitButtonControl: true, 46 submitButton: null 47 }; 48 49 /** 50 * Main tFormer constructor 51 * @param form_el - our form that should be empowered 52 * @param options - tFormer options 53 * @returns {tFormer} 54 */ 55 var tFormer = function ( form_el, options ){ 56 if ( !(this instanceof tFormer) ) { 57 return new tFormer( form_el, options ); 58 } 59 60 var self = this; 61 // our main form DOM element 62 self.form = (function ( form_el ){ 63 form_el = typeof(form_el) === 'string' ? document.forms[form_el] : form_el; 64 return __isForm( form_el ) ? form_el : null; 65 })( form_el ); 66 67 var my_form = self.form; 68 if ( self.form === null ) { 69 return null; 70 } 71 72 if ( !__getAttr( my_form, EMPOWERED ) ) { 73 __setAttr( my_form, EMPOWERED, 1 ); 74 __setAttr( my_form, 'novalidate', 'novalidate' ); 75 } else { 76 var cached = (function (){ 77 for ( var i = 0, c_l = self.cache.length; i < c_l; i++ ) { 78 if ( self.cache[i].form == my_form ) { 79 return self.cache[i]; 80 } 81 } 82 return null; 83 })(); 84 if ( cached ) { 85 var initialized = cached.inited; 86 cached.set( options ); 87 return cached; 88 } 89 } 90 self.config = (self.config || __clone( defaults )); 91 self.set( options ); 92 93 self.init(); 94 self.cache.push( self ); 95 return self; 96 }; 97 var tf_proto = tFormer.prototype; 98 tf_proto.cache = []; 99 100 101 tf_proto.init = function (){ 102 var self = this; 103 if ( self.inited ) { 104 return self; 105 } 106 107 self.fields = {}; 108 self.buttons = {}; 109 110 self.inited = true; 111 112 self.locked = 0; 113 self.holded = 0; 114 self.invalid = 0; 115 self.valid = true; 116 117 self.fields = {}; 118 self.buttons = {}; 119 120 // XHR stuff 121 self.xhr = {}; 122 self.xhrTimeout = {}; 123 124 var sb = self.get( 'submitButton' ); 125 if ( sb ) { 126 self.buttons['submit'] = new tButton( self, sb ); 127 } 128 129 for ( var i = 0, f_l = self.form.length; i < f_l; i++ ) { 130 var el = self.form[i], 131 type = el.type, 132 name = type !== 'button' ? __getAttr( el, 'name' ) : __data( el, 'check' ); 133 134 if ( __inArray( FIELD_TYPES, type ) !== -1 && name ) { 135 self.fields[name] = new tField( self, el ); 136 } 137 138 if ( __inArray( BUTTON_TYPES, type ) !== -1 ) { 139 name = (type == 'submit') ? type : name; 140 if ( name && !(name == 'submit' && self.button[name]) ) { 141 self.buttons[name] = new tButton( self, el ); 142 } 143 } 144 } 145 146 self.form.onsubmit = (function ( self ){ 147 return function ( event ){ 148 event = event || window.event; 149 150 var sb = self.button( 'submit' ), 151 sb_control = self.get( 'submitButtonControl' ), 152 s_func = typeof self.get( 'submit' ) == 'function', 153 processing = sb.get( 'processingClass' ), 154 prevent = self.valid && s_func; 155 156 // disable double submit 157 if ( (processing && sb.hasClass( processing )) || self.locked ) { 158 __prevent( event ); 159 return false; 160 } 161 162 if ( prevent ) { 163 __prevent( event ); 164 } 165 if ( self.valid ) { 166 if ( sb_control ) { 167 sb.processing( true ); 168 } 169 if ( s_func ) { 170 self.execute( self.form, 'submit', [event, self] ); 171 } 172 } 173 if ( prevent ) { 174 return false; 175 } 176
177 try { 178 if ( !self.valid && !self.validate( { no_timeout: true } ) ) { 179 __prevent( event ); 180 sb.processing( false ); 181 return false; 182 } 183 184 if ( s_func ) { 185 __prevent( event ); 186 self.execute( self.form, 'submit', [event, self] ); 187 return false; 188 } 189 self.form.submit(); 190 return true; 191 192 } catch ( e ) { 193 } 194 }; 195 })( self ); 196 197 198 self.validate( { 199 highlight : false, 200 fire_event: false, 201 202 silence: true 203 } ); 204 return self.submitControl(); 205 }; 206 207 208 tf_proto.destroy = function (){ 209 var self = this; 210 for ( var name in self.fields ) { 211 self.fields[name].destroy(); 212 } 213 for ( var name in this.buttons ) { 214 self.buttons[name].destroy(); 215 } 216 self.inited = false; 217 return self; 218 }; 219 220 // function for dropping options 221 tf_proto.drop = function (){ 222 var self = this, 223 fields = self.fields, 224 buttons = self.buttons; 225 self.destroy(); 226 self.set( __clone( defaults ) ); 227 for ( var name in fields ) { 228 fields[name].drop(); 229 } 230 for ( var name in buttons ) { 231 buttons[name].drop(); 232 } 233 self.locked = 0; 234 return self.init(); 235 }; 236 237 tf_proto.validate = function ( options ){ 238 var self = this, 239 fields = self.fields, 240 errors = 0; 241 242 for ( var key in fields ) { 243 errors += (fields[key].validate( options || {} )) ? 0 : 1; 244 } 245 self.invalid = errors; 246 self.valid = errors === 0; 247 return errors === 0; 248 }; 249 250 251 tf_proto.toObject = function (){ 252 var self = this, 253 fields = self.form, 254 obj = {}; 255 256 for ( var i = 0, f_l = fields.length; i < f_l; i++ ) { 257 var el = fields[i], 258 name = __getAttr( el, 'name' ); 259 if ( el.type == 'checkbox' ) { 260 obj[name] = el.checked; 261 } else if ( el.type == 'radio' ) { 262 if ( !obj[name] ) { 263 obj[name] = ''; 264 } 265 if ( el.checked ) { 266 obj[name] = el.value; 267 } 268 } else if (el.type !== 'submit' && el.type !== 'button'){ 269 obj[name] = el.value; 270 } 271 } 272 return obj; 273 }; 274 275 tf_proto.get = function ( option ){ 276 return this.config[option]; 277 }; 278 279 tf_proto.set = function ( options ){ 280 var self = this, 281 is_inited = self.inited; 282 if ( is_inited ) { 283 self.destroy(); 284 } 285 self.config = __extend( self.config, options ); 286 if ( is_inited ) { 287 self.init(); 288 } 289 return self; 290 }; 291 292 /** 293 * Get field object to work with 294 * @param {string} name - field name 295 * @returns {*} - field Object 296 */ 297 tf_proto.field = function ( name ){ 298 return this.fields[name]; 299 }; 300 301 /** 302 * Get button object to work with 303 * @param {string} name - button name 304 * @returns {*} - button Object 305 */ 306 tf_proto.button = function ( name ){ 307 return this.buttons[name]; 308 }; 309 310 /** 311 * Rewrite default form submit function with current one 312 * (default HTML form submit function will be prevented) 313 * @param {function} func - function that should be executed on form submit 314 * @returns {*} 315 */ 316 tf_proto.submit = function ( func ){ 317 var self = this; 318 if ( !self.config ) { 319 return self; 320 } 321 if ( typeof func == 'function' ) { 322 self.config.submit = func; 323 } 324 return self; 325 }; 326 327 /** 328 * Form Submit button control function 329 * (enabling/disabling while validating) 330 * @param {!boolean} valid - is current form valid or not? 331 * @returns {*} 332 */ 333 tf_proto.submitControl = function ( valid ){ 334 var self = this, 335 sb = self.button( 'submit' ), 336 sb_control = self.get( 'submitButtonControl' ); 337 338 self.valid = (self.invalid === 0 && self.holded === 0 && self.locked === 0); 339 valid = (valid === false || valid === true) ? valid : self.valid; 340 341 if ( sb && sb_control ) { 342 sb[(valid) ? 'enable' : 'disable'](); 343 } 344 return self; 345 }; 346 /** 347 * Submit button disable 348 * @returns {*} 349 */ 350 tf_proto.submitDisable = function (){ 351 return this.submitControl( false ); 352 }; 353 /** 354 * Submit button enable 355 * @returns {*} 356 */ 357 tf_proto.submitEnable = function (){ 358 return this.submitControl( true ); 359 }; 360 361 /** 362 * Submit button processing control 363 * @returns {*} 364 */ 365 tf_proto.processing = function ( action ){
366 var self = this, 367 sb = self.button( 'submit' ); 368 if ( sb ) { 369 sb.processing( action ); 370 } 371 return self; 372 }; 373 374 /** 375 * Lock form 376 * @returns {*} 377 */ 378 tf_proto.lock = function ( num ){ 379 var self = this; 380 self.locked += num || 1; 381 return self.submitControl( false ); 382 }; 383 384 /** 385 * Unlock form 386 * @returns {*} 387 */ 388 tf_proto.unlock = function ( num ){ 389 var self = this; 390 self.locked -= num || 1; 391 return self.submitControl(); 392 }; 393 394 /** 395 * Execute function 396 * @param context 397 * @param func 398 * @param params 399 * @returns {*} 400 */ 401 tf_proto.execute = function ( context, func, params ){ 402 var self = this; 403 if ( typeof func == 'string' ) { 404 func = self.get( func ); 405 } 406 if ( typeof func == 'function' ) { 407 return func.apply( context, (params || []) ); 408 } 409 return null; 410 }; 411 412 window.tFormer = tFormer; 413 414 415 /** 416 * Form element constructor 417 * @returns {*} 418 * @constructor 419 */ 420 var Element = function (){ 421 }; 422 var El_p = Element.prototype; 423 El_p.destroy = function (){ 424 var self = this, 425 el = this.el, 426 events = self.events; 427 428 for ( var i = 0, e_l = events.length; i < e_l; i++ ) { 429 self.off( events[0][0], events[0][1] ); 430 } 431 432 self.removeClass( self.get( 'errorClass' ) ); 433 self.removeClass( self.get( 'desabledClass' ) ); 434 self.removeClass( self.get( 'processignClass' ) ); 435 436 __data( el, 'holded', null ); 437 __data( el, 'error', null ); 438 // remove hold attributes 439 440 if ( self.timer ) { 441 clearTimeout( self.timer ); 442 } 443 if ( self.xhr ) { 444 self.xhr.abort(); 445 } 446 if ( self.xhrTimeout ) { 447 clearTimeout( self.xhrTimeout ); 448 } 449 450 if ( !self.valid ) { 451 self.parent.invalid = self.parent.invalid !== 0 ? self.parent.invalid - 1 : 0; 452 } 453 454 self.parent.submitControl(); 455 456 self.inited = false; 457 return self; 458 }; 459 460 El_p.drop = function (){ 461 return this.set( __clone( defaults ) ); 462 }; 463 464 El_p.set = function ( options ){ 465 var self = this, 466 name = __getAttr( self.el, 'name' ), 467 _set = function ( key, value ){ 468 self.config[key] = value; 469 if ( !self.parent.config.fields[name] ) { 470 self.parent.config.fields[name] = __clone( defaults ); 471 } 472 self.parent.config.fields[name][key] = value; 473 }, 474 is_inited = self.inited; 475 476 if ( is_inited ) { 477 self.destroy(); 478 } 479 for ( var key in options ) { 480 if ( ~__inArray( FIELD_OPTIONS, key ) ) { 481 _set( key, options[key] ); 482 } 483 } 484 if ( is_inited ) { 485 self.init(); 486 } 487 return self; 488 }; 489 490 El_p.get = function ( option ){ 491 return this.config[option]; 492 }; 493 494 /** 495 * subscribe this.el to some event 496 * @param {string} evnt - event name 497 * @param {function} func - function that should be executed on event 498 * @param {object} el - element to attach event 499 * @returns {*} 500 */ 501 El_p.on = function ( evnt, func, el ){ 502 var self = this, 503 el = el || self.el, 504 events = evnt.split( ' ' ); 505 506 for ( var i = 0, e_l = events.length; i < e_l; i++ ) { 507 if ( el.addEventListener ) { // W3C DOM 508 el.addEventListener( events[i], func, false ); 509 } else if ( el.attachEvent ) { // IE DOM 510 el.attachEvent( "on" + events[i], func ); 511 } else { // No much to do 512 el[events[i]] = func; 513 } 514 self.events.push( [events[i], func] ); 515 } 516 return self; 517 }; 518 519 /** 520 * unsubscribe this.el (Button || Field) from some event 521 * @param {string} evnt - event name 522 * @param {function} func - function that should be executed on event 523 * @returns {*} 524 */ 525 El_p.off = function ( evnt, func ){ 526 var self = this, 527 el = self.el, 528 events = self.events; 529 530 evnt = evnt.split( ' ' ); 531 if ( !func ) { 532 for ( var i = 0, e_l = events.length; i < e_l; i++ ) { 533 if ( __inArray( evnt, events[i][0] ) !== -1 ) { 534 events.splice( i, 1 ); 535 } 536 } 537 return self; 538 } 539 540 if ( el.removeEventListener ) { // W3C DOM 541 el.removeEventListener( evnt, func, false ); 542 } else if ( el.detachEvent ) { // IE DOM 543 el.detachEvent( "on" + evnt, func ); 544 } else { // No much to do 545 el[evnt] = null; 546 } 547 548 for ( var i = 0, e_l = events.length; i < e_l; i++ ) { 549 if ( __inArray( evnt, events[i][0] ) !== -1 && events[i][1] == func ) { 550 events.splice( i, 1 ); 551 return self; 552 } 553 } 554 return self; 555 }; 556 557 /** 558 * fire some event for this.field 559 * @param {string} evnt - event name 560 * @returns {*} 561 */ 562 El_p.trigger = function ( evnt ){ 563 var self = this, 564 el = self.el, 565 evt; 566 567 if ( self.silence ) { 568 return; 569 } 570 571 try {// every browser except IE8 and below works here 572 evt = document.createEvent( "HTMLEvents" ); 573 evt.initEvent( evnt, true, true ); 574 return !el.dispatchEvent( evt ); 575 } catch ( err ) { 576 try { 577 return el.fireEvent( 'on' + evnt ); 578 } catch ( error ) { 579 } 580 } 581 return self; 582 }; 583 584 585 El_p.addClass = function ( new_class ){ 586 var self = this, 587 el = self.el, 588 class_names = el.className.split( ' ' ) || []; 589 590 if ( __inArray( class_names, new_class ) === -1 ) { 591 class_names.push( new_class ); 592 class_names = __clear( class_names ); 593 el.className = (class_names.length > 0) ? class_names.join( ' ' ) : ''; 594 } 595 return self; 596 }; 597 598 El_p.removeClass = function ( old_class ){ 599 var self = this, 600 el = self.el; 601 602 if ( self.hasClass( old_class ) ) { 603 var re = new RegExp( '(\\s|^)' + old_class + '(\\s|$)' ); 604 el.className = el.className.replace( re, ' ' ); 605 } 606 return self; 607 } 608 609 El_p.hasClass = function ( name ){ 610 return !!((~(' ' + this.el.className + ' ').indexOf( ' ' + name + ' ' ))); 611 }; 612 613 El_p.data = function ( attr, value ){ 614 var self = this, 615 el = self.el, 616 result = __data( el, attr, value ); 617 618 if ( value === undefined ) { 619 return result; 620 } 621 return self; 622 }; 623 624 El_p.attr = function ( attr, value ){ 625 var self = this, 626 el = self.el; 627 switch ( value ) { 628 case null: 629 __delAttr( el, attr ); 630 break; 631 case undefined: 632 return __getAttr( el, attr ); 633 break; 634 default: 635 __setAttr( el, attr, value ); 636 break; 637 } 638 }; 639 640 El_p.processing = function ( action ){
641 var self = this, 642 processingClass = self.get( 'processingClass' ), 643 is_processing = self.hasClass( processingClass ); 644 645 if ( action === false || (action === null && is_processing) ) { 646 self.removeClass( processingClass ); 647 } else if ( action === true || (action === null && is_processing) ) { 648 self.addClass( processingClass ); 649 } 650 return this; 651 }; 652 653 654 /** extent subclass with superclass prototype */ 655 var __extend_proto = function ( Child, Parent ){ 656 var F = function (){ 657 } 658 F.prototype = Parent.prototype 659 Child.prototype = new F() 660 Child.prototype.constructor = Child 661 Child.superclass = Parent.prototype 662 }; 663 664 665 var tField = function ( parent, el ){ 666 var self = this, 667 type = el.type, 668 name, rules, 669 attr_required, attr_min, attr_max, 670 rules2add = [], 671 config; 672 673 self.parent = parent; 674 self.el = el; 675 self.config = {}; 676 677 self.events = []; 678 name = self.attr( 'name' ); 679 attr_required = self.attr( 'required' ) !== null; 680 attr_min = self.attr( 'min' ); 681 attr_max = self.attr( 'max' ); 682 683 config = parent.config.fields ? parent.config.fields[name] : {}; 684 if ( typeof config == 'string' ) { 685 config = { 686 rules: config 687 }; 688 } 689 self.set( __extend( __clone( parent.config ), __clone( config ) ) ); 690 self.config.rules = self.config.rules || self.data( 'rules' ); 691 692 if ( attr_required ) { 693 rules2add.push( '*' ); 694 } 695 696 if ( type == 'email' ) { 697 rules2add.push( '@' ); 698 } 699 700 if ( type == 'url' ) { 701 rules2add.push( 'url' ); 702 } 703 704 if ( type == 'number' ) { 705 rules2add.push( 'num' ) 706 707 if ( attr_min !== null ) { 708 rules2add.push( '>' + attr_min ); 709 } 710 if ( attr_max !== null ) { 711 rules2add.push( '<' + attr_max ); 712 } 713 } 714 715 716 if ( rules2add.length > 0 ) { 717 self.config.rules = _v_( '' ).rules( self.config.rules || self.data( 'rules' ) ).addRule( rules2add.join( ' ' ) ).rule; 718 } 719 720 self.value = el.value; 721 722 // validate after init 723 self.validationStart = 'v_start'; 724 self.validationSuccess = 'v_success'; 725 self.validationError = 'v_error'; 726 727 self.highlight = true; 728 self.fire_event = true; 729 self.silence = false; 730 731 return self.init(); 732 }; 733 __extend_proto( tField, Element ); 734 var tField_p = tField.prototype; 735 736 tField_p.init = function (){ 737 var self = this, 738 field = self.el, 739 value = field.value, 740 type = self.attr( 'type' ), 741 is_checkbox = type == 'checkbox'; 742 743 if ( self.inited ) { 744 return self; 745 } 746 747 self.events = []; 748 749 self.valid = true; 750 self.holded = false; 751 752 self.value = value; 753 754 // adding validate event to the field; 755 var validate_event = self.get( 'validateEvent' ); 756 757 if ( ~__inArray( CHANGE_ALSO, type ) && validate_event.indexOf( 'change' ) === -1 ) { 758 validate_event = validate_event.split( ' ' ); 759 validate_event.push( 'change' ); 760 validate_event = validate_event.join( ' ' ); 761 } 762 if ( ~__inArray( HAS_CHANGE_EVENT, type ) ) { 763 validate_event = 'change'; 764 } 765 766 self.set( { 767 validateEvent: validate_event 768 } ); 769 770 self.on( validate_event, function (){ 771 if ( !self.get( 'rules' ) ) { 772 return; 773 } 774 if ( self.value != self.el.value || is_checkbox ) { 775 self.value = self.el.value; 776 777 // clear field before validation 778 self.removeClass( self.get( 'errorClass' ) ); 779 self.removeClass( self.get( 'processingClass' ) ); 780 781 // disable submit button before validation 782 self.parent.invalid += (self.valid) ? 1 : 0; 783 self.valid = false; 784 self.removeClass( self.get( 'errorClass' ) ); 785 self.parent.submitControl(); 786 787 var timeout = self.get( 'timeout' ); 788 if ( self.get( 'timeout' ) > 0 ) { 789 if ( self.timer ) { 790 clearTimeout( self.timer ); 791 } 792 self.timer = setTimeout( function (){ 793 self.validate(); 794 }, timeout ); 795 } else { 796 self.validate(); 797 } 798 } 799 } ); 800 801 802 if ( self.hasRules( '=#' ) ) { 803 var self_v_ = _v_().rules( self.config.rules ), 804 depended_id = self_v_.parsedRules['=#'], 805 el = document.getElementById( depended_id ); 806 807 if ( el ) { 808 self.on( 'input keyup', function ( e ){ 809 self.validate( { 810 highlight: !!self.value 811 } ); 812 }, el ) 813 } 814 } 815 816 // blur validation without timeout 817 if ( !~validate_event.indexOf( 'blur' ) && validate_event !== 'change' ) { 818 self.on( 'blur', function (){ 819 if ( !self.get( 'rules' ) ) { 820 return; 821 } 822 if ( self.timer ) { 823 clearTimeout( self.timer ); 824 } 825 if ( !(self.valid && self.hasRules( 'request' ) ) ) { 826 self.validate( { 827 no_timeout: true 828 } ); 829 } 830 } ); 831 } 832 833 self.validate( { silence: true } ); 834 835 self.inited = true; 836 return self; 837 }; 838 tField_p.setRules = function ( rules, options ){ 839 var self = this; 840 self.set( { 841 rules: rules 842 }, options ); 843 self.validate( options ); 844 return self; 845 }; 846 847 tField_p.hasRules = function ( rules ){ 848 return _v_().rules( this.get( 'rules' ) ).hasRule( rules ); 849 }; 850 851 tField_p.addRules = function ( rules, options ){ 852 var self = this; 853 return self.setRules( _v_().rules( self.get( 'rules' ) ).addRule( rules ).rule, options ); 854 }; 855 856 tField_p.delRule = function ( rules, options ){ 857 var self = this; 858 return self.setRules( _v_().rules( self.get( 'rules' ) ).delRule( rules ).rule, options ); 859 }; 860 861 /** 862 * Highlight field with errorClass 863 * @param valid 864 * @returns {*} 865 */ 866 tField_p.error = function ( valid ){ 867 var self = this; 868 self.valid = (!valid) ? true : false; 869 870 if ( !self.highlight && !self.valid ) { 871 return this; 872 } 873 874 var field = self.el, 875 errorClass = self.get( 'errorClass' ); 876 877 if ( self.valid ) { 878 self.removeClass( errorClass ); 879 self.data( 'error', null ); 880 } else { 881 self.addClass( errorClass ); 882 self.data( 'error', '1' ); 883 } 884 return self; 885 }; 886 887 /** 888 * Hold field for a while 889 * @param {!boolean} hold 890 * @returns {*} 891 */ 892 tField_p.hold = function ( hold ){ 893 var self = this, 894 is_holded = self.data( 'holded' ); 895 896 if ( hold === undefined ) { 897 self.holded = !self.holded; 898 } else { 899 self.holded = (hold === true) ? true : false 900 } 901 self.data( 'holded', (self.holded ? 1 : null) ); 902 if ( !is_holded && self.holded === true ) { 903 self.parent.holded++; 904 } else if ( is_holded && self.holded === false ) { 905 self.parent.holded--; 906 } 907 self.parent.submitControl(); 908 return self; 909 }; 910 911 /** 912 * Here is the main field validation process 913 * @param {object} options 914 * @returns {boolean} 915 */ 916 tField_p.validate = function ( options ){ 917 var self = this, 918 field = self.el, 919 type = self.attr( 'type' ), 920 result = true, 921 own = self.get( 'own' ), 922 request = self.get( 'request' ), 923 rules = self.get( 'rules' ), 924 _v_check = _v_( self.el.value || '' ).rules( rules ), 925 is_checkbox = type == 'checkbox', 926 is_required = _v_check.hasRule( '*' ); 927 928 if ( !rules ) { 929 return result; 930 } 931 932 options = options || {}; 933 self.silence = (options.silence === true) ? true : false; 934 935 self.highlight = (options.highlight === false || options.silence === true) ? false : true; 936 self.fire_event = (options.fire_event === false || options.silence === true) ? false : true; 937 938 if ( !self.silence ) { 939 self.execute( 'before' ); 940 self.trigger( self.get( 'eventBefore' ) ); 941 self.trigger( self.validationStart ); 942 } 943 944 if ( typeof own == 'function' ) { 945 result = own.call( self.el ); 946 } else { 947 if ( is_checkbox ) { 948 var is_checked = field.checked; 949 if ( !is_checked && is_required ) { 950 result = false; 951 } 952 } else { 953 // check request validation 954 if ( is_required || self.el.value.length > 0 ) { 955 result = _v_check.validate(); 956 } 957 958 if ( request && result ) { 959 self.requestValidate( options ); 960 self.highlight = true; 961 return null; 962 } 963 } 964 } 965 966 if ( result ) { 967 self.__validationSuccess(); 968 } else { 969 self.__validationError(); 970 } 971 972 self.highlight = true; 973 self.fire_event = true; 974 self.silence = false; 975 return result; 976 }; 977 978 tField_p.__validationStart = function (){ 979 980 }; 981 982 tField_p.__validationError = function (){ 983 var self = this; 984 self.trigger( self.get( 'eventError' ) );
985 self.parent.invalid += (self.valid) ? 1 : 0; 986 self.parent.submitControl(); 987 self.error( true ); 988 self.execute( 'onerror' ); 989 }; 990 tField_p.__validationSuccess = function (){ 991 var self = this; 992 self.trigger( self.get( 'eventValid' ) ); 993 self.parent.invalid -= (!self.valid) ? 1 : 0; 994 self.parent.submitControl(); 995 self.error( false ); 996 self.execute( 'onvalid' ); 997 }; 998 999 1000 tField_p.requestValidate = function ( options ){ 1001 1002 var self = this, 1003 name = self.attr( 'name' ), 1004 value = self.el.value, 1005 request = self.get( 'request' ), 1006 check_btn = self.parent.button( __getAttr( self.el, 'name' ) ), 1007 method = request.method.toLowerCase() == 'post' ? 'POST' : 'GET', 1008 data = request.data || {}, 1009 success = request.success, 1010 url = (function (){ 1011 var url = request.url || window.location.href; 1012 if ( method == 'GET' ) { 1013 url += (~url.indexOf( '?' )) ? '&' : '?'; 1014 url += name + '=' + value; 1015 } else { 1016 data[name] = value; 1017 } 1018 return url; 1019 })(), 1020 1021 timeout = options.no_timeout ? 0 : (request.timeout || 0), 1022 1023 readyStateChange = function (){ 1024 var xhr = self.xhr; 1025 1026 if ( xhr.readyState == 1 ) { 1027 self.execute( request.start, [xhr] ); 1028 } 1029 1030 if ( xhr.readyState == 4 ) { 1031 // TODO: handle other errors (maybe with switch); 1032 if ( xhr.status == 200 ) { 1033 var result = (self.execute( request.end, [xhr.response] ) === true) ? true : false; 1034 self['__validation' + (result ? 'Success' : 'Error')](); 1035 } 1036 } 1037 if ( xhr.readyState == 4 || xhr.readystate == 0 ) { 1038 self.processing( false ).hold( false ); 1039 if ( check_btn ) { 1040 check_btn.processing( false ); 1041 } 1042 } 1043 }, 1044 1045 makeRequest = function (){ 1046 self.processing( true ).hold( true ); 1047 if ( check_btn ) { 1048 check_btn.processing( true ); 1049 } 1050 var xhr = HTTP.newRequest(); 1051 self.xhr = xhr; 1052 xhr.onreadystatechange = readyStateChange; 1053 xhr.open( method, url, true ); 1054 xhr.setRequestHeader( "Accept-Language", "en" ); 1055 if ( method == 'POST' ) { 1056 xhr.setRequestHeader( "Content-type", "application/x-www-form-urlencoded; charset=UTF-8" ); 1057 xhr.send( __serialize( data ) ); 1058 } else { 1059 xhr.send( null ); 1060 } 1061 }; 1062 1063 if ( self.xhr && (self.xhr.readyState !== 0 || self.xhr.readyState !== 4) ) { 1064 self.xhr.abort(); 1065 self.processing( false ).hold( false ); 1066 if ( check_btn ) { 1067 check_btn.processing( false ); 1068 } 1069 } 1070 1071 if ( self.xhrTimeout ) { 1072 clearTimeout( self.xhrTimeout ); 1073 } 1074 self.hold( false ); 1075 1076 if ( timeout > 0 && options.no_timeout !== true ) { 1077 self.xhrTimeout = setTimeout( function (){ 1078 makeRequest(); 1079 }, timeout ); 1080 } else { 1081 makeRequest(); 1082 } 1083 }; 1084 1085 tField_p.execute = function ( func, params ){ 1086 var self = this; 1087 if ( self.silence ) { 1088 return; 1089 } 1090 1091 if ( typeof func == 'string' ) { 1092 func = self.get( func ); 1093 } 1094 return self.parent.execute( self.el, func, params ); 1095 }; 1096 1097 1098 /* 1099 * Test tButton created by Element constructor 1100 */ 1101 var tButton = function ( parent, el ){ 1102 var self = this, 1103 type = self.attr( 'type' ), 1104 name; 1105 1106 self.parent = parent; 1107 self.el = el; 1108 self.config = {}; 1109 1110 self.events = []; 1111 1112 name = type === 'submit' ? type : self.data( 'check' ); 1113 self.name = name; 1114 self.set( __extend( __clone( parent.config ), __clone( (parent.config.buttons ? parent.config.buttons[name] : {}) ) ) ); 1115 1116 return self.init(); 1117 }; 1118 __extend_proto( tButton, Element ); 1119 var tButton_p = tButton.prototype; 1120 1121 tButton_p.init = function (){ 1122 var self = this, 1123 el = self.el, 1124 parent = self.parent; 1125 1126 if ( self.inited ) { 1127 self.destroy(); 1128 } 1129 if ( el.type == 'submit' || el == parent.get( 'submitButton' ) ) { 1130 self.on( 'click', function ( e ){ 1131 __prevent( e ); 1132 parent.form.onsubmit( e ); 1133 return false; 1134 } ); 1135 } else { 1136 self.on( 'click', function (){ 1137 var field = parent.field( self.data( 'check' ) ); 1138 if ( field ) { 1139 field.validate( {no_timeout: true} ); 1140 } 1141 } ); 1142 } 1143 this.inited = true; 1144 return this; 1145 }; 1146 1147 tButton_p.disable = function (){ 1148 var self = this; 1149 self.addClass( self.get( 'disabledClass' ) ); 1150 return self; 1151 }; 1152 1153 tButton_p.enable = function (){ 1154 var self = this; 1155 self.removeClass( self.get( 'disabledClass' ) ); 1156 return self; 1157 }; 1158 1159 1160 /* 1161 * Helpers 1162 * ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- 1163 */ 1164 /** 1165 * Object extend 1166 * @param object 1167 * @param new_obj 1168 * @returns {object} 1169 */ 1170 var __extend = function ( object, new_obj ){ 1171 for ( var key in new_obj ) { 1172 if ( new_obj.hasOwnProperty( key ) ) { 1173 object[key] = new_obj[key]; 1174 } 1175 } 1176 return object; 1177 }; 1178 1179 /** 1180 * Method for preventing default events 1181 * @param e 1182 * @private 1183 */ 1184 var __prevent = function ( e ){ 1185 if ( e.preventDefault ) { 1186 e.preventDefault(); 1187 } else { 1188 event.returnValue = false; 1189 } 1190 }; 1191 1192 /** 1193 * Object clone 1194 * @returns {*|array|string} 1195 */ 1196 var __clone = function ( obj ){ 1197 return __isArray( obj ) ? obj.slice() : __extend( {}, obj ); 1198 }; 1199 1200 1201 /** 1202 * is array function 1203 * @returns {boolean} 1204 */ 1205 var __isObject = function ( obj ){ 1206 return toString.call( obj ) == '[object Object]'; 1207 }; 1208 1209 1210 var __isForm = function ( obj ){ 1211 return obj && obj.nodeName === 'FORM'; 1212 }; 1213 1214 /** 1215 * is array function 1216 * @returns {boolean} 1217 */ 1218 var __isArray = function ( obj ){ 1219 return Object.prototype.toString.call( obj ) == '[object Array]'; 1220 }; 1221 1222 1223 /** 1224 * detects is defined array has some value 1225 * @param array 1226 * @param value 1227 * @returns {number} 1228 * @private 1229 */ 1230 var __inArray = function ( array, value ){ 1231 var index = -1, 1232 length = array ? array.length : 0; 1233 while ( ++index < length ) { 1234 if ( array[index] === value ) { 1235 return index; 1236 } 1237 } 1238 return -1; 1239 }; 1240 1241 var __clear = function ( arr ){ 1242 if ( __isArray( arr ) ) { 1243 var new_arr = []; 1244 for ( var i = 0, a_l = arr.length; i < a_l; i++ ) { 1245 if ( arr[i] !== '' ) { 1246 new_arr.push( arr[i] ); 1247 } 1248 } 1249 return new_arr; 1250 } 1251 }; 1252 1253 1254 /** 1255 * Work with data-attributes 1256 * 1257 * @param {object} element - element 1258 * @param {string} attr - data-attribute 1259 * @param {!string|number} value - new attribute value. [null - delete attr, undefined - return attr value, defined - set new value] 1260 * @returns {*} 1261 */ 1262 var __data = function ( element, attr, value ){ 1263 switch ( value ) { 1264 case null: 1265 __delAttr( element, 'data-' + attr ); 1266 break; 1267 case undefined: 1268 return __getAttr( element, 'data-' + attr ); 1269 break; 1270 default: 1271 __setAttr( element, 'data-' + attr, value ); 1272 break; 1273 } 1274 }; 1275 1276 var __getAttr = function ( el, attr ){ 1277 if ( !el ) { 1278 return null; 1279 } 1280 return el.getAttribute( attr ); 1281 }; 1282 var __setAttr = function ( el, attr, value ){ 1283 if ( !el ) { 1284 return null; 1285 } 1286 el.setAttribute( attr, value ); 1287 }; 1288 var __delAttr = function ( el, attr ){ 1289 if ( !el ) { 1290 return null; 1291 } 1292 el.removeAttribute( attr ); 1293 }; 1294 1295 1296 // AJAX Request functionality 1297 var HTTP = {}; 1298 HTTP._factories = [ 1299 function (){ 1300 return new XMLHttpRequest(); 1301 }, 1302 function (){ 1303 return new ActiveXObject( "Msxml2.XMLHTTP" ); 1304 }, 1305 function (){ 1306 return new ActiveXObject( "Microsoft.XMLHTTP" ); 1307 } 1308 ]; 1309 HTTP._factory = null; 1310 HTTP.newRequest = function (){ 1311 if ( HTTP._factory !== null ) { 1312 return HTTP._factory(); 1313 } 1314 for ( var i = 0; i < HTTP._factories.length; i++ ) { 1315 try { 1316 var factory = HTTP._factories[i];
1317 var request = factory(); 1318 if ( request !== null ) { 1319 HTTP._factory = factory; 1320 return request; 1321 } 1322 } catch ( e ) { 1323 continue; 1324 } 1325 } 1326 HTTP._factory = function (){ 1327 throw new Error( 'Object XMLHttpRequest not supported' ); 1328 }; 1329 HTTP._factory(); 1330 }; 1331 1332 1333 /** 1334 * Serrialize data for request 1335 * @param {object} data - object to serialize 1336 * @returns {string} 1337 */ 1338 var __serialize = function ( data ){ 1339 var pairs = []; 1340 var regexp = /%20/g; 1341 for ( var name in data ) { 1342 var pair = encodeURIComponent( name ).replace( regexp, '+' ) + '='; 1343 pair += encodeURIComponent( data[name].toString() ).replace( regexp, '+' ); 1344 pairs.push( pair ); 1345 } 1346 return pairs.join( '&' ); 1347 }; 1348 1349})( window, document ); 1350/** 1351 * _v_.js - validator 1352 * http://github.com/TjRus/_v_.js 1353 * (c) 2013 Vasiliy Zubach (aka TjRus) - http://tjrus.com/ 1354 * _v_ may be freely distributed under the MIT license. 1355 */ 1356"use strict"; 1357 1358(function ( window, document, undefined ){ 1359 1360 /** 1361 * Validator constructor 1362 * 1363 * @constructor 1364 * @param {!string} value 1365 * @returns {*} 1366 * @private 1367 */ 1368 var _v_ = function ( value ){ 1369 if ( !(this instanceof _v_) ) { 1370 return new _v_( value ); 1371 } 1372 this.value = value || ''; 1373 this.separator = ' '; 1374 this.rule = ''; 1375 1376 this.parsedRules = []; 1377 1378 return this; 1379 }; 1380 var _v_proto = _v_.prototype; 1381 1382 /** 1383 * sets rules separator 1384 * @param separator 1385 * @returns {*} 1386 */ 1387 _v_proto.separate = function ( separator ){ 1388 this.separator = separator; 1389 this.rules( this.rule ); // pars rules again 1390 return this; 1391 }; 1392 1393 /** 1394 * set rules to validator 1395 * @param rules 1396 * @returns {*} 1397 */ 1398 _v_proto.rules = function ( rules ){ 1399 this.rule = rules || ''; 1400 this.parseRules(); 1401 return this; 1402 }; 1403 1404 /** 1405 * add rules to existed rules str 1406 * @param rule 1407 * @returns {*} 1408 */ 1409 _v_proto.addRule = function ( rule ){ 1410 var parsed = this.parsedRules; 1411 var rule_v_ = _v_().rules( rule ).parsedRules; 1412 1413 for ( var key in rule_v_ ) { 1414 var rule = rule_v_[key]; 1415 if ( !rule ) { 1416 parsed[key] = rule; 1417 continue; 1418 } 1419 // has params and is in defined rules 1420 if ( rule && parsed.hasOwnProperty( key ) ) { 1421 // params is array 1422 parsed[key] = __toArray( parsed[key] ); 1423 rule = __toArray( rule ); 1424 for ( var i = 0, r_v_l = rule.length; i < r_v_l; i++ ) { 1425 if ( __inArray( parsed[key], rule[i] ) !== -1 ) { 1426 continue; 1427 } 1428 parsed[key].push( rule[i] ); 1429 } 1430 } else { 1431 parsed[key] = rule; 1432 } 1433 } 1434 1435 this.rule = __rulesStr( this.parsedRules, this.separator ); 1436 return this; 1437 }; 1438 1439 /** 1440 * Delete rule from validator rules 1441 * @param rule 1442 * @returns {*} 1443 */ 1444 _v_proto.delRule = function ( rule ){ 1445 var parsed = this.parsedRules; 1446 var rule_v_ = _v_().rules( rule ).parsedRules; 1447 for ( var key in rule_v_ ) { 1448 var rules = rule_v_[key]; 1449 if (!parsed.hasOwnProperty(key)) { 1450 continue; 1451 } 1452 if ( rules === undefined) { 1453 delete parsed[key]; 1454 continue; 1455 } 1456 1457 parsed[key] = __toArray( parsed[key] ); 1458 rules = __toArray( rules ); 1459 for ( var i = 0, r_l = rules.length; i < r_l; i++ ) { 1460 var index = __inArray( parsed[key], rules[i] ); 1461 if ( ~index ) { 1462 parsed[key].splice( index, 1 ); 1463 } 1464 } 1465 if ( parsed[key].length === 0 ) { 1466 delete parsed[key]; 1467 } else if ( parsed[key].length === 1 ) { 1468 parsed[key] = parsed[key][0]; 1469 } 1470 } 1471 this.rule = __rulesStr( this.parsedRules, this.separator ); 1472 return this; 1473 }; 1474 1475 /** 1476 * check has validator such rule or not 1477 * @param rules 1478 * @returns {boolean} 1479 */ 1480 _v_proto.hasRule = function ( rules ){ 1481 var rule_v_ = _v_().rules( rules ).parsedRules; 1482 var parsed = this.parsedRules; 1483 for ( var key in rule_v_ ) { 1484 var rule = rule_v_[key]; 1485 if ( !rule ) { 1486 if ( !parsed.hasOwnProperty( key ) ) { 1487 return false; 1488 } else { 1489 continue; 1490 } 1491 } 1492 parsed[key] = __toArray( parsed[key] ); 1493 rule = __toArray( rule ); 1494 for ( var i = 0, r_l = rule.length; i < r_l; i++ ) { 1495 if ( __inArray( parsed[key], rule[i] ) === -1 ) { 1496 return false; 1497 } 1498 } 1499 } 1500 return true; 1501 }; 1502 1503 1504 /** 1505 * parse validator rules and return object with rule keys and their values 1506 * @returns {{}} 1507 */ 1508 _v_proto.parseRules = function (){ 1509 var rules = this.rule.split( this.separator ); 1510 var parsed = {}; 1511 var keys = this.keys; 1512 1513 for ( var i = 0, rules_length = rules.length; i < rules_length; i++ ) { 1514 var option = rules[i], func, rule, params = undefined; 1515 if ( keys[ option ] ) { 1516 func = keys[ option ]; 1517 rule = option; 1518 } else { 1519 var keys_order = this.keys_order; 1520 for ( var j = 0, koLength = keys_order.length; j < koLength; j++ ) { 1521 var key = keys_order[j]; 1522 if ( option.indexOf( key ) === 0 ) {
1523 rule = key; 1524 params = option.replace( key, '' ); 1525 var paramArray = (/^\[(.+)\]$/).exec( params ); 1526 if ( paramArray ) params = paramArray[1].split( ',' ); // TODO: params separator 1527 func = this.keys[rule]; 1528 break; 1529 } 1530 } 1531 } 1532 if ( func ) { 1533 parsed[rule] = params; 1534 } 1535 } 1536 this.parsedRules = parsed; 1537 this.rule = __rulesStr( parsed, this.separator ); 1538 return parsed; 1539 }; 1540 1541 /** 1542 * main validate function. 1543 * @param rules 1544 * @returns {boolean} 1545 */ 1546 _v_proto.validate = function ( rules ){ 1547 if ( rules ) { 1548 this.rules( rules ); 1549 } 1550 var parsed = this.parsedRules; 1551 for ( var rule in parsed ) { 1552 try { 1553 if ( !this.keys[rule].call( this, parsed[rule] ) ) { 1554 return false; 1555 } 1556 } catch (e) { 1557 return false; 1558 } 1559 } 1560 return true; 1561 }; 1562 1563 1564 /** 1565 * method for extending _v_ with new rules and validate functions 1566 * @param rule 1567 * @param func 1568 */ 1569 _v_proto.extend = function ( rule, func ){ 1570 _v_proto.keys[rule] = func 1571 _v_proto.keys_order.push( rule ); 1572 1573 _v_proto.keys_order = this.keys_order.sort( function ( a, b ){ 1574 return b.length - a.length; 1575 } ); 1576 return this; 1577 }; 1578 1579 _v_proto.keys = {}; // object with rules 1580 _v_proto.keys_order = []; // array with ordered rules 1581 1582 window._v_ = _v_; 1583 1584 1585 /** 1586 * Rules extends 1587 * ---------- ---------- ---------- ---------- ---------- 1588 */ 1589 var rules = { 1590 1591 // required 1592 '*' : function (){ 1593 return this.value.length !== 0; 1594 }, 1595 1596 // alpha 1597 'a' : function (){ 1598 return (/^[a-z]+$/i).test( this.value ); 1599 }, 1600 1601 // alpha numeric 1602 'a1' : function (){ 1603 return (/^[a-z0-9]+$/i).test( this.value ); 1604 }, 1605 1606 // alpha dash 1607 'a_' : function (){ 1608 return (/^[a-z_-]+$/i).test( this.value ); 1609 }, 1610 1611 // alpha numeric dash 1612 'a1_' : function (){ 1613 return (/^[a-z0-9_-]+$/i).test( this.value ); 1614 }, 1615 1616 // email 1617 '@' : function (){ 1618 var mail_regexp = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i; 1619 return mail_regexp.test( this.value ); 1620 }, 1621 1622 // emails 1623 '@s' : function (){ 1624 var emails = this.value.split( ',' ); 1625 for ( var i = 0; i < emails.length; i++ ) { 1626 if ( !_v_( emails[i] ).validate( '@' ) ) { 1627 return false; 1628 } 1629 } 1630 return true; 1631 }, 1632 1633 // ip address 1634 'ip' : function (){ 1635 return (/^((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[0-9]{1,2})\.){3}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[0-9]{1,2})$/i).test( this.value ); 1636 }, 1637 1638 // base65 string 1639 'b64' : function (){ 1640 return (/^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{4})$/).test( this.value ); 1641 }, 1642 1643 // URL 1644 'url' : function (){ 1645 return (/^(https?|s?ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i).test( this.value ); 1646 }, 1647 1648 // integer 1649 'int' : function (){ 1650 return (/^\-?[0-9]+$/).test( this.value ); 1651 }, 1652 1653 // Numeric 1654 'num' : function (){ 1655 return (/^[0-9]+$/).test( this.value ); 1656 }, 1657 1658 // Decimal 1659 'dec' : function (){ 1660 return (/^\-?[0-9]*\.?[0-9]+$/).test( this.value ); 1661 }, 1662 1663 // Natural 1664 'nat' : function (){ 1665 return (/^[0-9]+$/i).test( this.value ); 1666 }, 1667 1668 // length equals to 1669 'l=' : function ( length ){ 1670 if ( typeof(length) == 'number' || typeof(length) == 'string' ) { 1671 return this.value.length == length; 1672 } else { 1673 for ( var key in length ) { 1674 if ( this.value.length == length[key] ) return true; 1675 } 1676 } 1677 return false; 1678 }, 1679 1680 // length more than 1681 'l>' : function ( length ){ 1682 return this.value.length > length; 1683 }, 1684 1685 // length more or equals to 1686 'l>=' : function ( length ){ 1687 return this.value.length >= length; 1688 }, 1689 1690 // length less than 1691 'l<' : function ( length ){ 1692 return this.value.length < length; 1693 }, 1694 1695 // length less or equals to 1696 'l<=' : function ( length ){ 1697 return this.value.length <= length; 1698 }, 1699 1700 // length is in range 1701 'lr=' : function ( range ){ 1702 return (this.value.length >= range[0] && this.value.length <= range[1]); 1703 }, 1704 1705 // is greater than 1706 '>' : function ( value ){ 1707 var test = this.value; 1708 return (_v_( test ).validate( 'dec' ) && parseFloat( test ) > parseFloat( value )); 1709 }, 1710 1711 // is greater or equals to 1712 '>=' : function ( value ){ 1713 var test = this.value; 1714 return (_v_( test ).validate( 'dec' ) && parseFloat( test ) >= parseFloat( value )); 1715 }, 1716 1717 // is less than 1718 '<' : function ( value ){ 1719 var test = this.value; 1720 return (_v_( test ).validate( 'dec' ) && parseFloat( test ) < parseFloat( value )); 1721 }, 1722 1723 // is less or equals to 1724 '<=' : function ( value ){ 1725 var test = this.value; 1726 return (_v_( test ).validate( 'dec' ) && parseFloat( test ) <= parseFloat( value )); 1727 }, 1728 1729 // is in range 1730 'r=' : function ( value ){ 1731 var test = this.value; 1732 return (_v_( test ).validate( 'dec' ) && parseFloat( test ) >= parseFloat( value[0] ) && parseFloat( test ) <= parseFloat( value[1] )); 1733 }, 1734 1735 // regular expression validation 1736 'reg=': function ( regExpression ){ 1737 var reg = new RegExp( regExpression, 'i' ); 1738 return reg.test( this.value ); 1739 }, 1740 1741 // matches to 1742 '=' : function ( value ){ 1743 var test = this.value; 1744 if ( typeof value == 'string' || typeof value == 'number' ) { 1745 return test == value; 1746 } else { 1747 for ( var i = 0, valueLength = value.length; i < valueLength; i++ ) { 1748 if ( value[i].indexOf( '#' ) === 0 ) { 1749 var el = __getElById( value[i].replace( '#', '' ) ); 1750 if ( !el ) { 1751 return false; 1752 } 1753 if ( test == el.value ) return true; 1754 } else { 1755 if ( test == value[i] ) return true; 1756 } 1757 } 1758 } 1759 return false; 1760 }, 1761 1762 // matches to id 1763 '=#' : function ( value ){ 1764 var test = this.value; 1765 if ( typeof value == 'string' || typeof value == 'number' ) { 1766 value = value.split( ' ' ); 1767 var el = __getElById( value ); 1768 if ( !el ) { 1769 return false; 1770 } 1771 return test == el.value; 1772 } else { 1773 for ( var i = 0; i < value.length; i++ ) { 1774 var el = __getElById( value[i] ); 1775 if ( el && test == el.value ) { 1776 return true; 1777 } 1778 } 1779 } 1780 return false; 1781 }, 1782 1783 // not matches to 1784 '!=' : function ( value ){ 1785 value = (__isArray( value )) ? '[' + value.join( ',' ) + ']' : value; // TODO: value separator 1786 return !(_v_( this.value ).validate( '=' + value )); 1787 }, 1788 1789 // not contain 1790 '!' : function ( value ){ 1791 if ( typeof value == 'string' || typeof value == 'number' ) { 1792 return (this.value).toString().indexOf( value.toString() ) === -1; 1793 } else { 1794 for ( var key in value ) { 1795 if ( ~(this.value).toString().indexOf( value[key].toString() ) ) { 1796 return false; 1797 } 1798 } 1799 } 1800 return true; 1801 }, 1802 1803 // credit card (all cards type) 1804 'c' : function ( value ){ 1805 return (/^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11})$/).test( this.value ); 1806 }, 1807 1808 // visa card 1809 'cv' : function ( value ){ 1810 return (/^4[0-9]{12}(?:[0-9]{3})?$/).test( this.value ); 1811 }, 1812 1813 // master card 1814 'cm' : function ( value ){ 1815 return (/^5[1-5][0-9]{14}$/).test( this.value ); 1816 }, 1817 1818 // american express card 1819 'ca' : function ( value ){ 1820 return (/^3[47][0-9]{13}$/).test( this.value ); 1821 }, 1822 1823 // discover card 1824 'cd' : function ( value ){ 1825 return (/^6(?:011|5[0-9]{2})[0-9]{12}$/).test( this.value ); 1826 }, 1827 1828 // date format validation 1829 'D=' : function ( format ){ 1830 return !!(__toDate( this.value, format )); 1831 } 1832 }; 1833 1834 // add extends 1835 for ( var key in rules ) { 1836 _v_().extend( key, rules[key] ); 1837 } 1838 1839 1840 /** 1841 * Helpers 1842 * ---------- ---------- ---------- ---------- ---------- 1843 */ 1844 1845 // Date functions 1846 var __dayNames = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; 1847 var __shortDayNames = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; 1848 var __dayChars = ['S', 'M', 'T', 'W', 'T', 'F', 'S']; 1849 1850 // Full, short and single character names for the months. Override these to provide multi-language support. 1851 var __monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; 1852 var __shortMonthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; 1853 var __monthChars = ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D']; 1854 1855 var __daysInMonth = function ( month, year ){ 1856 // If February, check for leap year 1857 if ( (month == 1) && (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0)) ) { 1858 return 29; 1859 } 1860 else { 1861 var days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; 1862 return days[month]; 1863 } 1864 }; 1865 1866 /** 1867 * Attempts to convert a string into a date based on a given format. Fields will 1868 * match either the long or short form, except in the case of the year, where the 1869 * string must match either a 2-digit or 4-digit format. Ranges are checked. Day
1870 * names are expected if they are included in the format string, but are otherwise 1871 * ignored. Use ^ to force the use of a literal character. In other words, to 1872 * have the character Y appear instead of the actual year, use ^Y. 1873 * 1874 * Field | Full Form | Short Form 1875 * -------------+---------------------+------------------- 1876 * Year | Y (4 digits) | y (2 digits) 1877 * Month | M (1 or 2 digits) | m (1 or 2 digits) 1878 * Month Name | N (full name) | n (abbr) 1879 * Day of Month | D (1 or 2 digits) | d (1 or 2 digits) 1880 * Day Name | W (full name) | w (abbr) 1881 * Hour (1-12) | H (1 or 2 digits) | h (1 or 2 digits) 1882 * Hour (0-23) | R (1 or 2 digits) | r (1 or 2 digits) 1883 * Minute | I (1 or 2 digits) | i (1 or 2 digits) 1884 * Second | S (1 or 2 digits) | s (1 or 2 digits) 1885 * AM/PM | A (upper case) | a (lower case) 1886 * 1887 * @param {Date} date Date that we should transform. 1888 * @param {string} format The basic format of the string. 1889 * @return {Date} The string as a date object. 1890 */ 1891 var __toDate = function ( date, format ){ 1892 // Default values set to midnight Jan 1 of the current year. 1893 var year = new Date().getFullYear(), 1894 month = 0, 1895 day = 1, 1896 hours = 0, 1897 minutes = 0, 1898 seconds = 0; 1899 1900 // Positions of each date element within the source string. Use to know 1901 // which backreference to check after a successful match. 1902 var yearPos = -1, 1903 monthPos = -1, 1904 dayPos = -1, 1905 hoursPos = -1, 1906 minutesPos = -1, 1907 secondsPos = -1, 1908 amPmPos = -1; 1909 1910 var monthStyle = 'm', // How we interpret the month, digits (M/m) or names (N/n) 1911 hoursStyle = 'h'; // How we interpret the hours, 12-hour (h) or 24-hour (r) 1912 1913 var position = 1, // Position of the current date element (year, month, day, etc.) in the source string 1914 pattern = ''; // Date pattern to be matched. 1915 1916 // Remove extraneous whitespace from source string and format string. 1917 var str = date.replace( /\s+/g, ' ' ); 1918 format = format.replace( /\s+/g, ' ' ); 1919 1920 // Loop throught the format string, and build the regex pattern 1921 // for extracting the date elements. 1922 for ( var i = 0, len = format.length; i < len; i++ ) { 1923 var c = format.charAt( i ); 1924 switch ( c ) { 1925 case 'Y' : 1926 pattern += '(\\d{4})'; 1927 yearPos = position++; 1928 break; 1929 case 'y' : 1930 pattern += '(\\d{2})'; 1931 yearPos = position++; 1932 break; 1933 case 'M' : 1934 case 'm' : 1935 pattern += '(\\d{1,2})'; 1936 monthPos = position++; 1937 monthStyle = 'm'; 1938 break; 1939 case 'N' : 1940 pattern += '(' + __monthNames.join( '|' ) + ')'; 1941 monthPos = position++; 1942 monthStyle = 'N'; 1943 break; 1944 case 'n' : 1945 pattern += '(' + __shortMonthNames.join( '|' ) + ')'; 1946 monthPos = position++; 1947 monthStyle = 'n'; 1948 break; 1949 case 'D' : 1950 case 'd' : 1951 pattern += '(\\d{1,2})'; 1952 dayPos = position++; 1953 break; 1954 case 'W' : // We'll match W, but won't do anything with it. 1955 pattern += '(' + __dayNames.join( '|' ) + ')'; 1956 position++; 1957 break; 1958 case 'w' : // We'll match w, but won't do anything with it. 1959 pattern += '(' + __shortDayNames.join( '|' ) + ')'; 1960 position++; 1961 break; 1962 case 'H' : 1963 case 'h' : 1964 pattern += '(\\d{1,2})'; 1965 hoursPos = position++; 1966 hoursStyle = 'h'; 1967 break; 1968 case 'R' : 1969 case 'r' : 1970 pattern += '(\\d{1,2})'; 1971 hoursPos = position++; 1972 hoursStyle = 'r'; 1973 break; 1974 case 'I' : 1975 case 'i' : 1976 pattern += '(\\d{1,2})'; 1977 minutesPos = position++; 1978 break; 1979 case 'S' : 1980 case 's' : 1981 pattern += '(\\d{1,2})'; 1982 secondsPos = position++; 1983 break; 1984 case 'A' : 1985 case 'a' : 1986 pattern += '(AM|am|PM|pm)'; 1987 amPmPos = position++; 1988 break; 1989 default : 1990 pattern += (c == '^' ? format.charAt( ++i ) : c); 1991 } 1992 } 1993 1994 // Pull out the date elements from the input string 1995 var matches = str.match( new RegExp( pattern ) ); 1996 if ( !matches ) { 1997 return null; 1998 } 1999 2000 // Now we have to interpret each of those parts... 2001 2002 if ( yearPos > -1 ) { 2003 year = parseInt( matches[yearPos], 10 ); 2004 year = (year < 50 ? year + 2000 : (year < 100 ? year + 1900 : year)); 2005 } 2006 2007 if ( monthPos > -1 ) { 2008 switch ( monthStyle ) { 2009 case 'm':
2010 month = parseInt( matches[monthPos], 10 ) - 1; // JavaScript months are zero based, user input generally is not. 2011 if ( month > 11 ) 2012 return null; 2013 break; 2014 case 'N': 2015 month = parseInt( __monthNumbers[matches[monthPos]], 10 ); 2016 if ( isNaN( month ) ) 2017 return null; 2018 break; 2019 case 'n': 2020 month = parseInt( __shortMonthNumbers[matches[monthPos]], 10 ); 2021 if ( isNaN( month ) ) 2022 return null; 2023 break; 2024 } 2025 } 2026 2027 if ( dayPos > -1 ) { 2028 day = parseInt( matches[dayPos], 10 ); 2029 if ( (day < 1) || (day > __daysInMonth( month, year )) ) 2030 return null; 2031 } 2032 2033 if ( hoursPos > -1 ) { 2034 hours = parseInt( matches[hoursPos], 10 ); 2035 if ( hoursStyle == 'h' && (hours === 0 || hours > 12) ) 2036 return null; 2037 else if ( hours > 23 ) 2038 return null; 2039 } 2040 2041 if ( minutesPos > -1 ) { 2042 minutes = parseInt( matches[minutesPos], 10 ); 2043 if ( minutes > 59 ) 2044 return null; 2045 } 2046 2047 if ( secondsPos > -1 ) { 2048 seconds = parseInt( matches[secondsPos], 10 ); 2049 if ( seconds > 59 ) 2050 return null; 2051 } 2052 2053 // Convert 12-hour time, if used, to 24-hour time. 2054 if ( amPmPos > -1 ) { 2055 var amPm = matches[amPmPos]; 2056 if ( (amPm == 'pm' || amPm == 'PM') && (hours < 12) ) 2057 hours += 12; 2058 } 2059 2060 return new Date( year, month, day, hours, minutes, seconds ); 2061 }; 2062 2063 2064 /** 2065 * Simple getElementByID function 2066 * @param {string} id - id of HTML element 2067 * @returns {HTMLElement} 2068 * @private 2069 */ 2070 var __getElById = function ( id ){ 2071 return document.getElementById( id ); 2072 } 2073 2074 /** 2075 * Check is obj is array or not 2076 * @param obj - element we should check 2077 * @returns {boolean} 2078 * @private 2079 */ 2080 var __isArray = function ( obj ){ 2081 return Object.prototype.toString.call( obj ) == '[object Array]'; 2082 }; 2083 2084 /** 2085 * detects is defined array has some value 2086 * @param array 2087 * @param value 2088 * @returns {number} 2089 * @private 2090 */ 2091 var __inArray = function ( array, value ){ 2092 var index = -1, 2093 length = array ? array.length : 0; 2094 while ( ++index < length ) { 2095 if ( array[index] === value ) { 2096 return index; 2097 } 2098 } 2099 return -1; 2100 }; 2101 window.__inArray = __inArray; 2102 2103 2104 /** 2105 * convert item to Array. 2106 * @param el 2107 * @returns {Array} 2108 * @private 2109 */ 2110 var __toArray = function ( el ){ 2111 return (!__isArray( el )) ? [el] : el; 2112 } 2113 2114 /** 2115 * convert rules object to string 2116 * @param obj 2117 * @param separator 2118 * @returns {string} 2119 * @private 2120 */ 2121 var __rulesStr = function ( obj, separator ){ 2122 separator = separator || ' '; 2123 var str = separator; 2124 for ( var key in obj ) { 2125 var rules = obj[key]; 2126 if ( __isArray( rules )){ 2127 rules = __clearArray(rules); 2128 } 2129 if ( !rules ) { 2130 str += key + separator; 2131 } else if ( __isArray( rules ) ) { 2132 str += key + '[' + rules.join( ',' ) + ']' + separator; // TODO: rule values separator 2133 } else { 2134 str += key + rules + separator; 2135 } 2136 } 2137 str = str.substr( separator.length, str.length - separator.length * 2 ); 2138 return str; 2139 }; 2140 2141 var __clearArray = function(arr){ 2142 var new_arr = []; 2143 for (var i = 0, a_l = arr.length; i < a_l; i++) { 2144 if (arr[i]) { 2145 new_arr.push(arr[i]); 2146 } 2147 } 2148 return (new_arr && new_arr.length > 1) ? new_arr : new_arr[0]; 2149 } 2150 2151})( window, document );
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.