| 1 |
/*
|
| 2 |
purple-include.js -- Purple Include (version 0.8)
|
| 3 |
|
| 4 |
Based on Mark Nottingham's most excellent hinclude.js (version 0.9)
|
| 5 |
|
| 6 |
Copyright (c) 2007 Jonathan Cheyer <jonathan@cheyer.biz>,
|
| 7 |
Eugene Eric Kim <eekim@blueoxen.com>,
|
| 8 |
Brad Neuberg <bradneuberg@yahoo.com>
|
| 9 |
Copyright (c) 2005-2006 Mark Nottingham <mnot@pobox.com>
|
| 10 |
|
| 11 |
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 12 |
of this software and associated documentation files (the "Software"), to deal
|
| 13 |
in the Software without restriction, including without limitation the rights
|
| 14 |
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 15 |
copies of the Software, and to permit persons to whom the Software is
|
| 16 |
furnished to do so, subject to the following conditions:
|
| 17 |
|
| 18 |
The above copyright notice and this permission notice shall be included in all
|
| 19 |
copies or substantial portions of the Software.
|
| 20 |
|
| 21 |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 22 |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 23 |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 24 |
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 25 |
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 26 |
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 27 |
SOFTWARE.
|
| 28 |
|
| 29 |
-------------------------------------------------------------------------------
|
| 30 |
|
| 31 |
See http://blueoxen.net/c/purple/purple-include/ for documentation.
|
| 32 |
See http://www.mnot.net/javascript/hinclude/ for hinclude documentation.
|
| 33 |
|
| 34 |
TODO:
|
| 35 |
- check navigator property to see if browser will handle this without
|
| 36 |
javascript
|
| 37 |
|
| 38 |
-------------------------------------------------------------------------------
|
| 39 |
*/
|
| 40 |
|
| 41 |
var hinclude = {
|
| 42 |
set_content_async: function (url, element, req) {
|
| 43 |
if (req.readyState == 4) {
|
| 44 |
element.className = "included include_" + req.status;
|
| 45 |
if (req.status == 200 | req.status == 304) {
|
| 46 |
this.setHTML(url, req.responseText, element);
|
| 47 |
}else{
|
| 48 |
var origURL = url;
|
| 49 |
if(url.indexOf("purple-proxy.php") != -1){
|
| 50 |
origURL = url.match(/(https?:.*$)/i)[1];
|
| 51 |
}
|
| 52 |
|
| 53 |
element.innerHTML = "<p>Include error for " + url + ": "
|
| 54 |
+ req.status + "</p>";
|
| 55 |
element.className += " include_error";
|
| 56 |
}
|
| 57 |
}
|
| 58 |
},
|
| 59 |
|
| 60 |
buffer: new Array(),
|
| 61 |
set_content_buffered: function (url, element, req) {
|
| 62 |
if (req.readyState == 4) {
|
| 63 |
hinclude.buffer.push({url: url, element: element, req: req});
|
| 64 |
hinclude.outstanding--;
|
| 65 |
if (hinclude.outstanding == 0) {
|
| 66 |
hinclude.show_buffered_content();
|
| 67 |
}
|
| 68 |
}
|
| 69 |
},
|
| 70 |
show_buffered_content: function () {
|
| 71 |
while (hinclude.buffer.length > 0) {
|
| 72 |
var include = hinclude.buffer.pop();
|
| 73 |
include.element.className = "included include_"
|
| 74 |
+ include.req.status;
|
| 75 |
if (include.req.status == 200 | include.req.status == 304) {
|
| 76 |
var replaceMe = include.element;
|
| 77 |
this.setHTML(include.url, include.req.responseText,
|
| 78 |
include.element);
|
| 79 |
}else{
|
| 80 |
var origURL = include.url;
|
| 81 |
if(include.url.indexOf("purple-proxy.php") != -1){
|
| 82 |
origURL = include.url.match(/(https?:.*$)/i)[1];
|
| 83 |
}
|
| 84 |
|
| 85 |
include.element.innerHTML = "<p>Include error for " + origURL
|
| 86 |
+ ": " + include.req.status
|
| 87 |
+ "</p>";
|
| 88 |
include.element.className += " include_error";
|
| 89 |
}
|
| 90 |
}
|
| 91 |
},
|
| 92 |
|
| 93 |
setHTML: function (url, html, element) {
|
| 94 |
// parse out the anchor
|
| 95 |
url = decodeURI(url);
|
| 96 |
var anchor = url.match(/#xpath!(.*)$/);
|
| 97 |
if(anchor){ // xpath! style
|
| 98 |
anchor = anchor[1];
|
| 99 |
}else{
|
| 100 |
anchor = url.match(/#(.*)$/);
|
| 101 |
if (anchor) { // purple number or anchor
|
| 102 |
anchor = "//a[@name='" + anchor[1] + "' or "
|
| 103 |
+ "@id='" + anchor[1] + "']/.."
|
| 104 |
+ " | "
|
| 105 |
+ "//p[@name='" + anchor[1] + "' or "
|
| 106 |
+ "@id='" + anchor[1] + "']";
|
| 107 |
|
| 108 |
}
|
| 109 |
else { // nothing; just grab whole document
|
| 110 |
anchor = "/body";
|
| 111 |
}
|
| 112 |
}
|
| 113 |
|
| 114 |
// simply write the HTML/XHTML into an iframe
|
| 115 |
// and then get our DOM document -- this is the
|
| 116 |
// only way to deal with unformed HTML, and it
|
| 117 |
// actually turns out to be a more robust way of
|
| 118 |
// dealing with XHTML than the DOMParser since
|
| 119 |
// malformed XHTML will still work in an iframe,
|
| 120 |
// but the DOMParser throws exceptions (and almost
|
| 121 |
// everyone's XHTML is actually broken)
|
| 122 |
var f = document.createElement("iframe");
|
| 123 |
f.style.display = "none";
|
| 124 |
document.getElementsByTagName("body")[0].appendChild(f);
|
| 125 |
var doc = f.contentDocument;
|
| 126 |
|
| 127 |
var self = this;
|
| 128 |
// the iframe doesn't immediately parse the content --
|
| 129 |
// it can take an arbitrary amount of time, so register
|
| 130 |
// ourselves to continue working once the iframe is
|
| 131 |
// finished loading
|
| 132 |
f.onload = function(){
|
| 133 |
var origURL = url;
|
| 134 |
if(url.indexOf("purple-proxy.php") != -1){
|
| 135 |
origURL = url.match(/(https?:.*$)/i)[1];
|
| 136 |
}
|
| 137 |
|
| 138 |
// find XPath matches and serialize
|
| 139 |
var isXML = /<\?xml/.test(html);
|
| 140 |
html = "";
|
| 141 |
var matches;
|
| 142 |
var exp = null;
|
| 143 |
try{
|
| 144 |
matches = self.evaluateXPath(doc, anchor, isXML);
|
| 145 |
}catch(e){
|
| 146 |
exp = e;
|
| 147 |
}
|
| 148 |
|
| 149 |
if(!exp && matches){
|
| 150 |
for(var i = 0; i < matches.length; i++){
|
| 151 |
html += (new XMLSerializer()).serializeToString(matches[i])
|
| 152 |
+ "\n";
|
| 153 |
}
|
| 154 |
}
|
| 155 |
|
| 156 |
if(!exp && !matches.length){
|
| 157 |
html = "<p>No include match for " + origURL + "</p>";
|
| 158 |
element.className += " include_error";
|
| 159 |
}else if(exp){
|
| 160 |
html = "<p>Invalid include address for " + origURL + ": "
|
| 161 |
+ exp.message + "</p>";
|
| 162 |
element.className += " include_error";
|
| 163 |
}
|
| 164 |
|
| 165 |
// inline our HTML fragment now, to replace the
|
| 166 |
// hx:include element
|
| 167 |
element.innerHTML = html;
|
| 168 |
};
|
| 169 |
|
| 170 |
// now have the iframe do its magic
|
| 171 |
doc.open();
|
| 172 |
doc.write(html);
|
| 173 |
doc.close();
|
| 174 |
},
|
| 175 |
|
| 176 |
evaluateXPath: function (aDoc, aExpr, isXML) {
|
| 177 |
var nsResolver = (isXML == false) ? null : function (prefix) {
|
| 178 |
return 'http://www.w3.org/1999/xhtml';
|
| 179 |
};
|
| 180 |
var nodes = aDoc.evaluate(aExpr, aDoc.documentElement, nsResolver,
|
| 181 |
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
|
| 182 |
null);
|
| 183 |
var results = [];
|
| 184 |
for(var i = 0; i < nodes.snapshotLength; i++){
|
| 185 |
results.push(nodes.snapshotItem(i));
|
| 186 |
}
|
| 187 |
|
| 188 |
return results;
|
| 189 |
},
|
| 190 |
|
| 191 |
outstanding: 0,
|
| 192 |
run: function () {
|
| 193 |
var mode = this.get_meta("include_mode", "buffered");
|
| 194 |
var callback = function(element, req) {};
|
| 195 |
var includes = document.getElementsByTagName("hx:include");
|
| 196 |
if (includes.length == 0) { // remove ns for IE
|
| 197 |
includes = document.getElementsByTagName("include");
|
| 198 |
}
|
| 199 |
if (mode == "async") {
|
| 200 |
callback = "set_content_async";
|
| 201 |
} else if (mode == "buffered") {
|
| 202 |
callback = "set_content_buffered";
|
| 203 |
var timeout = this.get_meta("include_timeout", 2.5) * 1000;
|
| 204 |
setTimeout("hinclude.show_buffered_content()", timeout);
|
| 205 |
}
|
| 206 |
for (var i=0; i < includes.length; i++) {
|
| 207 |
includes[i].innerHTML = '<img src="roller.gif" />';
|
| 208 |
this.include(includes[i], includes[i].getAttribute("src"),
|
| 209 |
callback);
|
| 210 |
}
|
| 211 |
},
|
| 212 |
|
| 213 |
include: function (element, url, incl_cb) {
|
| 214 |
var scheme = url.substring(0,url.indexOf(":"));
|
| 215 |
|
| 216 |
if (scheme.toLowerCase() == "data") { // just text/plain for now
|
| 217 |
data = unescape(url.substring(url.indexOf(",") + 1, url.length));
|
| 218 |
element.innerHTML = data;
|
| 219 |
}else {
|
| 220 |
if(scheme) {
|
| 221 |
var url_base = this.get_meta("purple_proxy_path",
|
| 222 |
"/hypertext/purple-include/purple-proxy.php");
|
| 223 |
url = url_base + "?url=" + encodeURI(url);
|
| 224 |
}
|
| 225 |
var req = false;
|
| 226 |
if(window.XMLHttpRequest) {
|
| 227 |
try {
|
| 228 |
req = new XMLHttpRequest();
|
| 229 |
} catch(e) {
|
| 230 |
req = false;
|
| 231 |
}
|
| 232 |
} else if(window.ActiveXObject) {
|
| 233 |
try {
|
| 234 |
req = new ActiveXObject("Microsoft.XMLHTTP");
|
| 235 |
} catch(e) {
|
| 236 |
req = false;
|
| 237 |
}
|
| 238 |
}
|
| 239 |
if(req) {
|
| 240 |
this.outstanding++;
|
| 241 |
var self = this;
|
| 242 |
req.onreadystatechange = function() {
|
| 243 |
self[incl_cb](url, element, req);
|
| 244 |
};
|
| 245 |
try {
|
| 246 |
req.open("GET", url, true);
|
| 247 |
req.send("");
|
| 248 |
} catch (e) {
|
| 249 |
this.outstanding--;
|
| 250 |
alert("Include error: " + url + " (" + e + ")");
|
| 251 |
}
|
| 252 |
}
|
| 253 |
}
|
| 254 |
},
|
| 255 |
|
| 256 |
get_meta: function (name, value_default) {
|
| 257 |
var metas = document.getElementsByTagName("meta");
|
| 258 |
for (var m=0; m < metas.length; m++) {
|
| 259 |
var meta_name = metas[m].getAttribute("name");
|
| 260 |
if (meta_name == name) {
|
| 261 |
return metas[m].getAttribute("content");
|
| 262 |
}
|
| 263 |
}
|
| 264 |
return value_default;
|
| 265 |
},
|
| 266 |
|
| 267 |
/*
|
| 268 |
* (c)2006 Dean Edwards/Matthias Miller/John Resig
|
| 269 |
* Special thanks to Dan Webb's domready.js Prototype extension
|
| 270 |
* and Simon Willison's addLoadEvent
|
| 271 |
*
|
| 272 |
* For more info, see:
|
| 273 |
* http://dean.edwards.name/weblog/2006/06/again/
|
| 274 |
* http://www.vivabit.com/bollocks/2006/06/21/a-dom-ready-extension-for-prototype
|
| 275 |
* http://simon.incutio.com/archive/2004/05/26/addLoadEvent
|
| 276 |
*
|
| 277 |
* Thrown together by Jesse Skinner (http://www.thefutureoftheweb.com/)
|
| 278 |
*
|
| 279 |
*
|
| 280 |
* To use: call addDOMLoadEvent one or more times with functions, ie:
|
| 281 |
*
|
| 282 |
* function something() {
|
| 283 |
* // do something
|
| 284 |
* }
|
| 285 |
* addDOMLoadEvent(something);
|
| 286 |
*
|
| 287 |
* addDOMLoadEvent(function() {
|
| 288 |
* // do other stuff
|
| 289 |
* });
|
| 290 |
*
|
| 291 |
*/
|
| 292 |
addDOMLoadEvent: function(func) {
|
| 293 |
if (!window.__load_events) {
|
| 294 |
var init = function () {
|
| 295 |
// quit if this function has already been called
|
| 296 |
if (arguments.callee.done) return;
|
| 297 |
|
| 298 |
// flag this function so we don't do the same thing twice
|
| 299 |
arguments.callee.done = true;
|
| 300 |
|
| 301 |
// kill the timer
|
| 302 |
if (window.__load_timer) {
|
| 303 |
clearInterval(window.__load_timer);
|
| 304 |
window.__load_timer = null;
|
| 305 |
}
|
| 306 |
|
| 307 |
// execute each function in the stack in the order they were added
|
| 308 |
for (var i=0;i < window.__load_events.length;i++) {
|
| 309 |
window.__load_events[i]();
|
| 310 |
}
|
| 311 |
window.__load_events = null;
|
| 312 |
|
| 313 |
// clean up the __ie_onload event
|
| 314 |
/*@cc_on @*/
|
| 315 |
/*@if (@_win32)
|
| 316 |
document.getElementById("__ie_onload").onreadystatechange = "";
|
| 317 |
/*@end @*/
|
| 318 |
};
|
| 319 |
|
| 320 |
// for Mozilla/Opera9
|
| 321 |
if (document.addEventListener) {
|
| 322 |
document.addEventListener("DOMContentLoaded", init, false);
|
| 323 |
}
|
| 324 |
|
| 325 |
// for Internet Explorer
|
| 326 |
/*@cc_on @*/
|
| 327 |
/*@if (@_win32)
|
| 328 |
document.write("<scr"+"ipt id=__ie_onload defer src=javascript:void(0)><\/scr"+"ipt>");
|
| 329 |
var script = document.getElementById("__ie_onload");
|
| 330 |
script.onreadystatechange = function() {
|
| 331 |
if (this.readyState == "complete") {
|
| 332 |
init(); // call the onload handler
|
| 333 |
}
|
| 334 |
};
|
| 335 |
/*@end @*/
|
| 336 |
|
| 337 |
// for Safari
|
| 338 |
if (/WebKit/i.test(navigator.userAgent)) { // sniff
|
| 339 |
window.__load_timer = setInterval(function() {
|
| 340 |
if (/loaded|complete/.test(document.readyState)) {
|
| 341 |
init(); // call the onload handler
|
| 342 |
}
|
| 343 |
}, 10);
|
| 344 |
}
|
| 345 |
|
| 346 |
// for other browsers
|
| 347 |
window.onload = init;
|
| 348 |
|
| 349 |
// create event function stack
|
| 350 |
window.__load_events = [];
|
| 351 |
}
|
| 352 |
|
| 353 |
// add function to event stack
|
| 354 |
window.__load_events.push(func);
|
| 355 |
}
|
| 356 |
}
|
| 357 |
|
| 358 |
hinclude.addDOMLoadEvent(function() { hinclude.run(); });
|
| 359 |
|
| 360 |
/* Simon Willison - http://simonwillison.net/2004/May/30/plinks/ */
|
| 361 |
function plinkHighlight() {
|
| 362 |
if (/#p-/.test(document.location)) {
|
| 363 |
// The user arrived via a plink
|
| 364 |
var plink_id = document.location.href.split('#')[1];
|
| 365 |
var para = document.getElementById(plink_id);
|
| 366 |
para.className = para.className + ' plinkHighlight';
|
| 367 |
}
|
| 368 |
}
|
| 369 |
|
| 370 |
function addpLinks() {
|
| 371 |
var paras = document.getElementsByTagName('p');
|
| 372 |
for (var i = 0; i < paras.length; i++) {
|
| 373 |
var current = paras[i];
|
| 374 |
if (/^p-/.test(current.id)) {
|
| 375 |
// It's a purple link paragraph
|
| 376 |
var plink = document.createElement('a');
|
| 377 |
plink.href = document.location.href.split('#')[0] + '#' + current.id;
|
| 378 |
plink.className = 'plink';
|
| 379 |
plink.appendChild(document.createTextNode(' #'));
|
| 380 |
current.appendChild(plink);
|
| 381 |
}
|
| 382 |
}
|
| 383 |
}
|
| 384 |
|
| 385 |
function addLoadEvent(func) {
|
| 386 |
var oldonload = window.onload;
|
| 387 |
if (typeof window.onload != 'function') {
|
| 388 |
window.onload = func;
|
| 389 |
} else {
|
| 390 |
window.onload = function() {
|
| 391 |
oldonload();
|
| 392 |
func();
|
| 393 |
}
|
| 394 |
}
|
| 395 |
}
|
| 396 |
|
| 397 |
addLoadEvent(addpLinks);
|
| 398 |
addLoadEvent(plinkHighlight);
|