Support des préférences, navigation au clavier améliorée (ou du moins testée), message "Vous êtes connecté et je suis ton père".

This commit is contained in:
Georges Dupéron 2011-05-22 00:45:26 +02:00
parent 4e16be17ce
commit 8e38bbd2c7
3 changed files with 241 additions and 221 deletions

View File

@ -17,16 +17,18 @@
body, html { margin:0; padding:0; height:100%; overflow:hidden; }
.screen { width:100%; height:100%; position:absolute; }
.highlight { display:none; width:100%; height:100%; border-width:medium; border-style:solid; border-radius:2em; position:absolute; }
a:hover .highlight { display:block; }
a:hover .highlight, a:focus .highlight { display:block; }
#frontpage a { text-decoration:none; display:inline-block; width:30%; top:32%; position:absolute; }
#frontpage .icon-container img { display:block; position:relative; margin:0 auto; }
#frontpage .icon-label { width:100%; height:30%; position:relative; }
a.button { text-decoration:none; padding:0.4em; margin:0.4em; display:inline-block; border-width:medium; border-style:solid; border-radius:0.4em; }
.button { text-decoration:none; padding:0.4em; margin:0; display:inline-block; border-width:medium; border-style:solid; border-radius:0.4em; cursor:pointer; }
.relationBox { border-width:3px; border-style:solid; border-radius:1em; padding:0.5em; width:95%; margin:0 auto; }
.formElement { width:46%; height:10%; position:absolute; }
.formElement { width:46%; height:12%; position:absolute; }
#score { text-align:center; }
.marginBox { width:90%; height:90%; top:5%; left:5%; position:absolute; }
#message { left:25%; top:5%; width:50%; height:10%; position:absolute; border-width:medium; border-style:solid; border-radius:0.5em; text-align:center; opacity:0.9; }
#message { left:30%; top:5%; width:40%; height:10%; position:absolute; border-width:medium; border-style:solid; border-radius:0.5em; text-align:center; opacity:0.9; }
.theme-button input { position:absolute; left: -9000px; top:-9000px; }
a:focus, :focus, input:focus+label { text-decoration:underline; }
.transition { transition:all 0.7s linear; -moz-transition:all 0.7s linear; -webkit-transition:all 0.7s linear; }
.transition-bg { transition:background-color 0.7s linear; -moz-transition:background-color 0.7s linear; -webkit-transition:background-color 0.7s linear; }
@ -34,9 +36,10 @@
#splash, #nojs { background-color:black; color:white; }
/* couleurs green */
body, .screen { background-color:#ffffe0; color:black; }
a.button { color:black; border-color:#4a4; background-color:#f0f8d0; }
.green.theme-button .button, .button { color:black; border-color:#4a4; background-color:#f0f8d0; }
#message { background-color:#f0f8d0; color:black; border-color:#4a4; }
#frontpage a { color:black; }
a { color:black; }
#info a { color:#4a4; }
#mc-caption { color:#8b4; }
#mn-caption-bg { background-color:#f0f8d0; }
.mn-caption { color:#4a4; }
@ -47,39 +50,27 @@
/* couleurs black */
body.black, .black .screen { background-color:black; color:white; }
.black a.button { color:white; border-color:#ccc; background-color:#222; }
.black .button { color:white; border-color:#ccc; background-color:#222; }
.green .black.theme-button .button { color:white; border-color: #000; background-color: #444; }
.black #message { background-color:#222; color:white; border-color:#ccc; }
.black #frontpage a { color:white; }
.black a { color:white; }
.black #info a { color:#aaa; }
.black #mc-caption { color:white; }
.black #mn-caption-bg { background-color:#222; }
.black .mn-caption { color:#ccc; }
.black .borderbar { background-color:#ccc; }
.black .relationBox { background-color:#222; border-color:#ccc; }
.black .relationBox { background-color:#111; border-color:#ccc; }
.black .highlight { background-color:#222; border-color:#ccc; }
.black .hot { background-color:#aaa; }
</style>
</head>
<body>
<div id="splash" class="screen">
<a id="splash" class="screen" href="#frontpage">
<img src="ressources/img/splash.png" class="center" style="width:320px; height: 480px;"/>
</div>
<div id="game" class="screen">
<div style="width: 100%; height:40%; position:absolute;">
<div style="width: 90%; height:37.5%; top:7.5%; left:5%; position:absolute;" class="fitFont">
<div id="mc-caption" class="mc center">Mot central</div>
</div>
<div class="borderbar" style="height:5%; width:100%; top:52.5%; position:absolute;"></div>
<div id="mn-caption-bg" style="top:57.5%; height:37.5%; width:100%; position:absolute;"></div>
<div id="mn-caption-box" style="width: 90%; height:25%; top:63.75%; left: 5%; position:absolute;" class="fitFont">
</div>
<div class="borderbar" style="height:5%; width:100%; top:95%; position:absolute;"></div>
</div>
<div class="relations fitFontGroup" style="height:60%; width:100%; top:40%; position:absolute;">
</div>
</div>
</a>
<div id="frontpage" class="screen fitFontGroup">
<a id="back2site" href="index.php" style="width:20%; height:4%; top:2%; right:2%; position:absolute;" class="fitFont button">
<span class="marginBox"><span class="center">Retour au site</span></span>
<a id="back2site" href="index.php" style="width:23%; height:8%; top:4%; right:3%; position:absolute; padding: 0.1em;" class="fitFont button">
<span class="center">Retour au site</span>
</a>
<div style="width:50%; height:24%; top:4%; left:25%; position:absolute;" class="fitFont">
<span class="center">PtiClic</span>
@ -105,17 +96,64 @@
<div class="icon-label subFitFont"><span class="text center">À propos</span></div>
</a>
</div>
<div id="score" class="screen">
<div class="marginBox">
<h1 class="fitFont" style="width:100%; height:8%; top: 8%; position:absolute; margin:0; text-align:center;">Score total : <span class="scoreTotal"></span></h1>
<div class="scores fitFont" style="width:100%; height:60%; top:24%; position:absolute;"></div>
<p class="fitFont" style="text-align: center; width:100%; height:12%; top:86%; margin:0; position:absolute;">
<a class="button" href="#" style="margin:0;">J'ai vu !</a>
</p>
<div id="game" class="screen">
<div style="width: 100%; height:40%; position:absolute;">
<div style="width: 90%; height:37.5%; top:7.5%; left:5%; position:absolute;" class="fitFont">
<div id="mc-caption" class="mc center">Mot central</div>
</div>
<div class="borderbar" style="height:5%; width:100%; top:52.5%; position:absolute;"></div>
<div id="mn-caption-bg" style="top:57.5%; height:37.5%; width:100%; position:absolute;"></div>
<div id="mn-caption-box" style="width: 90%; height:25%; top:63.75%; left: 5%; position:absolute;" class="fitFont">
</div>
<div class="borderbar" style="height:5%; width:100%; top:95%; position:absolute;"></div>
</div>
<div class="relations fitFontGroup" style="height:60%; width:100%; top:40%; position:absolute;">
</div>
</div>
<div id="score" class="screen">
<h1 class="fitFont" style="width:100%; height:8%; top: 6%; position:absolute; margin:0; text-align:center;">Score total : <span class="scoreTotal"></span></h1>
<div class="scores fitFont" style="width:100%; height:62%; top:20%; position:absolute;"></div>
<p class="fitFont" style="text-align: center; width:90%; height:12%; top:86%; left:5%; position:absolute; margin:0; padding:0;">
<a class="button" href="#">J'ai vu !</a>
</p>
</div>
<div id="prefs" class="screen">
<form action="#" method="GET" class="fitFontGroup" style="width:100%; height:100%;">
<fieldset id="theme" class="subFitFont" style="width:50%; max-height:25%; margin:0 auto; top:25%; position:relative;">
<legend>Thème</legend>
<div class="theme-button green" style="float:left;">
<input type="radio" id="theme-green" name="theme" value="green" />
<label class="button" for="theme-green" style="margin: 0.2em;">Colline verdoyante</label>
</div>
<div class="theme-button black" style="float:right;">
<input type="radio" id="theme-black" name="theme" value="black" />
<label class="button" for="theme-black" style="margin: 0.2em;">Bas-fond de cachot</label>
</div>
</fieldset>
<div class="formElement subFitFont" style="top:75%; right:52%; text-align:right;">
<input class="button setFont" type="reset" value="Annuler" />
</div>
<div class="formElement subFitFont" style="top:75%; left:52%; text-align:left;">
<input class="button setFont" type="submit" value="Appliquer" />
</div>
</form>
</div>
<div id="connection" class="screen">
<form action="#" method="GET" style="width:100%; height:100%;" class="fitFontGroup">
<div class="formElement subFitFont" style="right: 52%; top: 25%; text-align:right;"><label id="user-label" for="user">Login : </label></div>
<div class="formElement subFitFont" style="left: 52%; top: 25%; text-align:left;"><input type="text" name="user" id="user" class="setFont" /></div>
<div class="formElement subFitFont" style="right: 52%; top: 50%; text-align:right;"><label id="passwd-label" for="passwd">Mot de passe : </label></div>
<div class="formElement subFitFont" style="left: 52%; top: 50%; text-align:left;"><input type="password" name="passwd" id="passwd" class="setFont" /></div>
<div class="formElement subFitFont" style="right: 52%; top: 75%; text-align:right;">
<a class="button" href="#frontpage">Retour</a>
</div>
<div class="formElement subFitFont" style="left: 52%; top: 75%; text-align:left;">
<input class="button setFont" type="submit" value="Se connecter" />
</div>
</form>
</div>
<div id="info" class="screen">
<div class="marginBox fitFont">
<div class="marginBox fitFont" style="height: 76%;">
<p>
PtiClic a été conçu et développé par Mathieu Lafourcade
(LIRMM - Université Montpellier 2) et Virginie Zampa
@ -143,35 +181,10 @@
de vos commentaires, vous pouvez nous contacter par
courriel à l'adresse suivante : <a href="mailto:pticlic.android.beta@gmail.com">pticlic.android.beta@gmail.com</a>
</p>
<p style="text-align: center;">
<a class="button" href="#">Retour</a>
</p>
</div>
</div>
<div id="connection" class="screen">
<form action="#" method="GET" style="width:100%; height:100%;" class="fitFontGroup">
<div class="formElement subFitFont" style="right: 52%; top: 25%; text-align:right;"><label id="user-label" for="user">Login : </label></div>
<div class="formElement subFitFont" style="left: 52%; top: 25%; text-align:left;"><input type="text" name="user" id="user" class="setFont" /></div>
<div class="formElement subFitFont" style="right: 52%; top: 50%; text-align:right;"><label id="passwd-label" for="passwd">Mot de passe : </label></div>
<div class="formElement subFitFont" style="left: 52%; top: 50%; text-align:left;"><input type="password" name="passwd" id="passwd" class="setFont" /></div>
<div class="formElement subFitFont" style="right: 52%; top: 75%; text-align:right;">
<input type="button" value="Retour" class="setFont goFrontpage" />
</div>
<div class="formElement subFitFont" style="left: 52%; top: 75%; text-align:left;">
<input type="submit" name="connect" id="connect" value="Se connecter" class="setFont" />
</div>
</form>
</div>
<div id="prefs" class="screen">
<form id="prefs-form" action="#" method="GET" class="fitFontGroup">
<fieldset id="theme" class="subFitFont" style="width:50%; height:25%; left:25%; top:25%; position:absolute;">
<legend>Thème</legend>
<input type="radio" id="theme-green" name="theme" value="green" /><label for="theme-green">Colline verdoyante</label><br/>
<input type="radio" id="theme-black" name="theme" value="black" /><label for="theme-black">Bas-fond de cachot</label>
</fieldset>
<div class="formElement subFitFont" style="top:75%; right:52%"><input class="center setFont" type="reset" name="prefs-cancel" id="prefs-cancel" value="Annuler" /></div>
<div class="formElement subFitFont" style="top:75%; left:52%"><input class="center setFont" type="submit" name="prefs-apply" id="prefs-apply" value="Appliquer" /></div>
</form>
<p class="fitFont" style="text-align: center; width:90%; height:12%; top:86%; left:5%; position:absolute; margin:0; padding:0;">
<a class="button" href="#">Retour</a>
</p>
</div>
<div id="templates" style="display: none;">
<div class="relationBox subFitFont">

View File

@ -255,31 +255,3 @@ function Cache(resolver) {
return cache[k] = cache[k] || $.Deferred(function(dfd) { resolver(k, dfd, arg); }).fail(function() { cache[k] = false; }).promise();
};
}
/* Enchaînement des écrans
*** Utiliser un objet Deferred pour les fonctions qu'on ne veut apeller qu'une fois.
***
- Cache des parties récupérées & scores (key = pgid pour les deux, mais params supplémentaires pour scores)
new Cache(queryFn(k, dfd, cache) { cache.set(k,v); dfd.resolve(data); });
Cache.get(k) returns Promise; // Peut déclencher $.extend(Cache, queryFn(k)).
- Récupérer une partie aléatoire, et la stocker dans le cache à son arrivée
- Afficher $(#game) (et $(#score)) une fois la partie (score) récupéré(e) et le(la) consommer
- Sauf si l'action a été annulée.
$.when(getGame, goGame)
if (runstate.nextScreen == 'game')
- Lorsqu'une requête échoue, on demande le login, on retente la requête avec ce login/mdp. Si ça marche avec ce login/mdp, on .resolve(), sinon on .fail().
***
Aller sur un écran donné (parfois sans changer l'URL, par ex. pour splash→frontpage, et lorsqu'on force le login).
Recevoir des données avant d'entrer dans un écran.
Envoyer des données avant de quiter un écran.
Vérouiller l'écran courant pendant qu'on attend un transfert ou bien des écrans d'«attente».
Lorsqu'un transfert a échoué car non logué, on va sur l'écran de connexion et on retente le transfert ensuite.
Stocker uniquement les données importantes dans l'url (état, numéro de partie, réponses).
Pouvoir basculer sur un écran et exécuter quelque chose une fois qu'il est chargé (exécuter le commit pour l'url).
*/

View File

@ -1,29 +1,21 @@
// ==== URL persistante
var nullFunction = function(){};
var futureHashChange = null;
var runstate = {
screen: 'none',
};
var state = decodeHash("");
var oldstate = decodeHash("");
$.screen = function (name) {
return $(document.getElementById(name)).filter('.screen');
}
function hashchange() {
oldstate = state;
state = decodeHash(location.hash);
$.screen(state.screen).trigger(state.screen != runstate.screen ? "goto" : "update");
}
// ==== Code métier général
function init(fn) {
$(window).queue('init', function(next) {fn(); next();});
}
// ==== Code métier général
$.ajaj = function(url, data, callback) {
var user = '' + UI().getPreference("user");
var passwd = '' + UI().getPreference("passwd");
user = runstate.user || user;
passwd = runstate.passwd || passwd;
if (user && passwd) {
if (!data.user) data.user = user;
if (!data.passwd) data.passwd = passwd;
}
return $.getJSON(url, data, callback);
};
$(function() {
loadPrefs();
var lastWinSize = $(window).wh();
$(window).dequeue('init');
$(window).resize($.debounce(function resizeJSS() {
@ -37,9 +29,67 @@ $(function() {
runstate.loaded = true;
});
// ==== URL persistantes et passage d'un écran à l'autre
var runstate = { screen: 'none' };
var state = decodeHash("");
var oldstate = decodeHash("");
init(function() {
$('.screen').live('goto', function() {
var screen = this.id;
if (screen == '') return;
// Afficher "Chargement…"
/* location.hash = "#" + screen; */
$.screen(runstate.screen).trigger('leave').hide();
runstate.screen = screen;
UI().setScreen(screen);
$(this).trigger('pre-enter');
});
$('.screen').live('pre-enter', function() {
$(this).trigger('enter');
});
$('.screen').live('enter', function() {
$(this).show();
$(this).trigger('update');
});
$('.screen').live('update', function() {
jss();
});
$('.screen').live('leave', function() {
$(this).hide();
});
});
$.screen = function (name) {
return $(document.getElementById(name)).filter('.screen');
}
function hashchange() {
oldstate = state;
state = decodeHash(location.hash);
$.screen(state.screen).trigger(state.screen != runstate.screen ? "goto" : "update");
}
// ==== Interface Android
function UI () {
if (typeof(PtiClicAndroid) != "undefined") {
return PtiClicAndroid;
} else {
return {
isAndroid: function() { return false; },
setPreference: function() {},
getPreference: function() {return "";},
setScreen: function() {}
};
}
}
// ==== Nouveau jss
function jss() {
try {
if (jss.running) return;
jss.running = true;
$('body').removeClass().addClass(runstate.prefs.theme);
@ -77,57 +127,26 @@ function jss() {
$('#game.screen').trigger('update');
}
jss.running = false;
} catch(e) {alert("Error jss");alert(e);}
}
// ==== Passage d'un écran à l'autre
init(function() {
$('.screen').live('goto', function() {
var screen = this.id;
if (screen == '') return;
// Afficher "Chargement…"
/* location.hash = "#" + screen; */
$.screen(runstate.screen).trigger('leave').hide();
runstate.screen = screen;
$(this).trigger('pre-enter');
});
$('.screen').live('pre-enter', function() {
$(this).trigger('enter');
});
$('.screen').live('enter', function() {
$(this).show();
$(this).trigger('update');
});
$('.screen').live('update', function() {
jss();
});
$('.screen').live('leave', function() {
$(this).hide();
});
});
// ==== Bulle pour les messages
init(function() {
$('#message').hide();
});
function message(title, msg) {
try {
$('#message')
.qCss('opacity',0).qShow()
.queue(function(next){ $('#message .text').text(msg); jss(); next(); })
.fadeTo(700, 0.9).delay(5000).fadeOut(700);
} catch(e) {alert("Error UI().info");alert(e);}
$('#message')
.qCss('opacity',0).qShow()
.queue(function(next){ $('#message .text').text(msg || 'Une erreur est survenue, veuillez nous en excuser.'); jss(); next(); })
.fadeTo(700, 0.9).delay(5000).fadeOut(700);
}
// ==== Écran splash
init(function() {
$('#splash.screen').click(function(){ $('#frontpage').trigger('goto'); });
$('#splash.screen').click(function(){ $('#frontpage').trigger('goto'); return false; });
window.setTimeout(function() {
if (runstate.screen == 'splash') $('#frontpage').trigger('goto');
}, 5000);
$('#splash.screen').bind('goto', function(e){
if (runstate.loaded) {
$('#frontpage').trigger('goto');
@ -139,20 +158,17 @@ init(function() {
// ==== Écran d'accueil
init(function() {
$('.goFrontpage').click(function() { location.hash = "#frontpage"; });
$.screen('frontpage').bind('enter', function() { window.document.title = "PtiClic pre-alpha 0.2"; });
if (UI().isAndroid()) $('#back2site').hide();
});
// ==== Écran connexion
runstate.pendingGetPrefs = function() {
UI().log('Should execute pendingGetPrefs');
};
init(function() {
$('#connection.screen form').submit(function() {
runstate.user = $('#user').val();
runstate.passwd = $('#passwd').val();
UI().setPreference('user', runstate.user);
UI().setPreference('passwd', runstate.passwd);
if (runstate.pendingSetPrefs) {
runstate.pendingSetPrefs();
} else {
@ -162,6 +178,8 @@ init(function() {
$('#game').trigger('goto');
} else if (state.screen == 'score') {
$('#score').trigger('goto');
} else if (location.hash == "#frontpage") {
$.screen('frontpage').trigger('goto');
} else {
location.hash = "#frontpage";
}
@ -175,16 +193,6 @@ init(function() {
});
// ==== Écran game
$.ajaj = function(url, data, callback) {
var user = runstate.user; /* '' + UI().getPreference("user"); */
var passwd = runstate.passwd; /* '' + UI().getPreference("passwd"); */
if (user && passwd) {
if (!data.user) data.user = user;
if (!data.passwd) data.passwd = passwd;
}
return $.getJSON(url, data, callback);
};
runstate.gameCache = new Cache(function getGame(k, dfd) {
$.ajaj("getGame.php?callback=?", {pgid:k}, function(data) {
if (data.isError) {
@ -317,7 +325,6 @@ init(function() {
var score = $.screen('score');
score.bind('pre-enter', function() {
runstate.scoreCache.get(state.pgid, state.answers).done(function(data) {
console.log(data);
runstate.score = data;
score.trigger('enter');
});
@ -330,32 +337,94 @@ init(function() {
.text(s.scoreTotal)
.goodBad(s.minScore*s.scores.length, s.maxScore*s.scores.length, {r:255,g:0,b:0}, {r:0,g:192,b:0});
$.each(s.scores, function(i,e) {
try {
$("#templates .scoreLine")
.clone()
.find(".word")
.text(e.name)
.end()
.find(".score")
.text(e.score)
.goodBad(s.minScore, s.maxScore, {r:255,g:0,b:0}, {r:0,g:192,b:0})
.end()
.appendTo("#score .scores");
} catch(e) {alert("Error anonymous 1 in score.ui");alert(e);}
$("#templates .scoreLine")
.clone()
.find(".word").text(e.name).end()
.find(".score").text(e.score).goodBad(s.minScore, s.maxScore, {r:255,g:0,b:0}, {r:0,g:192,b:0}).end()
.appendTo("#score .scores");
});
});
});
// ==== Écran Préférences
function loadPrefs(prefs) {
runstate.prefs = (prefs && prefs.theme) ? prefs : {
theme: "green"
};
if (runstate.loaded) jss();
var previousTheme = runstate.prefs ? runstate.prefs.theme : 'green';
runstate.prefs = (prefs && prefs.theme) ? prefs : { theme: "green" };
runstate.serverPrefs = $.extend({}, runstate.prefs);
if (runstate.loaded && previousTheme != runstate.prefs.theme) jss();
}
loadPrefs(); // initialize runstate.prefs.
function setPrefs(prefs, callback) {
$.ajaj("server.php?callback=?", {
action: 8,
key: 'theme',
value: prefs.theme
}, function(data) {
if ((data.error == 10 || data.error == 3) && (state.screen == 'frontpage' || state.screen == 'prefs')) {
$.screen('connection').trigger('goto');
} else {
if (data.theme) {
runstate.pendingSetPrefs = false;
loadPrefs(data);
message("Préférences", "Les préférences ont été enregistrées.");
} else {
message("Erreur", data.msg);
message("Préférences", "Les préférences n'ont pas pu être enregistrées.");
}
}
});
}
runstate.pendingGetPrefs = function() {
$.ajaj("server.php?callback=?", { action: 7 }, function(data) {
if (data.theme) { message("Succès", "Vous êtes connecté.", data.msg); loadPrefs(data); }
if (data.isError) message("Erreur", data.msg);
});
};
init(function() {
$("#prefs").bind('enter', function() {
$("#prefs-form input:radio[name=theme]").attr('checked', function(i,val) {
return $(this).val() == runstate.prefs.theme;
});
});
var readPrefs = function() {
var newtheme = $("#prefs form input:radio[name=theme]:checked").eq(0).val();
if (runstate.prefs.theme != newtheme) {
runstate.prefs.theme = newtheme;
jss();
}
};
$("#prefs form").submit(function() {
readPrefs();
location.href = "#frontpage"
var p = $.extend({}, runstate.prefs);
runstate.pendingSetPrefs = function() { setPrefs(p); }
runstate.pendingSetPrefs();
return false;
});
$("#prefs form").bind('reset', function() {
runstate.prefs = runstate.serverPrefs;
location.hash = "#frontpage";
});
$("#prefs form input:radio[name=theme]").bind('change click', readPrefs);
});
/*prefs.enter = function() {
try {
$("#prefs-form").unbind('submit', prefs.apply).submit(prefs.apply);
@ -408,14 +477,6 @@ prefs.loadPrefs = function(data) {
if (runstate.loaded) jss();
} catch(e) {alert("Error anonymous in prefs.loadPrefs");alert(e);}
};
*/
game = {};
game.leave = function () {
@ -532,6 +593,7 @@ State.prototype.validate = function () {
} catch(e) {alert("Error State.prototype.validate");alert(e);}
};*/
/*
function _hashchange() {
try {
if (futureHashChange !== location.hash) {
@ -541,35 +603,6 @@ function _hashchange() {
} catch(e) {alert("Error hashchange");alert(e);}
}
// ==== Interface Android
function UI () {
try {
if (typeof(PtiClicAndroid) != "undefined") {
return PtiClicAndroid;
} else {
return {
isAndroid: function() { return false; },
setPreference: function() {},
getPreference: function() {return "";},
show: function(title, text) {},
dismiss: function() {},
exit: function() {},
log: function(msg) {
try {
window.console && console.log(msg);
} catch(e) {alert("Error UI().log");alert(e);}
},
info: function(title, msg) {
try {
alert(msg);
} catch(e) {alert("Error UI().info");alert(e);}
},
setScreen: function() {}
};
}
} catch(e) {alert("Error UI");alert(e);}
}
// ==== Asynchronous Javascript And Json.
ajaj = {};
ajaj.request = function(url, data, okFunction, smallErrorFunction, bigErrorFunction) {
@ -924,3 +957,5 @@ prefs.loadPrefs = function(data) {
if (runstate.loaded) jss();
} catch(e) {alert("Error anonymous in prefs.loadPrefs");alert(e);}
};
*/