1 /*
  2  * SIMPLICITE - runtime & framework
  3  * http://www.simplicite.fr
  4  * Copyright (c)2006-2013 Simplicite Software. All rights reserved.
  5  */
  6 
  7 // Simplicite is defined when using generic web UI but must be defined for standalone usage
  8 if (window["Simplicite"] === undefined) var Simplicite = {};
  9 
 10 /**
 11  * Simplicité® application.
 12  * <br/>Sample usage:
 13  * <pre>
 14  * // Using authenticated user thru UI gateway
 15  * var app = new Simplicite.Ajax("myapp");
 16  * // Using public user thru public UI gateway
 17  * var app = new Simplicite.Ajax("myapp", "uipublic");
 18  * // Using website user thru WS gateway
 19  * var app = new Simplicite.Ajax("myapp", "ws", "myuser", "mypassword");
 20  * // Using website user thru API gateway
 21  * var app = new Simplicite.Ajax("myapp", "api", "myuser", "mypassword");
 22  * </pre>
 23  * @param approot Application root (either application name or /<context root> or absolute base URL)
 24  * @param gateway Gateway type to use :
 25  * <ul>
 26  * <li>1 or "ui": Authenticated UI gateway (default)</li>
 27  * <li>2 or "uipublic" : Public UI gateway</li>
 28  * <li>3 or "ws" : Webservices gateway</li>
 29  * </ul>
 30  * @param login User's login (not required for UI gateways)
 31  * @param password User's password (not required for UI gateways)
 32  * @param async Asynchronous calls by default (true if absent) ?
 33  * @class
 34  */
 35 Simplicite.Ajax = function(approot, gateway, login, password, async) {
 36 
 37 	/** @ignore */
 38 	this._errorActive = true;
 39 	this.error = function(err) {
 40 		if (this._errorActive == true) {
 41 			var m = "ERROR" + (err.level !== undefined ? " (" + err.level + ")" : "") + ": " + this.getErrorMessage(err);
 42 			try { console.error(m); } catch(e) { alert(m); }
 43 			try { if (err.message.stack) console.error(err.message.stack); } catch(e) {}
 44 		}
 45 	};
 46 
 47 	/** @ignore */
 48 	this._warningActive = true;
 49 	this.warning = function(msg) {
 50 		if (this._warningActive == true) {
 51 			var m = "WARNING: " + msg;
 52 			try { console.warn(m); } catch (e) { console.log(m); }
 53 		}
 54 	};
 55 
 56 	/** @ignore */
 57 	this._infoActive = true;
 58 	this.info = function(msg) {
 59 		if (this._infoActive == true) {
 60 			try { console.log("INFO: " + msg); } catch(e) {}
 61 		}
 62 	};
 63 
 64 	/** @ignore */
 65 	this._debugActive = false;
 66 	this.debug = function(msg) {
 67 		if (this._debugActive == true) {
 68 			var m = "DEBUG: " + msg;
 69 			try { console.debug(m); } catch (e) { console.log(m); }
 70 		}
 71 	};
 72 
 73 	/** @ignore */
 74 	this._approot = "";
 75 	/** @ignore */
 76 	this._baseURL = "";
 77 
 78 	if (approot !== undefined && (approot.substring(0, 4) === "http" || approot.substring(0, 2) === "//")) {
 79 		this._baseURL = approot;
 80 	} else {
 81 		this._approot = approot === undefined  ? (Simplicite.ROOT === undefined ? (Simplicite.APPLICATION === undefined ? "__unknown__" : "/" + Simplicite.APPLICATION) : Simplicite.ROOT) : approot;
 82 		if (this._approot != "" && this._approot.charAt(0) != "/") this._approot = "/" + this._approot;
 83 
 84 		this._baseURL = window.location.protocol + "//" + window.location.hostname + (window.location.port != "" ? ":" + window.location.port : "");
 85 		this.debug("Base URL: " + this._baseURL + ", application root: " + this._approot + ", login: " + this._login + ", asynchronous: " + this._async);
 86 	}
 87 
 88 	/** @ignore */
 89 	this._gateway = undefined;
 90 	/** @ignore */
 91 	this._appURL = undefined;
 92 	/** @ignore */
 93 	this._objURL = undefined;
 94 	/** @ignore */
 95 	this._pcsURL = undefined;
 96 	/** @ignore */
 97 	this._documentURL = undefined;
 98 	/** @ignore */
 99 	this._contentURL = undefined;
100 	/** @ignore */
101 	this._resourceURL = undefined;
102 	/** @ignore */
103 	this._login = undefined;
104 	/** @ignore */
105 	this._password = undefined;
106 
107 	this.GATEWAY_UI = "ui";
108 	this.GATEWAY_UI_PUBLIC = "uipublic";
109 	this.GATEWAY_API = "api";
110 	this.GATEWAY_WS = "ws";
111 
112 	/** @ignore */
113 	this._version = 3;
114 
115 	this.setGateway = function(gateway, login, password) {
116 		if (!gateway || gateway == 1 || gateway == this.GATEWAY_UI) {
117 			this._gateway = this.GATEWAY_UI;
118 			if (this._version < 3) {
119 				this._appURL = "/jsp/SYS_json.jsp";
120 				this._objURL = "/jsp/ALL_json.jsp";
121 				this._pcsURL = "/jsp/PCS_json.jsp";
122 				this._contentURL  = "/PUB_content.jsp";
123 				this._resourceURL = "/PUB_resource.jsp";
124 			} else {
125 				var r = "/" + (this._version < 4 ? "jsp" : "ui")
126 				this._appURL = r + "/json/app";
127 				this._objURL = r + "/json/obj";
128 				this._pcsURL = r + "/json/pcs";
129 				this._contentURL  = "/content";
130 				this._resourceURL = "/resource";
131 			}
132 			this._documentURL = "/jsp/ALL_docblob.jsp";
133 			this._login = undefined;
134 			this._password = undefined;
135 			this.debug("Using UI gateway");
136 		} else if (gateway == 2 || gateway == this.GATEWAY_UI_PUBLIC) {
137 			this._gateway = this.GATEWAY_UI_PUBLIC;
138 			if (this._version < 3) {
139 				this._appURL = "/PUB_json_app.jsp";
140 				this._objURL = "/PUB_json_obj.jsp";
141 				this._pcsURL = "/PUB_json_pcs.jsp";
142 				this._contentURL  = "/PUB_content.jsp";
143 				this._resourceURL = "/PUB_resource.jsp";
144 			} else {
145 				this._appURL = "/json/app";
146 				this._objURL = "/json/obj";
147 				this._pcsURL = "/json/pcs";
148 				this._contentURL  = "/content";
149 				this._resourceURL = "/resource";
150 			}
151 			this._documentURL = "/PUB_document.jsp";
152 			this._login = undefined;
153 			this._password = undefined;
154 			this.debug("Using public UI gateway");
155 		} else if (gateway == 3 || gateway == this.GATEWAY_WS) {
156 			this._gateway = this.GATEWAY_WS;
157 			if (this._version < 3) {
158 				this._appURL = "/jsonappservice.jsp";
159 				this._objURL = "/jsonservice.jsp";
160 				this._pcsURL = "/jsonpcsservice.jsp";
161 				this._contentURL  = "/rawcontentservice.jsp";
162 				this._resourceURL = "/rawresourceservice.jsp";
163 				this._documentURL = "/rawdbdocservice.jsp";
164 			} else {
165 				this._appURL = "/json/app";
166 				this._objURL = "/json/obj";
167 				this._pcsURL = "/json/pcs";
168 				this._contentURL  = "/raw/content";
169 				this._resourceURL = "/raw/resource";
170 				this._documentURL = "/raw/dbdoc";
171 			}
172 			this._login = login;
173 			this._password = password;
174 			this.debug("Using WS gateway with login " + login);
175 		} else if (this._version >= 3 && gateway == this.GATEWAY_API) {
176 			this._gateway = this.GATEWAY_API;
177 			this._appURL = "/api/json/app";
178 			this._objURL = "/api/json/obj";
179 			this._pcsURL = "/api/json/pcs";
180 			this._contentURL  = "/api/raw/content";
181 			this._resourceURL = "/api/raw/resource";
182 			this._documentURL = "/api/raw/dbdoc";
183 			this._login = login;
184 			this._password = password;
185 			this.debug("Using API gateway with login " + login);
186 		} else
187 			this.error("Unknown gateway: " + gateway);
188 	};
189 	this.setGateway(gateway, login, password);
190 	this.getGateway = function() { return this._gateway; };
191 
192 	/** @ignore */
193 	this._async = true;
194 	this.setAsync = function(async) {
195 		if (async !== undefined) {
196 			this._async = async;
197 			this.debug("Async mode set to " + async);
198 		}
199 	};
200 	this.setAsync(async);
201 	this.getAsync = function() { return this._async; };
202 
203 	/** @ignore */
204 	this._timeout = 0;
205 	this.setTimeout = function(timeout) {
206 		if (timeout !== undefined) {
207 			this._timeout = timeout;
208 			this.debug("Timeout set to " + timeout);
209 		}
210 	};
211 	this.getTimeout = function() { return this._timeout; };
212 
213 	this.isWinIE = function() { return navigator.userAgent.indexOf("MSIE")!=-1 && navigator.userAgent.indexOf("Win") != -1; };
214 	this.winIEVersion = function() { return parseInt(navigator.appVersion.split("MSIE")[1]); };
215 	this.isFirefox = function() { return navigator.userAgent.toLowerCase().indexOf("firefox") != -1; };
216 	this.isWebkit = function() { return navigator.userAgent.toLowerCase().indexOf("webkit") != -1; };
217 
218 	/**
219 	 * Default row ID value (for creation).
220 	 * @constant
221 	 */
222 	this.DEFAULT_ROW_ID = "0";
223 
224 	/**
225 	 * No context.
226 	 * @constant
227 	 */
228 	this.CONTEXT_NONE = 0;
229 	/**
230 	 * Search context.
231 	 * @constant
232 	 */
233 	this.CONTEXT_SEARCH = 1;
234 	/**
235 	 * List context.
236 	 * @constant
237 	 */
238 	this.CONTEXT_LIST = 2;
239 	/**
240 	 * Creation context.
241 	 * @constant
242 	 */
243 	this.CONTEXT_CREATE = 3;
244 	/**
245 	 * Copy context.
246 	 * @constant
247 	 */
248 	this.CONTEXT_COPY = 4;
249 	/**
250 	 * Update context.
251 	 * @constant
252 	 */
253 	this.CONTEXT_UPDATE = 5;
254 	/**
255 	 * Delete context.
256 	 * @constant
257 	 */
258 	this.CONTEXT_DELETE = 6;
259 	/**
260 	 * Chart context.
261 	 * @constant
262 	 */
263 	this.CONTEXT_GRAPH = 7;
264 	/**
265 	 * Cross table context.
266 	 * @constant
267 	 */
268 	this.CONTEXT_CROSSTAB = 8;
269 	/**
270 	 * Publication template context.
271 	 * @constant
272 	 */
273 	this.CONTEXT_PRINTTMPL = 9;
274 	/**
275 	 * Bulk update context.
276 	 * @constant
277 	 */
278 	this.CONTEXT_UPDATEALL = 10;
279 	/**
280 	 * Reference selection context.
281 	 * @constant
282 	 */
283 	this.CONTEXT_REFSELECT = 11;
284 	/**
285 	 * Data mapping selection context.
286 	 * @constant
287 	 */
288 	this.CONTEXT_DATAMAPSELECT = 12;
289 	/**
290 	 * Pre-validate context.
291 	 * @constant
292 	 */
293 	this.CONTEXT_PREVALIDATE = 13;
294 	/**
295 	 * Post validate context.
296 	 * @constant
297 	 */
298 	this.CONTEXT_POSTVALIDATE = 14;
299 	/**
300 	 * State transition context.
301 	 * @constant
302 	 */
303 	this.CONTEXT_STATETRANSITION = 15;
304 	/**
305 	 * Export context.
306 	 * @constant
307 	 */
308 	this.CONTEXT_EXPORT = 16;
309 	/**
310 	 * Import context.
311 	 * @constant
312 	 */
313 	this.CONTEXT_IMPORT = 17;
314 	/**
315 	 * Association context.
316 	 * @constant
317 	 */
318 	this.CONTEXT_ASSOCIATE = 18;
319 	/**
320 	 * Panel list context.
321 	 * @constant
322 	 */
323 	this.CONTEXT_PANELLIST = 19;
324 	/**
325 	 * Action context.
326 	 * @constant
327 	 */
328 	this.CONTEXT_ACTION = 20;
329 	/**
330 	 * Agenda context.
331 	 * @constant
332 	 */
333 	this.CONTEXT_AGENDA = 21;
334 	/**
335 	 * Place map context.
336 	 * @constant
337 	 */
338 	this.CONTEXT_PLACEMAP = 22;
339 
340 	/**
341 	 * Internal ID (foreign key) type.
342 	 * @constant
343 	 */
344 	this.TYPE_ID = 0;
345 	/**
346 	 * Integer type.
347 	 * @constant
348 	 */
349 	this.TYPE_INT = 1;
350 	/**
351 	 * Float type.
352 	 * @constant
353 	 */
354 	this.TYPE_FLOAT = 2;
355 	/**
356 	 * String type.
357 	 * @constant
358 	 */
359 	this.TYPE_STRING = 3;
360 	/**
361 	 * Date type.
362 	 * @constant
363 	 */
364 	this.TYPE_DATE = 4;
365 	/**
366 	 * Date and time type.
367 	 * @constant
368 	 */
369 	this.TYPE_DATETIME = 5;
370 	/**
371 	 * Time type.
372 	 * @constant
373 	 */
374 	this.TYPE_TIME = 6;
375 	/**
376 	 * Single enumerated (list of values) type.
377 	 * @constant
378 	 */
379 	this.TYPE_ENUM = 7;
380 	/**
381 	 * Boolean type.
382 	 * @constant
383 	 */
384 	this.TYPE_BOOLEAN = 8;
385 	/**
386 	 * Password type.
387 	 * @constant
388 	 */
389 	this.TYPE_PASSWORD = 9;
390 	/**
391 	 * URL type.
392 	 * @constant
393 	 */
394 	this.TYPE_URL = 10;
395 	/**
396 	 * HTML content type.
397 	 * @constant
398 	 */
399 	this.TYPE_HTML = 11;
400 	/**
401 	 * Email type.
402 	 * @constant
403 	 */
404 	this.TYPE_EMAIL = 12;
405 	/**
406 	 * Long string (unlimited) type.
407 	 * @constant
408 	 */
409 	this.TYPE_LONG_STRING = 13;
410 	/**
411 	 * Multiple enumerated (list of values) type.
412 	 * @constant
413 	 */
414 	this.TYPE_ENUM_MULTI = 14;
415 	/**
416 	 * Regular expression type.
417 	 * @constant
418 	 */
419 	this.TYPE_REGEXP = 15;
420 	/**
421 	 * Document type
422 	 * @constant
423 	 */
424 	this.TYPE_DOC = 17;
425 	/**
426 	 * Float type (same as TYPE_FLOAT).
427 	 * @constant
428 	 */
429 	this.TYPE_FLOAT_EMPTY = 18;
430 	/**
431 	 * External file reference type.
432 	 * @constant
433 	 */
434 	this.TYPE_EXTFILE = 19;
435 	/**
436 	 * Image type.
437 	 * @constant
438 	 */
439 	this.TYPE_IMAGE = 20;
440 	/**
441 	 * Notepad (incremental long text) type.
442 	 * @constant
443 	 */
444 	this.TYPE_NOTEPAD = 21;
445 	/**
446 	 * Phone number type.
447 	 * @constant
448 	 */
449 	this.TYPE_PHONENUM = 22;
450 	/**
451 	 * Color type.
452 	 * @constant
453 	 */
454 	this.TYPE_COLOR = 23;
455 	/**
456 	 * Object type.
457 	 * @constant
458 	 */
459 	this.TYPE_OBJECT = 24;
460 	/**
461 	 * Geo coordinates type.
462 	 * @constant
463 	 */
464 	this.TYPE_GEOCOORDS = 25;
465 
466 	/**
467 	 * Not visible.
468 	 * @constant
469 	 */
470 	this.VIS_NOT = 0;
471 	/**
472 	 * Visible in lists.
473 	 * @constant
474 	 */
475 	this.VIS_LIST = 1;
476 	/**
477 	 * Visible in forms.
478 	 * @constant
479 	 */
480 	this.VIS_FORM = 2;
481 	/**
482 	 * Visible in lists and forms.
483 	 * @constant
484 	 */
485 	this.VIS_BOTH = 3;
486 
487 	/**
488 	 * Not searchable.
489 	 * @constant
490 	 */
491 	this.SEARCH_NONE = 0;
492 	/**
493 	 * Searchable.
494 	 * @constant
495 	 */
496 	this.SEARCH_MONO = 1;
497 	/**
498 	 * Searchable using check boxes.
499 	 * @constant
500 	 */
501 	this.SEARCH_MULTI_CHECK = 2;
502 	/**
503 	 * Searchable using list box.
504 	 * @constant
505 	 */
506 	this.SEARCH_MULTI_LIST = 3;
507 	/**
508 	 * Searchable using period.
509 	 * @constant
510 	 */
511 	this.SEARCH_PERIOD = 4;
512 
513 	/**
514 	 * Default rendering.
515 	 * @constant
516 	 */
517 	this.RENDERING_DEFAULT = "";
518 	/**
519 	 * Select box rendering (single or multiple select).
520 	 * @constant
521 	 */
522 	this.RENDERING_SELECTBOX = "SB";
523 	/**
524 	 * Rendering horizontal checkbox(es).
525 	 * @constant
526 	 */
527 	this.RENDERING_HORIZCHECKBOX = "HCB";
528 	/**
529 	 * Rendering vertical checkbox(es).
530 	 * @constant
531 	 */
532 	this.RENDERING_VERTCHECKBOX = "VCB";
533 	/**
534 	 * Rendering horizontal radio button(s).
535 	 * @constant
536 	 */
537 	this.RENDERING_HORIZRADIOBUTTON = "HRB";
538 	/**
539 	 * Rendering vertical radio button(s).
540 	 * @constant
541 	 */
542 	this.RENDERING_VERTRADIOBUTTON = "VRB";
543 
544 	/**
545 	 * True value
546 	 * @constant
547 	 */
548 	this.TRUE = "1";
549 	/**
550 	 * False value
551 	 * @constant
552 	 */
553 	this.FALSE = "0";
554 
555 	/**
556 	 * Fatal error value
557 	 * @constant
558 	 */
559 	this.ERRLEVEL_FATAL = 1;
560 	/**
561 	 * Error error value
562 	 * @constant
563 	 */
564 	this.ERRLEVEL_ERROR = 2;
565 	/**
566 	 * Minor error value
567 	 * @constant
568 	 */
569 	this.ERRLEVEL_WARNING = 3;
570 
571 	/**
572 	 * Application info data.
573 	 * <ul>
574 	 * <li>name: Application name</li>
575 	 * <li>version: Application version</li>
576 	 * <li>platformversion: Platform version</li>
577 	 * <li>encoding: Encoding</li>
578 	 * <li>server: Server vendor</li>
579 	 * <li>TO BE COMPLETED...</li>
580 	 * </ul>
581 	 * @field
582 	 */
583 	this.appinfo = new Object();
584 
585 	/**
586 	 * Grant data.
587 	 * <ul>
588 	 * <li>login: User login</li>
589 	 * <li>firstname: User first name</li>
590 	 * <li>lastname: User last name</li>
591 	 * <li>responsibilities: User active responsibilites array</li>
592 	 * <li>TO BE COMPLETED...</li>
593 	 * </ul>
594 	 * @field
595 	 */
596 	this.grant = new Object();
597 
598 	/**
599 	 * System parameters array. Use sysparams["<name>"] to get a system parameter value.
600 	 * @field
601 	 */
602 	this.sysparams = new Array();
603 
604 	/**
605 	 * Texts array. Use texts["<code>"] to get a text value.
606 	 * @field
607 	 */
608 	this.texts = new Array();
609 
610 	/**
611 	 * Menu data.
612 	 * <ul>
613 	 * <li>TO BE COMPLETED...</li>
614 	 * </ul>
615 	 * @field
616 	 */
617 	this.menu = new Array();
618 
619 	/**
620 	 * News array. Use news["<code>"] to get a news object), each news object is:
621 	 * <ul>
622 	 * <li>TO BE COMPLETED...</li>
623 	 * </ul>
624 	 * @field
625 	 */
626 	this.news = new Array();
627 };
628 
629 /**
630  * Set default JSON services version compatibility
631  * @param version Simplicite(R) JSON services version to be compatible with (e.g. 2.7)
632  * @function
633  */
634 Simplicite.Ajax.prototype.setVersionCompatibility = function(version) {
635 	this._version = version;
636 };
637 
638 /** @ignore */
639 Simplicite.Ajax.prototype.getStandardError = function(level, message, details) {
640 	return {
641 		level: level,
642 		message: message,
643 		details: details
644 	};
645 };
646 
647 /** @ignore */
648 Simplicite.Ajax.prototype.getErrorMessage = function(err) {
649 	var msg = "";
650 	if (err.messages !== undefined) {
651 		for (var i = 0; i < err.messages.length; i++)
652 			msg += (msg == "" ? "" : "\n") + err.messages[i];
653 	} else if (err.message !== undefined)
654 		msg = err.message;
655 	else if (err.description !== undefined)
656 		msg = err.description;
657 	else
658 		msg = err;
659 	return msg;
660 };
661 
662 /**
663  * Set default global error handler active or inactive.
664  * @param active Active status
665  * @function
666  */
667 Simplicite.Ajax.prototype.setErrorHandlerActive = function(active) {
668 	this._errorActive = active;
669 };/**
670  * Change default global error handler.
671  * @param errorHandler Error handler function
672  * @function
673  */
674 Simplicite.Ajax.prototype.setErrorHandler = function(errorHandler) {
675 	var self = this;
676 	self.error = function(err) {
677 		if (self._errorActive == true)
678 			errorHandler.call(this, err);
679 	};
680 };
681 
682 /**
683  * Set default global warning handler active or inactive.
684  * @param active Active status
685  * @function
686  */
687 Simplicite.Ajax.prototype.setWarningHandlerActive = function(active) {
688 	this._warningActive = active;
689 };
690 /**
691  * Change default global warning handler.
692  * @param warningHandler Warning handler function
693  * @function
694  */
695 Simplicite.Ajax.prototype.setWarningHandler = function(warningHandler) {
696 	var self = this;
697 	self.warning = function(msg) {
698 		if (self._warningActive == true)
699 			warningHandler.call(this, msg);
700 	};
701 };
702 
703 /**
704  * Set default global information handler active or inactive.
705  * @param active Active status
706  * @function
707  */
708 Simplicite.Ajax.prototype.setInfoHandlerActive = function(active) {
709 	this._infoActive = active;
710 };
711 /**
712  * Change default global information handler.
713  * @param infoHandler Information handler function
714  * @function
715  */
716 Simplicite.Ajax.prototype.setInfoHandler = function(infoHandler) {
717 	var self = this;
718 	self.info = function(msg) {
719 		if (self._infoActive == true)
720 			infoHandler.call(this, msg);
721 	};
722 };
723 
724 /**
725  * Set default global debug handler active or inactive.
726  * @param active Active status
727  * @function
728  */
729 Simplicite.Ajax.prototype.setDebugHandlerActive = function(active) {
730 	this._debugActive = active;
731 };
732 /**
733  * Change default global debug handler.
734  * @param debugHandler Debug handler function
735  * @function
736  */
737 Simplicite.Ajax.prototype.setDebugHandler = function(debugHandler) {
738 	var self = this;
739 	self.debug = function(msg) {
740 		if (self._debugActive == true)
741 			debugHandler.call(this, msg);
742 	};
743 };
744 
745 /** @ignore */
746 Simplicite.Ajax.prototype._url = function(url) {
747 	return this._baseURL + this._approot + (this._gateway == this.GATEWAY_WS ? "ws" : "") + url;
748 };
749 
750 /**
751  * Returns local data URL (e.g. suitable for src of img tags).
752  * @param doc Document
753  * @function
754  */
755 Simplicite.Ajax.prototype.dataURL = function(doc) {
756 	if (doc !== undefined && doc.mime !== undefined && doc.content !== undefined)
757 		return "data:" + doc.mime + ";base64," + doc.content;
758 };
759 
760 /**
761  * Returns document URL.
762  * @param object Object name
763  * @param field Field name
764  * @param rowId Object record row ID
765  * @param docId Document ID (can be omitted then a lookup is done on record matching rowId)
766  * @function
767  */
768 Simplicite.Ajax.prototype.documentURL = function(object, field, rowId, docId) {
769 	return this._url(this._documentURL + "?object=" + object + "&field=" + field + "&row_id=" + rowId + (docId !== undefined ? "&doc_id=" + docId : ""));
770 };
771 
772 /**
773  * Returns content URL.
774  * @param file Content file name
775  * @function
776  */
777 Simplicite.Ajax.prototype.contentURL = function(file) {
778 	return this._url(this._contentURL + "?file=" + file);
779 };
780 
781 /**
782  * Returns disposition resource URL.
783  * @param code Resource code
784  * @param type Resource type (IMG=image (default), ICO=Icon, CSS=stylesheet, JS=Javascript, HTML=HTML)
785  * @function
786  */
787 Simplicite.Ajax.prototype.dispositionResourceURL = function(code, type) {
788 	return this._url(this._resourceURL + "?code=" + code + "&type=" + type);
789 };
790 
791 /**
792  * Returns resource URL.
793  * @param id Resource ID (e.g. taken from business object or external object resources list in metadata)
794  * @function
795  */
796 Simplicite.Ajax.prototype.resourceURL = function(id) {
797 	return this._url(this._resourceURL + "?row_id=" + id);
798 };
799 
800 /** @ignore */
801 Simplicite.Ajax.prototype._callParams = function(data) {
802 	var p = "";
803 	if (data === undefined)
804 		return p;
805 	var n = 0;
806 	for (var i in data) {
807 		var d = data[i];
808 		if (d === undefined)
809 			d = "";
810 		if (d.id !== undefined && d.content !== undefined) // Document ?
811 			p += (n++ != 0 ? "&" : "") + i + "=" + encodeURIComponent("id|" + d.id + "|name|" + d.name + "|content|" + d.content);
812 		else if (d.object !== undefined && d.row_id !== undefined) // Object ?
813 			p += (n++ != 0 ? "&" : "") + i + "=" + encodeURIComponent("object|" + d.object + "|row_id|" + d.row_id);
814 		else if (d.sort) { // Array ?
815 			if (!d.length)
816 				p += (n++ != 0 ? "&" : "") + i + "="; // empty enum multi
817 			else
818 				for (var j = 0; j < d.length; j++)
819 					p += (n++ != 0 ? "&" : "") + i + "=" + encodeURIComponent(d[j]);
820 		} else
821 			p += (n++ != 0 ? "&" : "") + i + "=" + encodeURIComponent(d);
822 	}
823 	return p;
824 };
825 
826 /** @ignore */
827 Simplicite.Ajax.prototype._callAuth = function(login, password) {
828 	return "Basic " + this.base64Encode(login + ":" + password);
829 };
830 
831 /** @ignore */
832 Simplicite.Ajax.prototype._call = function(async, url, params, callback, scope) {
833 	try {
834 		var self;
835 		var xhr = undefined;
836 		if (window.XMLHttpRequest) {
837 			xhr = new XMLHttpRequest();
838 		} else if (window.ActiveXObject) {
839 			try {
840 				xhr = new ActiveXObject("Msxml2.XMLHTTP");
841 			} catch (e) {
842 				xhr = new ActiveXObject("Microsoft.XMLHTTP");
843 			}
844 		}
845 		if (xhr === undefined)
846 			throw new Exception("Ajax not available");
847 
848 		var a = (async === undefined) ? this._async : async;
849 		var u = this._url(url);
850 		xhr.open("POST", u, a);
851 		try { if (a) xhr.withCredentials = true; } catch(ex) {}
852 
853 		if (this._gateway === this.GATEWAY_API) {
854 			if (this.authToken !== undefined)
855 				xhr.setRequestHeader("X-Simplicite-Authorization", "Bearer " + this.authToken);
856 			else if (this._login !== undefined && this._password !== undefined)
857 				xhr.setRequestHeader("X-Simplicite-Authorization", this._callAuth(this._login, this._password));
858 		} else if (this._gateway === this.GATEWAY_WS) {
859 			xhr.setRequestHeader("Authorization", this._callAuth(this._login, this._password));
860 		}
861 
862 		xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
863 
864 		// Encoding must be explicitly set to UTF-8 with XMLHttpRequest posts
865 		xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
866 
867 		if (this._timeout > 0) {
868 			self = this;
869 			xhr.timeout = this._timeout;
870 			xhr.ontimeout = function() {
871 				self.error(self.getStandardError(this.LEVEL_ERROR, "The call is in timeout", "Timeout"));
872 			}
873 		}
874 
875 		var p = this._callParams(params);
876 		this.debug("POST call to URL: " + u + " with params: " + p);
877 		xhr.send(p);
878 
879 		if (a) {
880 			self = this;
881 			/** @ignore */
882 			xhr.onreadystatechange = function() {
883 				self._callResponse(xhr, callback, scope);
884 			};
885 		} else {
886 			return this._callResponse(xhr, callback, scope);
887 		}
888 	} catch (e) {
889 		this.error(this.getStandardError(this.LEVEL_ERROR, e.message, e.name));
890 	}
891 };
892 
893 /** @ignore */
894 Simplicite.Ajax.prototype._callError = function(level, message, details) {
895 	return {
896 		type: "error",
897 		response: this.getStandardError(level, message, details)
898 	};
899 };
900 
901 /** @ignore */
902 Simplicite.Ajax.prototype._callResponse = function(xhr, callback, scope) {
903 	if (xhr.readyState == 4) {
904 		this.debug("Response: status=" + xhr.status + ", length=" + xhr.responseText.length + " bytes");
905 		var r, res = xhr.responseText;
906 		try {
907 			if (xhr.status == 200) {
908 				if (res.indexOf("j_security_check") > 0) // In case of form based login (TODO : to be improved)
909 					throw "HTTP 401";
910 				try {
911 					r = JSON.parse(res);
912 				} catch(err) {
913 					console.log("JSON parsing error for " + res);
914 					throw err;
915 				}
916 				if (callback !== undefined)
917 					callback.call(scope !== undefined ? scope : this, r);
918 				return r !== undefined && r.type != "error" ? r.response : undefined;
919 			} else
920 				throw "HTTP " + xhr.status;
921 		} catch (e) {
922 			if (callback !== undefined)
923 				callback.call(scope !== undefined ? scope : this, this._callError(this.LEVEL_ERROR, e, res));
924 			else
925 				throw e;
926 		}
927 	}
928 };
929 
930 /**
931  * Loads application info data.
932  * @param callback Callback function called when loading is completed (loaded application info data is passed to this function)
933  * @param params Optional parameters:
934  * <ul>
935  * <li>async Asynchronous call (use default if absent) ?</li>
936  * <li>error Custom error handler (use default error handler if absent) ?</li>
937  * </ul>
938  * @function
939  */
940 Simplicite.Ajax.prototype.getAppInfo = function(callback, params) {
941 	var self = this;
942 	var url = self._appURL + "?action=getinfo";
943 	if (params === undefined)
944 		params = new Object();
945 	return self._call(params.async, url, undefined, function(r) {
946 		if (r.type == "error") {
947 			self.appinfo = new Object();
948 			if (params.error !== undefined)
949 				params.error.call(self, r.response);
950 			else
951 				self.error(r.response);
952 		} else {
953 			self.appinfo = r.response;
954 			if (callback !== undefined)
955 				callback.call(this, self.appinfo);
956 		}
957 	});
958 };
959 
960 /**
961  * Loads system info data.
962  * @param callback Callback function called when loading is completed (loaded system info data is passed to this function)
963  * @param params Optional parameters:
964  * <ul>
965  * <li>async Asynchronous call (use default if absent) ?</li>
966  * <li>error Custom error handler (use default error handler if absent) ?</li>
967  * </ul>
968  * @function
969  */
970 Simplicite.Ajax.prototype.getSysInfo = function(callback, params) {
971 	var self = this;
972 	var url = self._appURL + "?action=getsysinfo";
973 	if (params === undefined)
974 		params = new Object();
975 	return self._call(params.async, url, undefined, function(r) {
976 		if (r.type == "error") {
977 			if (params.error !== undefined)
978 				params.error.call(self, r.response);
979 			else
980 				self.error(r.response);
981 		} else {
982 			if (callback !== undefined)
983 				callback.call(this, r.response);
984 		}
985 	});
986 };
987 
988 /**
989  * Loads grant data.
990  * @param callback Callback function called when loading is completed (loaded grant data is passed to this function)
991  * @param params Optional parameters:
992  * <ul>
993  * <li>inlinePicture Inline picture (false if absent or undefined)</li>
994  * <li>async Asynchronous call (use default if absent) ?</li>
995  * <li>error Custom error handler (use default error handler if absent) ?</li>
996  * </ul>
997  * @function
998  */
999 Simplicite.Ajax.prototype.getGrant = function(callback, params) {
1000 	var self = this;
1001 	var url = self._appURL + "?action=getgrant";
1002 	if (params === undefined)
1003 		params = new Object();
1004 	if (params.inlinePicture !== undefined)
1005 		url += "&inline_picture=" + params.inlinePicture;
1006 	return self._call(params.async, url, undefined, function(r) {
1007 		if (r.type == "error") {
1008 			self.grant = new Object();
1009 			if (params.error !== undefined)
1010 				params.error.call(self, r.response);
1011 			else
1012 				self.error(r.response);
1013 		} else {
1014 			self.grant = r.response;
1015 			if (self.grant.picture !== undefined) {
1016 				var  p = self.grant.picture;
1017 				p.getURL = function() { return self.documentURL("User", "usr_image_id", self.grant.userid); };
1018 				p.getThumbnailURL = function() { return self.grant.picture.getURL() + "&thumbnail=true"; };
1019 				p.getDataURL = function() { return self.dataURL(p); };
1020 			}
1021 			/**
1022 			 * Get user ID
1023 			 * @function
1024 			 */
1025 			self.grant.getUserID = function() { return this.userid; };
1026 			/**
1027 			 * Get user login
1028 			 * @function
1029 			 */
1030 			self.grant.getLogin = function() { return this.login; };
1031 			/**
1032 			 * Get user language
1033 			 * @function
1034 			 */
1035 			self.grant.getLang = function() { return this.lang; };
1036 			/**
1037 			 * Get user email
1038 			 * @function
1039 			 */
1040 			self.grant.getEmail = function() { return this.email; };
1041 			/**
1042 			 * Get user first name
1043 			 * @function
1044 			 */
1045 			self.grant.getFirstName = function() { return this.firstname; };
1046 			/**
1047 			 * Get user last name
1048 			 * @function
1049 			 */
1050 			self.grant.getLastName = function() { return this.lastname; };
1051 			/**
1052 			 * Check if user has responsibility on specified group
1053 			 * @param group Group name
1054 			 * @function
1055 			 */
1056 			self.grant.hasResponsibility = function(group) { return this.responsibilities !== undefined && this.responsibilities.indexOf(group) != -1; };
1057 			if (callback !== undefined)
1058 				callback.call(this, self.grant);
1059 		}
1060 	});
1061 };
1062 
1063 /**
1064  * Loads menu data.
1065  * @param callback Callback function called when loading is completed (loaded menu data is passed to this function)
1066  * @param params Optional parameters:
1067  * <ul>
1068  * <li>async Asynchronous call (use default if absent) ?</li>
1069  * <li>error Custom error handler (use default error handler if absent) ?</li>
1070  * </ul>
1071  * @function
1072  */
1073 Simplicite.Ajax.prototype.getMenu = function(callback, params) {
1074 	var self = this;
1075 	var url = self._appURL + "?action=getmenu";
1076 	if (params === undefined)
1077 		params = new Object();
1078 	return self._call(params.async, url, undefined, function(r) {
1079 		if (r.type == "error") {
1080 			self.menu = new Array();
1081 			if (params.error !== undefined)
1082 				params.error.call(self, r.response);
1083 			else
1084 				self.error(r.response);
1085 		} else {
1086 			self.menu = r.response;
1087 			if (callback !== undefined)
1088 				callback.call(this, self.menu);
1089 		}
1090 	});
1091 };
1092 
1093 /**
1094  * Loads system parameters.
1095  * @param callback Callback function called when loading is completed (loaded system parameters array is passed to this function)
1096  * @param params Optional parameters:
1097  * <ul>
1098  * <li>async Asynchronous call (use default if absent) ?</li>
1099  * <li>error Custom error handler (use default error handler if absent) ?</li>
1100  * </ul>
1101  * @function
1102  */
1103 Simplicite.Ajax.prototype.getSysParams = function(callback, params) {
1104 	var self = this;
1105 	var url = self._appURL + "?action=getsysparams";
1106 	if (params === undefined)
1107 		params = new Object();
1108 	return self._call(params.async, url, undefined, function(r) {
1109 		if (r.type == "error") {
1110 			self.sysparams = new Object();
1111 			if (params.error !== undefined)
1112 				params.error.call(self, r.response);
1113 			else
1114 				self.error(r.response);
1115 		} else {
1116 			self.sysparams = new Object();
1117 			for (var i = 0; i < r.response.length; i++)
1118 				self.sysparams[r.response[i].name] = r.response[i].value;
1119 			if (callback !== undefined)
1120 				callback.call(this, self.sysparams);
1121 		}
1122 	});
1123 };
1124 
1125 /**
1126  * Get system parameter value.
1127  * @param name System parameter name
1128  * @param params Optional parameters:
1129  * <ul>
1130  * <li>async Asynchronous call (use default if absent) ?</li>
1131  * <li>error Custom error handler (use default error handler if absent) ?</li>
1132  * </ul>
1133  * @function
1134  */
1135 Simplicite.Ajax.prototype.getSysParam = function(callback, name, params) {
1136 	var self = this;
1137 	var url = self._appURL + "?action=getsysparam";
1138 	if (params === undefined)
1139 		params = new Object();
1140 	var res = self._call(params.async, url, { name: name }, function(r) {
1141 		if (r.type == "error") {
1142 			if (params.error !== undefined)
1143 				params.error.call(self, r.response);
1144 			else
1145 				self.error(r.response);
1146 		} else {
1147 			self.sysparams[name] = r.response.value;
1148 			if (callback !== undefined)
1149 				callback.call(this, r.response.value);
1150 		}
1151 	});
1152 	if (res !== undefined && res.value != undefined) return res.value;
1153 },
1154 
1155 /**
1156  * Set a user system parameter.
1157  * @param callback Callback function called when loading is completed (parameter is passed to this function)
1158  * @param name Parameter name
1159  * @param value Parameter value (if undefined parameter is unset)
1160  * @param save Save parameter in user parameters (if undefined parameter is not saved)
1161  * @param params Optional parameters :
1162  * <ul>
1163  * <li>async Asynchronous call (use default if absent) ?</li>
1164  * <li>error Custom error handler (use default error handler if absent) ?</li>
1165  * </ul>
1166  * @function
1167  */
1168 Simplicite.Ajax.prototype.setSysParam = function(callback, name, value, save, params) {
1169 	var self = this;
1170 	var url = self._appURL + "?action=setsysparam";
1171 	if (params === undefined)
1172 		params = new Object();
1173 	var p = { name: name, save: save !== undefined ? save : false };
1174 	if (value !== undefined)
1175 		p.value = value;
1176 	var res = self._call(params.async, url, p, function(r) {
1177 		if (r.type == "error") {
1178 			if (params.error !== undefined)
1179 				params.error.call(this, r.response);
1180 			else
1181 				self.error(r.response);
1182 		} else {
1183 			self.sysparams[name] = r.response.value;
1184 			if (callback !== undefined)
1185 				callback.call(this, r.response.value);
1186 		}
1187 	}, this);
1188 	if (res !== undefined && res.value != undefined) return res.value;
1189 };
1190 
1191 /**
1192  * Loads texts.
1193  * @param callback Callback function called when loading is completed (loaded texts array is passed to this function)
1194  * @param params Optional parameters:
1195  * <ul>
1196  * <li>async Asynchronous call (use default if absent) ?</li>
1197  * <li>error Custom error handler (use default error handler if absent) ?</li>
1198  * </ul>
1199  * @function
1200  */
1201 Simplicite.Ajax.prototype.getTexts = function(callback, params) {
1202 	var self = this;
1203 	var url = self._appURL + "?data=texts";
1204 	if (params === undefined)
1205 		params = new Object();
1206 	return self._call(params.async, url, undefined, function(r) {
1207 		if (r.type == "error") {
1208 			self.texts = new Object();
1209 			if (params.error !== undefined)
1210 				params.error.call(self, r.response);
1211 			else
1212 				self.error(r.response);
1213 		} else {
1214 			self.texts = new Object();
1215 			for (var i = 0; i < r.response.length; i++)
1216 				self.texts[r.response[i].code] = r.response[i].value;
1217 			if (callback !== undefined)
1218 				callback.call(this, self.texts);
1219 		}
1220 	});
1221 };
1222 
1223 /**
1224  * Get text value.
1225  * @param code Text code
1226  * @function
1227  */
1228 Simplicite.Ajax.prototype.getText = function(code) {
1229 	if (this.texts === undefined)
1230 		this.getTexts(undefined, { async: false });
1231 	return this.texts[code];
1232 },
1233 
1234 /**
1235  * Alias for getText
1236  * @function
1237  */
1238 Simplicite.Ajax.prototype.T = Simplicite.Ajax.prototype.getText;
1239 
1240 /**
1241  * Loads news.
1242  * @param callback Callback function called when loading is completed (loaded news array is passed to this function)
1243  * @param params Optional parameters:
1244  * <ul>
1245  * <li>inlineImages Inline news images (false if absent or undefined)</li>
1246  * <li>async Asynchronous call (use default if absent) ?</li>
1247  * <li>error Custom error handler (use default error handler if absent) ?</li>
1248  * </ul>
1249  * @function
1250  */
1251 Simplicite.Ajax.prototype.getNews = function(callback, params) {
1252 	var self = this;
1253 	var url = self._appURL + "?data=news";
1254 	if (params === undefined)
1255 		params = new Object();
1256 	if (params.inlineImages !== undefined)
1257 		url += "&inline_images=" + params.inlineImages;
1258 	return self._call(params.async, url, undefined, function(r) {
1259 		if (r.type == "error") {
1260 			self.news = new Array();
1261 			if (params.error !== undefined)
1262 				params.error.call(self, r.response);
1263 			else
1264 				self.error(r.response);
1265 		} else {
1266 			self.news = r.response;
1267 			var n = self.news;
1268 			n.getTitle = function(i) { return self.news[i].title; };
1269 			n.getDate = function(i) { return self.parseDateTimeValue(self.news[i].date); };
1270 			n.getContent = function(i) { return self.news[i].content; };
1271 			n.getImageURL = function(i) { return self.documentURL("WebNews", "nws_image", self.news[i].id); };
1272 			n.getImageThumbnailURL = function(i) { return self.news.getImageURL(i) + "&thumbnail=true"; };
1273 			n.getImageDataURL = function(i) { return self.dataURL(self.news[i].image); };
1274 			if (callback !== undefined)
1275 				callback.call(this, self.news);
1276 		}
1277 	});
1278 };
1279 
1280 /**
1281  * Loads external object.
1282  * @param callback Callback function called when loading is completed (loaded external object is passed to this function)
1283  * @param name External object name
1284  * @param params Optional parameters:
1285  * <ul>
1286  * <li>async Asynchronous call (use default if absent) ?</li>
1287  * <li>error Custom error handler (use default error handler if absent) ?</li>
1288  * </ul>
1289  * @function
1290  */
1291 Simplicite.Ajax.prototype.getExternalObject = function(callback, name, params) {
1292 	var self = this;
1293 	var url = self._appURL + "?data=extobject&name=" + name;
1294 	if (params === undefined)
1295 		params = new Object();
1296 	return self._call(params.async, url, undefined, function(r) {
1297 		if (r.type == "error") {
1298 			if (params.error !== undefined)
1299 				params.error.call(self, r.response);
1300 			else
1301 				self.error(r.response);
1302 		} else {
1303 			var extobj = r.response;
1304 			;
1305 			if (callback !== undefined)
1306 				callback.call(self, extobj);
1307 		}
1308 	});
1309 };
1310 
1311 /**
1312  * Server side session identifier.
1313  * @field
1314  */
1315 Simplicite.Ajax.prototype.sessionId = undefined;
1316 
1317 /**
1318  * Server side session authentication token.
1319  * @field
1320  */
1321 Simplicite.Ajax.prototype.authToken = undefined;
1322 
1323 /**
1324  * Session init (retrieves server side session identifier). Note that this call is forced to be a synchronous call.
1325  * @param successCallback Callback function called when session init is sucessful
1326  * @param failureCallback Callback function called when session init fails
1327  * @function
1328  */
1329 Simplicite.Ajax.prototype.session = function(successCallback, failureCallback) {
1330 	var self = this;
1331 	var url = self._appURL + "?data=session";
1332 	return self._call(true, url, undefined, function(r) {
1333 		if (r.type == "error") {
1334 			self.sessionId = undefined;
1335 			if (failureCallback !== undefined)
1336 				failureCallback.call(this, r.response);
1337 			else
1338 				self.error(r.response);
1339 		} else {
1340 			self.sessionId = r.response.id;
1341 			self.authToken = r.response.authtoken;
1342 			if (successCallback !== undefined)
1343 				successCallback.call(this, self.sessionId);
1344 		}
1345 	});
1346 };
1347 
1348 /**
1349  * Server side login (same as session()). Note that this call is forced to be a synchronous call.
1350  * @param successCallback Callback function called when login is sucessful
1351  * @param failureCallback Callback function called when login fails
1352  * @function
1353  */
1354 Simplicite.Ajax.prototype.login = function(successCallback, failureCallback) {
1355 	this.session(successCallback, failureCallback);
1356 },
1357 
1358 /**
1359  * Server side logout (does not work on some web browsers). Note that this call is forced to be a synchronous call.
1360  * @param successCallback Callback function called when login is sucessful
1361  * @param failureCallback Callback function called when login fails
1362  * @param params Optional parameters:
1363  * <ul>
1364  * <li>async Asynchronous call (use default if absent) ?</li>
1365  * <li>error Custom error handler (use default error handler if absent) ?</li>
1366  * </ul>
1367  * @function
1368  */
1369 Simplicite.Ajax.prototype.logout = function(successCallback, failureCallback, params) {
1370 	var self = this;
1371 	var url = self._appURL + "?data=logout";
1372 	return self._call(true, url, undefined, function(r) {
1373 		self._approot = "__none__";
1374 		self._gateway = undefined;
1375 		self._baseURL = undefined;
1376 		self._appURL = undefined;
1377 		self._objURL = undefined;
1378 		self._pcsURL = undefined;
1379 		self._documentURL = undefined;
1380 		self._contentURL = undefined;
1381 		self._resourceURL = undefined;
1382 		self._login = undefined;
1383 		self._password = undefined;
1384 		self.sessionId = undefined;
1385 		self.grant = new Object();
1386 		self.menu = new Array();
1387 		self.sysparams = new Array();
1388 		self.texts = new Array();
1389 		self.news = new Array();
1390 		self._businessObjectsCache = undefined;
1391 		if (r.type == "error") {
1392 			if (failureCallback !== undefined)
1393 				failureCallback.call(this, r.response);
1394 			else
1395 				self.error(r.response);
1396 		} else {
1397 			if (successCallback !== undefined)
1398 				successCallback.call(this);
1399 		}
1400 	});
1401 };
1402 
1403 /**
1404  * Parse a date value into a Javascript Date
1405  * @param v Date value (YYYY-MM-DD)
1406  * @function
1407  */
1408 Simplicite.Ajax.prototype.parseDateValue = function(v) {
1409 	return this.parseDateTimeValue(v + " 00:00:00");
1410 };
1411 
1412 /**
1413  * Parse a date time value into a Javascript Date
1414  * @param v Date time value (YYYY-MM-DD hh:mm:ss)
1415  * @function
1416  */
1417 Simplicite.Ajax.prototype.parseDateTimeValue = function(v) {
1418 	var yy = parseInt(v.substring(0, 4)),
1419 		mm = parseInt(v.substring(5, 7)) - 1,
1420 		dd = parseInt(v.substring(8, 10)),
1421 		h = parseInt(v.substring(11, 13)),
1422 		m = parseInt(v.substring(14, 16)),
1423 		s = parseInt(v.substring(17, 19));
1424 	return new Date(yy, mm, dd, h, m, s, 0);
1425 };
1426 
1427 /**
1428  * Parse a Javascript Date into a date value
1429  * @param d Javascript date
1430  * @function
1431  */
1432 Simplicite.Ajax.prototype.toDateValue = function(d) {
1433 	var yy = d.getFullYear();
1434 	var mm = d.getMonth() + 1;
1435 	var dd = d.getDate();
1436 	return yy + "-" + (mm < 10 ? "0" : "") + mm + "-" + (dd < 10 ? "0" : "") + dd;
1437 };
1438 
1439 /**
1440  * Parse a Javascript Date into a time value
1441  * @param d Javascript date
1442  * @function
1443  */
1444 Simplicite.Ajax.prototype.toTimeValue = function(d) {
1445 	var h = d.getHours();
1446 	var m = d.getMinutes();
1447 	var s = d.getSeconds();
1448 	return (h < 10 ? "0" : "") + h + ":" + (m < 10 ? "0" : "") + m + ":" + (s < 10 ? "0" : "") + s;
1449 };
1450 
1451 /**
1452  * Parse a Javascript Date into a date time value
1453  * @param d Javascript date
1454  * @function
1455  */
1456 Simplicite.Ajax.prototype.toDateTimeValue = function(d) {
1457 	return this.toDateValue(d) + " " + this.toTimeValue(d);
1458 };
1459 
1460 /**
1461  * Encode a string to base64
1462  * @param s Input string
1463  * @return Base64-encoded string
1464  * @function
1465  */
1466 Simplicite.Ajax.prototype.base64Encode = function(s) {
1467 	if (s === undefined || typeof s != "string" || s.length == 0) return "";
1468 	//if (typeof btoa !== "undefined") return btoa(s);
1469 
1470 	var o = s.replace(/\r\n/g, "\n");
1471 	var u = "";
1472 	for ( var n = 0; n < o.length; n++) {
1473 		var c = o.charCodeAt(n);
1474 		if (c < 128) {
1475 			u += String.fromCharCode(c);
1476 		} else if ((c > 127) && (c < 2048)) {
1477 			u += String.fromCharCode((c >> 6) | 192);
1478 			u += String.fromCharCode((c & 63) | 128);
1479 		} else {
1480 			u += String.fromCharCode((c >> 12) | 224);
1481 			u += String.fromCharCode(((c >> 6) & 63) | 128);
1482 			u += String.fromCharCode((c & 63) | 128);
1483 		}
1484 	}
1485 
1486 	o = "";
1487 	var c1, c2, c3 = "";
1488 	var e1, e2, e3, e4 = "";
1489 	var i = 0;
1490 	var k = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
1491 	do {
1492 		c1 = u.charCodeAt(i++);
1493 		c2 = u.charCodeAt(i++);
1494 		c3 = u.charCodeAt(i++);
1495 
1496 		e1 = c1 >> 2;
1497 		e2 = ((c1 & 3) << 4) | (c2 >> 4);
1498 		e3 = ((c2 & 15) << 2) | (c3 >> 6);
1499 		e4 = c3 & 63;
1500 
1501 		if (isNaN(c2)) {
1502 			e3 = e4 = 64;
1503 		} else if (isNaN(c3)) {
1504 			e4 = 64;
1505 		}
1506 
1507 		o = o +
1508 			k.charAt(e1) +
1509 			k.charAt(e2) +
1510 			k.charAt(e3) +
1511 			k.charAt(e4);
1512 		c1 = c2 = c3 = "";
1513 		e1 = e2 = e3 = e4 = "";
1514 	} while (i < u.length);
1515 
1516 	return o;
1517 };
1518 
1519 
1520 /**
1521  * Encode an array buffer (such as got from a local file read) to to base64
1522  * @param b Array buffer
1523  * @return Base64-encoded string
1524  * @function
1525  */
1526 Simplicite.Ajax.prototype.base64EncodeArrayBuffer = function(b) {
1527 	var s = "";
1528 	var bs = new Uint8Array(b);
1529 	for (var i = 0; i < bs.byteLength; i++)
1530 		s += String.fromCharCode(bs[i]);
1531 	return this.base64Encode(s);
1532 };
1533 
1534 /**
1535  * Decode a base64 string to string
1536  * @param s Base64-encoded string
1537  * @return Decoded string
1538  * @function
1539  */
1540 Simplicite.Ajax.prototype.base64Decode = function(s) {
1541 	if (s === undefined || typeof s != "string" || s.length == 0) return "";
1542 	//if (typeof atob !== "undefined") return atob(s);
1543 
1544 	var o = "";
1545 	var c1, c2, c3 = "";
1546 	var e1, e2, e3, e4 = "";
1547 	var i = 0;
1548 	var k = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
1549 	do {
1550 		e1 = k.indexOf(s.charAt(i++));
1551 		e2 = k.indexOf(s.charAt(i++));
1552 		e3 = k.indexOf(s.charAt(i++));
1553 		e4 = k.indexOf(s.charAt(i++));
1554 
1555 		c1 = (e1 << 2) | (e2 >> 4);
1556 		c2 = ((e2 & 15) << 4) | (e3 >> 2);
1557 		c3 = ((e3 & 3) << 6) | e4;
1558 
1559 		o = o + String.fromCharCode(c1);
1560 
1561 		if (e3 != 64) {
1562 			o = o + String.fromCharCode(c2);
1563 		}
1564 		if (e4 != 64) {
1565 			o = o + String.fromCharCode(c3);
1566 		}
1567 
1568 		c1 = c2 = c3 = "";
1569 		e1 = e2 = e3 = e4 = "";
1570 
1571 	} while (i < s.length);
1572 
1573 	var u = "";
1574 	var i = 0;
1575 	var c = c1 = c2 = 0;
1576 	while (i < o.length) {
1577 		c = o.charCodeAt(i);
1578 		if (c < 128) {
1579 			u += String.fromCharCode(c);
1580 			i++;
1581 		} else if ((c > 191) && (c < 224)) {
1582 			c2 = o.charCodeAt(i + 1);
1583 			u += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
1584 			i += 2;
1585 		} else {
1586 			c2 = o.charCodeAt(i + 1);
1587 			c3 = o.charCodeAt(i + 2);
1588 			u += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
1589 			i += 3;
1590 		}
1591 	}
1592 	return u;
1593 };
1594 
1595 // Backward compatibility...
1596 //SimpliciteAjax = Simplicite.Ajax;