I've been doing a fair bit of JScript of late, and as I find working with Registry for runtime configuration a bit tiresome, I've cooked up a Config object. It's defined using the
prototype
approach to object definition. Config files are, in this case, ANSI or Unicode (UTF16LE) files, in the formkey=value
Creating an instance of the object involves passing in the name of the config file. I've hit upon the following technique for tying the config file to the script, using the FileSystemObject
and the WScript
object. var oFSO = new ActiveXObject("Scripting.FileSystemObject"), sHere = oFSO.GetParentFolderName(WScript.ScriptFullName), sConfig = oFSO.BuildPath(sHere, oFSO.GetBaseName(WScript.ScriptName) + ".config");That way one may always be sure that (for example)
Melchizedek.js
will have a Melchizedek.config
next to it. First, the main function
function Config(sFile) { this.kind = ""; this.oFSO = new ActiveXObject("Scripting.FileSystemObject"); this.text = ""; var resp = []; this.file = sFile; if (this.oFSO.FileExists(sFile)) { resp = function(oFSO, sFilename) { var forReading = 1; var asUnicode = -1; var asANSI = 0; var resp = []; if (oFSO.FileExists(sFilename)) { var handle = oFSO.OpenTextFile(sFilename, forReading, false, asANSI); var BOM = handle.Read(2); handle.Close(); if (BOM.charCodeAt(0) == 0xFF && BOM.charCodeAt(1) == 0xFE) { handle = oFSO.OpenTextFile(sFilename, forReading, false, asUnicode); resp[0] = "unicode"; } else { handle = oFSO.OpenTextFile(sFilename, forReading, false, asANSI); resp[0] = "ansi"; } resp[1] = handle.ReadAll(); handle.Close(); return resp; } else { return resp; } }(this.oFSO, sFile); this.kind = resp[0]; this.text = resp[1]; this.name = sFile; this.filefound = true; } else { this.filefound = false; } return this.filefound; }Note that the code, in an attempt to deal appropriately with config files being in ANSI or UTF-16LE format, reads the first two bytes of the config file. If the UTF16LE BOM is there, it reads the file as Unicode. Otherwise, it reads as ANSI.
A few other things are set so that the other methods will work, namely for
exists
, name
and kind
.Config.prototype.name = function() { return this.name; }; Config.prototype.exists = function() { return this.filefound; }; Config.prototype.kind = function () { return this.kind; };Next come the main workhorses of the object:
define
, retrieve
and save
. Config.prototype.retrieve = function(sName) { if (arguments.length > 1) { var sDefault = arguments[1]; } else { sDefault = null; } var re = new RegExp("^" + sName + "=(.*?)$", "m"); var arr = re.exec(this.text); if (arr === null) { return sDefault; } else { return RegExp.$1; } }; Config.prototype.define = function(sName, sValue) { var sNew = sName + "=" + sValue; var re = new RegExp("^" + sName + "=(.*?)$", "m"); var arr = re.exec(this.text); if (arr === null) { this.text = this.text + "\n" + sNew; } else { this.text = this.text.replace(re, sNew); } }; Config.prototype.save = function() { var sFile = ""; var handle = ""; // allow for save to a different file if (arguments.length > 0) { sFile = arguments[0]; } else { sFile = this.file; } if (this.kind === "ansi") { handle = this.oFSO.CreateTextFile(sFile, true, false); } else { handle = this.oFSO.CreateTextFile(sFile, true, true); } handle.Write(this.text); handle.Close(); };
retrieve
accepts two parameters: the key in the config store, and the default (in the event that the key is not found.) Rather than using a Scripting.Dictionary
, the config data is stored as a string, and regular expressions are used to extract and update it.define
accepts two parameters: the key in the config file, and the value to be associated with it. If the key is not found in the stored keys and values, it is appended to it.save
accepts one optional parameter: the name of the file into which to write the stored string of keys and values. If no name is given, the filename given when Config()
was first called is used.The code may be used as follows:
var c = new Config("dog.cfg"); 'the define() will either add or update the store to ' sound=bark c.define("sound","bark"); 'the save() will write the store to dog.cfg c.save(); 'Config reloads dog.cfg var c = new Config("dog.cfg"); 'if "sound" is a key in dog.cfg, the associated value will be displayed 'otherwise the default. WScript.Echo(c.retrieve("sound","woof")); 'display the kind (ansi or unicode) WScript.Echo(c.kind); '... whether the file exists WScript.Echo(c.exists()); '... and what its name is WScript.Echo(c.name);The result of running the above code (from within SciTE):
>cscript /nologo Config.js bark unicode -1 dog.cfg >Exit code: 0I have recently pushed this code through Google's Closure Compiler and the difference in code size is significant -- the result is about 50% smaller than the original. I expect it runs faster too, though I haven't bothered to check it out thoroughly.
function Config(a) { this.kind = ""; this.oFSO = new ActiveXObject("Scripting.FileSystemObject"); this.text = ""; var b = []; this.file = a; if(this.oFSO.FileExists(a)) { var b = this.oFSO, d = []; if(b.FileExists(a)) { var c = b.OpenTextFile(a, 1, !1, 0), e = c.Read(2); c.Close(); 255 == e.charCodeAt(0) && 254 == e.charCodeAt(1) ? (c = b.OpenTextFile(a, 1, !1, -1), d[0] = "unicode") : (c = b.OpenTextFile(a, 1, !1, 0), d[0] = "ansi"); d[1] = c.ReadAll(); c.Close() } b = d; this.kind = b[0]; this.text = b[1]; this.name = a; this.filefound = !0 }else { this.filefound = !1 } return this.filefound } Config.prototype.name = function() { return this.name }; Config.prototype.exists = function() { return this.filefound }; Config.prototype.kind = function() { return this.kind }; Config.prototype.retrieve = function(a) { var b = 1 < arguments.length ? arguments[1] : null; return null === RegExp("^" + a + "=(.*?)$", "m").exec(this.text) ? b : RegExp.$1 }; Config.prototype.define = function(a, b) { var d = a + "=" + b, c = RegExp("^" + a + "=(.*?)$", "m"); this.text = null === c.exec(this.text) ? this.text + "\n" + d : this.text.replace(c, d) }; Config.prototype.save = function() { var a = "", a = "", a = 0 < arguments.length ? arguments[0] : this.file, a = "ansi" === this.kind ? this.oFSO.CreateTextFile(a, !0, !1) : this.oFSO.CreateTextFile(a, !0, !0); a.Write(this.text); a.Close() };Enjoy!
© Copyright Bruce M. Axtens, 2012