UNPKG

toloframework

Version:

Javascript/HTML/CSS compiler for Firefox OS or nodewebkit apps using modules in the nodejs style.

1,456 lines (1,355 loc) 43.6 kB
// Utilisée pour la compatibilité avec Trail-Passion 3. // Mais vouée à disparaitre. /** * ToloFrameWork v2.2 *------------------------------------------------------------------------------- * L'instanciation étant ce que l'on fait le plus souvent avec * Toloframework, l'écriture en est simplifiée au maximum. Voici un * exemple d'utilisation : * var a = $$("sys.widget.Button", {caption: "Click Me!"}); * * Si la classe n'existe pas, on le trace dans le log, et on retourne * null. Attention ! si la classe existe mais possède une erreur de * syntaxe, on retourne aussi null. * * @param className Nom complet de la classe. * @param attribs [Optionel] Dictionnaire des attributs et de leurs valeurs initiales. */ window.$$ = function(className, attribs) { var single, cls, obj, k, path, names, signals, cur, i, f, sn, n, s; // S'agit-t-il d'un singleton déjà instancié ? single = $$._.singletons[className]; if (single && typeof single === "object") { return single; } if (attribs) $$.trace(attribs, 9); cls = $$.loadClass(className); obj = new cls(); obj.$id = $$.newId(); // Mise à jour des attributs. if (typeof attribs === "object") { for (k in attribs) { if (!k) continue; obj["_" + k] = attribs[k]; } } // La mécanique suivante sert à récupérer la hiérarchie des // classes afin d'appeler les constructeurs les uns après les // autres. Sans cette astuce, les attributs de construction // ne sont pas renseignés quand la super classe exécute son // constructeur. path = []; names = []; signals = {}; cur = cls.prototype; while (cur) { if (cur.$signals) { for (i in cur.$signals) { signals[cur.$signals[i]] = 1; } } f = cur.$init; /** * Attention ! * Quand on ne définit pas de méthode init() et que l'on * hérite d'une classe en définissant une, f pointera non * pas sur null, mais sur la méthode init() du parent. */ if (f) { if (f != path[path.length - 1]) { path.push(f); names.push(cur.$classname); } } cur = cur.$parent; } // Création des signals. for(sn in signals) { // Signal Name. if (sn) { s = new $$.Signal(obj, sn); obj["$_" + sn] = s; } } while (path.length > 0) { f = path.pop(); n = names.pop(); if (f) { f.call(obj); } } if (obj.$singleton) { // C'est un singleton, alors on stoque son unique instance // en mémoire pour réutilisation. $$._.singletons[className] = obj; } return obj; }; /** * Charger la classe className. * @param className Nom de la classe à charger en mémoire. * @param def Si cet argument est renseigné, on ne télécharge pas la classe, * mais on prend cette valeur comme définition. */ $$.loadClass = function(className, def) { var cls = $$._.classes[className], superclass, s, k, i, // Signal name. sn, // Function name. name; if (!cls) { // La classe n'a pas encore été téléchargée. if (def) { // Si on spécifie la défintion de la classe en argument, // on écrase l'ancienne définition potentielle. $$._.classes[className] = null; } else { def = window["TFW::" + className]; } if (!def) { console.error("[$$.loadClass] This class does not exist: " + className); return; } // Création de la classe à partir de sa définition. cls = function () {}; superclass = $$.Object; if (def.superclass) { superclass = $$.loadClass(def.superclass); } $$.trace("This class extends " + superclass.prototype.$classname, 9); // Héritage cls.prototype = new superclass(); // Eviter que le constructeur de cette classe soit celui de la super classe. cls.prototype.constructor = cls; // Définition de l'attribut $parent pour accéder aux méthodes parentes. cls.prototype.$parent = superclass.prototype; // Multilangues. cls.prototype.$lang = def.lang || {}; if (def.lang && typeof def.lang === "object") { for (k in def.lang) { cls.prototype.$defLang = k; break; } cls.prototype.$ = $$.dico; } // Conserver le nom de la super classe. cls.prototype.$superclass = superclass.prototype.$classname; // Conserver le nom de la classe. cls.prototype.$classname = className; // Les noms des signals. cls.prototype.$signals = def.signals; // Est-ce un singleton ? cls.prototype.$singleton = def.singleton; // Espace réservé aux membres statiques de la classe. s = {className: className}; $$._.statics[className] = s; cls.prototype.$static = $$._.statics; // Valeurs par défaut des attributs. if (def.attributes) { for (k in def.attributes) { cls.prototype["_" + k] = def.attributes[k]; } } // Déclaration du constructeur. cls.prototype.$init = def.init; // Définition des méthodes liées aux signaux. if (def.signals) { for (i in def.signals) { sn = def.signals[i]; $$._declareSignal(cls, sn); } } // Détacher tous les signaux : entrants et sortans. cls.prototype.unbind = function() { console.log("TODO: unbind!"); }; // Assigner toutes les méthodes. for (name in def.functions) { cls.prototype[name] = def.functions[name]; } // Mise en cache de la classe. $$._.classes[className] = cls; if (def.css) { // Chargement d'un CSS. $$.loadCss(className, "all", "style/" + className + "/style.css"); } // Appel d'un éventuel constructeur de classe. if (typeof def.classInit === "function") { def.classInit($$._.statics[className]); } } return cls; }; $$._declareSignal = function(cls, sn) { cls.prototype["fire" + sn] = function(a) { this["$_" + sn].emit(a); }; cls.prototype[sn] = function(a,b,c) { this["$_" + sn].connect(a,b,c); return this; }; cls.prototype["unbind" + sn] = function(a,b,c) { this["$_" + sn].disconnect(a,b,c); return this; }; }; $$.addScript = function(code, id) { var s = document.createElement("script"); if (id !== undefined) { s.setAttribute("id", id); } s.innerHTML = "//<!-- \n" + code + "\n// -->"; document.querySelector("head").appendChild(s); return s; }; $$.stopPropagation = function(e) { var evt = e ? e : window.event; if (evt.stopPropagation) { evt.stopPropagation(); evt.preventDefault(); } if (evt.cancelBubble) { evt.cancelBubble = true; evt.returnValue = false; } }; $$.addEvent = function(element, type, handler) { var handlers; if (type === "mouseenter") { return $$.addEvent(element, "mouseover", $$._.mouseEnter(handler)); } if (type === "mouseleave") { return $$.addEvent(element, "mouseout", $$._.mouseEnter(handler)); } if (!handler.$$guid) handler.$$guid = $$.addEvent.guid++; if (!element.$events) element.$events = {}; handlers = element.$events[type]; if (!handlers) { handlers = element.$events[type] = {}; if (element["on" + type]) { handlers[0] = element["on" + type]; } } handlers[handler.$$guid] = handler; if (type == "DOMMouseScroll") { // Firefox gère un événement particulier pour le MouseWheel. element.addEventListener(type, handler, false); } else { element["on" + type] = $$._.handleEvent; } return null; }; $$.addEvent.guid = 1; $$.removeEvent = function(element, type, handler) { if (element.$events && element.$events[type]) { if (handler === undefined) { delete element.$events[type]; } else { delete element.$events[type][handler.$$guid]; } } }; /** * Retourne true si "child" est un enfant de "parent". */ $$.isChildElement = function(parent, child) { if (parent === child) { return false; } while (child && child !== parent) { child = child.parentNode; } return child === parent; }; $$.App = {}; $$.Object = function() {}; /** * Cette méthod sert à récupérer le prototype d'un ancètre particulier. * Vous pouvez ainsi appeler une méthode d'un parent. * Il est aussi possible d'utiliser cette méthode pour savoir si un objet * appartient à une classe donnée. En effet, la méthode retourne null si * la classe n'existe pas dansla chaîne d'héritage. * @code * var a = $$("B"); * a.foo(3.14); * a.$super("A").foo.call(a, 3.14); * @code */ $$.Object.prototype.$super = function(classname) { var p = window[this.$classname]; while (p) { if (p.prototype.$classname == classname) { return p.prototype; } p = p.prototype.$parent; if (!p) break; p = window[p.$classname]; } return null; }; // URL d'une resource. $$.Object.prototype.$url = function(file) { return $$.url(file, this.$classname); }; $$.Object.prototype.$classname = null; $$.Object.prototype.$parent = null; $$.Signal = function(sender, name) { this._name = name; this._sender = sender; this._slots = {}; }; $$.Signal.prototype.connect = function(obj, slot, data) { var k = this._key(obj, slot), s = this._slots[k]; if (s === undefined) { s = [obj, slot, []]; this._slots[k] = s; } s[2].push(data); return k; }; $$.Signal.prototype.disconnect = function(obj, slot) { var k = (slot ? this._key(obj, slot) : obj); delete this._slots[k]; }; $$.Signal.prototype.emit = function(arg) { var i, s, obj, f, j, d, msg; try { for (i in this._slots) { s = this._slots[i]; if (s) { obj = s[0]; // Slot function. f = s[1]; if (f) { f = $$.slot(obj, f); for (j in s[2]) { d = s[2][j]; try { f.call(obj, arg, d, this._sender); } catch (e) { throw Error("[$$.Signal.emit] Exception occured in slot " + obj.$classname + "." + s[1] + "\n" + e); } } } else { f = s[0]; f(arg, s[2], this._sender); } } } } catch (e) { msg = "[$$.Signal.emit] Exception occured in signal " + this._sender.$classname + "." + this._name + "\n" + e; $$.trace(msg); throw Error(msg); } }; $$.Signal.prototype._key = function(obj, slot) { if (!slot) { return "#" + $$.newId(); } return obj.$id + slot; }; /** * Tous les paramètres de toloframework se trouvent ici. */ $$.params = { // Graine utilisée pour empêcher la mise en cache des fichiers lus // par AJAX. En effet, cette valeur sera ajoutée à la fin de l'URL // pour éviter d'aller la chercher dans le cache. seed: 0, // Emplacement des fichiers de classes. root: "tfw", // Extension des fichiers de classes. // Certains proxies rejettent l'extension ".js" : // il peut alors être utile d'utiliser ".htm". jsExt: "js.htm", debug: 0, compression: true, lang: "fr" }; /** * Les variables internes sont stockées ici. * Il ne faut surtout pas les modifier depuis l'extérieur. */ $$._ = { // Identifiant unique pour les objets. id: 0, // Liste des classes téléchargées. classes: {}, // Liste des codes dources pour les classes téléchargées. classesSrc: {}, // Liste les classes qui sont des singletons. // La clef est le nom complet de la classe et la valeur est // soit une chaîne vide pour indiquer que la classe est un // singleton non instancié, soit l'objet singleton lui-même. singletons: {}, // Liste des CSS téléchargés. styles: {}, // Liste des variables statiques de chaque classe. statics: {}, // Indentation pour la trace. traceIndent: "", /** * Voir http://blog.stchur.com/2007/03/15/mouseenter-and-mouseleave-events-for-firefox-and-other-non-ie-browsers/ */ mouseEnter: function(f) { return function(e) { var child = e.relatedTarget; if (this === child || $$.isChildElement(this, child)) { return; } f.call(this, e); }; }, handleEvent: function(evt) { var handlers, i; evt = evt || window.event; if (this.$events) { if (!evt.stopPropagation) { // IE. evt.stopPropagation = function() { window.event.cancelBubble = true; }; } if (!evt.preventDefault) { // IE. evt.preventDefault = function() { window.event.returnValue = false; }; } handlers = this.$events[evt.type]; for (i in handlers) { this.$$handleEvent = handlers[i]; this.$$handleEvent(evt); } } }, /** * Constructeur commun à tous les objets. * Pour créer un nouvel objet, on lui passe un dictionnaire * d'attributs. Les noms finaux de ces attributs seront préfixés * par un underscore. * * @param obj Objet à construire. * @param attribs Dictionnaire des attributs par défaut. */ init: function (obj, attribs) { var k; if (typeof attribs == "object") { for (k in attribs) { if (k == null) continue; obj["_" + k] = attribs[k]; } } else { obj["_init"] = attribs; } } }; /** * Certaines classes peuvent avoir besoin de fichier annexes * (images, fontes, css, ...). La convention est de les stoquer * dans un répertoire portant le nom du package de la classe. * Par exemple, la classe "sys.widget.Widget" utilise des fichiers * provenant du sous-répertoire "sys.widget/". * Pour connaître l'URL du fichier dont on a besoin, il suffit de * faire appel à cette méthode. * * Pour simplifier les appels, tous les objets ont une méthode $url(). * @param filename Fichier resource dont on veut l'URL. * @param classname Nom de la classe. */ $$.url = function(filename, classname) { var prefix = "", items; if (classname) { items = classname.split('.'); items.pop(); prefix = items.join('.') + "/"; } return $$.params.root + "/src/" + prefix + filename; }; /** * Lit un fichier au format JSON stoqué dans le répertoire "pub". * * @param filename Nom du fichier à accéder. * @param obj Objet à notifier quand le fichier est téléchargé. * @param slotSuccess Méthode à appeler en cas de succés. * Elle doit accepter deux arguments : * la donnée téléchargée et le nom du fichier. * @param slotError Méthode à appeler en cas d'échec. * Elle doit accepter deux arguments : * le message d'erreur et le nom du fichier. */ $$.getData = function(filename, obj, slotSuccess, slotError) { return $$.loadJSON($$.params.root + "/pub/" + filename); }; $$.loadJSON = function(url, obj, slotSuccess, slotError) { if (!url) { throw new Error("[$$.loadJSON(url, obj, success, error)] Missing url!"); } if (!obj) { throw new Error("[$$.loadJSON(url, obj, success, error)] Missing obj!"); } var local_filename = url, local_obj = obj, local_success = $$.slot(obj, slotSuccess), local_error = (slotError ? $$.slot(obj, slotError) : null); try { $$.ajax( { url: url, cache: false, dataType: "json", error: function(xhr, status, msg) { if (local_error) { local_error.call(local_obj, msg, local_filename); } else { alert("Fatal error in $$.loadJSON(" + local_filename + ", ...):\n" + "url = " + url + "\n" + msg); } }, success: function(data) { // Si tout va bien, on appelle la callback. try { if (local_success) { local_success.call(local_obj, data, local_filename); } } catch (e) { throw Error("[$$.loadJSON] Unexpected error in success function " + local_obj.$classname + "." + local_success + "()\n" + e); } } } ); } catch (err) { throw Error("[Error in $$.loadJSON] url = " + url + "\n" + err); } }; $$.loadText = function(url, obj, slotSuccess, slotError) { if (!url) { throw Error("[$$.loadText(url, obj, success, error)] Missing url!"); } if (!obj) { throw Error("[$$.loadText(url, obj, success, error)] Missing obj!"); } var local_filename = url, local_obj = obj, local_success = $$.slot(obj, slotSuccess), local_error = (slotError ? $$.slot(obj, slotError) : null); try { $$.ajax( { url: url, cache: false, dataType: "text", error: function(xhr, status, msg) { if (local_error) { local_error.call(local_obj, msg, local_filename); } else { alert("Fatal error in $$.loadText(" + local_filename + ", ...):\n" + "url = " + url + "\n" + msg); } }, success: function(data) { // Si tout va bien, on appelle la callback. try { if (local_success) { local_success.call(local_obj, data, local_filename); } } catch (e) { throw Error("[$$.loadText] Unexpected error in success function " + local_obj.$classname + "." + local_success + "()\n" + e); } } } ); } catch (err) { throw Error("[Error in $$.loadText] url = " + url + "\n" + err); } }; /** * Charge de façon asynchrone une feuille de style CSS. * Si elle a déjà été chargée, rien ne se passe. */ $$.loadCss = function(name, media, path) { var key, url, head, css; if (media === undefined) media = "all"; if (path === undefined) path = $$.params.root + "/src/" + name; key = name + "." + media; if ($$._.styles[key] == null) { url = path + "?seed=" + $$.params.seed + Math.random(); head = document.getElementsByTagName("head")[0]; css = document.createElement('link'); css.type = 'text/css'; css.rel = 'stylesheet'; css.href = url; css.media = media; head.appendChild(css); $$._.styles[key] = true; } }; /** * Ajoute à l'objet original tous les attributs de l'objet overrider. * S'il y a des attributs en communs, les valeurs seront écrasées par * overrider. * * @param original Objet initial. * @param overrider Objet qui va surcharger l'original. * @param onlyNewValues Si true, on ajoute uniquement les clefs inexistantes. * Par défaut, il est à false. * @return Objet original étendu par overrider. */ $$.extend = function(original, overrider, onlyNewValues) { var k; if (original === undefined) original = {}; if (overrider) { for (k in overrider) { if (k === undefined) continue; if (onlyNewValues && original[k] === undefined) continue; original[k] = overrider[k]; } } return original; }; /** * Copie un objet de type JSON, c'est-à-dire * qui ne contient pas de fonctions ni d'objets natifs. */ $$.clone = function(obj) { var r, k, i; if ($$.isArray(obj)) { r = []; for (i in obj) { r.push($$.clone(obj[i])); } return r; } if (typeof obj == "object") { r = {}; for (k in obj) { r[k] = $$.clone(obj[k]); } return r; } return obj; }; /** * Retourne un identifiant unique. */ $$.newId = function() { $$._.id++; if ($$._.id > 2000000000) { $$._.id = 0; } return $$._.id; }; /** * Retourne un slot sur un objet donné. * Par exemple, si l'objet toto a une méthode toString(), * on peut l'appeler comme ceci : * $$.slot(toto, "toString").call(toto) * * @param obj Objet sur lequel on veut appeler une méthode. * @param functionName Chaîne contenant le nom de la méthode à appeler. */ $$.slot = function(obj, functionName) { var cls, f; if (!obj.$classname) { $$.trace("============================================================"); $$.trace("ERROR: [$$.slot] Not a Toloramework object!"); $$.trace("obj=...",5);$$.trace(obj,5); $$.trace("functionName=...",5);$$.trace(functionName,5); $$.trace("------------------------------------------------------------"); throw Error("[$$.slot(" + functionName + ")] Not a Toloframework object!"); } cls = $$._.classes[obj.$classname]; if (!cls) { throw Error("[$$.slot(" + functionName + ")] Class not defined: " + obj.$classname); } f = cls.prototype[functionName]; if (!f) { throw Error("[$$.slot()] Slot not found: " + obj.$classname + "." + functionName); } return f; }; /** * Appel d'une méthode au bout d'un certain nombre de millisecondes. * Le premier argument peut être omis. Dans ce cas, il vaudra 1 milliseconde. * * @param delay Nombre de millisecondes avant exécution de la méthode. * @param obj Objet sur lequel on veut appeler une méthode. * @param slot Chaîne contenant le nom de la méthode à appeler. * @param data [Optionel] Argument à passer à la méthode. * @return Identifiant servant à annuler l'appel avec la fonction window.clearTimeout(id). */ $$.invokeLater = function(delay, obj, slot, data) { if (typeof delay == 'object') { data = slot; slot = obj; obj = delay; delay = 1; } var local_obj = obj, local_data = data, process = $$.slot(obj, slot); return window.setTimeout( function() { if (typeof local_obj == 'function') { try { local_obj(slot); } catch (x) { $$.trace("Exception in the following function:"); $$.trace(local_obj); throw Error("[$$.invokeLater]\n" + x); } } else { try { process.call(local_obj, local_data); } catch (x) { throw Error("[$$.invokeLater] " + local_obj.$classname + "." + slot + ":\n" + x); } } }, delay ); }; $$.init = function(options) { $$.params = $$.extend($$.params, options, true); }; if (String.prototype.trim === undefined) { $$._trimLeft = new RegExp("^\\s+"); $$._trimRight = new RegExp("\\s+$"); String.prototype.trim = function() { return this.toString() .replace( $$._trimLeft, "" ) .replace( $$._trimRight, "" ); }; } if ( window.JSON && window.JSON.parse ) { $$.parseJSON = window.JSON.parse; } else { $$.parseJSON = function(json) { try { if ( typeof json !== "string" || !json ) { return null; } json = $$.trim(json); return (new Function("return " + json))(); } catch (x) { throw Error("Invalid JSON!\n" + x + "\n" + json); } }; } /** * Cette fonction provient du site http://code.google.com/p/jquery-json/ * --------------------------------------------------------------------- * Convertit un objet en chaîne de caractères au format JSON. */ $$.toJSON = function(o){ var type = typeof(o), month, day, year, hours, minutes, seconds, milli, ret, i, pairs, k, name, val; if (o === null || type == "undefined") return "null"; if (type == "number" || type == "boolean") return o + ""; if (type == "string") return $$.quoteString(o); if (type == 'object') { if (typeof o.toJSON == "function") return $$.toJSON( o.toJSON() ); if (o.constructor === Date) { month = o.getUTCMonth() + 1; if (month < 10) month = '0' + month; day = o.getUTCDate(); if (day < 10) day = '0' + day; year = o.getUTCFullYear(); hours = o.getUTCHours(); if (hours < 10) hours = '0' + hours; minutes = o.getUTCMinutes(); if (minutes < 10) minutes = '0' + minutes; seconds = o.getUTCSeconds(); if (seconds < 10) seconds = '0' + seconds; milli = o.getUTCMilliseconds(); if (milli < 100) milli = '0' + milli; if (milli < 10) milli = '0' + milli; return '"' + year + '-' + month + '-' + day + 'T' + hours + ':' + minutes + ':' + seconds + '.' + milli + 'Z"'; } if (o.constructor === Array) { ret = []; for (i = 0; i < o.length; i++) ret.push( $$.toJSON(o[i]) || "null" ); return "[" + ret.join(",") + "]"; } pairs = []; for (k in o) { type = typeof k; if (type == "number") { name = '"' + k + '"'; } else if (type == "string") { name = $$.quoteString(k); } else { continue; //skip non-string or number keys } if (typeof o[k] == "function") { continue; //skip pairs where the value is a function. } val = $$.toJSON(o[k]); pairs.push(name + ":" + val); } return "{" + pairs.join(", ") + "}"; } return "null"; }; /** * Cette fonction provient du site http://code.google.com/p/jquery-json/ * --------------------------------------------------------------------- * Returns a string-repr of a string, escaping quotes intelligently. * Mostly a support function for toJSON. * * Examples: * >>> $$.quoteString("apple") * "apple" * * >>> $$.quoteString('"Where are we going?", she asked.') * "\"Where are we going?\", she asked." */ $$.quoteString = function(string) { var _escapeable = /["\\\x00-\x1f\x7f-\x9f]/g, _meta = { '\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\' }; if (string.match(_escapeable)) { return '"' + string.replace( _escapeable, function (a) { var c = _meta[a]; if (typeof c === 'string') return c; c = a.charCodeAt(); return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16); }) + '"'; } return '"' + string + '"'; }; $$.xhr = function() { var xhr=null; try { xhr = new XMLHttpRequest(); } catch(e) { try { xhr = new ActiveXObject("Msxml2.XMLHTTP"); } catch (e2) { try { xhr = new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) { throw Error("Unable to create XMLHttpRequest needed for Ajax!"); } } } return xhr; }; $$.xhrResponse = function(response, dataType) { if (dataType == "xml") { return response.responseXML; } var text = response.responseText; if (dataType == "json") { return $$.parseJSON(text); } return text; }; $$.ajax = function(options) { options = $$.extend( { async: true, dataType: "text", type: "GET", data: null, cache: true, success: function(data) { $$.trace("[$$.ajax] Success!", 3); $$.trace(data, 4); }, error: function(data) { $$.trace("[$$.ajax] Error!", 3); $$.trace(data, 4); } }, options ); // Astuce pour contrôler la mise en cache. if (options.url.indexOf("?") > -1) { options.url += "&seed=" + $$.params.seed + Math.random(); } else { options.url += "?" + $$.params.seed + Math.random(); } var xhr = $$.xhr(), params, key, val; if (options.async) { xhr.onreadystatechange = function() { if(xhr.readyState == 4) { if(xhr.status == 200) { options.success($$.xhrResponse(xhr, options.dataType), xhr.statusText, xhr); } else { options.error(xhr, xhr.statusText); } } }; } xhr.open(options.type, options.url, options.async); params = null; if (options.type == "POST" && options.data) { params = ""; if (typeof options.data == "object") { for (key in options.data) { val = options.data[key]; if (params.length > 0) { params += "&"; } params += key + "=" + encodeURIComponent(val); } } //xhr.setRequestHeader("Content-Type", "text/plain;charset=UTF-8"); xhr.setRequestHeader( "Content-type", "application/x-www-form-urlencoded"); xhr.setRequestHeader("Content-length", params.length); xhr.setRequestHeader("Connection", "close"); } try { xhr.send(params); } catch (e) { if (options.error) { options.error(e.message); } else { throw Error("[$$.ajax] " + e.message + "\n" + options.url); } } if (!options.async) { return $$.xhrResponse(xhr, options.dataType); } return null; }; $$.service = function(p_service, p_input, p_obj, p_slot) { var callback = null, obj, slot, f, arg; if (!p_obj) { throw Error("[$$.service] Missing argument #3: object!"); } if (!p_slot) { callback = p_obj; } obj = p_obj; slot = p_slot; f = slot ? $$.slot(obj, slot): null; arg = { s: p_service, i: $$.toJSON(p_input) }; $$.ajax( { url: $$.params.root + "/server.php", data: arg, type: "POST", cache: false, dataType: "json", success: function(data) { // Si tout va bien, on appelle la callback. try { if (callback) { callback(data); } else { f.call(obj, data); } } catch (e) { throw Error("[$$.service] Unexpected error in callback function " + obj.$classname + "." + slot + "()\n" + e); } } } ); }; // Local storage if (window["localStorage"]) { /** * Ce navigateur supporte le localStorage. */ $$.localLoad = function(key, defVal) { var val = window.localStorage.getItem( key ); if (val === null) { return defVal; } try { val = $$.parseJSON(val); } catch(e) { val = defVal; } return val; }; $$.localSave = function(key, val) { window.localStorage.setItem(key, $$.toJSON(val)); }; $$.sessionLoad = function(key, defVal) { var val = window.sessionStorage.getItem( key ); if (val === null) { return defVal; } return $$.parseJSON( val ); }; $$.sessionSave = function(key, val) { window.sessionStorage.setItem( key, $$.toJSON( val ) ); }; } else { /** * Ce navigateur n supporte pas le localStorage, * alors nous allons l'émuler avec des cookies. */ $$.localLoad = function(key) { var theCookie = "" + document.cookie, ind = theCookie.indexOf(key), ind1; if (ind == -1 || key == "") return null; ind1 = theCookie.indexOf(';', ind); if (ind1 == -1) ind1 = theCookie.length; return $$.parseJSON(decodeURIComponent(theCookie.substring(ind + key.length + 1, ind1))); }; $$.localSave = function(key, val) { var nDays = 365, today = new Date(), expire = new Date(); expire.setTime(today.getTime() + 3600000*24*nDays); document.cookie = key + "=" + encodeURIComponent($$.toJSON(val)) + ";expires=" + expire.toGMTString(); }; } /** * Il peut-être utile d'activer les traces en cas de problème. * On peut donc trufer le code d'appels à cette fonction $$.trace() * et régler leur affichage au runtime à l'aide de la variable $$.params.debug. * Cette dernière est nue valeur numérique aui indique le niveau le plus élévé * de a trace à afficher. Ainsi, s'il vaut 2, l'appel suivant produira une trace : * $$.trace("Coucou", 2); * mais pas celui-ci : * $$.trace("Plus de détails", 3); */ $$.trace = function(msg, level) { if (!$$.params.debug) return; if (!level) level = 1; if ($$.params.debug >= level) { var begin = ""; if (typeof msg === 'string') { begin = msg.substring(0, 4); if (begin == '<<< ') { if ($$._.traceIndent.length > 2) { $$._.traceIndent = $$._.traceIndent.substring(0, $$._.traceIndent.length - 2); } } } console.log($$._.traceIndent, msg); if (begin == '>>> ') { $$._.traceIndent += " "; } } }; // Prévenir les erreurs sur les browsers qui ne supportent pas l'objet "console". if (window.console === undefined) { window.console = {}; } var consoleMethods = ["log", "info", "warn", "error"], i; for (i = 0 ; i < consoleMethods.length ; i++) { if (window.console[consoleMethods[i]] === undefined) { window.console[consoleMethods[i]] = function(){}; } } /** * Retourne un dictionnaire contenant les arguments passés dans l'URL. * Ceci fonctionne pour des arguments du style "a=toto&b=titi". * Si le "=" est omis, la valeur est renvoyée dans le champ "" (chaîne vide) du dictionnaire. */ $$.urlArgs = function() { var f = {}, t = location.search, i, x; if (t.length < 2) return f; t = t.substring(1).split('&'); for (i=0; i<t.length; i++){ x = t[i].split('='); if (x.length == 1) { f[""] = decodeURIComponent(x[0]); } else { f[x[0]] = decodeURIComponent(x[1]); } } return f; }; $$.browser = function() { var s = navigator.userAgent, j, i = s.indexOf("MSIE"); if (i > -1) { j = s.indexOf(".", i + 5); return ["msie", parseInt(s.substring(i + 4, j))]; } i = s.indexOf("Firefox/"); if (i > -1) { j = s.indexOf(".", i + 8); return ["firefox", parseInt(s.substring(i + 8, j))]; } return [s, 0]; }; $$.isArray = function (x) { return (typeof x == 'object' && x instanceof Array); }; $$.htmlentities = function(t) { var entities = { '"': '&quot;', // 34 22 '&': '&amp;', // 38 26 '\'': '&#39;', // 39 27 '<': '&lt;', // 60 3C '>': '&gt;', // 62 3E '\^': '&circ;', // 94 5E '‘': '&lsquo;', // 145 91 '’': '&rsquo;', // 146 92 '“': '&ldquo;', // 147 93 '”': '&rdquo;', // 148 94 '•': '&bull;', // 149 95 '–': '&ndash;', // 150 96 '—': '&mdash;', // 151 97 '˜': '&tilde;', // 152 98 '™': '&trade;', // 153 99 'š': '&scaron;', // 154 9A '›': '&rsaquo;', // 155 9B 'œ': '&oelig;', // 156 9C '': '&#357;', // 157 9D 'ž': '&#382;', // 158 9E 'Ÿ': '&Yuml;', // 159 9F ' ': '&nbsp;', // 160 A0 '¡': '&iexcl;', // 161 A1 '¢': '&cent;', // 162 A2 '£': '&pound;', // 163 A3 ' ': '&curren;', // 164 A4 '¥': '&yen;', // 165 A5 '¦': '&brvbar;', // 166 A6 '§': '&sect;', // 167 A7 '¨': '&uml;', // 168 A8 '©': '&copy;', // 169 A9 'ª': '&ordf;', // 170 AA '«': '&laquo;', // 171 AB '¬': '&not;', // 172 AC '­': '&shy;', // 173 AD '®': '&reg;', // 174 AE '¯': '&macr;', // 175 AF '°': '&deg;', // 176 B0 '±': '&plusmn;', // 177 B1 '²': '&sup2;', // 178 B2 '³': '&sup3;', // 179 B3 '´': '&acute;', // 180 B4 'µ': '&micro;', // 181 B5 '¶': '&para', // 182 B6 '·': '&middot;', // 183 B7 '¸': '&cedil;', // 184 B8 '¹': '&sup1;', // 185 B9 'º': '&ordm;', // 186 BA '»': '&raquo;', // 187 BB '¼': '&frac14;', // 188 BC '½': '&frac12;', // 189 BD '¾': '&frac34;', // 190 BE '¿': '&iquest;', // 191 BF 'À': '&Agrave;', // 192 C0 'Á': '&Aacute;', // 193 C1 'Â': '&Acirc;', // 194 C2 'Ã': '&Atilde;', // 195 C3 'Ä': '&Auml;', // 196 C4 'Å': '&Aring;', // 197 C5 'Æ': '&AElig;', // 198 C6 'Ç': '&Ccedil;', // 199 C7 'È': '&Egrave;', // 200 C8 'É': '&Eacute;', // 201 C9 'Ê': '&Ecirc;', // 202 CA 'Ë': '&Euml;', // 203 CB 'Ì': '&Igrave;', // 204 CC 'Í': '&Iacute;', // 205 CD 'Î': '&Icirc;', // 206 CE 'Ï': '&Iuml;', // 207 CF 'Ð': '&ETH;', // 208 D0 'Ñ': '&Ntilde;', // 209 D1 'Ò': '&Ograve;', // 210 D2 'Ó': '&Oacute;', // 211 D3 'Ô': '&Ocirc;', // 212 D4 'Õ': '&Otilde;', // 213 D5 'Ö': '&Ouml;', // 214 D6 '×': '&times;', // 215 D7 'Ø': '&Oslash;', // 216 D8 'Ù': '&Ugrave;', // 217 D9 'Ú': '&Uacute;', // 218 DA 'Û': '&Ucirc;', // 219 DB 'Ü': '&Uuml;', // 220 DC 'Ý': '&Yacute;', // 221 DD 'Þ': '&THORN;', // 222 DE 'ß': '&szlig;', // 223 DF 'à': '&agrave;', // 224 E0 'á': '&aacute;', // 225 E1 'â': '&acirc;', // 226 E2 'ã': '&atilde;', // 227 E3 'ä': '&auml;', // 228 E4 'å': '&aring;', // 229 E5 'æ': '&aelig;', // 230 E6 'ç': '&ccedil;', // 231 E7 'è': '&egrave;', // 232 E8 'é': '&eacute;', // 233 E9 'ê': '&ecirc;', // 234 EA 'ë': '&euml;', // 235 EB 'ì': '&igrave;', // 236 EC 'í': '&iacute;', // 237 ED 'î': '&icirc;', // 238 EE 'ï': '&iuml;', // 239 EF 'ð': '&eth;', // 240 F0 'ñ': '&ntilde;', // 241 F1 'ò': '&ograve;', // 242 F2 'ó': '&oacute;', // 243 F3 'ô': '&ocirc;', // 244 F4 'õ': '&otilde;', // 245 F5 'ö': '&ouml;', // 246 F6 '÷': '&divide;', // 247 F7 'ø': '&oslash;', // 248 F8 'ù': '&ugrave;', // 249 F9 'ú': '&uacute;', // 250 FA 'û': '&ucirc;', // 251 FB 'ü': '&uuml;', // 252 FC 'ý': '&yacute;', // 253 FD 'þ': '&thorn;', // 254 FE 'ÿ': '&yuml;' // 255 FF }, newText = "", idx, lastIdx = 0, r; for (idx = 0 ; idx < t.length ; idx++) { r = entities[t.charAt(idx)]; if (r !== undefined) { newText += t.substr(lastIdx, idx) + r; lastIdx = idx + 1; } } newText += t.substr(lastIdx); return newText; }; /** * Retourne la taille d'un dictionnaire. */ $$.size = function(obj) { var size = 0, key; for (key in obj) { if (obj.hasOwnProperty(key)) size++; } return size; }; /** * Retourner la valeur numérique entière de x. * Si min et/ou max sont définis, ce sont des contraintes à appliquer à x. */ $$.intVal = function(x, min, max) { x = parseInt(x); if (isNaN(x)) return 0; if (min !== undefined && x < min) x = min; if (max !== undefined && x > max) x = max; return x; }; /** * Retourner la valeur numérique flottante de x. * Si min et/ou max sont définis, ce sont des contraintes à appliquer à x. */ $$.floatVal = function(x, min, max) { x = parseFloat(x); if (isNaN(x)) return 0; if (min !== undefined && x < min) x = min; if (max !== undefined && x > max) x = max; return x; }; /** * Multilangue. */ $$.lang = function(lang) { if (lang === undefined) { return $$.localLoad("tfw.Language", $$.params.lang) || navigator.language || navigator.browserLanguage || "fr"; } $$.params.lang = lang; $$.localSave("tfw.Language", lang); }; $$.dico = function(k) { var dic = this.$lang[$$.lang()], txt, newTxt, i, c, lastIdx, pos; if (!dic) { dic = this.$lang[this.$defLang]; } if (!dic) { return "[[" + k + "]]"; } txt = dic[k]; if (!txt) { return "[[" + k + "]]"; } if (arguments.length > 1) { newTxt = ""; lastIdx = 0; for (i = 0 ; i < txt.length ; i++) { c = txt.charAt(i); if (c === '$') { newTxt += txt.substring(lastIdx, i); i++; pos = txt.charCodeAt(i) - 48; if (pos < 1 || pos >= arguments.length) { newTxt += "$" + txt.charAt(i); } else { newTxt += arguments[pos]; } lastIdx = i + 1; } else if (c === '\\') { newTxt += txt.substring(lastIdx, i); i++; newTxt += txt.charAt(i); lastIdx = i + 1; } } newTxt += txt.substr(lastIdx); txt = newTxt; } return txt; };