I'm developing a mobile app with Cordova, jQuery and jQuery mobile. To synchronise the data saved on a device I make AJAX
calls to a server (I'm sending and requesting data). The data is saved in a database on the device with this SQLite plugin for cordova. The server-side connection is handled by a php file.
Everytime the app is started a GET
call is made to check for updates and everytime a new note is made the data is send with a PUSH
call.
function onDeviceReady(){
//global variables, bad practice I know
user = 'user';
pass = 'pass';
deviceID = device.uuid;
console.log(deviceID);
db = window.sqlitePlugin.openDatabase({name: 'mood.db', location: 'default'},function(){
db.transaction(function(tx){
tx.executeSql('CREATE TABLE IF NOT EXISTS update_table (uid integer primary key autoincrement, timestamp)',[],function(){},function(e){
console.log(e.message);
});
tx.executeSql('CREATE TABLE IF NOT EXISTS mood_table (uid integer primary key autoincrement, uuid, id, smileyid, textdata, activity, created, last_change, deleted)',[],function(){},function(e){
console.log(e.message);
});
tx.executeSql('SELECT * FROM update_table',[],function(tx, res){
if(res.rows.length === 0){
tx.executeSql('INSERT INTO update_table (timestamp) VALUES (?)',[0],function(){},function(e){
console.log(e.message);
});
}
},function(e){
console.log(e.message);
});
checkForUpdates();
},function(e){
console.log(e.message);
});
},function(e){
console.log(JSON.stringify(e));
});
}
function make_base_auth(username, password) {
var tok = username + ':' + password;
var hash = btoa(tok);
return 'Basic ' + hash;
}
function checkForUpdates(){
console.log('checkForUpdates');
//internet connection check
if(navigator.connection.type != Connection.NONE) {
db.executeSql('SELECT * FROM update_table WHERE uid = 1',[],function(res){
var data,
new_tsmp,
tsmp = (typeof res.rows.item(0).timestamp === undefined) ? '0' : res.rows.item(0).timestamp;
console.log(tsmp);
$.when(
$.ajax({
url: 'http://mydomain.de/my.php',
crossDomain: true,
timeout: 30000,
method: 'GET',
dataType: 'text',
data: {'command': 'check_update',
'unix_time': tsmp,
'uuid': deviceID},
xhrFields: {
withCredentials: true
},
beforeSend: function(xhr){
xhr.setRequestHeader('Authorization', make_base_auth(user, pass));
},
success: function(textdata, status, jqXHR){
data = $.parseJSON(textdata);
//check for updates
console.log(JSON.stringify(data));
console.log(status);
},
error: ajaxErrorCb
})
).done(function(){
switch(data.case){
case 'update':
//update
new_tsmp = data.timestamp;
$.makeArray(data.notes_id).forEach(function(ele, i){
if(data.deleted[i] === '0'){
getData(ele);
} else {
removeData(data.uuid[i], data.id[i]);
}
});
break;
case 'stay':
//???
new_tsmp = tsmp;
populate();
break;
default:
console.log('something is not right');
}
updateTimestamp(new_tsmp);
});
},function(e){
console.log(e.message);
});
} else {
//popup??
alert('Die Daten können nicht mit dem Server synchronisiert werden, da keine Internetverbindung vorhanden ist.')
populate();
}
}
function removeData(uuid, id){
db.executeSql('DELETE FROM mood_table WHERE uuid = ? AND id = ?',[uuid, id],function(){
console.log('removed: '+uuid+', '+id);
},function(e){
console.log(e.message);
})
}
function updateTimestamp(tsmp){
db.executeSql('UPDATE update_table SET timestamp = ? WHERE uid = 1',[tsmp],function(){},function(e){
console.log(e.message);
});
}
/**
*Downloads data from the server and inserts it into the local database.
*@param {integer} id - The id of the row that will be downloaded
*/
function getData(id){
$.ajax({
url: 'http://mydomain.de/my.php',
crossDomain: true,
timeout: 30000,
method: 'GET',
dataType: 'json',
data: {'command': 'get_update_data',
'id': id},
xhrFields: {
withCredentials: true
},
beforeSend: function(xhr){
xhr.setRequestHeader('Authorization', make_base_auth(user, pass));
},
success: function(data,status,jqXHR){
//insert updated data
console.log(JSON.stringify(data));
var activityArr = new Array(),
smileyimg = getSmileyImg(data.smileyid, data.id),
activityValues = data.activity.split(","),
date = new Date(parseInt(data.created, 10)),
dd = date.getDate(),
mm = date.getMonth()+1, //January is 0!
yyyy = date.getFullYear(),
hh = date.getHours(),
mins = date.getMinutes();
if(dd<10) {
dd='0'+dd;
}
if(mm<10) {
mm='0'+mm;
}
if(mins<10) {
mins='0'+mins;
}
var created = hh+':'+mins+' '+dd+'.'+mm+'.'+yyyy,
activityValues.forEach(function(ele, i){
if(ele == 1){
activityArr.push(getActivity(i));
}
});
//check uuid & id
db.transaction(function(tx){
tx.executeSql('SELECT * FROM mood_table WHERE uuid = ? AND id = ?',[data.uuid, data.id],function(tx, res){
if(res.rows.length != 0){
tx.executeSql('UPDATE mood_table SET smileyid = ?, textdata = ?, activity = ?, last_change = ?, deleted = ? WHERE uuid = ? AND id = ?',[data.smileyid, data.textdata, data.activity, data.last_change, data.deleted, data.uuid, data.id],function(){
console.log('insert success');
},function(e){
console.log(e.message);
});
} else {
tx.executeSql('INSERT INTO mood_table (uuid, id, smileyid, textdata, activity, created, last_change, deleted) VALUES (?,?,?,?,?,?,?,?)',[data.uuid, data.id, data.smileyid, data.textdata, data.activity, created, data.last_change, data.deleted],function(){
console.log('insert succes');
},function(e){
console.log(e.message);
});
}
},function(e){
console.log(e.message);
});
//populate?
$('#mytext').prepend('<div class="mytextarea" id="'+data.id+'"></div>');
if(data.uuid == deviceID){
$('#'+data.id).append('<a href="#popupDialog" data-rel="popup" data-position-to="window" class="ui-btn ui-shadow ui-corner-all ui-nodisc-icon ui-alt-icon ui-icon-bars ui-btn-icon-notext ui-btn-inline delete-button" onclick="openPopup('+data.id+');"></a>');
} else {
$('#'+data.id).append('</br>');
}
$('#'+data.id).append(smileyimg+'<p id="'+data.id+'-text" style="margin-top: 10px;">'+data.textdata+'</p><p><div id="taetigkeiten_'+data.id+'"></div>'+created+'</p>');
$('#taetigkeiten_'+data.id).text('Tätigkeiten: ');
$('#taetigkeiten_'+data.id).append(activityArr.join(', '));
graphMalen(smileyArr);
});
},
error: ajaxErrorCb
});
}
/**
*Uploads data to the server.
*@param {integer} id - ID of the div
*@param {integer} smileyid - ID of the smiley
*@param {string} text - Descriptiontext
*@param {integer} activity - ID of the activity
*@param {string} date - Date and time of the creatino
*/
function insertData(id, smileyid, text, activity){
var insertObj = {
'command': 'insert_data',
'uuid': deviceID,
'id': id,
'smileyid': smileyid,
'text': text,
'activity': activity.join(',')
};
$.ajax({
url: 'http://mydomain.de/my.php',
crossDomain: true,
timeout: 30000,
method: 'POST',
dataType: 'text',
data: insertObj,
xhrFields: {
withCredentials: true
},
beforeSend : function(xhr){
xhr.setRequestHeader('Authorization', make_base_auth(user, pass));
},
success: function(data,status){
console.log(JSON.stringify(data));
console.log(status);
},
error: ajaxErrorCb
});
}
function ajaxErrorCb(obj, text, e){
console.log(JSON.stringify(obj));
console.log(text);
}
In the checkForUpdates
function I'm using $.when(...).done(...);
because I previously had troubles appending the HTML
in a for/foreach loop which is done in the success part of the getData
ajax call.
populate()
appends the HTML
with local saved data.
graphMalen()
creates a graph with CSS
.
The data I get in all the ajax calls look like this:
checkForUpdates:
{'case': string,
'notes_id': integer,
'uuid': string,
'id': integer,
'deleted': integer,
'timestamp': integer}
getData:
{'uuid': string,
'id': integer,
'smileyid': integer,
'textdata': string,
'activity': string,
'timedate': integer,
'created': integer,
'last_change': integer,
'deleted': integer}
Are there any improvements I could make the the code? Specifically the ajax calls, I always have a hard time because they are asynchronous and I don't want to set async to false
because of performance.