Replaced loadResource() with the more streamlined getResource(), for all your resource-getting needs (well, except for binary files, which should be coming soon)

This commit is contained in:
Jeff Parsons 2016-02-21 15:51:54 -08:00
commit f032f95611
26 changed files with 3881 additions and 3777 deletions

View file

@ -701,7 +701,9 @@ C1PDiskController.prototype.setBinding = function(sHTMLType, sBinding, control,
sFileURL = "http://" + window.location.host + "/api/v1/dump?disk=" + sFilePath;
}
controller.println("loading " + str.getBaseName(sFilePath) + "...");
web.loadResource(sFileURL, true, null, controller, controller.loadDisk);
web.getResource(sFileURL, null, true, function(sURL, sResponse, nErrorCode) {
controller.loadDisk(sURL, sResponse, nErrorCode);
});
}
};
}(this);

View file

@ -74,7 +74,10 @@ function C1PROM(parmsROM)
if (sFileExt != DumpAPI.FORMAT.JSON && sFileExt != DumpAPI.FORMAT.HEX) {
sFileURL = web.getHost() + DumpAPI.ENDPOINT + '?' + DumpAPI.QUERY.FILE + '=' + this.sImage + '&' + DumpAPI.QUERY.FORMAT + '=' + DumpAPI.FORMAT.BYTES;
}
web.loadResource(sFileURL, true, null, this, this.convertImage);
var rom = this;
web.getResource(sFileURL, null, true, function(sURL, sResponse, nErrorCode) {
rom.convertImage(sURL, sResponse, nErrorCode);
});
}
}

View file

@ -129,7 +129,9 @@ C1PSerialPort.prototype.setBinding = function(sHTMLType, sBinding, control, sVal
if (serial.bindings["listSerial"]) {
var sFile = serial.bindings["listSerial"].value;
// serial.println("loading " + sFile + "...");
web.loadResource(sFile, true, null, serial, serial.loadFile);
web.getResource(sFile, null, true, function(sURL, sResponse, nErrorCode) {
serial.loadFile(sURL, sResponse, nErrorCode);
});
}
};
return true;
@ -228,8 +230,8 @@ C1PSerialPort.prototype.startLoad = function()
/**
* @this {C1PSerialPort}
* @param {String} sFileName
* @param {string} sFileData (null if loadResource() encountered an error)
* @param {string} sFileName
* @param {string} sFileData (null if getResource() encountered an error)
* @param {number} nResponse from server
*/
C1PSerialPort.prototype.loadFile = function(sFileName, sFileData, nResponse)
@ -245,7 +247,7 @@ C1PSerialPort.prototype.loadFile = function(sFileName, sFileData, nResponse)
this.println("auto-loading " + sFileName);
/*
* QUESTION: Is this setFocus() call strictly necessary? We're being called in the
* context of loadResource(), not some user action. If there was an original user action,
* context of getResource(), not some user action. If there was an original user action,
* then the handler for THAT action should take care to switch focus back, not us.
*/
this.cpu.setFocus();

View file

@ -36,26 +36,28 @@
"use strict";
var fs = require("fs");
var path = require("path");
var http = require("http");
var mkdirp = require("mkdirp");
var crypto = require("crypto");
var defines = require("../../shared/lib/defines");
var net = require("../../shared/lib/netlib");
var proc = require("../../shared/lib/proclib");
var str = require("../../shared/lib/strlib");
var usr = require("../../shared/lib/usrlib");
var DiskAPI = require("../../shared/lib/diskapi");
var DumpAPI = require("../../shared/lib/dumpapi");
var X86 = require("../../pcjs/lib/x86");
/**
* @class exports
* @property {string} name
* @property {string} version
*/
var pkg = require("../../../package.json");
if (NODE) {
var fs = require("fs");
var path = require("path");
var http = require("http");
var mkdirp = require("mkdirp");
var crypto = require("crypto");
var defines = require("../../shared/lib/defines");
var net = require("../../shared/lib/netlib");
var proc = require("../../shared/lib/proclib");
var str = require("../../shared/lib/strlib");
var usr = require("../../shared/lib/usrlib");
var web = require("../../shared/lib/weblib");
var DiskAPI = require("../../shared/lib/diskapi");
var DumpAPI = require("../../shared/lib/dumpapi");
var X86 = require("../../pcjs/lib/x86");
/**
* @class exports
* @property {string} name
* @property {string} version
*/
var pkg = require("../../../package.json");
}
/*
* fConsole controls console messages; it is false by default but is enable by the CLI interface.
@ -425,6 +427,29 @@ DiskDump.CLI = function()
}
};
/**
* API
*
* Client-side version of the server-side function HTTPAPI.processDumpAPI(req, res).
*
* @param {Object} aParms (analogous to req.query on the server)
*/
DiskDump.API = function(aParms)
{
var sDisk = aParms[DumpAPI.QUERY.DISK];
var sFormat = aParms[DumpAPI.QUERY.FORMAT] || DumpAPI.FORMAT.JSON;
var fComments = (aParms[DumpAPI.QUERY.COMMENTS]? true : false);
var disk = new DiskDump(sDisk, null, sFormat, fComments);
disk.loadFile(function(err) {
if (!err) {
var sResponse = disk.convertToJSON();
if (sResponse) web.downloadJSON(sResponse);
}
});
};
/**
* outputDisk(err, disk, sDiskPath, sOutputFile, fOverwrite, sManifestTitle)
*
@ -983,7 +1008,7 @@ DiskDump.prototype.dumpBuffer = function(sKey, buf, len, cbItem, offData)
* @this {DiskDump}
* @param {string} sTrackSig
* @param {number} nTrackNum
* @param {number} nTrackType
* @param {number|null} nTrackType
* @param {number} [nTrackLoad]
* @return {string}
*/
@ -2784,4 +2809,9 @@ DiskDump.prototype.convertToIMG = function()
return this.bufDisk;
};
module.exports = DiskDump;
if (NODE) {
module.exports = DiskDump;
} else {
var aParms = web.getURLParameters();
DiskDump.API(aParms);
}

View file

@ -14,5 +14,8 @@
],
"bin": {
"diskdump": "./bin/diskdump"
},
"dependencies": {
"mkdirp": "^0.3.5"
}
}

View file

@ -13,6 +13,9 @@
}
],
"bin": {
"example": "./bin/htmlout"
"htmlout": "./bin/htmlout"
},
"dependencies": {
"mkdirp": "^0.3.5"
}
}

View file

@ -280,7 +280,10 @@ function Computer(parmsComputer, parmsMachine, fSuspended) {
if (!sStatePath) {
this.setReady();
} else {
web.loadResource(sStatePath, true, null, this, this.doneLoad);
var cmp = this;
web.getResource(sStatePath, null, true, function(sURL, sResource, nErrorCode) {
cmp.doneLoad(sURL, sResource, nErrorCode);
});
}
if (!this.bindings["power"]) this.fAutoPower = true;
@ -436,15 +439,14 @@ Computer.prototype.doneLoad = function(sURL, sStateData, nErrorCode)
/**
* wait(fn, parms)
*
* wait() waits until every component is ready (including ourselves, the last component we check),
* then calls the specified Computer method.
* wait() waits until every component is ready (including ourselves, the last component we check), then calls the
* specified Computer method.
*
* TODO: As with web.loadResource(), the Closure Compiler makes it difficult for us to define
* a function type for "fn" that works in all cases; sometimes we want to pass a function that takes
* only a "number", and other times we want to pass a function that takes only an "Array" (the type
* will mirror that of the "parms" parameter). However, the Closure Compiler insists that both functions
* must be declared as accepting both types of parameters. So once again, we must use an untyped function
* declaration, instead of something stricter like:
* TODO: The Closure Compiler makes it difficult for us to define a function type for "fn" that works in all cases;
* sometimes we want to pass a function that takes only a "number", and other times we want to pass a function that
* takes only an "Array" (the type will mirror that of the "parms" parameter). However, the Closure Compiler insists
* that both functions must be declared as accepting both types of parameters. So once again, we must use an untyped
* function declaration, instead of something stricter like:
*
* param {function(this:Computer, (number|Array|undefined)): undefined} fn
*
@ -845,7 +847,7 @@ Computer.prototype.powerReport = function(stateComputer)
* Power every component "down" and optionally save the machine state.
*
* There's one scenario that powerOff() isn't currently able to deal with very effectively: what to do when
* the user switches away while it's still being restored, causing Disk loadResource() calls to fail. The
* the user switches away while it's still being restored, causing Disk getResource() calls to fail. The
* Disk component calls notify() when that happens -- see Disk.mount() -- but the FDC and HDC controllers don't
* notify *us* of those problems, so Computer assumes that the restore was completely successful, when in fact
* it was only partially successful.
@ -1178,7 +1180,7 @@ Computer.prototype.verifyUserID = function(sUserID)
var fMessages = DEBUG && this.messageEnabled();
if (fMessages) this.printMessage("verifyUserID(" + sUserID + ")");
var sRequest = web.getHost() + UserAPI.ENDPOINT + '?' + UserAPI.QUERY.REQ + '=' + UserAPI.REQ.VERIFY + '&' + UserAPI.QUERY.USER + '=' + sUserID;
var response = web.loadResource(sRequest);
var response = web.getResource(sRequest);
var nErrorCode = response[0];
var sResponse = response[1];
if (!nErrorCode && sResponse) {
@ -1278,24 +1280,24 @@ Computer.prototype.storeServerState = function(sUserID, sState, fSync)
* TODO: Determine whether or not any browsers cancel our request if we're called during a browser "shutdown" event,
* and whether or not it matters if we do an async request (currently, we're not, to try to ensure the request goes through).
*/
var data = {};
data[UserAPI.QUERY.REQ] = UserAPI.REQ.STORE;
data[UserAPI.QUERY.USER] = sUserID;
data[UserAPI.QUERY.STATE] = State.key(this, Computer.APPVERSION);
data[UserAPI.QUERY.DATA] = sState;
var dataPost = {};
dataPost[UserAPI.QUERY.REQ] = UserAPI.REQ.STORE;
dataPost[UserAPI.QUERY.USER] = sUserID;
dataPost[UserAPI.QUERY.STATE] = State.key(this, Computer.APPVERSION);
dataPost[UserAPI.QUERY.DATA] = sState;
var sRequest = web.getHost() + UserAPI.ENDPOINT;
if (!fSync) {
web.loadResource(sRequest, true, data);
web.getResource(sRequest, dataPost, true);
} else {
var response = web.loadResource(sRequest, false, data);
var sResponse = response[1];
if (response[0]) {
var response = web.getResource(sRequest, dataPost);
var sResponse = response[0];
if (response[1]) {
if (sResponse) {
var i = sResponse.indexOf('\n');
if (i > 0) sResponse = sResponse.substr(0, i);
if (!sResponse.indexOf("Error: ")) sResponse = sResponse.substr(7);
}
sResponse = '{"' + UserAPI.RES.CODE + '":' + response[0] + ',"' + UserAPI.RES.DATA + '":"' + sResponse + '"}';
sResponse = '{"' + UserAPI.RES.CODE + '":' + response[1] + ',"' + UserAPI.RES.DATA + '":"' + sResponse + '"}';
}
if (DEBUG && this.messageEnabled()) this.printMessage(sResponse);
return JSON.parse(sResponse);
@ -1359,7 +1361,7 @@ Computer.prototype.onReset = function()
* or not to unload/reload all their original auto-mounted disk images.
*
* However, if we started with a predefined state (ie, sStatePath is set), we take this shortcut, because
* we don't (yet) have code in place to gracefully reload the initial state (requires calling loadResource()
* we don't (yet) have code in place to gracefully reload the initial state (requires calling getResource()
* again); alternatively, we could avoid throwing that state away, but it seems better to save the memory.
*
* TODO: Make this more graceful, so that we can stop using the reloadPage() sledgehammer.

View file

@ -1099,8 +1099,10 @@ Disk.prototype.load = function(sDiskName, sDiskPath, file, fnNotify, controller)
}
}
}
var result = web.loadResource(sDiskURL, true, null, this, this.doneLoad);
return result.length > 0;
var disk = this;
return !!web.getResource(sDiskURL, null, true, function(sURL, sResponse, nErrorCode) {
disk.doneLoad(sURL, sResponse, nErrorCode);
});
};
/**
@ -2032,8 +2034,11 @@ Disk.prototype.readRemoteSectors = function(iCylinder, iHead, iSector, nSectors,
sParms += '&' + DiskAPI.QUERY.ADDR + '=' + iCylinder + ':' + iHead + ':' + iSector + ':' + nSectors;
sParms += '&' + DiskAPI.QUERY.MACHINE + '=' + this.controller.getMachineID();
sParms += '&' + DiskAPI.QUERY.USER + '=' + this.controller.getUserID();
var disk = this;
var sDiskURL = web.getHost() + DiskAPI.ENDPOINT + '?' + sParms;
web.loadResource(sDiskURL, fAsync, null, this, this.doneReadRemoteSectors, [iCylinder, iHead, iSector, nSectors, fAsync, done]);
web.getResource(sDiskURL, null, fAsync, function(sURL, sResponse, nErrorCode) {
disk.doneReadRemoteSectors(sURL, sResponse, nErrorCode, [iCylinder, iHead, iSector, nSectors, fAsync, done]);
});
return;
}
if (done) done(-1, false);
@ -2121,17 +2126,20 @@ Disk.prototype.writeRemoteSectors = function(iCylinder, iHead, iSector, nSectors
}
if (this.fRemote) {
var data = {};
var dataPost = {};
this.fWriteInProgress = true;
data[DiskAPI.QUERY.ACTION] = DiskAPI.ACTION.WRITE;
data[DiskAPI.QUERY.VOLUME] = this.sDiskPath;
data[DiskAPI.QUERY.CHS] = this.nCylinders + ':' + this.nHeads + ':' + this.nSectors + ':' + this.cbSector;
data[DiskAPI.QUERY.ADDR] = iCylinder + ':' + iHead + ':' + iSector + ':' + nSectors;
data[DiskAPI.QUERY.MACHINE] = this.controller.getMachineID();
data[DiskAPI.QUERY.USER] = this.controller.getUserID();
data[DiskAPI.QUERY.DATA] = JSON.stringify(abSectors);
dataPost[DiskAPI.QUERY.ACTION] = DiskAPI.ACTION.WRITE;
dataPost[DiskAPI.QUERY.VOLUME] = this.sDiskPath;
dataPost[DiskAPI.QUERY.CHS] = this.nCylinders + ':' + this.nHeads + ':' + this.nSectors + ':' + this.cbSector;
dataPost[DiskAPI.QUERY.ADDR] = iCylinder + ':' + iHead + ':' + iSector + ':' + nSectors;
dataPost[DiskAPI.QUERY.MACHINE] = this.controller.getMachineID();
dataPost[DiskAPI.QUERY.USER] = this.controller.getUserID();
dataPost[DiskAPI.QUERY.DATA] = JSON.stringify(abSectors);
var disk = this;
var sDiskURL = web.getHost() + DiskAPI.ENDPOINT;
return web.loadResource(sDiskURL, fAsync, data, this, this.doneWriteRemoteSectors, [iCylinder, iHead, iSector, nSectors, fAsync]);
web.getResource(sDiskURL, dataPost, fAsync, function(sURL, sResponse, nErrorCode) {
disk.doneWriteRemoteSectors(sURL, sResponse, nErrorCode, [iCylinder, iHead, iSector, nSectors, fAsync]);
});
}
return false;
};
@ -2188,7 +2196,7 @@ Disk.prototype.disconnectRemoteDisk = function()
sParms += '&' + DiskAPI.QUERY.MACHINE + '=' + this.controller.getMachineID();
sParms += '&' + DiskAPI.QUERY.USER + '=' + this.controller.getUserID();
var sDiskURL = web.getHost() + DiskAPI.ENDPOINT + '?' + sParms;
web.loadResource(sDiskURL, true);
web.getResource(sDiskURL, null, true);
this.fRemote = false;
}
};

View file

@ -117,7 +117,10 @@ function ROM(parmsROM)
if (sFileExt != DumpAPI.FORMAT.JSON && sFileExt != DumpAPI.FORMAT.HEX) {
sFileURL = web.getHost() + DumpAPI.ENDPOINT + '?' + DumpAPI.QUERY.FILE + '=' + this.sFilePath + '&' + DumpAPI.QUERY.FORMAT + '=' + DumpAPI.FORMAT.BYTES + '&' + DumpAPI.QUERY.DECIMAL + '=true';
}
web.loadResource(sFileURL, true, null, this, this.doneLoad);
var rom = this;
web.getResource(sFileURL, null, true, function(sURL, sResponse, nErrorCode) {
rom.doneLoad(sURL, sResponse, nErrorCode);
});
}
}

View file

@ -200,13 +200,12 @@ function Video(parmsVideo, canvas, context, textarea, container)
*/
this.fHasFocus = false;
var video = this;
/*
* Here's the gross code to handle full-screen support across all supported browsers. The lack of standards
* is exasperating; browsers can't agree on 'full' or 'Full, 'request' or 'Request', 'screen' or 'Screen', and
* while some browsers honor other browser prefixes, most browsers don't.
*/
var video = this;
this.fGecko = web.isUserAgent("Gecko/");
var i, sEvent, asPrefixes = ['', 'moz', 'webkit', 'ms'];
@ -288,7 +287,9 @@ function Video(parmsVideo, canvas, context, textarea, container)
if (sFileExt != "json") {
sFileURL = web.getHost() + DumpAPI.ENDPOINT + '?' + DumpAPI.QUERY.FILE + '=' + sFileURL + '&' + DumpAPI.QUERY.FORMAT + '=' + DumpAPI.FORMAT.BYTES;
}
web.loadResource(sFileURL, true, null, this, this.doneLoad);
web.getResource(sFileURL, null, true, function(sURL, sResponse, nErrorCode) {
video.doneLoad(sURL, sResponse, nErrorCode);
});
}
}

View file

@ -58,7 +58,7 @@ var cAsyncMachines = 0;
/**
* loadXML(sFile, idMachine, sParms, fResolve, display, done)
*
* This is the preferred way to load all XML and XSL files. It uses loadResource()
* This is the preferred way to load all XML and XSL files. It uses getResource()
* to load them as strings, which parseXML() can massage before parsing/transforming them.
*
* For example, since I've been unable to get the XSLT document() function to work inside any
@ -97,7 +97,7 @@ function loadXML(sXMLFile, idMachine, sParms, fResolve, display, done)
parseXML(sXML, sXMLFile, idMachine, sParms, fResolve, display, done);
};
display("Loading " + sXMLFile + "...");
web.loadResource(sXMLFile, fAsync, null, null, doneLoadXML);
web.getResource(sXMLFile, null, fAsync, doneLoadXML);
}
/**
@ -177,7 +177,7 @@ function parseXML(sXML, sXMLFile, idMachine, sParms, fResolve, display, done)
* Supposedly, the IE XML DOM parser will throw an exception, but I haven't tested that, and unless all other
* browsers do that, that's not helpful.
*
* The best I can do at this stage (assuming web.loadResource() didn't drop any error information on the floor)
* The best I can do at this stage (assuming web.getResource() didn't drop any error information on the floor)
* is verify that the requested resource "looks like" valid XML (in other words, it begins with a '<').
*/
var xmlDoc = null;
@ -308,7 +308,7 @@ function resolveXML(sXML, display, done)
};
display("Loading " + sRefFile + "...");
web.loadResource(sRefFile, fAsync, null, null, doneReadXML);
web.getResource(sRefFile, null, fAsync, doneReadXML);
return;
}
done(sXML, null);

View file

@ -325,35 +325,29 @@ net.downloadFile = function(sURL, sFile, done)
};
/**
* loadResource(sURL, fAsync, data, componentNotify, fnNotify, pNotify)
* getResource(sURL, dataPost, fAsync, done)
*
* Request the specified resource (sURL), and once the request is complete,
* optionally call the specified method (fnNotify) of the specified component (componentNotify).
*
* TODO: Figure out how we can strongly type the fnNotify parameter, because the Closure Compiler has issues with:
*
* {function(this:Component, string, (string|null), number, (number|string|null|Object|Array|undefined))} [fnNotify]
*
* NOTE: This function is a mirror image of the weblib version, for server-side component testing within Node;
* since it is NOT intended for production use, it may make liberal use of synchronous functions, warning messages, etc.
* Request the specified resource (sURL), and once the request is complete, notify done().
*
* @param {string} sURL
* @param {Object|null} [dataPost] for a POST request (default is a GET request)
* @param {boolean} [fAsync] is true for an asynchronous request
* @param {Object|null} [data] for a POST request (default is a GET request)
* @param {Component} [componentNotify]
* @param {function(...)} [fnNotify]
* @param {number|string|null|Object|Array} [pNotify] optional fnNotify parameter
* @return {Array} containing errorCode and responseText (empty array if async request)
* @param {function(string,string,number)} [done]
* @return {Array|null} Array containing [sResource, nErrorCode], or null if no response yet
*/
net.loadResource = function(sURL, fAsync, data, componentNotify, fnNotify, pNotify) {
var nErrorCode = -1;
var sResponse = null;
net.getResource = function(sURL, dataPost, fAsync, done)
{
var nErrorCode = -1, sResource = null, response = null;
if (net.isRemote(sURL)) {
console.log('net.loadResource("' + sURL + '"): unimplemented');
console.log('net.getResource("' + sURL + '"): unimplemented');
} else {
if (!sServerRoot) {
sServerRoot = path.join(path.dirname(fs.realpathSync(__filename)), "../../../");
}
/*
* TODO: Revisit why we pass back sBaseName instead of the original sURL....
*/
var sBaseName = str.getBaseName(sURL);
var sFile = path.join(sServerRoot, sURL);
if (fAsync) {
@ -362,21 +356,14 @@ net.loadResource = function(sURL, fAsync, data, componentNotify, fnNotify, pNoti
* TODO: If err is set, is there an error code we should return (instead of -1)?
*/
if (!err) {
sResponse = s;
sResource = s;
nErrorCode = 0;
}
if (fnNotify) {
if (!componentNotify) {
fnNotify(sBaseName, sResponse, nErrorCode, pNotify);
} else {
fnNotify.call(componentNotify, sBaseName, sResponse, nErrorCode, pNotify);
}
}
if (done) done(sBaseName, sResource, nErrorCode);
});
return [];
} else {
try {
sResponse = fs.readFileSync(sFile, {encoding: "utf8"});
sResource = fs.readFileSync(sFile, {encoding: "utf8"});
nErrorCode = 0;
} catch(err) {
/*
@ -384,16 +371,11 @@ net.loadResource = function(sURL, fAsync, data, componentNotify, fnNotify, pNoti
*/
console.log(err.message);
}
if (fnNotify) {
if (!componentNotify) {
fnNotify(sBaseName, sResponse, nErrorCode, pNotify);
} else {
fnNotify.call(componentNotify, sBaseName, sResponse, nErrorCode, pNotify);
}
}
if (done) done(sBaseName, sResource, nErrorCode);
response = [sResource, nErrorCode];
}
}
return [nErrorCode, sResponse];
return response;
};
if (NODE) module.exports = net;

