PageSourceSearch

https://xfoundationx.org/cms3t/res/vit-lib/ajax.js

js xfoundationx.org collected 2026-10-04 13:10:52 UTC 37,187 bytes, 1,169 lines download raw bytes

1/** ************************************************************************************************************************ **\
2* 
3*	Set of classes and functions that will help generate and process AJAX queries.
4* 
5* 	@package	VIT-Lib
6* 	@subpackage	Ajax
7*
8* 	@license	MIT License
9*			Permission  is  hereby  granted,  free  of  charge, to any person obtaining a copy of this software and
10*			associated documentation files (the "Software"), to deal in the Software without restriction, including
11*			without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12*			copies  of  the  Software, and to permit persons to whom the Software is furnished to do so, subject to
13*			the following conditions:
14*			
15*			The  above  copyright  notice and this permission notice shall be included in all copies or substantial
16*			portions of the Software.
17*			
18*			THE  SOFTWARE  IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
19*			LIMITED  TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
20*			NO  EVENT  SHALL  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21*			WHETHER  IN  AN  ACTION  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22*			SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23*
24*	@author		Victor Denisenkov aka Mr.V!T
25*	@copyright	Copyright (c) 2008 - 2012 Victor Denisenkov aka Mr.V!T
26*	@version	1.0
27* 
28\** ************************************************************************************************************************ **/
29
30/** //** ----= OBJECT vAjax	=--------------------------------------------------------------------------------------\**//** \
31*
32* 	Ajax procesing object. Implements both jQuery common ajax and ajaxUpload plugin for iframe upload tricks (for old 
33*	browsers). Designed to use together with server side vAjax object. Supports POST progress monitoring using server 
34*	side plugins like APC or HTML5 files API. By default enables progress monitoring for forms with files and progressbar 
35*	present.
36*
37* 	@package	VIT-Lib
38* 	@subpackage	Ajax
39* 	@category	Helper
40*
41*	@param	string	url		- Path to controller to make request to.
42*
43*	@param	MIXED	[data]		- Data to post on request. Can be DOM form, HTML5 FormData object, jQuery selector 
44*					  or object which points to form, or plain object with parameters.
45*
46*	@param	object	[options]	- Additional options if necessary.
47*
48*	@todo	Known bug: ajaxUpload can't handle server errors. Need workaround (possible write own iframe upload?)
49*
50\**//** ----------------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
51
52// ToDo: Cross domain support with both JSONP and Access-Control-Allow-Origin: *
53
54function vAjax (url, data, options) {
55
56	// Creating basic object with events assigned
57	var obj = vEventObject(['success', 'error', 'start', 'stop', 'done', 'progressStart', 'progressStop'], {
58	
59	opt		: {				// Default object options
60		async			: true,			// Set FALSE to make sync call
61		simple			: false,		// Expect simple answers and threat them as success or trigger errors on those.
62		modern			: true,			// Allow or not modern APIs
63
64		progress		: '.progressFill',	// Default progressbar fill element selector. Can be replaced with callback or own selector.
65		progressNum		: '.progressNum',	// Default progressbar Numeric indicator. Will be set to current %. Ignored if callback given for fill.
66		progressOn		: false,		// Forces progress monitoring (not used currently).
67
68		progressUrl		: '/upload/?key=',	// Progress monitoring controller. Key marker will be added to end of this.
69		progressDelay		: 500,			// Delay before starting progress monitoring using server side plugin.
70		progressInterval	: 500			// Interval to update progress.
71	}, //OBJECT options
72	
73	resSplit	: "-=-CUT-=-",			// Unique marker used to split results.
74
75	controller	: url,				// Url to make request to.
76	source		: data || {},			// Source given.
77
78	sourceForm	: false,			// Source form if available.
79	sourceData	: {},				// Source data if available (if form given - will contain form inputs as array).
80	sourceElement	: false,			// Source form as jQuery pointer.
81	
82	isModern	: false,			// HMTL5 usage marker. Will be set to TRUE if system decides to use HTML5 
83							// formData and XHR2 to post data
84
85	Method		: '',				// Will store used upload method.
86	
87							
88	prgInterval	: false,			// Progress bar monitor interval resource
89	prgTimeout	: false,			// Progress bar monitoring timeout resource
90	
91	_processor	: false,			// Stores custom XHR processor if specified. 
92	
93	/** //** ----= options	=--------------------------------------------------------------------------------------\**//** \
94	*
95	* 	Used to setup options on run time.
96	*
97	*	@param	object	options	- Data object with options to setup.
98	* 
99	*	@return SELF
100	* 
101	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
102	options	: function (options) {
103		
104		options = options || {};
105
106		// Merging options
107		jQuery.extend(this.opt, options);
108
109		// Testing browser to be modern
110		this.isModern = this.opt.modern && isXHR2();
111		
112		return this;
113	}, //FUNC options
114
115	/** //** ----= processor	=------------------------------------------------------------------------------\**//** \
116	*
117	* 	Custom processor is used to use external ajax xhr implementation. vAjax is passed as parameter to processor to 
118	*	allow implementation of final events. Processor should call .ajaxSuccess() / .ajaxError() for appropiate events.
119	*
120	*	@param	function	proc	- Processor callback.
121	* 
122	*	@return SELF
123	* 
124	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
125	processor	: function ( proc ) {
126		this._processor = proc;
127		return this;		
128	}, //FUNC processor
129	
130	/** //** ----= parseResult	=------------------------------------------------------------------------------\**//** \
131	*
132	* 	Prses and format result data. Generates result to pass to callbacks. For internal use.
133	*
134	* 	@param	object	result		- Request responce.
135	* 
136	*	@return	object			- Object with result data.
137	*
138	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
139	parseResult	: function (result) {
140
141		var self = this;
142		
143		// Making sure that result have all vields
144		if ( !result.responseText )
145			result.responseText = '';
146
147	// ---- Preparing data ----
148
149		// Preparing result wrapper
150		var data	= {
151			
152			req	: self.sourceData,	// Request source data.
153			
154			status	: {			// Responce meta like status and fields list. Default one for simple responces. Full responce will provide own head.
155				major	: (result.statusMinor == 'success'),	// Major operation status TRUE or FALSE. Simples are TRUE normally.
156				minor	: result.statusMinor,			// Minor/detailed status as string (like 'success', 'error', 'warning', 'simple' etc)
157				code	: result.status,			// Satus code if available
158				text	: result.statusText,			// Request status text
159				full	: false,				// Full(complex) responce marker
160				fields	: []					// Custom responce fileds/types list
161			}, //OBJECT meta
162			
163			res	: ''			// Request basic response - whole simple request, or head/status message for complex response. Will be added with parsing below.
164		}; //OBJECT data
165
166		// Trying to split as complex result
167		var tmp		= result.responseText.split(this.resSplit);
168
169		// Smth basic will present even for plain responce 
170		// (for example in case of server script fatal erorrs).
171		// Correcting res to have at least smth
172		data.res = tmp[0] || '';
173		
174		// Determinig wether result was full(complex) or simple
175		data.status.full	= tmp.length > 1;
176
177	// ---- Simple result ----
178
179		// If got simple result (either error or success)
180		if ( !data.status.full ) {
181
182			// Updating status
183			data.status.major = data.status.major ? this.opt.simple : data.status.major;
184			data.status.minor = 'simple';
185			
186			// If simple is threating as error - filling result as status text (expecting server errors)
187			if ( !this.opt.simple && !data.status.text )
188				data.status.text = data.res;
189
190			// Running callbacks
191	
192			// We are done anyway, so triggering stop event, passing self as param
193			this.stop(data, this);
194	
195			// Error/success
196			if ( data.status.major && this.opt.simple ) 
197				this.success(data, this);
198			else 
199				this.error(data, this);
200	
201			// Both stop and done are called anyway, difference is calling order: stop, success/error, done
202			this.done(data, this);
203
204			return data;
205			
206		} // If simple
207
208	// ---- Complex result head and fields ----
209
210		// For full responce status - header will go second (after possible debug)
211		var head = JSON.parse(tmp[1]);
212
213		// Updating data status, merging existing response with given one
214		// Mergin only if no text supplied already (usually errors have text)
215		if ( !data.status.text )
216			jQuery.extend(data.status, head);
217
218		// Looping through given fields (if present) and decoding them to response
219		var di = 2;
220		if ( head.fields ) {
221			
222			for ( var name in head.fields ) {
223
224				// Decoding non scalar values
225				if ( !head.fields[name] ) {
226					data[name] = JSON.parse(tmp[di]);
227				} else { //IF need to decode
228					data[name] = tmp[di];
229				} //IF no need to decode (scalar)
230				
231				// Increasing split counter
232				di++;
233			} //FOR each field
234			
235		} //IF was fields posted
236
237		// Running callbacks
238
239		// We are done anyway, so triggering stop event, passing self as param
240		this.stop(data, this);
241
242		// Error/success
243		if ( data.status.major ) 
244			this.success(data, this);
245		else 
246			this.error(data, this);
247
248		// Both stop and done are called anyway, difference is calling order: stop, success/error, done
249		this.done(data, this);
250
251		return data;
252		
253	}, //FUNC parseResult
254
255// ==== CALLBAKCS ==============================================================================================================
256
257	/** //** ----= ajaxSuccess	=------------------------------------------------------------------------------\**//** \
258	*
259	* 	Ajax success event callback. For internal use, can be altered though.
260	*
261	* 	@see	jQuery.ajax callbacks for parameters.
262	* 
263	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
264	ajaxSuccess	: function (res, status) {
265
266		this.ajaxProgressStop(100);
267		
268		status = status || 'success';
269		
270		// Creating result object
271		return this.parseResult({
272			responseText	: res,
273			status		: 0,
274			statusText	: '',
275			statusMinor	: status
276		}); //parseResult.result
277		
278	}, //FUNC ajaxSuccess
279	
280	/** //** ----= ajaxError	=------------------------------------------------------------------------------\**//** \
281	*
282	* 	Ajax Error event callback. For internal use, can be altered though.
283	*
284	* 	@see	jQuery.ajax callbacks for parameters.
285	* 
286	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
287	ajaxError	: function (res, status) {
288
289		this.ajaxProgressStop('error');
290		
291		// Adding minor status to common result		
292		res.statusMinor = status;
293		
294		return this.parseResult(res);
295		
296	}, //FUNC ajaxError
297
298	/** //** ----= ajaxXHR	=--------------------------------------------------------------------------------------\**//** \
299	*
300	* 	Ajax XHR callback. Used to alter jQuery XHR.
301	*
302	* 	@see	jQuery.ajax callbacks for parameters.
303	* 
304	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
305	ajaxXHR	: function () {
306		
307		var self = this;
308		
309		var x = jQuery.ajaxSettings.xhr();
310
311		// Adding listener only if really needed. If not - xhr goes back untouched
312		if ( this.hasProgress() ) {
313			
314			// If have progress - good place to reset it
315			this.ajaxProgressStart();
316			
317			// We are sure that .upload exists: previously it was tested in .post()
318			x.upload.addEventListener('progress', function (e) {
319
320				if ( e.lengthComputable ) {
321
322					// Calculating procents
323					var proc = (e.loaded * 100) / e.total;
324					
325					self.ajaxProgress( Math.floor(proc) );
326					
327				} //IF there is progress available
328				
329			}, false);
330			
331		} //IF need progress monitoring
332
333		return x;
334	}, //FUNC ajaxXHR
335
336// ==== Progress ===============================================================================================================
337
338	/** //** ----= getFormElement	=------------------------------------------------------------------------------\**//** \
339	*
340	* 	For internal use. Checks given parameter as for element. Mainly used for progress indication elements search.
341	*
342	*	@param	MIXED		- jQuery selector to search.
343	*
344	*	@return	MIXED		- jQuery element to use as progress element, or FALSE if not applicable. 
345	*
346	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
347	getFormElement	: function (el) {
348		
349		// This does not assumes callback functions. Those should be checked outside this and processed separately.
350		
351		// If already jQuery - only checking length
352		if ( isJQ(el) ) {
353			if ( el.length > 0 )
354				return el;
355				
356			return false;
357		} //IF jQuery given
358		
359		// If string selector given - searching it within form 
360		// Search on form or FALSE, done this way for optimization 
361		// and to avoid multiple for updates with common class selectors
362		// If user needs global selector search - he still can pass ready jQuery or use callback
363		if ( isStr(el) ) {
364
365			// Not allowing selector search outside of form.			
366			if ( !this.sourceElement )
367				return false;
368				
369			var j = this.sourceElement.find(el);
370		
371			// If smth found - we're good to go
372			if ( j.length > 0 )
373				return j;
374			
375			return false;
376		} //IF string passsed
377
378		// At this point assuming that element is given as DOM element
379		// Then just wrapping it with jQuery
380		return jQuery(el);
381
382	}, //FUNC getFormElement
383
384	/** //** ----= progress	=--------------------------------------------------------------------------------------\**//** \
385	*
386	* 	Progress monitoring event. Triggers on progress update event. Can accept either callbacks or valid jQuery 
387	*	selectors (string, jQuery or DOMElement). Callbacks will get current precentage as parameter. In case if 
388	*	selector given - for progressBar system will set width, for numeric indicator will set .html(). jQuery selectors 
389	*	do not process errors or other status updates, though those will be passed into callbacks.  
390	*
391	*	@param	MIXED	bar[,num]	- Progress bar and numeric bar callbacks or elements to process.
392	*
393	*	@param	bool	[force]		- Forcing progress event. If set to FALSE system will try to detect if progress 
394	*					  monitoring is necessary.
395	*	
396	*	@return	SELF 
397	* 
398	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
399	progress	: function (bar, num, force) {
400		
401		this.opt.progress	= bar;
402		this.opt.progressNum	= num;
403		
404		this.opt.progressOn	= force || false; 	
405		
406		return this;
407	}, //FUNC progress
408
409	/** //** ----= hasProgress	=------------------------------------------------------------------------------\**//** \
410	*
411	* 	For internal use. Checks if need to even setup progress indication.
412	*
413	*	@return	MIXED		- jQuery element to use as progress element, or 
414	*
415	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
416	hasProgress	: function () {
417		
418		// Nothing to do if nothing is given as progress
419		if ( !this.opt.progress && !this.opt.progressNum )
420			return false;
421		
422		// Progress is always enabled for callbacks
423		// ToDo: review this behavior, mb add hasFiles for testing.
424
425		// If not callbacks - checking to be jQuery
426		if ( !isFunction(this.opt.progress) )
427			this.opt.progress = this.getFormElement(this.opt.progress);
428
429		if ( !isFunction(this.opt.progressNum) )
430			this.opt.progressNum = this.getFormElement(this.opt.progressNum);
431
432		// Now they are callback, jQuery or just disabled, so fast to check. Enough one of either.
433		if ( this.opt.progress || this.opt.progressNum )
434			return true;
435
436		return false;
437	}, //FUNC hasProgress
438
439	/** //** ----= ajaxProgressStart	=----------------------------------------------------------------------\**//** \
440	*
441	* 	Resets progress elements if set.
442	*
443	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
444	ajaxProgressStart	: function (proc) {
445
446		// If jQuery element set for progress - good to show them
447
448		if ( isJQ(this.opt.progress) )
449			this.opt.progress.fadeIn();
450
451		if ( isJQ(this.opt.progressNum) )
452			this.opt.progressNum.fadeIn();
453
454		this.ajaxProgress(0);
455		this.progressStart(0);
456		
457	}, //FUNC ajaxProgressStart
458
459	/** //** ----= ajaxProgress	=------------------------------------------------------------------------------\**//** \
460	*
461	* 	Progress event callback. Used on progress monitoring. For internal use, can be altered though.
462	*
463	*	@param	int	proc - Current progress precentage.	
464	*
465	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
466	ajaxProgress	: function (proc) {
467
468		// Nothing to do if nothing is given as progress
469		if ( !this.opt.progress && !this.opt.progressNum )
470			return;
471
472		// For selectors processing only numeric proc 
473		// Errors and other mess will be processed in callbacks if necessary
474
475	// ---- Progress bar ----
476		
477		if ( this.opt.progress ) {
478			
479			// Can be callback or jQuery at this poing
480			if ( isFunction(this.opt.progress) )
481				this.opt.progress(proc);
482				
483			else if ( isNumeric(proc) )
484				this.opt.progress.width(proc + '%');
485			
486		} //IF progress given
487			
488	// ---- Numeric indicator ----
489		
490		if ( this.opt.progressNum ) {
491			
492			// Can be callback or jQuery at this poing
493			if ( isFunction(this.opt.progressNum) )
494				this.opt.progressNum(proc);
495				
496			else if ( isNumeric(proc) )
497				this.opt.progressNum.html( proc != 0 ? proc + '%' : '');
498			
499		} //IF progress given
500			
501	}, //FUNC ajaxProgress
502
503	/** //** ----= ajaxProgressStop	=------------------------------------------------------------------------------\**//** \
504	*
505	* 	Forces progress monitoring stop.
506	*
507	*	@param	int	proc - Current progress precentage.	
508	*
509	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
510	ajaxProgressStop	: function (proc) {
511
512		if ( this.prgInterval )
513			clearInterval(this.prgInterval); 
514
515		if ( this.prgTimeout )
516			clearInterval(this.prgTimeout); 
517
518		if ( this.hasProgress() )
519			this.ajaxProgress(proc);
520
521		this.progressStop(proc);
522
523	}, //FUNC ajaxProgressStop
524	
525// ==== Processors =============================================================================================================
526
527	/** //** ----= hasFiles	=--------------------------------------------------------------------------------------\**//** \
528	*
529	* 	Checks if form or data object contains files inputs.
530	*
531	*	@param	jQuery	form	- Form element to search within.
532	*
533	*	@return	SELF 
534	*
535	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
536	hasFiles	: function ($source) {
537		
538		// If jQuery for form given - just searching for file inputs
539		if ( isJQ($source) ) {
540			if ( $source.find('INPUT[type=file]').length > 0 )
541				return true;
542		} //IF form given
543		else {
544			
545			// Non modern browsers can't have files anyway
546			if ( !this.isModern )
547				return false;
548			
549			// Looking for files in dataObject
550			for ( var $i in $source ) {
551				
552				// Doing className check in addition, 
553				// because of sometimes weird brwosers passing data between windows
554				if ( $source[$i] instanceof File || getClassOf($source[$i]) === 'File' )
555					return true;
556			} 
557		} //IF dataObject
558
559		return false;
560	}, //FUNC hasFiles
561
562	/** //** ----= postArray	=------------------------------------------------------------------------------\**//** \
563	*
564	* 	For internal use. Sends data object using common jQuery ajax. Does not supports progress monitoring 
565	*	(no reason for arrays).
566	*
567	*	@return	SELF 
568	*
569	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
570	postArray	: function () {
571
572		var self	= this;
573		
574		this.Method	= 'array';
575		
576		jQuery.ajax({
577			type		: 'POST', 
578			dataType	: 'text',
579			async		: self.opt.async,
580			context		: self,
581			url		: self.controller,
582			data		: self.sourceData,
583			success		: self.ajaxSuccess,
584			error		: self.ajaxError
585		}); //OBJECT jQuery.ajax.settings
586
587		return this;
588	}, //FUNC postArray
589
590	/** //** ----= postData	=--------------------------------------------------------------------------------------\**//** \
591	*
592	* 	For internal use. Sends data using formData object. Optionally uses xhr for progress monitoring.
593	*
594	*	@return	SELF 
595	*
596	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
597	postData	: function () {
598
599		var self	= this;
600		var post	= false;
601		var $form	= this.sourceForm ? _jq(this.sourceForm) : false; 
602		var $files	= false;
603
604		// Marking method as data
605		this.Method	= 'data';
606		
607		// Disabling empty files if happen on form
608		// Doing this to fix XHR multipart bug in some Safari versions
609		// https://bugs.webkit.org/show_bug.cgi?id=184490
610		
611		if ( $form ) {
612			
613			// Looking for non disabled file inputs
614			$files	= $form.find('input[type="file"]:not([disabled])');
615			$files.each( function () {
616			
617				// Disabling input
618				if ( this.files.length == 0 )
619					jQuery(this).prop('disabled', true);
620				
621			}); //each file
622			
623		} //IF form provided
624		
625		// Need to generate FormData object
626		// Creating FormData from form, if one given
627		// Or using one if one come as sourceData
628		// Otherwise generating one from dataObject
629		
630		if ( this.sourceData instanceof FormData ) {
631			post = this.sourceData
632		}
632 //IF sourceData is FormData
633		else if ( this.sourceForm ) {
634			post = new FormData(this.sourceForm);
635		} //IF form given
636		else {
637			post = new FormData();
638			for ( var $i in this.sourceData )
639				post.append($i, this.sourceData[$i]);
640		} //IF dataObject given
641
642		// Restoring disabled file inputs (see bugfix above)
643		if ( $files )
644			$files.prop('disabled', false);
645
646		// Sending ajax using custom XHR
647		jQuery.ajax({
648			type		: 'POST', 
649			dataType	: 'text',
650			async		: self.opt.async,
651			context		: self,
652			url		: self.controller,
653			data		: post,
654			success		: self.ajaxSuccess,
655			error		: self.ajaxError,
656			
657			// Disablong jQuery headers and source processing
658			contentType	: false, 
659			processData	: false,
660			
661			// Adding progress monitoring, upload listener will be added optionally if necessary
662			// With function wrapper to have proper this
663			xhr		: function () { return self.ajaxXHR() }
664		}); //OBJECT jQuery.ajax.settings
665		
666		return this;
667		
668	}, //FUNC postData
669
670	/** //** ----= postFrame	=------------------------------------------------------------------------------\**//** \
671	*
672	* 	For internal use. Sends data using iframe send trick. Used to simulate ajax upload on classic systems. 
673	*	Uses ajaxupload jQuery plugin if available. Uses server side progress monitoring if necessary.
674	*
675	*	@return	SELF 
676	*
677	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
678	postFrame	: function () {
679
680		var self = this;
681
682		this.Method = 'frame';
683		
684		if ( !self.sourceForm )
685			throw('Source form is not set.');
686			
687	// ---- Setting up progress ----
688	
689		// Saving test result, for later reuse
690		var prg = this.opt.progressUrl && this.hasProgress(); 
691	
692		if ( prg ) {
693	
694			var sn = 'PRG' + randomString();
695			
696			// Adding additional inputs for monitoring on server side
697			this.sourceElement.append('<input type="hidden" name="UPLOAD_IDENTIFIER" value="' + sn + '" />');
698			this.sourceElement.append('<input type="hidden" name="APC_UPLOAD_PROGRESS" value="' + sn + '" />');
699			
700			// Resetting filler
701			this.ajaxProgressStart();
702			
703		} //IF progress is set
704	
705	// ---- Posting ----	
706		
707		// Saving action and target, cuz ajaxUpload breaks them	sometimes	
708		var action = this.sourceForm.action;
709		var target = this.sourceForm.target;
710
711		jQuery.ajaxUpload ({
712			type		: 'POST', 
713			dataType	: 'text',
714			url		: self.controller,
715			secureuri	: false,
716			uploadform	: self.sourceForm,
717			// Simulating context with wrappers
718			success		: function ( res, status ) { self.ajaxSuccess(res, status) },
719			error		: function ( res, status ) { self.ajaxError(res, status) }
720		}); //AJAX
721		
722	// ---- Monitoring progress ----	
723
724		if ( prg ) {
725			
726			// Bar should start progressing with additional delay, to do not overflow on small files
727			self.prgTimeout = setTimeout( function () { 
728				
729				// Adding check timer
730				self.prgInterval = setInterval( function() {
731					
732					jQuery.get(self.opt.progressUrl + sn, {}, function(data) {
733
734						self.ajaxProgress(data, self);
735
736						// If got 100 or error - stopping timer
737						if ( data == '100' || !isNumeric(data) ) {
738							clearInterval(self.prgInterval);
739						} //IF done
740
741					}); //FUNC jQuery.get.callback
742					
743				}, self.opt.progressInterval); //FUNC setInterval.callback
744			}, self.opt.progressDelay);  //FUNC setTimeout.callback
745			
746			// Cleaning progress inputs
747			this.sourceElement.find('INPUT[name="APC_UPLOAD_PROGRESS"], INPUT[name="UPLOAD_IDENTIFIER"]').remove();
748		} //IF progress set
749		
750	// ---- Cleaning form ----
751		
752		this.sourceForm.action = action;
753		this.sourceForm.target = target;
754		
755		return this;
756	}, //FUNC postFrame
757
758	/** //** ----= post	=--------------------------------------------------------------------------------------\**//** \
759	*
760	* 	Fires request with settings given.
761	*
762	*	@return	SELF
763	*
764	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
765	post	: function () {
766
767	// ---- Checking source ----
768
769		var source 		= this.source;		// Just shortcut
770		
771		// Making sure that there is smth meaningfull
772		if ( !source )
773			source = {};
774
775		// Restting sources
776		this.sourceElement	= false;		// jQuery pointer to form element	
777		this.sourceForm		= false;		// DOM form element
778		this.sourceData		= source;		// By default assuming data object or formData
779
780		// Converting jQuery selector to jQuery
781		if ( typeof this.source == 'string' )
782			source = jQuery(this.source);
783			
784		// Getting DOM form from jQuery
785		if ( source instanceof jQuery ) {
786			
787			if ( source.length < 1 )
788				throw('Source form is not found.');
789			
790			// Saving jQuery
791			this.sourceElement = source;
792			source = source.get(0);
793		} //IF got jQuery
794
795		// At this step source should be either DOM form or data object
796		// Checking it and setting sources
797		// ToDo: tricky check for DOM form, improove
798		if ( source.elements ) {
799	
800			// Trying to correct url using form action
801			if ( !this.controller )
802				this.controller = source.action; 
803			
804			this.sourceForm = source;
805			
806			// Serializing form as array anyway for callbacks
807			this.sourceData = formToArray(source);
808
809			// Adding jQuery if not already there
810			if ( !this.sourceElement )
811				this.sourceElement = jQuery(source);
812			
813		} //If got form
814
815	// ---- Testing environment ----
816
817		// If still no controller (even after from action correction) - nothing to do
818		if ( !this.controller )
819			throw('Destination url is not set.');
820
821	// ---- Selecting method and sending ----
822	
823		// Triggering start event, passing self as param
824		this.start(this);
825	
826		// In most cases postData will be used
827		// Only reason to use tricky frame ajaxUpload is in old browser 
828		// and files really present on form
829		// Of course if ajaxUpload is available
830		// So checking it in complexity order i.e. easyer checks goes first 
831
832		// Checking if custom processor is given
833		if ( isFunction(this._processor) ) {
834			this._processor(this);
835			return this;
836		} //IF custom processor
837
838		// Form data is always served with postData
839		if ( this.isModern && this.sourceData instanceof FormData )
840			return this.postData();
841
842		// If form or files were not given uploading with common jQuery
843		// Non modern browser can't have files anyway
844		if ( !this.sourceForm && !this.hasFiles(this.sourceData) )
845			return this.postArray();
846
847		// If modern - using formData for all other cases
848		if ( this.isModern )
849			return this.postData();
850
851		// If form is not for files, and modern uploading is not available - uploading as array
852		if ( this.sourceForm.enctype != 'multipart/form-data' ) 
853			return this.postArray();
854
855		// Now looking for files on form (last chance to not use .postFrame() )
856		if ( !this.hasFiles(this.sourceElement) )
857			return this.postArray();
858
859		// Finally we tried everything, have to use ajaxUpload
860		// Testing if ajaxUpload even exists first
861		if ( !isSet(jQuery.ajaxUpload) )
862			throw('Can\'t upload files with ajax on this browser.');
863
864		return this.postFrame();
865	} //FUNC post
866
867	}); //OBJECT vAjax
868	
869	// Setting options on startup
870	obj.options(options);
871	
872	return obj;
873
874} //CONSTRUCTOR vAjax
875
876// #### DEPRECATED #############################################################################################################
877
878var ajaxReturn = function (status_place, destination) {
879	if (status_place) this.statusPlace = status_place;
880	if (destination) this.controller = destination;
881} //CONSTRUCTOR ajaxReturn
882
883ajaxReturn.prototype = {
884	controller	: '',
885	post		: {},
886	ModalDialog	: '',
887	ModalLoader	: '',
888	statusPlace	: '',
889	
890	onSuccess	: function (data) {},
891	onError		: function (data) {},
892	onFail		: function (data) {},
893	onSimple	: function (data) {},
894	onStart		: function () {},
895	onStatus	: function (html) {},
896	onProgress	: function (proc) {},
897	
898	parseResult	: function (result) {
899
900		var res		= {};
901		res._post	= this.post;
902
903		var data		= result.split('-=-CUT-=-');
904		res._message	= data[0];
905		if ( !res._message ) res._message = '';
906		
907		res._fullhead	= data.length != 1;
908		
909		if (res._fullhead) {
910			var head		= JSON.parse(data[1]);
911			res._major	= head.major;
912			res._minor	= head.minor;
913			res._status	= head.minor;
914			res._fields	= head.fields;
915//			res.data	= [];
916			var di = 2;
917			if ( res._fields.length != 0 ) {
918				for (i = 0; i < res._fields.length; i++) {
919					res[res._fields[i]] = data[di];
920					di++;
921				} //FOR each field
922			} //IF was fields posted
923		} //IF was full result
924		return res;
925	},//FUNC parseResult
926
927	/*--------------------------------------------------------------------------------*\
928	|	DoStatus
929	|----------------------------------------------------------------------------------
930	| Updates status.
931	| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
932	|	Params:
933	|		message		string		- Status message to display.
934	|		callback	funciton	- Funciton to call.
935	|		data		MIXED		- Data to pass to callback function.
936	|	Return:
937	|				bool		- ALways FALSE.
938	\*--------------------------------------------------------------= by Mr.V!T =-----*/
939	DoStatus	: function (message, callback, data) {
940		
941		if ( this.statusPlace && message ) jQuery('#'+this.statusPlace).html( status_string(message) );
942		
943		if ( callback instanceof Function ) callback(data);
944		
945		this.doModals('finish');
946		
947		return false;
948	}, //FUNC DoStatus
949
950	ajax_success	:  function (res) {
951		var data = this.parseResult(res);
952		
953		if (this.statusPlace) jQuery('#'+this.statusPlace).html(data._message);
954		
955		var cb = this.onSimple;
956		if ( data._fullhead ) 
957			cb = ( data._major )? this.onSuccess : this.onError;
958		
959		this.DoStatus('', cb, data);
960		
961	}, //FUNC sucess
962
963	ajax_error	: function (message, res) {
964		
965		this.DoStatus(message, this.onFail, res);
966		
967	}, //FUNC error
968
969	postForm	: function (form_id) {
970		var _ar = this;
971		
972		if (!_ar.controller) return this.DoStatus('Invalid destination. Aborting.');
973		
974		if ( !form_id ) return this.postData();
975		
976		var form = (form_id.elements)? form_id : document.getElementById(form_id);
977		if ( !form.elements ) return this.DoStatus('Can\'t find data to POST. Aborting.');
978		
979		if ( form.enctype != 'multipart/form-data' ) return this.postData( formToArray(form) );
980
981		//if ( _ar.statusPlace ) insert_bar(_ar.statusPlace);
982		
983		this.doModals('start');
984		
985		this.onStart();
986
987		var old_action = form.action;
988
989		jQuery.ajaxUpload ({
990			type		: 'POST', dataType : 'html',
991			url		: _ar.controller,
992			secureuri	: false,
993			uploadform	: form,
994			success		: function (res) {
995				_ar.ajax_success(res);
996			}, //FUNC success
997			error		: function (res, status) {
998				_ar.ajax_error(res.statusText, res.responseText);
999			} //FUNC error
1000		}); //AJAX
1001		
1002		form.action = old_action;
1003		form.target = '';
1004		
1005		return false;
1006	}, //FUNC postForm
1007
1008	postData	: function (post_data) {
1009		
1010		if ( !post_data ) post_data = {};
1011		
1012	//	post_data = arrayToPost(post_data);
1013		
1014		this.post = post_data;
1015		var _ar = this;
1016
1017		//if ( _ar.statusPlace ) insert_bar(_ar.statusPlace);
1018		
1019		this.doModals('start');
1020		
1021		if (!_ar.controller) return this.DoStatus('Invalid destination. Aborting.');
1022
1023		this.onStart();
1024
1025		jQuery.ajax({
1026			type	: 'POST', dataType: 'html',
1027			url	: _ar.controller,
1028			data	: post_data,
1029			success		: function (res) {
1030				_ar.ajax_success(res);
1031			}, //FUNC success
1032			error		: function (res, status) {
1033				_ar.ajax_error(res.statusText, res.responseText);
1034			} //FUNC error
1035		}); //AJAX
1036		
1037		return false;
1038	}, //FUNC postData
1039
1040	postFormData	: function (post_data) {
1041		
1042		this.post	= post_data;
1043		var _ar		= this;
1044		var self	= this;
1045
1046		this.doModals('start');
1047		
1048		if (!_ar.controller) return this.DoStatus('Invalid destination. Aborting.');
1049
1050		this.onStart();
1051
1052		jQuery.ajax({
1053			type		: 'POST', dataType: 'html',
1054			url		: _ar.controller,
1055			data		: post_data,
1056			contentType	: false,
1057			processData	: false,
1058
1059			success		: function (res) {
1060				_ar.ajax_success(res);
1061			}, //FUNC success
1062			
1063			error		: function (res,status) {
1064				_ar.ajax_error(res.statusText, res.responseText);
1065			}, //FUNC error
1066
1067			// Attempting to alter xhr to add progress listener			
1068			xhr		: function() {
1069				
1070				var req		= $.ajaxSettings.xhr();
1071				
1072				if (req) {
1073					
1074					req.upload.addEventListener('progress', function(e) {
1075
1076						if ( e.lengthComputable ) {
1077							var proc = (e.loaded * 100) / e.total;
1078							
1079							self.onProgress( Math.floor(proc) );
1080							
1081						} //IF there is progress available
1082
1083					}, false); //FUNC XHR.upload.onProgress
1084				
1085				} //IF xhr returned
1086				
1087				return req;
1088			} //FUNC xhr
1089			
1090		}); //AJAX
1091		
1092		return false;
1093	}, //FUNC postFormData
1094	
1095	/*--------------------------------------------------------------------------------*\need
1096	|	post
1097	|----------------------------------------------------------------------------------
1098	| Autoselects best post method and posts source.
1099	| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1100	|	Params:
1101	|		source	MIXED	- Data source.
1102	|	Return:
1103	|			bool	- Always FALSE.
1104	\*--------------------------------------------------------------= by Mr.V!T =-----*/
1105	post	: function (source) {
1106		
1107		if ( typeof source == 'string' ) return this.postForm(source);
1108		if ( source.elements ) return this.postForm(source);
1109
1110		if ( source instanceof FormData ) return this.postFormData(source);
1111
1112		return this.postData(source);
1113	}, //FUNC post
1114
1115	/*--------------------------------------------------------------------------------*\
1116	|	setModals
1117	|----------------------------------------------------------------------------------
1118	| Set modal windows parameters.
1119	| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1120	|	Params:
1121	|		dialog	string	- ID of window to use as Dialog.
1122	|		loader	string	- ID of window to use as loader window.
1123	|	Return:
1124	|			SELF
1125	\*--------------------------------------------------------------= by Mr.V!T =-----*/
1126	setModals	: function (dialog, loader) {
1127		
1128		this.ModalDialog = dialog;
1129		
1130		if ( !loader ) return this;
1131		
1132		tmp = document.getElementById(loader);
1133		if ( !tmp ) return this;
1134		
1135		this.ModalLoader = loader;
1136		
1137		return this;
1138	}, //FUNC setModals
1139
1140	/*--------------------------------------------------------------------------------*\
1141	|	doModals
1142	|----------------------------------------------------------------------------------
1143	| Displays or hides modals windows.
1144	| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1145	|	Params:
1146	|		mode	string	- Status mode:
1147	|				  'start' - Set starting mode.
1148	|				  *other* - Finished mode.
1149	|	Return:
1150	|		NULL
1151	\*--------------------------------------------------------------= by Mr.V!T =-----*/
1152	doModals	: function ( mode ) {
1153		
1154		if ( mode == 'start' ) {
1155			if ( this.ModalLoader ) { 
1156				jQuery('#'+this.ModalDialog).jqm();
1157				jQuery('#'+this.ModalDialog).jqmHide();
1158				jqmShowCentered('#'+this.ModalLoader);
1159			} //IF loader is set
1160			
1161			if ( this.ModalDialog && !this.ModalLoader ) jqmShowCentered('#'+this.ModalDialog);
1162		} else { //IF
1163			if ( this.ModalLoader ) jQuery('#'+this.ModalLoader).jqmHide();
1164			if ( this.ModalDialog ) jqmShowCentered('#'+this.ModalDialog);
1165		} //IF
1166		
1167	} //FUNC doModals
1168
1169} //CLASS ajaxReturn

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.