toloframework
Version:
Javascript/HTML/CSS compiler for Firefox OS or nodewebkit apps using modules in the nodejs style.
317 lines (302 loc) • 10.4 kB
JavaScript
require("polyfill.promise");
var Storage = require("tfw.storage");
var Listeners = require("tfw.listeners");
var currentUser = null;
var changeEvent = new Listeners();
var config = {url: "tfw"};
var saved = Storage.local.get("nigolotua");
if (Array.isArray(saved)) {
config.usr = saved[0];
config.pwd = saved[1];
}
exports.BAD_ROLE = -1;
exports.BAD_TYPE = -2;
exports.CONNECTION_FAILURE = -3;
exports.MISSING_AUTOLOGIN = -4;
exports.UNKNOWN_USER = -5;
exports.HTTP_ERROR = -6;
function svc(name, args, url) {
console.info("[tfw.web-service]", name, args);
return new Promise(
function(resolve, reject) {
if (typeof url === 'undefined') url = config.url;
var that = this;
var xhr = new XMLHttpRequest({mozSystem: true});
if ('withCredentials' in xhr) {
xhr.open("POST", url + "/svc.php", true);
xhr.withCredentials = true; // Indispensable pour le CORS.
} else {
// IE
xhr = new XDomainRequest();
xhr.open("POST", url + "/svc.php");
}
xhr.onload = function() {
if (xhr.status != 200) {
reject(
{
id: exports.HTTP_ERROR,
msg: "(" + xhr.status + ") " + xhr.statusText,
status: xhr.status
}
);
}
var value = xhr.responseText;
if (typeof value === "string") {
if (value.substr(0, 1) == "!") {
reject(
{
id: exports.BAD_ROLE,
err: Error("Service \"" + name + "\" needs role \""
+ value.substr(1) + "\"!")
}
);
}
var valueObject;
try {
valueObject = JSON.parse(value);
}
catch (ex) {
console.error("[tfw.web-service:svc] Value = ", value);
reject(
{
id: exports.BAD_TYPE,
err: Error("Service \"" + name
+ "\" should return a valid JSON!\n" + ex)
}
);
}
resolve(valueObject);
} else {
reject(
{
id: exports.BAD_TYPE,
err: Error("Service \"" + name + "\" should return a string!")
}
);
}
};
xhr.onerror = function() {
reject(
{
id: exports.HTTP_ERROR,
err: "(" + xhr.status + ") " + xhr.statusText,
status: xhr.status
}
);
};
var params = "s=" + encodeURIComponent(name)
+ "&i=" + encodeURIComponent(JSON.stringify(args));
xhr.setRequestHeader(
"Content-type",
"application/x-www-form-urlencoded");
xhr.withCredentials = true; // Indispensable pour le CORS.
xhr.send(params);
}
);
}
/**
* Load a JSON file and return a Promise.
* @param {string} path Local path relative to the current HTML page.
*/
exports.loadJSON = function(path) {
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest({mozSystem: true});
xhr.onload = function() {
var text = xhr.responseText;
try {
resolve(JSON.parse(text));
}
catch (ex) {
reject(Error("Bad JSON format for \"" + path + "\"!\n" + ex + "\n" + text));
}
};
xhr.onerror = function() {
reject(Error("Unable to load file \"" + path + "\"!\n" + xhr.statusText));
};
xhr.open("GET", path, true);
xhr.withCredentials = true; // Indispensable pour le CORS.
xhr.send();
});
};
/**
* Event fired when login status has changed.
* @type {tfw.listeners}
*/
exports.changeEvent = changeEvent;
exports.eventChange = changeEvent;
/**
* @return If there is a user connected or not.
*/
exports.isLogged = function(){
if (!currentUser) return false;
return true;
};
/**
* Disconnect current user.
* @return {Promise} A _thenable_ object resolved as soon as the server answered.
*/
exports.logout = function() {
currentUser = null;
delete config.usr;
delete config.pwd;
changeEvent.fire();
Storage.local.set("nigolotua", null);
return svc("tfw.login.Logout");
};
/**
* Try to connect a user.
* @param {string} usr Login name.
* @param {string} pwd Password.
* @return {Promise}
*
*/
exports.login = function(usr, pwd) {
if (typeof usr === 'undefined') usr = config.usr;
if (typeof pwd === 'undefined') pwd = config.pwd;
return new Promise(
function (resolve, reject) {
if (typeof usr === 'undefined') {
var autologin = Storage.local.get("nigolotua");
if (!Array.isArray(autologin)) return reject({id: exports.MISSING_AUTOLOGIN});
usr = autologin[0];
pwd = autologin[1];
}
Storage.local.set("nigolotua", null);
svc("tfw.login.Challenge", usr)
.then(
function(code) {
// Hashage du mot de passe à l'aide du code.
var output = [0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0],
i, j = 0,
pass = [],
k1, k2, k3;
for (i=0 ; i<pwd.length ; i++) {
pass.push(pwd.charCodeAt(i));
}
if (256 % pass.length == 0) {
pass.push(0);
}
for (i=0 ; i<256 ; i++) {
output[i % 16] ^= i + pass[i % pass.length];
k1 = code[j++ % code.length]%16;
k2 = code[j++ % code.length]%16;
k3 = code[j++ % code.length]%16;
output[k3] ^= (output[k3] + 16*k2 + k3)%256;
output[k2] ^= (output[k1] + output[k3])%256;
}
return svc("tfw.login.Response", output);
},
reject
)
.then(
function(user) {
if (typeof user === 'object') {
currentUser = {
data: user,
hasRole: function(role) {
for (var i = 0 ; i < user.roles.length ; i++) {
var item = user.roles[i];
if (item == role) return true;
}
return false;
}
};
Storage.local.set("nigolotua", [usr, pwd]);
changeEvent.fire();
resolve(user);
} else {
currentUser = null;
reject({id: exports.UNKNOWN_USER});
}
},
reject
);
}
);
};
/**
* Call a webservice.
*/
exports.get = function(name, args, url) {
return new Promise(
function(resolve, reject) {
svc(name, args, url).then(
resolve,
function(err) {
if (typeof err === 'object' && err.id == exports.BAD_ROLE) {
// Echec de connexion, on retente de se connecter avant d'abandonner.
exports.login().then(
function() {
svc(name, args, url).then(resolve, reject);
},
reject
);
} else {
reject(err);
}
}
);
}
);
};
exports.isAdmin = function(role) {
return exports.hasRole('ADMIN');
};
exports.hasRole = function(role) {
if (!currentUser) return false;
return currentUser.hasRole(role);
};
exports.user = function() {
return currentUser;
};
Object.defineProperty( exports, 'userData', {
get: function() {
if (currentUser) return currentUser.data || {};
return {};
},
set: function() {},
configurable: true,
enumerable: true
});
exports.config = function(key, val) {
if (typeof val === 'undefined') {
return config[key];
}
config[key] = val;
return val;
};
// _Backward compatibility.
if (window.$$) {
window.$$.service = function (name, args, caller, onSuccess, onError) {
var p = exports.get(name, args);
p.then(
function(value) {
if (onSuccess) {
return caller[onSuccess].call(caller, value);
}
return value;
},
function(reason) {
if (onError) {
return caller[onError].call(caller, reason);
}
return reason;
}
);
};
}
/**
*
*/
Object.defineProperty( exports, 'userID', {
get: function() {
if( !currentUser ) return 0;
return currentUser.data.id;
},
set: function(v) {},
configurable: true,
enumerable: true
});