1 /**
  2  * @file AJAX requests
  3  *
  4  * @author Dominik Kocuj
  5  * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2 or later
  6  * @copyright Copyright (c) 2016-2018 kocuj.pl
  7  */
  8 
  9 (function() {})(); // empty function for correct minify with comments
 10 //'use strict'; // for jshint uncomment this and comment line above
 11 
 12 /* jshint strict: true */
 13 /* jshint -W034 */
 14 
 15 /* global document */
 16 /* global jQuery */
 17 /* global window */
 18 
 19 /* global ajaxurl */
 20 
 21 /* global kocujILV12aHelper */
 22 
 23 /* global kocujILV12aAllJsAjaxVals */
 24 
 25 /**
 26  * AJAX prototype constructor
 27  *
 28  * @constructs
 29  * @namespace kocujILV12aCAllJsAjax
 30  * @public
 31  * @return {void}
 32  */
 33 function kocujILV12aCAllJsAjax() {
 34 	'use strict';
 35 	/* jshint validthis: true */
 36 	// get this object
 37 	var self = this;
 38 	// initialize objects
 39 	self._objHelper = kocujILV12aHelper;
 40 	// get current script filename
 41 	self._thisFilename = document.scripts[document.scripts.length-1].src;
 42 	// get settings
 43 	var vals = kocujILV12aAllJsAjaxVals;
 44 	if (vals.throwErrors === '1') {
 45 		self._valsThrowErrors = true;
 46 	} else {
 47 		self._valsThrowErrors = false;
 48 	}
 49 	self._valsPrefix = vals.prefix;
 50 	self._valsSecurity = vals.security;
 51 	if (vals.canUseProxy === '1') {
 52 		self._valsCanUseProxy = true;
 53 	} else {
 54 		self._valsCanUseProxy = false;
 55 	}
 56 	self._valsAjaxUrl = vals.ajaxUrl;
 57 }
 58 
 59 /**
 60  * AJAX prototype
 61  *
 62  * @namespace kocujILV12aCAllJsAjax
 63  * @public
 64  */
 65 kocujILV12aCAllJsAjax.prototype = {
 66 	/**
 67 	 * Object kocujILV12aHelper
 68 	 *
 69 	 * @private
 70 	 * @type {Object}
 71 	 */
 72 	_objHelper : null,
 73 
 74 	/**
 75 	 * Current script filename
 76 	 *
 77 	 * @private
 78 	 * @type {string}
 79 	 */
 80 	_thisFilename : '',
 81 
 82 	/**
 83 	 * Projects list
 84 	 *
 85 	 * @private
 86 	 * @type {Array}
 87 	 */
 88 	_prj : [],
 89 
 90 	/**
 91 	 * Script settings - throw errors (true) or not (false)
 92 	 *
 93 	 * @private
 94 	 * @type {string}
 95 	 */
 96 	_valsThrowErrors : false,
 97 
 98 	/**
 99 	 * Script settings - prefix
100 	 *
101 	 * @private
102 	 * @type {string}
103 	 */
104 	_valsPrefix : '',
105 
106 	/**
107 	 * Script settings - security string
108 	 *
109 	 * @private
110 	 * @type {string}
111 	 */
112 	_valsSecurity : '',
113 
114 	/**
115 	 * Script settings - proxy can be used (true) or not (false)
116 	 *
117 	 * @private
118 	 * @type {boolean}
119 	 */
120 	_valsCanUseProxy : true,
121 
122 	/**
123 	 * Script settings - AJAX URL
124 	 *
125 	 * @private
126 	 * @type {string}
127 	 */
128 	_valsAjaxUrl : '',
129 
130 	/**
131 	 * Add project
132 	 *
133 	 * @public
134 	 * @param {string} projectId Project identifier
135 	 * @return {void}
136 	 * @throws {kocujILV12aCException} kocujILV12aExceptionCode.PROJECT_ALREADY_EXISTS if project identifier entered in projectId already exists
137 	 */
138 	addProject : function(projectId) {
139 		'use strict';
140 		// parse arguments
141 		var args = this._checkAddProject(projectId);
142 		// add project
143 		if (this._prj['prj_' + args.projectId] === undefined) {
144 			this.addProjectIfNotExists(args.projectId);
145 		} else {
146 			this._throwError('PROJECT_ALREADY_EXISTS', args.projectId);
147 			return;
148 		}
149 	},
150 
151 	/**
152 	 * Add project if not exists
153 	 *
154 	 * @public
155 	 * @param {string} projectId Project identifier
156 	 * @return {void}
157 	 */
158 	addProjectIfNotExists : function(projectId) {
159 		'use strict';
160 		// parse arguments
161 		var args = this._checkAddProject(projectId);
162 		// add project
163 		if (this._prj['prj_' + args.projectId] === undefined) {
164 			this._prj['prj_' + args.projectId] = [];
165 		}
166 	},
167 
168 	/**
169 	 * Send AJAX request with GET method
170 	 *
171 	 * @public
172 	 * @param {string} projectId Project identifier
173 	 * @param {string} connectionId Connection identifier
174 	 * @param {string} url URL
175 	 * @param {string} dataType Data type
176 	 * @param {Object} [data] Data to send
177 	 * @param {Object} [callbacks] Callback functions for AJAX request; there are the following callbacks available: error (when there was an error with request), retryWait (when there will be a retring of connection after a few seconds), retryNow (when there will be a retring of connection now), success (when there was a success)
178 	 * @return {void}
179 	 */
180 	sendGet : function(projectId, connectionId, url, dataType, data, callbacks) {
181 		'use strict';
182 		// send AJAX
183 		this.send(projectId, connectionId, url, 'GET', dataType, data, callbacks);
184 	},
185 
186 	/**
187 	 * Send AJAX request with POST method
188 	 *
189 	 * @public
190 	 * @param {string} projectId Project identifier
191 	 * @param {string} connectionId Connection identifier
192 	 * @param {string} url URL
193 	 * @param {string} dataType Data type
194 	 * @param {Object} [data] Data to send
195 	 * @param {Object} [callbacks] Callback functions for AJAX request; there are the following callbacks available: error (when there was an error with request), retryWait (when there will be a retring of connection after a few seconds), retryNow (when there will be a retring of connection now), success (when there was a success)
196 	 * @return {void}
197 	 */
198 	sendPost : function(projectId, connectionId, url, dataType, data, callbacks) {
199 		'use strict';
200 		// send AJAX
201 		this.send(projectId, connectionId, url, 'POST', dataType, data, callbacks);
202 	},
203 
204 	/**
205 	 * Send AJAX request with JSON data
206 	 *
207 	 * @public
208 	 * @param {string} projectId Project identifier
209 	 * @param {string} connectionId Connection identifier
210 	 * @param {string} url URL
211 	 * @param {Object} [data] Data to send
212 	 * @param {Object} [callbacks] Callback functions for AJAX request; there are the following callbacks available: error (when there was an error with request), retryWait (when there will be a retring of connection after a few seconds), retryNow (when there will be a retring of connection now), success (when there was a success)
213 	 * @return {void}
214 	 */
215 	sendJson : function(projectId, connectionId, url, data, callbacks) {
216 		'use strict';
217 		// send AJAX
218 		this.send(projectId, connectionId, url, 'GET', 'jsonp', data, callbacks);
219 	},
220 
221 	/**
222 	 * Send AJAX request
223 	 *
224 	 * @public
225 	 * @param {string} projectId Project identifier
226 	 * @param {string} connectionId Connection identifier
227 	 * @param {string} url URL
228 	 * @param {string} method Request method
229 	 * @param {string} dataType Data type
230 	 * @param {Object} [data] Data to send
231 	 * @param {Object} [callbacks] Callback functions for AJAX request; there are the following callbacks available: error (when there was an error with request), retryWait (when there will be a retring of connection after a few seconds), retryNow (when there will be a retring of connection now), success (when there was a success)
232 	 * @return {void}
233 	 */
234 	send : function(projectId, connectionId, url, method, dataType, data, callbacks) {
235 		'use strict';
236 		// get this object
237 		var self = this;
238 		(function($) {
239 			// parse arguments
240 			projectId = self._parseProjectId(projectId);
241 			connectionId = self._objHelper.initString(connectionId);
242 			url = self._objHelper.initString(url);
243 			if (url === '') {
244 				self._throwError('JS_AJAX_EMPTY_URL');
245 				return;
246 			}
247 			method = self._objHelper.initString(method);
248 			if (method === '') {
249 				self._throwError('JS_AJAX_EMPTY_METHOD');
250 				return;
251 			}
252 			dataType = self._objHelper.initString(dataType);
253 			if (dataType === '') {
254 				self._throwError('JS_AJAX_EMPTY_DATA_TYPE');
255 				return;
256 			}
257 			data = self._objHelper.initObject(data);
258 			if (data === '') {
259 				data = {};
260 			}
261 			callbacks = self._objHelper.initObject(callbacks);
262 			if (callbacks === '') {
263 				callbacks = {};
264 			}
265 			if (callbacks.error === undefined) {
266 				callbacks.error = false;
267 			}
268 			if (callbacks.retryWait === undefined) {
269 				callbacks.retryWait = false;
270 			}
271 			if (callbacks.retryNow === undefined) {
272 				callbacks.retryNow = false;
273 			}
274 			if (callbacks.success === undefined) {
275 				callbacks.success = false;
276 			}
277 			// optionally clear retries timer
278 			if ((typeof self._prj['prj_' + projectId]['con_' + connectionId] !== 'undefined') && (self._prj['prj_' + projectId]['con_' + connectionId].retries > 0)) {
279 				self._deactivateTimer(projectId, connectionId);
280 			}
281 			// initialize connection data
282 			if (typeof self._prj['prj_' + projectId]['con_' + connectionId] === 'undefined') {
283 				self._prj['prj_' + projectId]['con_' + connectionId] = {
284 					timer   : null,
285 					retries : 0
286 				};
287 			}
288 			// optionally change AJAX URL from relative to absolute
289 			if ((typeof ajaxurl !== 'undefined') && (url === ajaxurl)) {
290 				url = self._valsAjaxUrl;
291 			}
292 			// optionally set proxy
293 			var isProxy = false;
294 			if ((self._valsCanUseProxy) && (url !== self._valsAjaxUrl)) {
295 				// add proxy settings to data
296 				data._kocuj_internal_lib_proxy_url = url;
297 				data._kocuj_internal_lib_proxy_security = self._valsSecurity;
298 				if (typeof data.action !== 'undefined') {
299 					data._kocuj_internal_lib_proxy_old_action = data.action;
300 				} else {
301 					data._kocuj_internal_lib_proxy_old_action = '';
302 				}
303 				data.action = self._valsPrefix + '_' + projectId + '__js_ajax';
304 				// change url
305 				url = self._valsAjaxUrl;
306 				// set proxy flag
307 				isProxy = true;
308 			}
309 			// optionally set timeout if this request is to different server
310 			if (url !== self._valsAjaxUrl) {
311 				// deactivate timer
312 				self._deactivateTimer(projectId, connectionId);
313 				// set timer
314 				self._prj['prj_' + projectId]['con_' + connectionId].timer = window.setTimeout(function() {
315 					self._error(projectId, connectionId, isProxy, callbacks.error, callbacks.retryWait, callbacks.retryNow, null, 'timeout', '');
316 				}, 30000);
317 			}
318 			// send AJAX request
319 			$.ajax({
320 				url           : url,
321 				async         : true,
322 				cache         : false,
323 				data          : data,
324 				dataType      : dataType,
325 				error         : function(obj, status, err) {
326 					self._error(projectId, connectionId, isProxy, callbacks.error, callbacks.retryWait, callbacks.retryNow, new Array(
327 						projectId,
328 						connectionId,
329 						url,
330 						method,
331 						dataType,
332 						data,
333 						callbacks
334 					), obj, status, err);
335 				},
336 				success       : function(data, status, obj) {
337 					self._success(projectId, connectionId, isProxy, callbacks.success, data, status, obj);
338 				},
339 				timeout       : 30000,
340 				type          : method
341 			});
342 		}(jQuery));
343 	},
344 
345 	/**
346 	 * Deactivate timer
347 	 *
348 	 * @private
349 	 * @param {string} projectId Project identifier
350 	 * @param {string} connectionId Connection identifier
351 	 * @return {void}
352 	 */
353 	_deactivateTimer : function(projectId, connectionId) {
354 		'use strict';
355 		// clear timer
356 		if (this._prj['prj_' + projectId]['con_' + connectionId].timer !== null) {
357 			window.clearTimeout(this._prj['prj_' + projectId]['con_' + connectionId].timer);
358 			this._prj['prj_' + projectId]['con_' + connectionId].timer = null;
359 		}
360 	},
361 
362 	/**
363 	 * AJAX loading success
364 	 *
365 	 * @private
366 	 * @param {string} projectId Project identifier
367 	 * @param {string} connectionId Connection identifier
368 	 * @param {boolean} isProxy AJAX has been sent through proxy (true) or directly (false)
369 	 * @param {function} callbackSuccess Callback function for AJAX success
370 	 * @param {anything} data Data
371 	 * @param {string} status Text status
372 	 * @param {Object} obj Request object
373 	 * @return {void}
374 	 */
375 	_success : function(projectId, connectionId, isProxy, callbackSuccess, data, status, obj) {
376 		'use strict';
377 		// get this object
378 		var self = this;
379 		(function($) {
380 			// parse arguments
381 			status = self._objHelper.initString(status);
382 			obj = self._objHelper.initObject(obj);
383 			if (obj === '') {
384 				obj = null;
385 			}
386 			// clear retries
387 			self._prj['prj_' + projectId]['con_' + connectionId].retries = 0;
388 			// optionally deactivate AJAX timeout
389 			if (isProxy) {
390 				self._deactivateTimer(projectId, connectionId);
391 			}
392 			// optionally execute callback function
393 			if (callbackSuccess !== false) {
394 				callbackSuccess(projectId, connectionId, data, status, obj);
395 			}
396 		}(jQuery));
397 	},
398 
399 	/**
400 	 * AJAX loading error
401 	 *
402 	 * @private
403 	 * @param {string} projectId Project identifier
404 	 * @param {string} connectionId Connection identifier
405 	 * @param {boolean} isProxy AJAX has been sent through proxy (true) or directly (false)
406 	 * @param {function} callbackError Callback function for AJAX error
407 	 * @param {function} callbackRetryWait Callback function for doing something before waiting for AJAX request retry
408 	 * @param {function} callbackRetryNow Callback function for doing something at the beginning of AJAX request retry
409 	 * @param {Array} sendMethodArguments Argument for method send() which are used when there is trying to connect again
410 	 * @param {Object} obj Request object
411 	 * @param {string} status Text status
412 	 * @param {string} err Error
413 	 * @return {void}
414 	 */
415 	_error : function(projectId, connectionId, isProxy, callbackError, callbackRetryWait, callbackRetryNow, sendMethodArguments, obj, status, err) {
416 		'use strict';
417 		// get this object
418 		var self = this;
419 		(function($) {
420 			// parse arguments
421 			obj = self._objHelper.initObject(obj);
422 			if (obj === '') {
423 				obj = null;
424 			}
425 			status = self._objHelper.initString(status);
426 			err = self._objHelper.initString(err);
427 			// optionally deactivate AJAX timeout
428 			if (isProxy) {
429 				self._deactivateTimer(projectId, connectionId);
430 			}
431 			// optionally retry connection
432 			++self._prj['prj_' + projectId]['con_' + connectionId].retries;
433 			if (self._prj['prj_' + projectId]['con_' + connectionId].retries < 3) {
434 				if (callbackRetryWait !== false) {
435 					callbackRetryWait(projectId, connectionId, self._prj['prj_' + projectId]['con_' + connectionId].retries, obj, status, err);
436 				}
437 				self._prj['prj_' + projectId]['con_' + connectionId].timer = window.setTimeout(function() {
438 					if (callbackRetryNow !== false) {
439 						callbackRetryNow(projectId, connectionId, self._prj['prj_' + projectId]['con_' + connectionId].retries, obj, status, err);
440 					}
441 					self.send(sendMethodArguments[0], sendMethodArguments[1], sendMethodArguments[2], sendMethodArguments[3], sendMethodArguments[4], sendMethodArguments[5], sendMethodArguments[6]);
442 				}, 5000);
443 				return;
444 			}
445 			// clear retries
446 			self._prj['prj_' + projectId]['con_' + connectionId].retries = 0;
447 			// optionally execute callback function
448 			if (callbackError !== false) {
449 				callbackError(projectId, connectionId, obj, status, err);
450 			}
451 		}(jQuery));
452 	},
453 
454 	/**
455 	 * Parse project identifier
456 	 *
457 	 * @private
458 	 * @param {string} projectId Project identifier
459 	 * @return {string} Parsed project identifier
460 	 * @throws {kocujILV12aCException} kocujILV12aExceptionCode.EMPTY_PROJECT_ID if project identifier entered in projectId is empty
461 	 * @throws {kocujILV12aCException} kocujILV12aExceptionCode.PROJECT_DOES_NOT_EXIST if project identifier entered in projectId does not exist
462 	 */
463 	_parseProjectId : function(projectId) {
464 		'use strict';
465 		// parse project identifier
466 		projectId = this._objHelper.initString(projectId);
467 		if (projectId === '') {
468 			this._throwError('EMPTY_PROJECT_ID');
469 			return;
470 		}
471 		// check if project exists
472 		if (this._prj['prj_' + projectId] === undefined) {
473 			this._throwError('PROJECT_DOES_NOT_EXIST', projectId);
474 			return;
475 		}
476 		// exit
477 		return projectId;
478 	},
479 
480 	/**
481 	 * Check arguments for adding project
482 	 *
483 	 * @private
484 	 * @param {string} projectId Project identifier
485 	 * @return {Object} Parsed arguments for adding project
486 	 * @throws {kocujILV12aCException} kocujILV12aExceptionCode.EMPTY_PROJECT_ID if project identifier entered in projectId is empty
487 	 */
488 	_checkAddProject : function(projectId) {
489 		'use strict';
490 		// parse arguments
491 		projectId = this._objHelper.initString(projectId);
492 		if (projectId === '') {
493 			this._throwError('EMPTY_PROJECT_ID');
494 			return;
495 		}
496 		// exit
497 		return {
498 			projectId : projectId
499 		};
500 	},
501 
502 	/**
503 	 * Throw an error if debugging is enabled
504 	 *
505 	 * @private
506 	 * @param {string} codeString Error code in string format
507 	 * @param {string} [param] Parameter for error information
508 	 * @return {void}
509 	 */
510 	_throwError : function(codeString, param) {
511 		'use strict';
512 		// parse arguments
513 		codeString = this._objHelper.initString(codeString);
514 		if (codeString === '') {
515 			return;
516 		}
517 		param = this._objHelper.initString(param);
518 		// throw an error
519 		if (this._valsThrowErrors) {
520 			/* jshint evil: true */
521 			eval('throw new kocujILV12aCException(kocujILV12aExceptionCode.' + codeString + ', this._thisFilename, param);');
522 		}
523 	}
524 };
525 
526 // initialize
527 var kocujILV12aAllJsAjax = new kocujILV12aCAllJsAjax();
528