PageSourceSearch

https://www.tmitrail.org.tw/sites/all/themes/theme945/js/tformer.js?tlvfej

js tmitrail.org.tw collected 2026-09-24 23:48:14 UTC 54,885 bytes, 2,151 lines download raw bytes

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.