View file

@ -44,7 +44,7 @@ if (NODE) {
* savePC(idMachine, sPCJSFile)
*
* @param {string} idMachine
* @param {string} [sPCJSFile]
* @param {string} sPCJSFile
* @return {boolean} true if successful, false if error
*/
function savePC(idMachine, sPCJSFile)
@ -61,7 +61,9 @@ function savePC(idMachine, sPCJSFile)
sPCJSFile = "/versions/pcjs/" + (XMLVERSION || APPVERSION) + "/pc" + (dbg? "-dbg" : "") + ".js";
}
}
web.loadResource(sPCJSFile, true, null, null, downloadCSS, [idMachine, str.getBaseName(sPCJSFile, true), sParms, sState]);
web.getResource(sPCJSFile, null, true, function(sURL, sResponse, nErrorCode) {
downloadCSS(sURL, sResponse, nErrorCode, [idMachine, str.getBaseName(sPCJSFile, true), sParms, sState]);
});
return true;
}
web.alertUser("Unable to identify machine '" + idMachine + "'");
@ -94,7 +96,9 @@ function downloadCSS(sURL, sPCJS, nErrorCode, aMachineInfo)
*/
downloadPC(sURL, null, 0, aMachineInfo);
} else {
web.loadResource(sCSSFile, true, null, null, downloadPC, aMachineInfo);
web.getResource(sCSSFile, null, true, function(sURL, sResponse, nErrorCode) {
downloadPC(sURL, sResponse, nErrorCode, aMachineInfo);
});
}
return;
}

View file

