XpmLoader改良
昨日id:bellbind:20050806:p1のXpmLoader関数群をメソッド化し、さらに非同期で表示もできるように改造してみた。
// functions for backward compatibility
function loadXpm(url) {
var loader = new XpmLoader(true);
loader.load(url);
}
function loadXpmAt(url, parentId) {
var loader = new XpmLoader(true);
loader.loadAt(url, parentId);
}
// XpmLoader prototype
function XpmLoader(isAsync) {
this.document = document;
this.window = window;
this.isAsync = isAsync;
}
XpmLoader.prototype.load = function (url) {
this.document.write("<div id='" + url + "'></div>");
this.loadAt(url, url);
};
XpmLoader.prototype.loadAt = function (url, parentId) {
var xmlhttp = this.createXmlHttp();
xmlhttp.open("GET", url, true);
var loader = this;
xmlhttp.onreadystatechange = function () {
loader.handleResponse(xmlhttp, parentId);
};
xmlhttp.send(null);
};
XpmLoader.prototype.createXmlHttp = function (){
if (this.window.ActiveXObject) {
return new this.window.ActiveXObject("Microsoft.XMLHTTP");
} else if (this.window.XMLHttpRequest) {
return new this.window.XMLHttpRequest();
} else {
return null;
}
};
XpmLoader.prototype.handleResponse = function (xmlhttp, parentId) {
if (xmlhttp.readyState == 4) {
if (xmlhttp.status == 200) {
this.writeImage(parentId, xmlhttp.responseText);
//this.dumpError(parentId, xmlhttp.responseText);
}
}
};
XpmLoader.prototype.dumpError = function (parentId, message) {
var textNode = this.document.createTextNode(message);
this.document.getElementById(parentId).appendChild(textNode);
};
XpmLoader.prototype.writeImage = function (parentId, source) {
xpmWriter = new XpmLoader.Writer(parentId, source, this.document, this.window, this.isAsync);
xpmWriter.write();
};
// XpmLoader.Witer prototype
XpmLoader.Writer = function (parentId, source, document, window, isAsync) {
this.src = source;
this.parentId = parentId;
this.document = document;
this.window = window;
this.isAsync = isAsync;
this.infoLinePattern = new RegExp();
this.infoLinePattern.compile('"([^"]+)"');
};
XpmLoader.Writer.prototype.dumpError = function (message) {
var textNode = this.document.createTextNode(message);
this.document.getElementById(this.parentId).appendChild(textNode);
};
XpmLoader.Writer.prototype.write = function () {
try {
this.parseMetadata();
this.parseColorLines();
this.parseDataLines();
} catch (error) {
this.dumpError(error);
}
};
XpmLoader.Writer.prototype.getLine = function(errorMessage) {
var result = this.src.match(this.infoLinePattern);
if (result !== null) {
var line = result[1];
this.src = this.src.substring(result.index + result[0].length);
return line;
} else {
throw errorMessage;
}
};
XpmLoader.Writer.prototype.parseMetadata = function () {
var line = this.getLine("Could not read metadata line.");
var nums = line.split(" ");
this.width = parseInt(nums[0]);
this.height = parseInt(nums[1]);
this.colorNum = parseInt(nums[2]);
this.colorSize = parseInt(nums[3]);
this.colors = new Array(this.colorNum);
};
XpmLoader.Writer.prototype.parseColorLines = function () {
var colorNo = 0;
while (colorNo < this.colorNum) {
this.parseColorLine();
colorNo += 1;
}
};
XpmLoader.Writer.prototype.parseColorLine = function () {
var line = this.getLine("Could not read color line.");
var colorId = line.substring(0, this.colorSize);
var color = line.substring(this.colorSize + 1 + 2);
this.colors[colorId] = color;
};
XpmLoader.Writer.prototype.parseDataLines = function () {
this.lineNo = 0;
this.imageRoot = this.createImageRoot();
this.document.getElementById(this.parentId).appendChild(this.imageRoot);
if (this.isAsync) {
this.parseDataLineAsync();
} else {
while (this.lineNo < this.height) {
this.parseDataLine();
this.lineNo += 1;
}
}
};
XpmLoader.Writer.prototype.parseDataLineAsync = function () {
try {
if (this.lineNo < this.height) {
this.parseDataLine();
this.lineNo += 1;
var writer = this;
this.window.setTimeout(function () {writer.parseDataLineAsync();}, 1);
}
} catch (error) {
this.dumpError(error);
}
};
XpmLoader.Writer.prototype.parseDataLine = function () {
var line = this.getLine("Could not read data line.");
var imageLine = this.addImageLine(this.imageRoot);
for (var i = 0; i < this.width; i += 1) {
var colorId = line.substring(i * this.colorSize, (i + 1) * this.colorSize);
var color = this.colors[colorId];
this.addImageCell(imageLine, color);
}
};
XpmLoader.Writer.prototype.createImageRoot = function () {
var table = this.document.createElement("TABLE");
table.border = "0";
table.cellPadding = "0";
table.cellSpacing = "0";
return table;
};
XpmLoader.Writer.prototype.addImageLine = function (imageRoot) {
var tr = imageRoot.insertRow(imageRoot.rows.length);
return tr;
};
XpmLoader.Writer.prototype.addImageCell = function (imageLine, color) {
var cell = imageLine.insertCell(imageLine.cells.length);
cell.bgColor = color;
var table = this.document.createElement("TABLE");
table.border = "0";
table.cellPadding = "0";
table.cellSpacing = "0";
var tr = table.insertRow(table.rows.length);
var td = tr.insertCell(tr.cells.length);
td.width = "1";
td.height = "1";
cell.appendChild(table);
return cell;
};メソッド化した場合は、JavaScriptでも例外処理は便利な機構かもしれない。
メソッド化したほうが副作用として処理が多少早くなるみたい。これはグローバル名前空間にアクセスしないからだと思う。
※Opera8でも動くようRegExpの使い方を修正した
※ JSLintチェックに通した