Commit 5c1e4b25 authored by Sven Franck's avatar Sven Franck

stabilized authentication, updating expiry per validated JIO command

parent bff47a45
......@@ -179,7 +179,7 @@
* @param {object} obj Action Object
**/
"logout_user": function (obj) {
var temp_store = app.default_dict.state_dict.preserve_in;
var provider, temp_store = app.default_dict.state_dict.preserve_in;
hello(obj.id).logout(function(x){
return;
......@@ -187,7 +187,11 @@
// update flux
if (flux && flux.state) {
delete flux.state[obj.id.slice(0,2)];
if (flux.state[obj.id.slice(0,2)]) {
delete flux.stat[obj.id.slice(0,2)]
} else {
delete flux.state["self"];
}
}
// update preserve
......@@ -214,8 +218,6 @@
factory.util.convertDict(undefined, "login", true),
obj.element
);
// TODO: Shoudl we also remove the user from storage?
},
/**
......@@ -224,14 +226,12 @@
* @param {object} obj Action Object
**/
"login_user": function (obj) {
var signature, match_string, reply, element, provider, temp_store,
links, login, i, query, user, token;
var signature, match_string, reply, element;
// create signature and ticket
element = obj.element;
signature = util.generateRandomIdentifier(36);
flux[signature] = util.generateUuid();
temp_store = app.default_dict.state_dict.preserve_in;
// pass to 3rd party
hello.settings.state = signature;
......@@ -248,52 +248,16 @@
// valid roundtrip
if (flux[match_string]) {
if (flux.state === undefined) {
flux.state = {};
}
provider = response.network.slice(0,2);
// store in flux
flux.state[provider] = response.authResponse.access_token;
// catch errors
try {
// store in preserve
if (temp_store) {
switch (temp_store) {
case "sessionStorage":
window.sessionStorage
.setItem("state", JSON.stringify(flux.state));
break;
// WARNING: you should not!
case "localStorage":
window.localStorage
.setItem("state", JSON.stringify(flux.state));
break;
// WARNING: you really should NOT!!!
case "cookie":
util.cookieHandler
.setItem("state", JSON.stringify(flux.state));
break;
};
}
// clear token and set flux
delete flux[match_string];
// update flux
app.setLoginStatus(response, undefined);
} catch (e) {
util.errorHandler(e);
}
}
// update/replace button
// TODO: don't access it like this!!!
links = util.getHeader().getElementsByTagName("A");
for (i = 0; i < links.length; i += 1) {
if (links[i].getAttribute("data-depend") === "login_state") {
login = links[i];
login.parentNode.replaceChild(
factory.util.convertDict(undefined, "logoff", true),
login
);
}
}
}
// since we are jquerying, close (all) popups
......@@ -751,12 +715,10 @@
* @returns {object} config Configuration object to pass to factory
**/
"mapFormField": function (spec, overrides, passed_value) {
var validation_list,
class_list,
element,
type,
prevail,
clear,
var validation_list, class_list, element, type, prevail, clear, config,
error_log;
// build config object
config = {};
// set empty overrides if none are passed
......@@ -787,6 +749,7 @@
// required
if (prevail.properties.required || spec.properties.required) {
class_list += "required ";
error_log = true;
}
// maximum_length
......@@ -1341,7 +1304,14 @@
target = document.createDocumentFragment();
if (answer.data.total_rows === 0) {
// no auth
if (answer === null) {
target.appendChild(app.noItemsFound("auth"));
return target;
}
if ((answer.data && answer.data.total_rows === 0) ||
(answer.data === undefined && answer.request_new === undefined)) {
target.appendChild(app.noItemsFound());
} else {
for (i = 0; i < spec.children.length; i += 1) {
......@@ -1349,13 +1319,13 @@
// new
if (element.type === "form") {
if (answer && answer.data) {
if (answer.data.total_rows > 1) {
if (answer && (answer.data || answer.request_new)) {
if (answer.request_new || answer.data.total_rows === 0) {
element.data = {};
} else if (answer.data.total_rows > 1) {
util.errorHandler({
"error": "Fieldlist: More than 1 record"
});
} else if (answer.data.total_rows === 0) {
element.data = {};
} else {
element.data = answer.data.rows[0].doc;
}
......@@ -1367,7 +1337,6 @@
target.appendChild(app.setContent(element));
}
}
return target;
}
};
......@@ -1603,7 +1572,6 @@
* @param {object} spec configuration of gadget
* @return {object} form object of fragment
*/
// TODO: the gadget should not be wrapped by the form - only the form should
factory.util.wrapInForm = function (spec) {
if (spec.form) {
return factory.element(
......@@ -1614,7 +1582,8 @@
"id": spec.id,
"className": (spec.class_list || "")
},
{"data-ajax": false}
{"data-ajax": false, "autocomplete":"off"},
{"data-set": spec.update || null}
);
}
return document.createDocumentFragment();
......@@ -2050,13 +2019,19 @@
* @return controlgroup
*/
// TODO: crap to use both layout and children!
// TODO: the submit button should have a data-reference to the login gadget
// which will not be in the DOM, so it can update after success. Until then
// data-reference will be like helloJS "google", only use "self"
// NOTE: securing ? http://nedbatchelder.com/text/stopbots.html
factory.form = function (spec) {
var i, j, k, layout, element, container, area, field, overrides,
position, doc, config, value, stamp, sauce, encode, secure,
safety_box, noscript, fragment, wrap;
safety_box, noscript, fragment, wrap, update;
secure = spec.property_dict.secure;
if (spec.property_dict.update) {
spec.update = spec.property_dict.update;
}
fragment = factory.util.wrapInForm(spec);
wrap = function (area, captcha) {
var keys = {};
......@@ -3246,10 +3221,13 @@
// helper
addLabel = function (config, label_class_list) {
var star = util.testForString("equire", config.direct.className, true);
return factory.element(
"label",
{
"className": label_class_list + " translate" +
(star ? " required_label " : " ") +
((label === undefined || config.logic.text === "") ?
" ui-hidden-accessible" : "")
},
......@@ -3362,6 +3340,10 @@
config.attributes["data-type"] = "search";
text = "search";
}
// disable IOS auto functionality
config.attributes.autocapitalize = "off";
config.attributes.autocorrect = "off";
// update class_list
container_class_list = "ui-input-" + text + " ui-body-inherit ";
}
break;
......@@ -3670,20 +3652,14 @@
// TODO: generate common promise handler for add/remove/clone/export/etc
// TODO: make form validation and captcha generic
storage.add = function (config) {
var property,
replace,
i,
obj,
value,
form_elements,
form_element,
pass = false,
test_full,
test_empty,
test_time,
form_to_submit = document.getElementById(config.id),
anti_spam = document.getElementById(form_to_submit.id + "_not_a_secret"),
formData = new FormData(),
var property, replace, i, obj, value, form_elements, form_element, pass,
test_full, test_empty, test_time, form_to_submit, anti_spam, formData,
valid;
pass = false
form_to_submit = document.getElementById(config.id);
anti_spam = document.getElementById(form_to_submit.id + "_not_a_secret");
formData = new FormData();
valid = $(form_to_submit).triggerHandler("submitForm");
if (valid !== false) {
......@@ -3711,9 +3687,6 @@
}
}
// NOTE: secret is passed along with timestamp, so varying the secret
// along with the timestamp will prevent playback bots
// can we pass
if (test_empty && test_full && test_time) {
pass = true;
......@@ -3739,25 +3712,26 @@
}
}
// create ERP5 event
// TODO: remove once normal access is possible
util.updateStatus("show", "", "global_dict.status_dict.uploading");
util.ajax({
"url": "https://nexedi.erp5.net/" +
"ERP5Site_addApplicationSubmissionRequest",
"type": "POST",
"data": formData
util.updateStatus("show", "", "global_dict.status_dict.saving");
// TODO: a post should always include all required fields
// TODO: find a way to set default fields, like timestamp etc
app.postDataToStorage({
"response": [obj],
"pass": {"type": config.gadget.state.type, "reply": true}
})
// .then(function () {
// // TODO: a post should always include all required fields
// // TODO: find a way to set default fields, like timestamp etc
// return app.postDataToStorage({
// "response": [obj],
// "pass": {"type": config.portal_type_source}
// })
// })
.then(function() {
$.mobile.changePage("#thanks");
.then(function(answer) {
// TODO: This is not the correct place to run all status updates!
if (answer.response.result === "success") {
switch (config.gadget.getAttribute("data-set")) {
case "login_state":
app.setLoginStatus(answer.response, config.portal_type_source);
break;
}
util.updateStatus(
"show", "", "global_dict.status_dict.saved", "check"
);
}
})
.fail(util.errorHandler);
}
......@@ -4162,12 +4136,68 @@
};
/**
* return a placeholder with back button in case no items were found
* return a placeholder with button in case no items were found
* @method noItemsFound
* @param {string} type Type of placeholder to create
* @return {object} HTML fragment
*/
app.noItemsFound = function () {
var reply = factory.element(
// TODO: don't get global popup like this!!!!!!
// TODO: make this element fit to whoever calls (tr, li, div...)
app.noItemsFound = function (type) {
var reply, content, message, message_i18n;
// TODO: find less content-heavy way!
switch (type) {
case "auth":
message = "Requires authorization.";
message_i18n = "validation_dict.requires_auth";
content = {
"generate": "widget",
"type": "controlgroup",
"property_dict": {"direction": "horizontal"},
"children": [{
"type": "a",
"direct":{"className":"translate action", "href":"#global-popup"},
"attributes":{
"data-i18n":"global_dict.common_dict.login",
"data-action":"login",
"data-icon":"lock",
"data-rel": "popup"
},
"logic": {"text":"Login"}
}, {
"type": "a",
"direct": {"className":"translate", "href": "#person::new"},
"attributes": {
"data-i18n": "global_dict.common_dict.register",
"data-icon": "edit"
},
"logic": {"text":"Register"}
}
]
};
break;
default:
message = "No items found.";
message_i18n = "validation_dict.no_items_found";
content = {
"type": "a",
"direct": {
"href":"#",
"className": "ui-corner-all ui-btn ui-shadow ui-btn-inline " +
"ui-icon-chevron-sign-left ui-btn-icon-left translate "
},
"attributes": {
"data-rel":"back",
"data-i18n":"global_dict.pagination_dict.back"
},
"logic": {"Text": "Back"}
}
break;
};
// <p> is not flexible...
reply = factory.element(
"p",
{"className": "responsive ui-content-element"}
);
......@@ -4175,32 +4205,88 @@
reply.appendChild(factory.element(
"span",
{"className": "translate"},
{"data-i18n": "validation_dict.no_items_found"},
{"text": "No items found"}
));
reply.appendChild(factory.element(
"a",
{
"className": "ui-corner-all ui-btn ui-shadow ui-btn-inline " +
"ui-icon-chevron-sign-left ui-btn-icon-left translate"
},
{"data-i18n": "global_dict.pagination_dict.back", "data-rel": "back"},
{"text": "Back"}
{"data-i18n": message_i18n},
{"text": message}
));
reply.appendChild(app.setContent(content));
return reply;
};
/**
* Update login status
* @method updateLoginStatus
* @param {object} response Object initiating the status update
* @param {string} portal_type Portal Type concerned
**/
app.setLoginStatus = function (response, portal_type) {
var temp_store, stamp, provider, auth, links, i, login;
temp_store = app.default_dict.state_dict.preserve_in;
stamp = Date.now();
provider = response.network ? response.network.slice(0,2) : "self";
auth = response.authResponse;
if (flux.state === undefined) {
flux.state = {};
}
// store in flux
flux.state[provider] = {
"token": auth ? auth.access_token : response.id,
"issued": stamp,
"expires": auth ? auth.expires_in : 3600
}
// if specified store in preserve
if (temp_store) {
switch (temp_store) {
case "sessionStorage":
window.sessionStorage
.setItem("state", JSON.stringify(flux.state));
break;
// WARNING: you should not!
case "localStorage":
window.localStorage
.setItem("state", JSON.stringify(flux.state));
break;
// WARNING: you really should NOT!!!
case "cookie":
util.cookieHandler
.setItem("state", JSON.stringify(flux.state));
break;
};
}
// need to update the .... button
// TODO: make a login gadget, store dependend buttons in state
links = util.getHeader().getElementsByTagName("A");
for (i = 0; i < links.length; i += 1) {
if (links[i].getAttribute("data-depend") === "login_state") {
login = links[i];
login.parentNode.replaceChild(
factory.util.convertDict(undefined, "logoff", true),
login
);
}
}
};
/**
* Check if a login status is still valid
* @method checkLoginStatus
* @param {boolean} skip Return out
* @param {object} pass Passed information to be returned
* @return {boolean} true/false
**/
// TODO: make this work with a regular login, too!!!
app.checkLoginStatus = function (skip) {
var state, temp_store, lookup, logger, provider, verify, url, influx, info;
app.checkLoginStatus = function (skip, pass) {
var state, temp_store, lookup, logger, provider, verify, url, influx, info,
valid, auth_config, stamp, expires, expired, token;
if (skip === undefined) {
// TODO: name collusion! make normal pass object!
return RSVP.resolve({"pass": skip});
} else {
logger = app.default_dict.state_dict.login_pointer;
......@@ -4234,12 +4320,40 @@
if (state) {
for (provider in state) {
if (state.hasOwnProperty(provider)) {
auth_config = state[provider];
stamp = Date.now();
// assemble url
switch (provider) {
case "go": url = verify.google + state.go; break;
case "go":
url = verify.google + auth_config.token;
break;
case "self":
url = undefined;
break;
}
// test for timeout
if (auth_config.issued && auth_config.expires) {
expires = auth_config.issued + auth_config.expires;
if (expires < stamp) {
valid = true;
} else {
expired = true;
delete state[provider];
}
}
}
}
}
// token in memory still valid, add it to the pass object
if (valid) {
if (pass) {
pass.active_login = true;
pass.token = auth_config.token;
pass.expires_in = expires;
return RSVP.resolve(pass);
}
return RSVP.resolve({"expires_in": expires});
}
// if we have a url, we have an access token, check if it's valid
if (url) {
......@@ -4255,7 +4369,11 @@
throw util.parseIfNeeded(event.target);
});
}
return RSVP.resolve({"error": "no state found"});
if (pass) {
pass.active_login = false;
return RSVP.resolve(pass);
}
return RSVP.resolve({"error": "no state found/not logged in"});
} else {
util.errorHandler({"error":"loginStatus: Missing login handler."});
}
......@@ -4388,45 +4506,6 @@
}
};
/**
* parse a link into query-able parameters
* @method parseLink
* @param {string} url Url to go to
* @return {object} pointer object
*/
// TODO: renderJS should parse a link
app.parseLink = function (url) {
var i,
query,
parameter,
path = $.mobile.path.parseUrl(url.replace($.mobile.dialogHashKey, "")),
clean_hash = path.hash.replace("#", ""),
config = {
"url": url
};
if (clean_hash === "") {
config.id = config.layout_identifier = util.getActivePage();
} else {
// do we have a mode?
query = clean_hash.split("?");
for (i = 0; i < query.length; i += 1) {
parameter = query[i].split("=");
if (parameter.length === 2 && parameter[0] === "mode") {
config.mode = parameter[1];
}
}
config.fragment_list = clean_hash.split("::");
config.id = clean_hash;
config.layout_level = config.fragment_list.length - 1;
config.deeplink = true;
config.layout_identifier = clean_hash.split("::")[0];
}
return config;
};
/**
* Action handler, routing actions to specified method
* @method action
......@@ -4573,13 +4652,58 @@
return obj;
};
/**
* parse a link into query-able parameters
* @method parseLink
* @param {string} url Url to go to
* @return {object} pointer object
*/
// TODO: renderJS should parse a link
app.generateLinkObject = function (url) {
var i, query, parameter, path, clean_hash, config;
parameter = 0;
path = $.mobile.path.parseUrl(url.replace($.mobile.dialogHashKey, "")),
clean_hash = path.hash.replace("#", ""),
config = {
"url": url
};
if (clean_hash === "") {
config.id = config.layout_identifier = util.getActivePage();
} else {
// NOTE: as the hash will be set as page id by JQM, we cannot pass
// complex structures or query parameters to not produce invalid HTML
// check for mode(s)
query = clean_hash.split("::");
for (i = 0; i < query.length; i += 1) {
switch(query[i]) {
case "new":
// person::new = create a new entry for person
// NOTE: this will allow loading of field definitions in auth!
config.mode = "new";
parameter = 1;
break;
};
}
config.fragment_list = clean_hash.split("::");
config.id = clean_hash;
config.layout_level = config.fragment_list.length - 1 - parameter;
config.deeplink = true;
config.layout_identifier = clean_hash.split("::")[0];
}
return config;
};
/**
* Generate an action object (vs duplicate in every action call)
* @method generateActionObject
* @param {object} e Event triggering an action
* @return {object} action object
*/
// TODO: integrate in popup handler, make sure "pop" works!
// TODO: id ... is crap
// TODO: passing empty state is not good, refactor, when adding state
app.generateActionObject = function (e) {
......@@ -4749,6 +4873,7 @@
"pass": pass
})
.then(app.parseConfiguration)
.then(app.loadFieldDefinition)
// ===== SAMPLE DATA ========
.then(app.testStorageForData)
.then(app.retrieveSampleData)
......@@ -4829,11 +4954,12 @@
* @return {object} response object/promise
*/
app.generateGadgetContent = function (reply) {
var selector,
element,
translate,
pass = reply.pass,
var selector, element, pass, constructor, translate, request_new;
pass = reply.pass;
constructor = map.gadgets[pass.constructor];
// TODO: this always sets "new" urls to an empty result. Good?
request_new = pass.mode === "new" ? {"request_new": true} : null;
if (constructor === undefined) {
util.errorHandler(
......@@ -4845,10 +4971,11 @@
if (pass.skip) {
return constructor(pass.content_dict, pass.url_dict);
} else {
// generate content
element = constructor(
pass.config,
(reply.response ? util.parseIfNeeded(reply.response) : null),
(reply.response ? util.parseIfNeeded(reply.response) : request_new),
pass.fields,
(pass.create === false ? true : null),
{"layout": (pass.layout_level || 0), "fragment_list": pass.fragment_list}
......@@ -4857,15 +4984,21 @@
// translate
map.actions.translateNodeList(element);
// not auth, no mojo!
if (pass.no_auth || (pass.auth && pass.active_login ||
pass.mode === "new")) {
if (pass.create === false) {
selector = pass.state.gadget;
} else {
// NOTE: in case of update, element will be the section to update
// not the gadget/fragment, so we need to find the gadget
// NOTE: in case of forms, we find the form!
// NOTE: in case of fragments, we punt to the firstElementChild...
selector = element.querySelector("#" + pass.id) ||
document.getElementById(pass.config.form) ||
element;
(element.tagName === undefined ? element.firstElementChild : element);
pass.state.gadget = selector;
}
......@@ -4881,6 +5014,7 @@
pass.state.query,
pass.state.total
);
}
return element;
}
......@@ -4895,7 +5029,8 @@
app.fetchDataQuery = function (reply) {
var parsed, pass = reply.pass;
if (pass.skip === undefined) {
if (pass.skip === undefined &&
(pass.no_auth || (pass.auth && pass.active_login))) {
// single item query
if (reply.response) {
......@@ -4937,7 +5072,9 @@
app.fetchDataTotal = function (reply) {
var pass = reply.pass;
if (pass.skip === undefined) {
if (pass.skip === undefined &&
(pass.no_auth || (pass.auth && pass.active_login ||
pass.mode === "new"))) {
// create or update state object
if (pass.create === false) {
......@@ -4947,6 +5084,7 @@
pass.state.query.limit = [];
} else {
pass.state = {};
pass.state.type = pass.type;
pass.state.method = pass.constructor;
pass.state.fragment_list = pass.fragment_list;
......@@ -4969,7 +5107,6 @@
// skip total for single item layouts!
if (pass.config.initial_query) {
// make sure limit is reset from any sample loading [0,1]
pass.state.query.limit = [];
......@@ -4995,7 +5132,9 @@
* @return {object} promise object/pass
*/
app.postDataToStorage = function (reply) {
var i, obj, key, promises, record, items, store, pass;
var i, obj, key, promises, record, items, store, pass, send_status,
method;
pass = reply.pass;
if (reply.response) {
......@@ -5017,13 +5156,25 @@
}
// add portal type
obj.portal_type = pass.type;
// post to JIO with generated id
promises[i] = store.post(obj);
// make sure to overwrite existing files, so try to fetch...
// TODO: is there no easier way to check whether to put/post
console.log(obj)
console.log(flux)
console.log(pass)
promises[i] = store.post(obj)
.then(function(answer) {
return answer;
});
}
return RSVP.all(promises)
.then(function () {
.then(function (response) {
// for single item actions, allow to return a status message
if (response.length === 1 && reply.pass.reply) {
send_status = response[0];
}
return {
"response": undefined,
"response": send_status,
"pass": pass
};
})
......@@ -5063,7 +5214,9 @@
.fail(util.errorHandler);
}
}
return {"pass": pass};
return {
"pass": pass
};
};
/**
......@@ -5081,7 +5234,9 @@
}
// try to get 1 record
if (pass.create !== false && pass.config.initial_query) {
if (pass.create !== false && pass.config.initial_query &&
(pass.no_auth || (pass.auth && pass.active_login))) {
return app.fetchData({
"storage": "items",
"query": app.generateQueryObject({"limit": [0, 1]}, pass.type),
......@@ -5094,6 +5249,42 @@
};
};
// ======================== SAMPLE DATA ==================================
/**
* Load field definitions if necessary
* @method loadFieldDefinition
* @param {object} pass through
* @return {object} field defintions and pass through
*/
app.loadFieldDefinition = function (reply) {
var pass = reply.pass;
// active login?
if (pass.auth) {
if (pass.active_login) {
pass.grant = true;
}
} else {
pass.no_auth = true;
}
// fetch field definitions
if (pass.skip === undefined &&
(pass.no_auth || (pass.auth && pass.active_login ||
pass.mode === "new")) &&
pass.create !== false && pass.config.portal_type_fields) {
return app.fetchConfiguration({
"storage": app.default_dict.storage_dict.settings,
"file": app.default_dict.storage_dict.data_type,
"attachment": pass.config.portal_type_fields,
"pass": pass
});
}
return {
"pass": pass
};
};
/**
* parses a gadget configuration file
* @method parseConfiguration
......@@ -5107,21 +5298,26 @@
parsed = util.parseIfNeeded(reply.response);
pass.config = parsed;
pass.type = parsed.portal_type_source;
// if app uses login and gadget requires auth
if (app.default_dict.state_dict.login &&
parsed.property_dict.requires_authentication) {
pass.auth = parsed.property_dict.requires_authentication;
}
}
if (pass.skip === undefined &&
pass.create !== false &&
pass.config.portal_type_fields) {
return app.fetchConfiguration({
"storage": app.default_dict.storage_dict.settings,
"file": app.default_dict.storage_dict.data_type,
"attachment": pass.config.portal_type_fields,
"pass": pass
});
// test for authentication
if (pass.auth) {
return app.checkLoginStatus(true, pass)
.then(function(new_pass) {
return {
"pass": new_pass
}
})
.fail(util.errorHandler);
}
return {
"pass": pass
};
}
};
/**
......@@ -5291,7 +5487,7 @@
}
if (typeof raw_url === "string") {
config = app.parseLink(raw_url);
config = app.generateLinkObject(raw_url);
if (e) {
page = document.getElementById(raw_url.split("#").pop());
......@@ -5300,16 +5496,16 @@
if (first || (page && base) || raw_url === $.mobile.getDocumentUrl() ||
data.options.role === "popup") {
// console.log("STOP us, JQM go")
console.log("STOP us, JQM go")
return;
}
if ((document.getElementById(config.id) && base !== null)) {
e.preventDefault();
// console.log("STOP us, STOP JQM")
console.log("STOP us, STOP JQM")
return;
}
handle = true;
// console.log("GO us, STOP JQM")
console.log("GO us, STOP JQM")
e.preventDefault();
} else {
......@@ -5334,7 +5530,6 @@
if (config.deeplink) {
create = true;
}
// prevent browser loading hash?query.json
if (config.layout_identifier.split("?").length > 1) {
destination = config.layout_identifier.split("?")[0];
......@@ -5362,12 +5557,15 @@
*/
//TODO: merge with pagebindings, so all jQuery is at one place!
app.setGlobalBindings = function () {
console.log("SETTING BINDING")
$(document)
.enhanceWithin()
// generate dynamic pages
.on("pagebeforechange", function (e, data) {
console.log("PBC")
console.log(e)
console.log(data)
app.parsePage(e, data);
})
......@@ -5403,7 +5601,7 @@
default:
val = element.value;
last = element.getAttribute("data-last");
if ((last && last === val) && type !== "Submit") {
if ((last && last === val) && type !== "submit") {
return;
}
util.clearTimer();
......@@ -5752,9 +5950,10 @@
* @param {boolean} show Whether to show or hide the loader
* @param {string} message The message to display in the loader
* @param {string} msg_i18n lookup for status message
* @param {string} theme background theme
* @param {string} icon Which icon to display when overriding the loader
*/
util.updateStatus = function (show, message, msg_i18n, icon) {
util.updateStatus = function (show, message, msg_i18n, icon, theme) {
var text_to_display = i18n && msg_i18n ?
map.actions.translateLookup(msg_i18n) : message;
......@@ -5768,17 +5967,16 @@
"textVisible": true,
"theme": app.default_dict.loader_theme,
"html": "<span class='ui-icon-" +
(icon ? (icon + " loading_icon") : "loading") + "'> </span>" +
(icon ? (icon + " loader_icon") : "loading") + "'> </span>" +
"<h1 class='loader_message'>" + text_to_display + "</h1>"
}
);
// allow multiple updates and remove after 1000ms
util.clearTimer();
app.timer = window.setTimeout(function () {
$.mobile.loading("hide");
}, 500);
}, 2500);
} else {
util.errorHandler({"error": "showStatus: Loader not enabled"});
}
......@@ -6045,9 +6243,12 @@
**/
app.contentLoaded(window, function () {
// TODO: don't dump, sync...
// Don't wipe if opened in a popup (like in oAuth redirect_uri)
if (window.opener === null) {
// TODO: don't wipe, sync...
window.localStorage.clear();
window.sessionStorage.clear();
}
app.getFromDisk({
"url": util.getPathFromScriptTag("data-storage"),
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment