1 /*
  2  * SIMPLICITE - runtime & framework
  3  * http://www.simplicite.fr
  4  * Copyright (c)2006-2013 Simplicite Software. All rights reserved.
  5  */
  6 
  7 /**
  8  * Tools
  9  * @class
 10  */
 11 Simplicite.Tools = {
 12 
 13 log : function(text, popup) {
 14 	try {
 15 		console.log(text);
 16 	} catch(e) {
 17 		if (popup) alert(text);
 18 		else console.log(text);
 19 	}
 20 },
 21 
 22 debug : function(text) { this.log("[DEBUG]: " + text); },
 23 
 24 info : function(text, popup) { this.log("[INFO]: " + text, popup); },
 25 
 26 warning : function(text, popup) { this.log("[WARNING]: " + text, popup); },
 27 
 28 error : function(text, e, popup) {
 29 	var t = "[ERROR]: " + (text ? text : "") + (e ? "\nException: "+ (e.message ? e.message : e.description) : "");
 30 	try {
 31 		console.error(t);
 32 	} catch(e) {
 33 		if (popup) alert(t);
 34 		else console.log(t);
 35 	}
 36 },
 37 
 38 T : function(code) {
 39 	var t = Simplicite.TEXT !== undefined ? Simplicite.TEXT[code] : code;
 40 	return t === undefined ? code : t;
 41 },
 42 
 43 wait : function(ms) {
 44 	if (ms<0) return;
 45 	ms += new Date().getTime();
 46 	while (new Date() < ms){}
 47 },
 48 
 49 isWinIE : navigator.userAgent.indexOf("MSIE")!=-1 && navigator.userAgent.indexOf("Win")!=-1,
 50 winIEVersion : parseInt(navigator.appVersion.split("MSIE")[1]),
 51 isFirefox : navigator.userAgent.indexOf("Firefox")!=-1,
 52 isSafari  : navigator.userAgent.indexOf("Safari")!=-1,
 53 isChrome  : navigator.userAgent.indexOf("Chrome")!=-1,
 54 isIPad : navigator.userAgent.indexOf("iPad")!=-1,
 55 isIPhone : navigator.userAgent.indexOf("iPhone")!=-1,
 56 
 57 urlEncodeParam : function(val) {
 58 	if (Simplicite.ENCODING == "UTF-8")
 59 		return encodeURIComponent(val);
 60  	else
 61 		return escape(val).replace(/\+/g, "%2B");
 62 },
 63 
 64 isCanvasSupported : function () {
 65 	var elem = document.createElement('canvas');
 66 	return !!(elem.getContext && elem.getContext('2d'));
 67 },
 68 
 69 getEvent : function(e) {
 70 	return e ? e : window.event;
 71 },
 72 getEventElement : function(e) {
 73 	e = this.getEvent(e);
 74 	return (e.srcElement ? e.srcElement: (e.target ? e.target : e.currentTarget));
 75 },
 76 stopEvent : function(e) {
 77 	e = this.getEvent(e);
 78 	if (e.stopPropagation) {
 79 		e.stopPropagation();
 80 	} else { // IE8- or IE9/HTML4
 81 		e.cancelBubble = true;
 82 	}
 83 },
 84 freezeEvent : function(e) {
 85 	e = this.getEvent(e);
 86 	if (e.stopPropagation) {
 87 		e.preventDefault();
 88 		e.stopPropagation();
 89 	} else { // IE8- or IE9/HTML4
 90 		e.returnValue = false;
 91 		e.cancelBubble = true;
 92 	}
 93 },
 94 getEventPosX : function(e) {
 95 	var posx = 0;
 96 	e = this.getEvent(e);
 97 	if (e.pageX) {
 98 		posx = e.pageX;
 99 	} else if (e.clientX) {
100 		posx = e.clientX + document.body.scrollLeft
101 			+ document.documentElement.scrollLeft;
102 	}
103 	return posx;
104 },
105 getEventPosY : function(e) {
106 	var posy = 0;
107 	e = this.getEvent(e);
108 	if (e.pageY) {
109 		posy = e.pageY;
110 	} else if (e.clientY) {
111 		posy = e.clientY + document.body.scrollTop
112 			+ document.documentElement.scrollTop;
113 	}
114 	return posy;
115 },
116 addEvent : function(el, evnt, func) {
117 	if (el.addEventListener) {
118 		el.addEventListener(evnt, func, false);
119 	} else if (el.attachEvent) {
120 		el.attachEvent('on'+evnt, func);
121 	}
122 },
123 addClickEvent : function(el, f) {
124 	this.addEvent(el, "click", f);
125 },
126 fireEvent : function(el, evnt) {
127 	if (!el) return;
128 	if(document.createEvent) {
129 		var ev = document.createEvent('HTMLEvents');
130 		ev.initEvent(evnt, true, true);
131 		el.dispatchEvent(ev);
132 	} else if(document.createEventObject) {
133 		var ev = document.createEventObject();
134 		el.fireEvent('on'+evnt, ev);
135 	} else if(el['on'+evnt]) {
136 		el['on'+evnt]();
137 	}
138 },
139 
140 isArray : function(v) {
141 	return v.constructor == Array;
142 },
143 
144 toInt : function(v) {
145 	if (!v || v=='') return 0;
146 	v = v.replace(/[^0-9]+|[\r\n\t\f\s]+/gi,'');
147 	return parseInt(v);
148 },
149 toFloat : function(v) {
150 	if (!v || v=='') return 0.0;
151 	v = v.replace(/[^0-9.,]+|[\r\n\t\f\s]+/gi,'').replace(/,+/,'.');
152 	return parseFloat(v);
153 },
154 contains : function(ar, obj) {
155 	for (var i = 0; ar && i < ar.length; i++)
156 		if (ar[i]==obj) return true;
157 	return false;
158 },
159 
160 KEY_BACKSPACE	: 8,
161 KEY_TAB			: 9,
162 KEY_ENTER		: 13,
163 KEY_SHIFT		: 16,
164 KEY_CTRL		: 17,
165 KEY_ALT			: 18,
166 KEY_PAUSE		: 19,
167 KEY_CAPS_LOCK	: 20,
168 KEY_ESCAPE		: 27,
169 KEY_PAGE_UP		: 33,
170 KEY_PAGE_DOWN	: 34,
171 KEY_END			: 35,
172 KEY_HOME		: 36,
173 KEY_LEFT_ARROW	: 37,
174 KEY_UP_ARROW	: 38,
175 KEY_RIGHT_ARROW	: 39,
176 KEY_DOWN_ARROW	: 40,
177 KEY_INSERT		: 45,
178 KEY_DELETE		: 46,
179 
180 getKeyCode : function(e) {
181 	if (!e) return 0;
182 	var key = e.keyCode;
183 	if (!key || key==0) key = e.charCode;
184 	return key;
185 },
186 
187 isWithinNode : function(elem, id, clas, tag, obj) {
188 	var answer = false;
189 	var te = elem;
190 	while(te && !answer) {
191 		if ((te.id && te.id==id)
192 		|| (te.className && te.className==id+"Class")
193 		|| (!tag && clas && te.className && te.className==clas)
194 		|| (!tag && clas && te.className && te.className.indexOf(clas)!=-1)
195 		|| (tag && te.tagName && te.tagName.toLowerCase()==tag.toLowerCase())
196 		|| (obj && te==obj)) {
197 			answer = te;
198 		} else {
199 			te = te.parentNode;
200 		}
201 	}
202 	return te;
203 },
204 
205 posX : function(obj) {
206 	if (!obj) return 0;
207 	var curleft = 0;
208 	if (obj.offsetParent) {
209 		while(obj.offsetParent) {
210 			curleft += obj.offsetLeft;
211 			obj = obj.offsetParent;
212 		}
213 	} else if (obj.x) {
214 		curleft += obj.x;
215 	}
216 	return curleft;
217 },
218 
219 posY : function(obj) {
220 	if (!obj) return 0;
221 	var curtop = 0;
222 	if (obj.offsetParent) {
223 		while(obj.offsetParent) {
224 			curtop += obj.offsetTop;
225 			obj = obj.offsetParent;
226 		}
227 	} else if (obj.y) {
228 		curtop += obj.y;
229 	}
230 	return curtop;
231 },
232 width : function(obj) {
233 	if (!obj) return 0;
234 	if (obj.offsetWidth) return obj.offsetWidth;
235 	if (obj.offsetParent) {
236 		while(obj.offsetParent) {
237 			if (obj.offsetParent.offsetWidth) return obj.offsetParent.offsetWidth;
238 			obj = obj.offsetParent;
239 		}
240 	}
241 	return 0;
242 },
243 height : function(obj) {
244 	if (!obj) return 0;
245 	if (obj.offsetHeight) return obj.offsetHeight;
246 	if (obj.offsetParent) {
247 		while(obj.offsetParent) {
248 			if (obj.offsetParent.offsetHeight) return obj.offsetParent.offsetHeight;
249 			obj = obj.offsetParent;
250 		}
251 	}
252 	return 0;
253 },
254 getElementPos : function(elt) {
255 	return [ this.posX(elt), this.posY(elt) ];
256 },
257 getElementSize : function (elt) {
258 	return [ this.width(elt), this.height(elt) ];
259 },
260 
261 windowWidth : function() {
262 	var w = 0;
263 	if (typeof( window.innerWidth) == 'number') {
264 		w = window.innerWidth;
265 	} else if( document.documentElement && document.documentElement.clientWidth) {
266 		w = document.documentElement.clientWidth;
267 	} else if( document.body && document.body.clientWidth) {
268 		w = document.body.clientWidth;
269 	}
270 	return w;
271 },
272 windowHeight : function() {
273 	var h = 0;
274 	if (typeof(window.innerHeight) == 'number') {
275 		h = window.innerHeight;
276 	} else if( document.documentElement && document.documentElement.clientHeight) {
277 		h = document.documentElement.clientHeight;
278 	} else if( document.body && document.body.clientHeight) {
279 		h = document.body.clientHeight;
280 	}
281 	return h;
282 },
283 
284 getRelMousePos : function(e) {
285 	var x = 0, y = 0;
286 	if (!e) { e = window.event; }
287 	if (typeof e.offsetX === 'number') {
288 		x = e.offsetX;
289 		y = e.offsetY;
290 	} else if (typeof e.layerX === 'number') {
291 		x = e.layerX;
292 		y = e.layerY;
293 	}
294 	return { x: x, y: y };
295 },
296 
297 
298 getViewPos : function() {
299 	if(typeof window.pageYOffset === 'number') {
300 		return [window.pageXOffset, window.pageYOffset];
301 	} else if(document.body && (document.body.scrollLeft || document.body.scrollTop)) {
302 		return [document.body.scrollLeft, document.body.scrollTop];
303 	} else if(document.documentElement && (document.documentElement.scrollLeft || document.documentElement.scrollTop)) {
304 		return [document.documentElement.scrollLeft, document.documentElement.scrollTop];
305 	} else {
306 		return [0, 0];
307 	}
308 },
309 getViewSize : function() {
310 	if(typeof window.innerWidth === 'number') {
311 		return [window.innerWidth, window.innerHeight];
312 	} else if(document.body && (document.body.clientWidth || document.body.clientHeight)) {
313 		return [document.body.clientWidth, document.body.clientHeight];
314 	} else if(document.documentElement && (document.documentElement.clientWidth || document.documentElement.clientHeight)) {
315 		return [document.documentElement.clientWidth, document.documentElement.clientHeight];
316 	} else {
317 		return [0, 0];
318 	}
319 },
320 
321 getPickerPos : function(elt, align, ps) {
322 	var tp = this.getElementPos(elt);
323 	var ts = this.getElementSize(elt);
324 	var vp = this.getViewPos();
325 	var vs = this.getViewSize();
326 	var a, b, c;
327 	switch(align) {
328 		case 'left': a=1; b=0; c=-1; break;
329 		case 'right':a=1; b=0; c=1;  break;
330 		case 'top':  a=0; b=1; c=-1; break;
331 		default:     a=0; b=1; c=1;  break;
332 	}
333 	var pp = new Array();
334 	var l = (ts[b]+ps[b])/2;
335 	if (!align) {
336 		pp = [tp[a], tp[b]+ts[b]-l+l*c];
337 	}
338 	else {
339 		pp = [
340 		-vp[a]+tp[a]+ps[a] > vs[a] ?
341 			(-vp[a]+tp[a]+ts[a]/2 > vs[a]/2 && tp[a]+ts[a]-ps[a] >= 0 ? tp[a]+ts[a]-ps[a] : tp[a]) :
342 			tp[a],
343 		-vp[b]+tp[b]+ts[b]+ps[b]-l+l*c > vs[b] ?
344 			(-vp[b]+tp[b]+ts[b]/2 > vs[b]/2 && tp[b]+ts[b]-l-l*c >= 0 ? tp[b]+ts[b]-l-l*c : tp[b]+ts[b]-l+l*c) :
345 			(tp[b]+ts[b]-l+l*c >= 0 ? tp[b]+ts[b]-l+l*c : tp[b]+ts[b]-l-l*c)
346 		];
347 	}
348 	return [pp[a], pp[b]];
349 },
350 
351 // TODO : should be in Simplicite.UI ?
352 screenshot : function(type) {
353 	try { return getTop().document.theFeedbackApplet.screenshot(type); } catch(e) {}
354 	return null;
355 },
356 
357 // HTTP Request tools
358 httpCall : function(obj, type, url, params, onSuccess, onError, async, method, user, password) {
359 	try {
360 		if (!method) method = (params ? "POST" : "GET");
361 		if (!async) async = false;
362 
363 		var req;
364 		if (window.XMLHttpRequest) {
365 			req = new XMLHttpRequest();
366 		} else if (window.ActiveXObject) {
367 			try {
368 				req = new ActiveXObject("Msxml2.XMLHTTP");
369 			} catch (e) {
370 				req = new ActiveXObject("Microsoft.XMLHTTP");
371 			}
372 		}
373 		if (!req)
374 			throw ("Error creating the XMLHttpRequest object");
375 		try { if (async) req.withCredentials = true; } catch(ex) {}
376 		req.open(method, url, async, user, password);
377 		req.setRequestHeader("X-Requested-With", "XMLHttpRequest");
378 		if (method=="POST")
379 			// POST encoding must be explicitly set to UTF-8 with XMLHttpRequest posts
380 			req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
381 		if (async) {
382 			var self = this;
383 			req.onreadystatechange = function() { self.httpCallStateChange(obj, req, type, onSuccess, onError); };
384 			req.send(params);
385 			return "";
386 		} else {
387 			req.send(params);
388 			return this.httpCallStateChange(obj, req, type, onSuccess, onError);
389 		}
390 	} catch (e) {
391 		this.error("httpCall for URL : "+url, e);
392 		return null;
393 	}
394 },
395 httpCallStateChange : function(obj, req, type, onSuccess, onError) {
396 	if (req && req.readyState==4) {
397 		if (req.status==200) {
398 			try {
399 				if (type=='json') {
400 					var res = eval("("+req.responseText+")");
401 					return (onSuccess ? onSuccess(res, obj) : res);
402 				} else if (type=='xml') {
403 					return (onSuccess ? onSuccess(req.responseXML, obj) : req.responseXML);
404 				} else
405 					return (onSuccess ? onSuccess(req.responseText, obj) : req.responseText);
406 			} catch(e) {
407 				var err = "httpCallStateChange: Error reading the response";
408 				this.error(err, e);
409 				return (onError ? onError(err, obj) : err);
410 			}
411 		} else if (req.status>0) {
412 			var err = "httpCallStateChange: http status "+req.status+" "+req.statusText;
413 			this.error(err);
414 			return (onError ? onError(err, obj) : err);
415 		}
416 	}
417 }
418 };
419 
420 // Base64 functions
421 (function() {
422 	/**
423 	 * Encode to base64
424 	 * @param input Input string
425 	 * @return Base64 encoded string
426 	 * @function
427 	 */
428 	Simplicite.Tools.base64Encode = function(s) {
429 		if (s === undefined || typeof s != "string" || s.length == 0) return "";
430 		//if (typeof btoa !== "undefined") return btoa(s);
431 
432 		var o = s.replace(/\r\n/g, "\n");
433 		var u = "";
434 		if (Simplicite.ENCODING == "UTF-8") {
435 			for ( var n = 0; n < o.length; n++) {
436 				var c = o.charCodeAt(n);
437 				if (c < 128) {
438 					u += String.fromCharCode(c);
439 				} else if ((c > 127) && (c < 2048)) {
440 					u += String.fromCharCode((c >> 6) | 192);
441 					u += String.fromCharCode((c & 63) | 128);
442 				} else {
443 					u += String.fromCharCode((c >> 12) | 224);
444 					u += String.fromCharCode(((c >> 6) & 63) | 128);
445 					u += String.fromCharCode((c & 63) | 128);
446 				}
447 			}
448 		} else
449 			u = o;
450 
451 		o = "";
452 		var c1, c2, c3 = "";
453 		var e1, e2, e3, e4 = "";
454 		var i = 0;
455 		var k = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
456 		do {
457 			c1 = u.charCodeAt(i++);
458 			c2 = u.charCodeAt(i++);
459 			c3 = u.charCodeAt(i++);
460 
461 			e1 = c1 >> 2;
462 			e2 = ((c1 & 3) << 4) | (c2 >> 4);
463 			e3 = ((c2 & 15) << 2) | (c3 >> 6);
464 			e4 = c3 & 63;
465 
466 			if (isNaN(c2)) {
467 				e3 = e4 = 64;
468 			} else if (isNaN(c3)) {
469 				e4 = 64;
470 			}
471 
472 			o = o +
473 				k.charAt(e1) +
474 				k.charAt(e2) +
475 				k.charAt(e3) +
476 				k.charAt(e4);
477 			c1 = c2 = c3 = "";
478 			e1 = e2 = e3 = e4 = "";
479 		} while (i < u.length);
480 
481 		return o;
482 	};
483 
484 	/**
485 	 * Decode base64
486 	 * @param s Base64 encoded input string
487 	 * @return Decoded string
488 	 * @function
489 	 */
490 	Simplicite.Tools.base64Decode = function(s) {
491 		if (s === undefined || typeof s != "string" || s.length == 0) return "";
492 		//if (typeof atob !== "undefined") return atob(s);
493 
494 		var o = "";
495 		var c1, c2, c3 = "";
496 		var e1, e2, e3, e4 = "";
497 		var i = 0;
498 		var k = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
499 		do {
500 			e1 = k.indexOf(s.charAt(i++));
501 			e2 = k.indexOf(s.charAt(i++));
502 			e3 = k.indexOf(s.charAt(i++));
503 			e4 = k.indexOf(s.charAt(i++));
504 
505 			c1 = (e1 << 2) | (e2 >> 4);
506 			c2 = ((e2 & 15) << 4) | (e3 >> 2);
507 			c3 = ((e3 & 3) << 6) | e4;
508 
509 			o = o + String.fromCharCode(c1);
510 
511 			if (e3 != 64) {
512 				o = o + String.fromCharCode(c2);
513 			}
514 			if (e4 != 64) {
515 				o = o + String.fromCharCode(c3);
516 			}
517 
518 			c1 = c2 = c3 = "";
519 			e1 = e2 = e3 = e4 = "";
520 
521 		} while (i < s.length);
522 
523 		if (Simplicite.ENCODING == "UTF-8") {
524 			var u = "";
525 			var i = 0;
526 			var c = c1 = c2 = 0;
527 			while (i < o.length) {
528 				c = o.charCodeAt(i);
529 				if (c < 128) {
530 					u += String.fromCharCode(c);
531 					i++;
532 				} else if ((c > 191) && (c < 224)) {
533 					c2 = o.charCodeAt(i + 1);
534 					u += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
535 					i += 2;
536 				} else {
537 					c2 = o.charCodeAt(i + 1);
538 					c3 = o.charCodeAt(i + 2);
539 					u += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
540 					i += 3;
541 				}
542 			}
543 			return u;
544 		} else
545 			return o;
546 	};
547 
548 	/**
549 	 * HTTP Basic auth header
550 	 * @param user User name
551 	 * @param password User password
552 	 * @return Authorization header
553 	 * @function
554 	 */
555 	Simplicite.Tools.basicAuthString = function(user, password) {
556 		return "Basic " + this.base64Encode(user + ":" + password);
557 	};
558 
559 })();
560 
561 // Event listener
562 Simplicite.EventListener = {
563 _listeners : {},
564 
565 /**
566  * Get the event listener
567  * @param name event name
568  * @function
569  */
570 get : function(name) {
571 	// Singleton on top window
572 	var l = getTop().Simplicite.EventListener._listeners;
573 	// Named event handlers or all listeners
574 	return name ? l[name] : l;
575 },
576 
577 /**
578  * Add one event listener
579  * @param name event name
580  * @param handler listener function
581  * @param target targetted object
582  * @function
583  */
584 add : function(name, handler, target) {
585 	if (!name) return;
586 	var l = Simplicite.EventListener.get();
587 	if (!l[name]) l[name] = [];
588 	handler = { fn: handler, target: target || this };
589 	l[name].push(handler);
590 },
591 
592 /**
593  * Remove one event listener
594  * @param name event name
595  * @param handler handler, function or target
596  * @function
597  */
598 remove : function(name, handler) {
599 	if (!name) return;
600 	var l = Simplicite.EventListener.get(name);
601 	if (!l) return;
602 	for (var i=0; i<l.length; i++) {
603 		if (l[i]==handler || l[i].fn===handler || l[i].target===handler) {
604 			l.splice(i,1);
605 			i--;
606 		}
607 	}
608 },
609 
610 /**
611  * Fire one event
612  * @function
613  */
614 fire : function(name, emitter, data) {
615 	if (!name) return;
616 	var l = Simplicite.EventListener.get(name);
617 	if (l) {
618 		var event = { name: name, emitter: emitter || this, data: data };
619 		for (var i=0; i<l.length; i++) {
620 			var h = l[i];
621 			h.fn.call(h.target, event);
622 		}
623 	}
624 }
625 }; // Simplicite.EventListener
626