repoindex/web/js/alpm.js

287 lines
11 KiB
JavaScript

var repoindex = (function() {
// a list of all possible connection states
repoindex.ConnectionStatus = {
Disconnected: "Disconnected",
Connecting: "Connecting",
Connected: "Connected"
};
// a list of all possible request/response types
repoindex.RequestType = {
BasicRepoInfo: "basicrepoinfo",
BasicPackageInfo: "basicpkginfo",
FullPackageInfo: "fullpkginfo",
GroupInfo: "groupinfo",
CheckForUpdates: "checkforupdates"
};
// responsible for the connection to the server
var AlpmClient = function(uri) {
// basic initialization
this.uri = uri;
this.socket = undefined;
this.hasBasicRepoInfo = false;
this.hasGroupInfo = false;
this.scheduledRequests = [];
this.sentRequests = [];
// define functions control the connections
this.resetReconnectTries = function(tries) {
if(tries !== undefined && tries >= 0) {
this.remainingReconnectTries = this.reconnectTries = tries;
} else {
this.remainingReconnectTries = this.reconnectTries;
}
};
this.resetReconnectTries(2);
this.reconnect = function() {
if(this.remainingReconnectTries > 0) {
--this.remainingReconnectTries;
var reconnectTimer = window.setInterval(function() {
window.clearInterval(reconnectTimer);
repoindex.alpmClient.init();
}, 5000);
}
};
this.init = function() {
repoindex.pageManager.setConnectionStatus(repoindex.ConnectionStatus.Connecting);
if(typeof WebSocket != 'function' && typeof MozWebSocket == 'function') {
WebSocket = MozWebSocket;
}
if(this.socket && this.socket.readyState === 1) {
this.socket.onclose = function() {
this.client.init();
};
this.socket.close();
return;
}
this.socket = new WebSocket(this.uri);
this.socket.client = this;
this.socket.onopen = function(event) {
repoindex.pageManager.setConnectionStatus(repoindex.ConnectionStatus.Connected);
this.client.resetReconnectTries();
this.client.doScheduledRequests();
};
this.socket.onclose = function(event) {
repoindex.pageManager.setConnectionStatus(repoindex.ConnectionStatus.Disconnected);
this.client.reconnect();
};
this.socket.onmessage = function(event) {
var msg = JSON.parse(event.data);
switch(msg.class) {
case "error":
repoindex.pageManager.addError("Server replied error: " + msg.msg);
break;
case "results":
this.client.parseResults(msg.id, msg.what, msg.values ? msg.values : msg.value);
break;
}
};
this.socket.onerror = function(event) {
repoindex.pageManager.addError("Connecting to server failed: " + event.reason);
repoindex.pageManager.setConnectionStatus(repoindex.ConnectionStatus.Disconnected);
};
};
this.isOpen = function() {
return this.socket.readyState === 1;
};
this.stop = function() {
if(this.socket) {
this.socket.close();
}
};
// define functions to manage requests
this.requestById = function(id) {
if(id) {
var i;
// check scheduled requests as well as sent requests
for(i = 0; i < this.scheduledRequests.length; ++i) {
if(this.scheduledRequests[i].params.id === id) {
return this.scheduledRequests[i];
}
}
for(i = 0; i < this.sentRequests.length; ++i) {
if(this.sentRequests[i].params.id === id) {
return this.sentRequests[i];
}
}
}
};
this.scheduleRequest = function(type, params, callback) {
// add request information to params
params.class = "query";
params.what = type;
// check whether the same kind of request has already been scheduled
var i = 0;
switch(type) {
case repoindex.RequestType.BasicRepoInfo:
case repoindex.RequestType.GroupInfo:
// -> update existing reuquest in this case
for(; i < this.scheduledRequests.length; ++i) {
if(this.scheduledRequests[i].params.class === "query" && this.scheduledRequests[i].params.what === type) {
// there is already such a request
this.scheduledRequests[i].callbacks.push(callback); // add callback
params.id = this.scheduledRequests[i].params.id; // keep the old ID
this.scheduledRequests[i].params = params; // update params
break;
}
}
break;
default:
// -> other request types can't be updated
i = this.scheduledRequests.length;
}
if(i >= this.scheduledRequests.length) {
// a new request is required
// -> add callback array
var callbacks = [];
if(callback) {
callbacks.push(callback);
}
// -> add unique request ID
while(this.requestById(params.id = repoindex.makeId()));
// -> schedule request
this.scheduledRequests.push({params: params, sent: false, callbacks: callbacks});
}
// try to send scheduled requests
this.doScheduledRequests();
};
this.doScheduledRequests = function() {
// can only send requests if connection is established
if(this.isOpen()) {
while(this.scheduledRequests.length > 0) {
var request = this.scheduledRequests.shift();
request.sent = true;
this.sentRequests.push(request);
this.socket.send(JSON.stringify(request.params));
}
}
};
this.parseResults = function(id, what, values) {
if(!what) {
repoindex.pageManager.addError("Server replied insufficiant results.");
} else if(!values) {
repoindex.pageManager.addError("Server replied \"" + what + "\" with insufficiant data.");
} else {
var request = this.requestById(id);
if(request && request.sent) {
// use the received information
switch(what) {
case repoindex.RequestType.BasicRepoInfo:
this.useBasicRepoInfo(values);
break;
case repoindex.RequestType.BasicPackageInfo:
// the info is used via the registred callbacks only
break;
case repoindex.RequestType.FullPackageInfo:
// the info is used via the registred callbacks only
break;
case repoindex.RequestType.GroupInfo:
this.useGroupInfo(values);
break;
case repoindex.RequestType.CheckForUpdates:
// the info is used via the registred callbacks only
break;
default:
pageManager.addError("Server replied unknown results: " + what);
return; // don't invoke callbacks when results are of unknown type
}
// also invoke callbacks registred for this request
for(var i = 0; i < request.callbacks.length; ++i) {
request.callbacks[i](values);
}
} else {
repoindex.pageManager.addError("Server replied \"" + what + "\" with unknown ID.");
}
}
};
// define functions to perform several requests
this.requestBasicRepoInfo = function(callback) {
this.scheduleRequest(repoindex.RequestType.BasicRepoInfo, {updates: "true"}, callback);
};
this.requestBasicPackagesInfo = function(packageSelection, callback) {
this.scheduleRequest(repoindex.RequestType.BasicPackageInfo, {sel: packageSelection}, callback);
};
this.requestFullPackagesInfo = function(packageSelection, callback) {
this.scheduleRequest(repoindex.RequestType.FullPackageInfo, {sel: packageSelection}, callback);
};
this.requestGroupInfo = function(callback) {
this.scheduleRequest(repoindex.RequestType.GroupInfo, {updates: "true"}, callback);
};
this.checkForUpdates = function(dbName, syncdbNames, callback) {
var params = {
db: dbName
};
if(syncdbNames) {
params.syncdbs = syncdbNames;
}
this.scheduleRequest(repoindex.RequestType.CheckForUpdates, params, callback);
};
// define functions to use differend kinds of results
this.useBasicRepoInfo = function(values) {
if(!values) {
repoindex.pageManager.addError("Server replied insufficiant basic repo info.");
return false;
}
// update repos and package list
var repoMgr = repoindex.pageManager.repoManager;
var pkgMgr = repoindex.pageManager.packageManager;
repoMgr.removeEntries();
pkgMgr.removeEntries();
for(var i1 = 0; i1 < values.length; ++i1) {
var repoEntry = repoMgr.addEntry(values[i1]);
var packages = repoEntry.info.packages;
for(var i2 = 0; i2 < packages.length; ++i2) {
pkgMgr.addEntry(repoEntry, packages[i2]);
}
}
this.hasBasicRepoInfo = true;
pkgMgr.invalidate();
repoMgr.invalidate();
};
this.useGroupInfo = function(values) {
var groupMgr = repoindex.pageManager.groupManager;
groupMgr.removeEntries();
var groupEntries = groupMgr.entries;
for(var i1 = 0; i1 < values.length; ++i1) {
var info = values[i1];
if(info.repo && info.groups) {
for(var i2 = 0; i2 < info.groups.length; ++i2) {
var group = info.groups[i2];
groupMgr.addEntry(info.repo, group.name, group.pkgs);
}
}
}
this.hasGroupInfo = true;
groupMgr.useRequestedData();
};
};
// create a global client used within the entire document
repoindex.alpmClient = new AlpmClient((window.location.protocol === "https:" ? "wss://" : "ws://") + document.domain + ":1234");
return repoindex;
})(repoindex || {});