@ -171,37 +171,23 @@ web.notice = function(s, fPrintOnly, id)
};
/**
* loadResource(sURL, fAsync, data, componentNotify, fnNotify, pNotify)
* getResource(sURL, dataPost, fAsync, done)
*
* Request the specified resource (sURL), and once the request is complete,
* optionally call the specified method (fnNotify) of the specified component (componentNotify).
*
* TODO: Figure out how we can strongly type the fnNotify parameter, because the Closure Compiler has issues with:
*
* {function(this:Component, string, (string|null), number, (number|string|null|Object|Array|undefined))} [fnNotify]
* Request the specified resource (sURL), and once the request is complete, notify done().
*
* @param {string} sURL
* @param {Object|null} [dataPost] for a POST request (default is a GET request)
* @param {boolean} [fAsync] is true for an asynchronous request
* @param {Object} [data] for a POST request (default is a GET request)
* @param {Component} [componentNotify]
* @param {function(...)} [fnNotify]
* @param {number|string|null|Object|Array} [pNotify] optional fnNotify info parameter
* @return {Array} containing errorCode and responseText (empty array if fAsync is true)
* @param {function(string,string,number)} [done]
* @return {Array|null} Array containing [sResource, nErrorCode], or null if no response yet
*/
web.loadResource = function(sURL, fAsync, data, componentNotify, fnNotify, pNotify)
web.getResource = function(sURL, dataPost, fAsync, done)
{
var nErrorCode = 0;
var sURLData = null;
var nErrorCode = 0, sResource = null, response = null;
if (typeof resources == 'object' && (sURLData = resources[sURL])) {
if (fnNotify) {
if (!componentNotify) {
fnNotify(sURL, sURLData, nErrorCode, pNotify);
} else {
fnNotify.call(componentNotify, sURL, sURLData, nErrorCode, pNotify);
}
}
return [nErrorCode, sURLData];
if (typeof resources == 'object' && (sResource = resources[sURL])) {
if (done) done(sURL, sResource, nErrorCode);
return [sResource, nErrorCode];
}
if (NODE) {
@ -212,7 +198,7 @@ web.loadResource = function(sURL, fAsync, data, componentNotify, fnNotify, pNoti
* if (!Component) Component = require("./component");
*/
var net = require("./netlib");
return net.loadResource(sURL, fAsync, data, componentNotify, fnNotify, pNotify);
return net.getResource(sURL, dataPost, fAsync, done);
}
var xmlHTTP = (window.XMLHttpRequest? new window.XMLHttpRequest() : new window.ActiveXObject("Microsoft.XMLHTTP"));
@ -228,64 +214,52 @@ web.loadResource = function(sURL, fAsync, data, componentNotify, fnNotify, pNoti
*
* xmlHTTP.onreadystatechange = undefined;
*/
sURLData = xmlHTTP.responseText;
sResource = xmlHTTP.responseText;
/*
* The normal "success" case is an HTTP status code of 200, but when testing with files loaded
* from the local file system (ie, when using the "file:" protocol), we have to be a bit more "flexible".
*/
if (xmlHTTP.status == 200 || !xmlHTTP.status && sURLData.length && web.getHostProtocol() == "file:") {
if (MAXDEBUG) web.log("xmlHTTP.onreadystatechange(" + sURL + "): returned " + sURLData.length + " bytes");
if (xmlHTTP.status == 200 || !xmlHTTP.status && sResource.length && web.getHostProtocol() == "file:") {
if (MAXDEBUG) web.log("xmlHTTP.onreadystatechange(" + sURL + "): returned " + sResource.length + " bytes");
}
else {
nErrorCode = xmlHTTP.status || -1;
web.log("xmlHTTP.onreadystatechange(" + sURL + "): error code " + nErrorCode);
}
if (fnNotify) {
if (!componentNotify) {
fnNotify(sURL, sURLData, nErrorCode, pNotify);
} else {
fnNotify.call(componentNotify, sURL, sURLData, nErrorCode, pNotify);
}
}
if (done) done(sURL, sResource, nErrorCode);
}
};
}
if (data) {
var sData = "";
for (var p in data) {
if (!data.hasOwnProperty(p)) continue;
if (sData) sData += "&";
sData += p + '=' + encodeURIComponent(data[p]);
if (dataPost) {
var sDataPost = "";
for (var p in dataPost) {
if (!dataPost.hasOwnProperty(p)) continue;
if (sDataPost) sDataPost += "&";
sDataPost += p + '=' + encodeURIComponent(dataPost[p]);
}
sData = sData.replace(/%20/g, '+');
if (MAXDEBUG) web.log("web.loadResource(POST " + sURL + "): " + sData.length + " bytes");
sDataPost = sDataPost.replace(/%20/g, '+');
if (MAXDEBUG) web.log("web.getResource(POST " + sURL + "): " + sDataPost.length + " bytes");
xmlHTTP.open("POST", sURL, !!fAsync); // ensure that fAsync is a valid boolean (Internet Explorer xmlHTTP functions insist on it)
xmlHTTP.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xmlHTTP.send(sData);
xmlHTTP.send(sDataPost);
} else {
if (MAXDEBUG) web.log("web.loadResource(GET " + sURL + ")");
if (MAXDEBUG) web.log("web.getResource(GET " + sURL + ")");
xmlHTTP.open("GET", sURL, !!fAsync); // ensure that fAsync is a valid boolean (Internet Explorer xmlHTTP functions insist on it)
xmlHTTP.send();
}
var response = [];
if (!fAsync) {
sURLData = xmlHTTP.responseText;
sResource = xmlHTTP.responseText;
if (xmlHTTP.status == 200) {
if (MAXDEBUG) web.log("web.loadResource(" + sURL + "): returned " + sURLData.length + " bytes");
response = sResource;
if (MAXDEBUG) web.log("web.getResource(" + sURL + "): returned " + sResource.length + " bytes");
} else {
nErrorCode = xmlHTTP.status || -1;
web.log("web.loadResource(" + sURL + "): error code " + nErrorCode);
response = nErrorCode = xmlHTTP.status || -1;
web.log("web.getResource(" + sURL + "): error code " + nErrorCode);
}
if (fnNotify) {
if (!componentNotify) {
fnNotify(sURL, sURLData, nErrorCode, pNotify);
} else {
fnNotify.call(componentNotify, sURL, sURLData, nErrorCode, pNotify);
}
}
response = [nErrorCode, sURLData];
if (done) done(sURL, sResource, nErrorCode);
response = [sResource, nErrorCode];
}
return response;
};
@ -305,15 +279,15 @@ web.loadResource = function(sURL, fAsync, data, componentNotify, fnNotify, pNoti
*/
web.sendReport = function(sApp, sVer, sURL, sUser, sType, sReport, sHostName)
{
var data = {};
data[ReportAPI.QUERY.APP] = sApp;
data[ReportAPI.QUERY.VER] = sVer;
data[ReportAPI.QUERY.URL] = sURL;
data[ReportAPI.QUERY.USER] = sUser;
data[ReportAPI.QUERY.TYPE] = sType;
data[ReportAPI.QUERY.DATA] = sReport;
var dataPost = {};
dataPost[ReportAPI.QUERY.APP] = sApp;
dataPost[ReportAPI.QUERY.VER] = sVer;
dataPost[ReportAPI.QUERY.URL] = sURL;
dataPost[ReportAPI.QUERY.USER] = sUser;
dataPost[ReportAPI.QUERY.TYPE] = sType;
dataPost[ReportAPI.QUERY.DATA] = sReport;
var sReportURL = (sHostName? sHostName : "http://" + SITEHOST) + ReportAPI.ENDPOINT;
web.loadResource(sReportURL, true, data);
web.getResource(sReportURL, dataPost, true);
};
/**
@ -596,7 +570,7 @@ web.isMobile = function()
web.getURLParameters = function(sParms)
{
var aParms = {};
if (window) { // an alternative to "if (typeof module === 'undefined')" if require("defines") has been invoked
if (window) { // an alternative to "if (typeof module === 'undefined')" if require("defines") was used
if (!sParms) {
/*
* Note that window.location.href returns the entire URL, whereas window.location.search
@ -605,7 +579,7 @@ web.getURLParameters = function(sParms)
sParms = window.location.search.substr(1);
}
var match;
var pl = /\+/g; // RegExp for replacing addition symbol with a space
var pl = /\+/g; // RegExp for replacing addition symbol with a space
var search = /([^&=]+)=?([^&]*)/g;
var decode = function(s) { return decodeURIComponent(s.replace(pl, " ")); };
@ -616,6 +590,45 @@ web.getURLParameters = function(sParms)
return aParms;
};
/**
* downloadJSON()
*
* @param {string} sJSON
* @param {string} [sFileName]
*/
web.downloadJSON = function(sJSON, sFileName)
{
var link, sAlert;
var fDownload = !!sFileName;
var sURI = "data:application/json,";
if (!web.isUserAgent("Firefox")) {
fDownload = false;
sURI += encodeURI(sJSON);
} else {
sURI += encodeURIComponent(sJSON);
if (fDownload) {
link = document.createElement('a');
if (typeof link.download != 'string') fDownload = false;
}
}
if (fDownload) {
link.href = sURI;
link.download = sFileName;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
sAlert = 'Check your Downloads folder for "' + sFileName + '".';
} else {
window.open(sURI);
sAlert = 'Check your browser for a new window/tab containing the requested data.';
}
web.alertUser(sAlert);
};
/**
* onCountRepeat(n, fn, fnComplete, msDelay)
*