/*
* MAJAX - The Millennium AJAX Library
*
* Copyright 2007 by Godmar Back godmar@gmail.com
* and Annette Bailey, Virginia Tech.
*
* License: This software is released under the LGPL license,
* See http://www.gnu.org/licenses/lgpl.txt
*
* $Id: majax.js,v 1.15 2008/02/16 13:59:17 gback Exp gback $
*
* Instructions:
* ------------
* This file must be placed into the /screens directory of a III
* Millennium installation. Subsequently, other webpages can
* include this file to make AJAX calls to the Millennium system.
*
*/
/* To customize, either change this section of the file, or create
* your own .js file and include that before or after majax.js.
* The file you create should look like this:
*
* var noCopiesFound = "We don't have this item";
*
* with entries for all message you wish to customize.
*/
/*****************************************************************/
/* A regular expression that is matched against a status to determine if
* an item should count as available.
*/
if (!isAvailableRegex)
var isAvailableRegex = /AVAILABLE/;
/*
* Messages used by majax-showholdings
* showholdings always reports on the status of all copies found.
*/
if (!noCopiesFound)
var noCopiesFound = "No copies found.";
/* 1 copy was found, say what it's status is. */
if (!singleCopyStatus)
var singleCopyStatus = "1 copy is %s";
/* If multiple copies were found, say how many and what their statuses are. */
/* %n is substituted with how many. */
/* %s is substituted with a comma-separated list of statuses. */
if (!multipleCopyStatus)
var multipleCopyStatus = "%n copies found: %s";
/*****************************************************************/
/* Messages used by majax-showholdings-brief - showholdings brief
* says that it's available if at least 1 copy is available.
*/
if (!itemAvailableMsg)
var itemAvailableMsg = "This item is available";
/* If no copy was found (this is suppressed if 856$u is present.) */
if (!noCopyHeld)
var noCopyHeld = "No copy held";
/* If 1 copy was found, but it's not available. */
if (!singleItemUnavailable)
var singleItemUnavailable = "This item is %s";
/* If >1 copy was found, but none is available. */
if (!multipleItemsUnavailable)
var multipleItemsUnavailable = "No copy currently available (copies are %s)";
/* message shown in majax-ebook */
var electronicBookMsg = "[Electronic Book]";
/*****************************************************************/
/*
* Add an event handler, browser-compatible.
* This code taken from http://www.dustindiaz.com/rock-solid-addevent/
* See also http://www.quirksmode.org/js/events_compinfo.html
* http://novemberborn.net/javascript/event-cache
*/
function addEvent( obj, type, fn ) {
if (obj.addEventListener) {
obj.addEventListener( type, fn, false );
EventCache.add(obj, type, fn);
}
else if (obj.attachEvent) {
obj["e"+type+fn] = fn;
obj[type+fn] = function() { obj["e"+type+fn]( window.event ); }
obj.attachEvent( "on"+type, obj[type+fn] );
EventCache.add(obj, type, fn);
}
else {
obj["on"+type] = obj["e"+type+fn];
}
}
/* unload all event handlers on page unload to avoid memory leaks */
var EventCache = function(){
var listEvents = [];
return {
listEvents : listEvents,
add : function(node, sEventName, fHandler){
listEvents.push(arguments);
},
flush : function(){
var i, item;
for(i = listEvents.length - 1; i >= 0; i = i - 1){
item = listEvents[i];
if(item[0].removeEventListener){
item[0].removeEventListener(item[1], item[2], item[3]);
};
if(item[1].substring(0, 2) != "on"){
item[1] = "on" + item[1];
};
if(item[0].detachEvent){
item[0].detachEvent(item[1], item[2]);
};
item[0][item[1]] = null;
};
}
};
}();
addEvent(window,'unload',EventCache.flush);
// end of rock-solid addEvent
String.prototype.trim = function() {
return this.replace(/^\s+|\s+$/g, '');
};
// Begin MAJAX code
function majaxProcessRemainingSpans(spanElems) {
var processed = 0;
while (spanElems.length > 0) {
var spanElem = spanElems.pop();
if (spanElem.expanded)
continue;
var cName = spanElem.className;
if (cName == null)
continue;
var mReq = {
span: spanElem,
removeTitle: function () {
this.span.setAttribute('title', '');
},
success: new Array(),
failure: new Array(),
onsuccess: function (result) {
for (var i = 0; i < this.success.length; i++)
try {
this.success[i](this, result);
} catch (er) { }
this.removeTitle();
},
onfailure: function (status) {
for (var i = 0; i < this.failure.length; i++)
try {
this.failure[i](this, status);
} catch (er) { }
this.removeTitle();
},
/* get the search item that's sent to III
* The search term may be in the title, or in the body.
* It's in the body if the title contains a "*".
* Example:
*
* 0123456789
*/
getSearchItem: function () {
if (this.searchitem === undefined) {
var m, req = this.span.getAttribute('title');
if ((m = req.match(/^(o|i|t|\.b)\*$/)) != null) {
var text = this.span.innerText || this.span.textContent || "";
text = text.trim().toLowerCase();
// ignore surrounding content for ISBN which can occur in 020$a fields
// such as in 1412936373 (cloth)
if (m[1] == "i") {
var m2 = text.match(/((\d|x|X){10,13})/);
if (m2) {
text = m2[1];
}
}
this.searchitem = m[1] + text;
// remove children and make sure is visible
while (this.span.hasChildNodes())
this.span.removeChild(this.span.firstChild);
this.span.style.display = "inline";
} else
this.searchitem = req.toLowerCase();
}
return this.searchitem;
},
printSearchItem: function () {
var m, req = this.searchitem;
if ((m = req.match(/^o(\S*)/)) != null)
return "OCLC# '" + m[1] + "'";
else if ((m = req.match(/^i(\S*)/)) != null)
return "ISBN '" + m[1] + "'";
else if ((m = req.match(/^t(\S*)/)) != null)
return "Title '" + m[1] + "'";
else if ((m = req.match(/^\.(b\S*)/)) != null)
return "Bibrecord '" + m[1] + "'";
return
return "illegal majax request: " + req;
}
};
function addHandler(majaxClass, mReq) {
// insert field datafield/subfield only
var m = majaxClass.match(/majax-marc-(\d\d\d)-(\S)/);
if (m == null) {
m = majaxClass.match(/majax-marc-(\d\d\d)/);
}
if (m) {
mReq.success.push(function (mReq, result) {
var msg = null;
var dfield = result.marc['f' + m[1]];
if (dfield == null)
return;
if (m[2]) { // m[2] is MARC subfield
var d = dfield[m[2]];
if (d !== undefined)
msg = d + " ";
} else {
var _1 = "abcdefghijklmnopqrstuvwxyz0123456789";
msg = "";
for (var i = 0; i < _1.length; i++) {
var d = dfield[_1.charAt(i)];
if (d !== undefined)
msg += d + " ";
}
}
if (msg != null)
mReq.span.appendChild(document.createTextNode(msg));
});
return true;
}
var ms = majaxClass.match(/majax-syndetics-(\S+)/i);
if (ms) {
var clientid = ms[1];
mReq.success.push(function (mReq, result) {
var req = mReq.getSearchItem();
if (req.charAt(0) == 'i') {
var img = document.createElement("img");
var isbn = req.substring(1);
img.setAttribute('src',
"http://syndetics.com/hw7.pl?isbn=" + isbn
+ "/SC.GIF&client=" + clientid);
mReq.span.appendChild(img);
}
});
return true;
}
switch (majaxClass) {
case "majax-showholdings-div":
case "majax-shd":
mReq.success.push(function (mReq, result) {
var divHTML = "";
for (var i = 0; i < result.holdings.length; i++) {
divHTML += "Copy " + (i+1) + ": "
+ result.holdings[i].toLowerCase() + "
";
}
var div = document.createElement("div");
div.innerHTML = divHTML;
mReq.span.appendChild(div);
});
break;
case "majax-newline":
case "majax-nl":
mReq.success.push(function (mReq, result) {
mReq.span.appendChild(document.createElement("br"));
});
break;
case "majax-space":
case "majax-s":
mReq.success.push(function (mReq, result) {
mReq.span.appendChild(document.createTextNode(" "));
});
break;
case "majax-showholdings-brief":
case "majax-shb":
mReq.success.push(function (mReq, result) {
var isAvailable = false;
var msg = "";
for (var i = 0; i < result.holdings.length; i++) {
if (result.holdings[i].match(isAvailableRegex))
isAvailable = true;
msg += (i == 0 ? "" : " ") + result.holdings[i].toLowerCase();
}
if (isAvailable) {
msg = itemAvailableMsg;
} else {
switch (result.holdings.length) {
case 1:
msg = singleItemUnavailable.replace(/%s/, msg);
break;
case 0:
msg = noCopyHeld;
if (result.marc.f856 && result.marc.f856.u)
msg = "";
break;
default:
msg = multipleItemsUnavailable.replace(/%s/, msg);
break;
}
}
mReq.span.appendChild(document.createTextNode(msg));
});
break;
case "majax-showholdings":
case "majax-sh":
mReq.success.push(function (mReq, result) {
var msg = "";
var isAvailable = false;
for (var i = 0; i < result.holdings.length; i++) {
msg += (i == 0 ? "" : ", ") + result.holdings[i].toLowerCase();
if (result.holdings[i].match(isAvailableRegex)) {
isAvailable = true;
}
}
switch (result.holdings.length) {
case 0:
msg = noCopiesFound;
if (result.marc.f856 && result.marc.f856.u)
msg = "";
break;
case 1:
msg = singleCopyStatus.replace(/%s/, msg);
break;
default:
msg = multipleCopyStatus.replace(/%n/, result.holdings.length).replace(/%s/, msg);
break;
}
mReq.span.appendChild(document.createTextNode(msg));
// XXX: if !isAvailable && bibnumber given, add request button
});
break;
case "majax-reportfailure":
case "majax-rf":
mReq.failure.push(function (mReq, status) {
var msg = mReq.printSearchItem() + " not found";
mReq.span.appendChild(document.createTextNode(msg));
});
break;
case "majax-endnote":
case "majax-en":
mReq.success.push(function (mReq, result) {
var p = document.createElement("PRE");
p.className += "majax-endnote-style";
p.appendChild(document.createTextNode(result.endnote));
mReq.span.appendChild(p);
});
break;
case "majax-endnote-switch":
case "majax-ens":
mReq.success.push(function (mReq, result) {
var p = result.majaxMakeEndnoteDisplay(document,
" Endnote", "Show", "Hide", "majax-endnote-style");
mReq.span.appendChild(p);
});
break;
case "majax-harvard-reference":
case "majax-hr":
mReq.success.push(function (mReq, result) {
mReq.span.innerHTML += result.majaxMakeHarvardReference();
});
break;
case "majax-endnote-import":
case "majax-eni":
mReq.success.push(function (mReq, result) {
var a = result.majaxMakeEndnoteImport(document);
a.appendChild(document.createTextNode("Click here to import into EndNote"));
mReq.span.appendChild(a);
});
break;
case "majax-ebook":
case "majax-eb":
mReq.success.push(function (mReq, result) {
try {
// do not consider this an electronic book if there is a 856|3.
// TBD: implement http://roytennant.com/proto/856/analysis.html
if (result.marc.f856.subfields['3'] != null) {
return;
}
var a = document.createElement("a");
a.setAttribute("href", result.marc.f856.u);
a.appendChild(document.createTextNode(electronicBookMsg));
mReq.span.appendChild(a);
} catch (er) { }
});
break;
case "majax-linktocatalog":
case "majax-l":
mReq.success.push(function (mReq, result) {
var p = mReq.span.parentNode;
var s = mReq.span.nextSibling;
p.removeChild(mReq.span);
var a = document.createElement("a");
a.setAttribute("href", majax.majaxSearchURL(mReq.getSearchItem()));
a.appendChild(mReq.span);
p.insertBefore(a, s);
});
break;
default:
return false;
}
return true;
}
var hasMajax = false;
var classEntries = cName.split(/\s+/);
for (var i = 0; i < classEntries.length; i++) {
if (addHandler(classEntries[i], mReq))
hasMajax = true;
}
if (!hasMajax)
continue;
mReq.span.expanded = true; // optimistically
majax.majaxSearch(mReq.getSearchItem(), mReq);
// send up to 2 requests every 100ms, that's 20 per second.
if (++processed >= 2) {
window.setTimeout(function () {
majaxProcessRemainingSpans(spanElems);
}, 100);
return;
}
}
}
function majaxProcessSpans() {
var span = document.getElementsByTagName("span");
var spanElems = new Array();
for (var i = 0; i < span.length; i++) {
spanElems[i] = span[span.length - 1 - i];
}
majaxProcessRemainingSpans(spanElems);
}
var majax; // set in majaxOnLoad when current window finishes loading
// hidden iframe is loaded, commence majax processing
function majaxLoaded() {
majax.debug = false;
for (var i = 0; i < majaxLoadHandlers.length; i++) {
majaxLoadHandlers[i]();
}
majax.loaded = true;
majaxProcessSpans();
}
// current window is loaded, add hidden-iframe for cross-domain communication
function majaxOnLoad() {
try {
var iframe = document.createElement("iframe");
iframe.setAttribute("id", "hiddeniframe");
iframe.setAttribute("name", "hiddeniframe");
// find our install location, then replace majax.js with majax.html
var scripts = document.getElementsByTagName('script');
for (var si = 0; si < scripts.length; si++) {
var script = scripts[si];
if (script.src && script.src.match(/.*\/majax.js/)) {
var html = script.src.replace(/.js$/, ".html");
break;
}
}
iframe.style.display = "none";
addEvent(iframe, "load", majaxLoaded);
document.body.appendChild(iframe);
majax = document.frames ? document.frames['hiddeniframe'] :
document.getElementById("hiddeniframe").contentWindow;
// must delay setting src until after 'majax' is set so that majaxLoaded can be called safely.
iframe.setAttribute("src", html);
} catch (er) {
alert("error in majaxOnLoad: " + er);
}
}
// array of functions which majax runs before doing the span processing.
var majaxLoadHandlers = new Array();
/*
* Add a function to be run before processing spans
* Conservatively, this function works even if called after MAJAX
* has completed loading and has already processed the spans once.
*/
function majaxRunBeforeSpanProcessing(func) {
if (majax !== undefined && majax.loaded) {
func();
majaxProcessSpans();
} else {
majaxLoadHandlers.push(func);
}
}
try {
var superdomain = document.domain.replace(/.*\.(([^.]+\.){1}[^.]+)$/, "$1");
document.domain = superdomain;
} catch (e) {
alert("majax: Cannot set document.domain to " + superdomain);
}
addEvent(window, "load", majaxOnLoad);