UNPKG

toloframework

Version:

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

343 lines (323 loc) 9.36 kB
/** * */ "use strict"; require( "polyfill.promise" ); var Cfg = require( "$" ).config; var Storage = require( "tfw.storage" ); var Listeners = require( "tfw.listeners" ); var currentUser = null; var changeEvent = new Listeners(); var config = { // In `package.json`, you can override the services URL. // { "tfw": { "consts": { "debug": "tfw", "release": "http://tolokoban.org/tfw" } } } url: typeof Cfg.consts.tfw === 'string' ? Cfg.consts.tfw : "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 ) { return 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: "HTTP_ERROR (" + xhr.status + ") " + xhr.statusText, status: xhr.status } ); }; var params = "s=" + encodeURIComponent( name ); if ( typeof args !== 'undefined' ) { params += "&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; Storage.local.set( "nigolotua", null ); changeEvent.fire(); 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 ) { console.info( "[tfw.web-service] user=...", 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; } ); }; } var properties = { userID: function () { if ( !currentUser ) return 0; return currentUser.data.id; }, userLogin: function () { if ( !currentUser ) return null; return currentUser.data.login; }, userName: function () { if ( !currentUser ) return null; return currentUser.data.name; } }; for ( var key in properties ) { Object.defineProperty( exports, key, { get: properties[ key ], set: function ( v ) { throw Error( "Property " + key + " is read-only!" ); }, configurable: true, enumerable: true } ); }