User:Galil/Sandbox/ajaxifier.js

/**** * ajaxifier.js, as used in User:Galil/GWW Ajaxifier. *  * by User:Galil * * Use however you wish, but be sure to put credit where credit is due. * Uses following libraries, see their pages for the related code: * AJS, AJS.fx, GreyBox */

/******************************************************************************* **   Stylesheet                                                             ** *******************************************************************************/ var ssheet = // autocomplete ' ul#autoCompleteList li { overflow: hidden; padding-left: 4px; padding-right: 4px; ' +                         ' white-space: nowrap; cursor: default; } ' + ' ul#autoCompleteList li:hover { background-color: #f9f9f9; } ' + ' ul#autoCompleteList li span { font-weight: 900; } ' + ' ul#autoCompleteList { margin: 0px; padding: 0px; } ' + ' div#autoComplete { background-color: #ffffff; border: 1px solid #aaaaaa; ' +                   ' padding: 1px; position: absolute; z-index: 5; } ' // ajax watchpage adding + ' li#ca-watch a img { margin-right: 4px; } ' + ' li#ca-unwatch a img { margin-right: 4px; } ' // ajax patrol marking + ' td.diff-ntitle a img { margin-right: 4px; } ' // gallery + ' #GB_overlay { background-color: #000000; position: absolute; margin: auto; top: 0; left: 0; ' +              ' z-index: 100; } ' + ' #GB_window { left: 0; top: 0; font-size: 1px; position: absolute; overflow: visible; ' +             ' z-index: 150; } ' + ' #GB_window .content { width: auto; margin: 0; padding: 0; text-align: center; } ' + ' #GB_frame #GB_loading { margin-top: 50px; margin-left: auto; margin-right: auto; } ' + ' #GB_frame { border: 0; margin: 0; padding: 0; overflow: auto; white-space: nowrap; } ' + ' .GB_Gallery { margin: 0 22px 0 22px; } ' + ' .GB_Gallery .content { background-color: #fff; border: 3px solid #ddd; } ' + ' .GB_header { top: 10px; left: 0; margin: 0; z-index: 500; position: absolute; ' +             ' border-bottom: 2px solid #555555; border-top: 2px solid #555555; } ' + ' .GB_header .inner { background-color: #333333; font-family: Arial, Verdana, sans-serif; ' +                    ' padding: 2px 20px 2px 20px; } ' + ' .GB_header table { background-color: inherit; margin: 0; width: 100%; ' +                   ' border-collapse: collapse; } ' + ' .GB_header .caption { text-align: left; color: #eeeeee; white-space: nowrap; font-size: 20px; } ' + ' .GB_header .close { text-align: right; } ' + ' .GB_header .close img { z-index: 500; cursor: pointer; } ' + ' .GB_header .middle { white-space: nowrap; text-align: center; } ' + ' #GB_middle { color: #eeeeee; } ' + ' #GB_middle img { cursor: pointer; vertical-align: middle; } ' + ' #GB_middle .disabled { cursor: default; } ' + ' #GB_middle .left { padding-right: 10px; } ' + ' #GB_middle .right { padding-left: 10px; } ' + ' .GB_Window .content { background-color: #ffffff; border: 3px solid #cccccc; border-top: none; } ' + ' .GB_Window .header { border-bottom: 1px solid #aaaaaa; border-top: 1px solid #999999; ' +                     ' border-left: 3px solid #cccccc; border-right: 3px solid #cccccc; margin: 0; ' +                     ' height: 22px; font-size: 12px; padding: 3px 0; color: #333333; } ' + ' .GB_Window .caption { font-size: 12px; text-align: left; font-weight: bold; ' +                      ' white-space: nowrap; padding-right: 20px; } ' + ' .GB_Window .close { text-align: right; } ' + ' .GB_Window .close span { font-size: 12px; cursor: pointer; } ' + ' .GB_Window .close img { cursor: pointer; padding: 0 3px 0 0; } ' + ' .GB_Window .on { border-bottom: 1px solid #333333; } ' + ' .GB_Window .click { border-bottom: 1px solid red; } ';

var sstyle = AJS.createDOM('style', { type: 'text/css' }); AJS.setHTML(sstyle, ssheet); AJS.$bytc('head', null)[0].appendChild(sstyle);

/******************************************************************************* **   Resources                                                              ** *******************************************************************************/ var Resources = { HeaderLoadingImage: "data:image/gif;base64,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", TangoCheckIcon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAAAXNSR0IArs4c6QAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9gBDAMXErFz6W0AAAFJSURBVCjPY2QgAih4tfMzMDDwMDAwPGckpNg+dVaosrzkNGbG/xxHz942Y8Gn2Dh6soWGqtyc/wwMbNduPIz99uPXDZwatEP6xIx0lJawsrHxXrt+L+PIvPQ1DAwMDEy4NBhoyvVz8fIov3vzbsW9x69nw8SxarCIm+olLikW+efXz2eXbjwuerCt8j+KBgWvdmat4F4bKJvL1EClh4GJieH23afVV9YUvkA2jEXFt5Pb3FClT1ZKJIGHa6qruAifHjc/v8bn9+9P37r/Yhm67SyMjIzsosJ8Nt8ZOdic7Q22MTAwMjEzMzGevniv8cG2yl/oGpjf3dz9/YeAyUFtFYmID7+Z+X/9Z2L9/eXj5XVdkUXY/MfEwMDAcGFF/tWTZ29kywlxMCiK8zA8eoIIFQwnwRh3H75a+WvXSSY2VhaeZ68+bMKlAQBOGm9OVewMhgAAAABJRU5ErkJggg==", TangoCrossIcon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAAAXNSR0IArs4c6QAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAALEgAACxIB0t1+/AAAAAd0SU1FB9gBDAMXMoodyaUAAAERSURBVCjPlZG9SgNREIW/mXvd5YY10c0uKCnEwlZRFCwU7H0HsfUNbBXxJUxnKwhBSWOwEyvBRhtNoWAhAdFC0SBjEyHkp8ipZuCcmTNzYES4rnoJmAc+gE8gABtACrz0CgvrIbSOs8wW4vgOKFfiuFYtlWxT5AvI+1aNiRych2C3SWKp948neW7X3luiegOUB9nz06qXzRDsPsvs2XubU30HtoFo2E2LO4VC+9t723POgCowMYwczUTRxUOxaA3nrCliK849AZMD2bHq/lmSWF3ViiJvR87Zlaqlqqc93wRgfFn19RBsVrUFbOWq9V0RWxVpA5V/onSJpoA14BdoAD+dHDKg1smnD9IzhAH9aPgDrrlBOx9lMpwAAAAASUVORK5CYII=" };

/******************************************************************************* **   Helper functions                                                       ** *******************************************************************************/ function findPos(obj) { var curleft = curtop = 0; if (obj.offsetParent) { curleft = obj.offsetLeft curtop = obj.offsetTop while (obj = obj.offsetParent) { curleft += obj.offsetLeft curtop += obj.offsetTop }	}	return [curleft,curtop]; }

function getElement(path) { return document.evaluate(path, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; }

function extend(obj, methods) { for (var i in methods) obj[i] = methods[i]; }

/******************************************************************************* **   Search box auto completion                                             ** *******************************************************************************/ var SearchBox = { initialize: function(elem) { if (elem) { AJS.bindMethods(SearchBox);

// turns off IE's and firefox's autocomplete "feature" elem.setAttribute('autocomplete', 'off'); this.element = elem; // initialize sub-objects this.autoComplete.initialize; this.cache.initialize; // set up events AJS.AEV(elem, 'blur', this.onElementBlur); AJS.AEV(elem, 'focus', this.onElementFocus); AJS.AEV(elem, 'keyup', this.onElementKeyUp); }	},

onElementBlur: function(event) { if (this.timeout) { window.clearTimeout(this.timeout); this.timeout = null; }		// we have to wait a bit with hiding, so click events go through this.timeout = window.setTimeout('SearchBox.autoComplete.hide', 100); },

onElementFocus: function(event) { if (this.element.value.length > 0) this.autoComplete.display; },

onElementKeyUp: function(event) { if (this.timeout) { window.clearTimeout(this.timeout); this.timeout = null; }		if (event.keyCode == 40) { // down arrow this.autoComplete.next; this.autoComplete.display; this.element.value = this.autoComplete.selected; } else if (event.keyCode == 38) { // up arrow this.autoComplete.previous; this.autoComplete.display; this.element.value = this.autoComplete.selected; } else if (event.keyCode == 13) { // enter key this.autoComplete.hide; } else if (event.keyCode == 27) { // escape key this.autoComplete.hide; } else if (this.element.value) { if (this.cache.contains(this.element.value)){ this.autoComplete.populate(this.element.value, this.cache.fetch(this.element.value)); this.autoComplete.display; } else { this.timeout = window.setTimeout('SearchBox.fetch', 1000); }		}	},	fetch: function { var input = this.element.value;

if (this.timeout) this.timeout = null;

if (input.length > 0) { var ajax = AJS.getRequest('http://wiki.guildwars.com/api.php?action=opensearch&search='+input, null, 'GET'); ajax.addCallback(function(res_txt, req) {				var json = AJS.evalTxt(res_txt);

if (json.length == 2) { SearchBox.cache.add(json[0], json[1]); SearchBox.autoComplete.populate(json[0], json[1]); if (SearchBox.autoComplete.count > 0) SearchBox.autoComplete.display; else SearchBox.autoComplete.hide; } else { SearchBox.cache.add(input, []); SearchBox.autoComplete.populate(input, []); SearchBox.autoComplete.hide; }			});			ajax.sendReq;		}	},

autoComplete: { initialize: function { var fn = AJS.bind(function {				AJS.bindMethods(SearchBox.autoComplete);				this.current = -1;				this.key = '';				this.arr = new Array;				this.list = AJS.UL({ id: 'autoCompleteList' });				this.container = AJS.DIV({ id: 'autoComplete' }, this.list);				AJS.hideElement(this.container);				AJS.getBody.appendChild(this.container);			}, SearchBox.autoComplete, []);

fn; },		clear: function { while (this.list.hasChildNodes) { this.list.removeChild(this.list.firstChild); }

this.arr = new Array; },

count: function { return this.arr.length; },		display: function { if (this.count > 0 && !this.visible) { var elem = SearchBox.element; var div = this.container; elem_pos = findPos(elem); AJS.setLeft(div, elem_pos[0] - 1); AJS.setTop(div, elem_pos[1] + elem.offsetHeight); AJS.setWidth(div, elem.offsetWidth - 4); AJS.showElement(div); }		},		hide: function { AJS.hideElement(this.container); },

visible: function { return !(AJS.isElementHidden(this.container)); },		next: function { if (this.current == this.count - 1) this.setCurrent(-1); else this.setCurrent(this.current + 1); },		previous: function { if (this.current == -1) this.setCurrent(this.count - 1); else this.setCurrent(this.current - 1); },		reset: function { if (this.list.childNodes.length > this.current && this.current != -1) { this.list.childNodes[this.current].style.backgroundColor = ''; this.list.childNodes[this.current].style.color = 'highlighttext'; }

this.current = -1; },		populate: function(key, arr) { if (key && arr) { if (key.length > 0) { this.clear; this.key = key; this.arr = arr; this.reset; for (var i in arr) { var li = AJS.LI({ id: 'autoCompleteLi' + i, childid: i, title: arr[i]}, 							AJS.SPAN({}, key), arr[i].substr(key.length)); this.list.appendChild(li); var self = this; AJS.AEV(li, 'click', function(event){							var elem = event.target;							var i = elem.getAttribute('childid');							self.setCurrent(i);							SearchBox.element.value = self.selected;							self.hide;						}); }					if (this.count <= 0 && this.visible) this.hide; }			}		},		selected: function { if (this.current != -1) { return this.arr[this.current]; } else { return this.key; }		},		setCurrent: function(id) { if (this.current != -1) { this.list.childNodes[this.current].style.backgroundColor = ''; this.list.childNodes[this.current].style.color = ''; }			if (id != -1) { this.list.childNodes[id].style.backgroundColor = 'highlight'; this.list.childNodes[id].style.color = 'highlighttext'; }			this.current = id; }	},	cache: { initialize: function { AJS.bindMethods(SearchBox.cache); SearchBox.cache.cache = new Object; },		add: function(key, arr) { this.cache[key] = arr; },		contains: function(key) { if (this.cache[key]) return true; else return false; },		count: function(key) { if (this.cache[key]) return this.cache[key].length; else return -1; },		fetch: function(key) { return this.cache[key]; },		remove: function(key) { if (this.cache[key]) this.cache[key] = null; }	} };

/******************************************************************************* **   AJAX link class                                                        ** *******************************************************************************/ function AjaxLink(elem) { this.initialize(elem); }

extend(AjaxLink.prototype, {	initialize: function(elem) {		if (!elem)			return;

// init variables this.link = elem; this.url = elem.href; this.working = false;

// "nullify" the url this.link.href = 'javascript:void(0);';

// insert the loading image first in the link this.image = AJS.IMG({ src: Resources.HeaderLoadingImage, alt: 'Working' }); AJS.hideElement(this.image);

if (this.link.hasChildNodes) this.link.insertBefore(this.image, this.link.firstChild); else this.link.appendChild(this.image);

// events and binds var self = this; this.onLinkClick = AJS.bind(this.onLinkClick, this); this.disable = AJS.bind(this.disable, this); AJS.AEV(this.link, 'click', this.onLinkClick); },

onLinkClick: function(event) { this.working = true;

var self = this; var ajax = AJS.getRequest(this.url, null, 'GET');

ajax.addCallback(function(res_txt, req) {			self.working = false;			AJS.hideElement(self.image);

var fn = AJS.bind(self.onSuccess, self, [ res_txt ]); fn; });		ajax.addErrback(function(res_txt, req) { self.working = false; AJS.hideElement(self.image); var fn = AJS.bind(self.onError, self, [ res_txt ]); fn; });		this.image.src = Resources.HeaderLoadingImage;		AJS.showElement(this.image);		ajax.sendReq;	},

disable: function { AJS.REV(this.link, 'click', this.onLinkClick); },

onError: function(response) { // empty since it's to be overridden anyway },

onSuccess: function(response) { // empty since it's to be overridden anyway } });

/******************************************************************************* **   Various AJAX links                                                     ** *******************************************************************************/ var WatchPage = { initialize: function { AJS.bindMethods(WatchPage);

// init variables if (AJS.$('ca-watch')) this.link = new AjaxLink(AJS.$('ca-watch').firstChild); else if (AJS.$('ca-unwatch')) this.link = new AjaxLink(AJS.$('ca-unwatch').firstChild); else return; // is this page watched? if (this.link.url.indexOf('action=watch') != -1) this.watched = false; else this.watched = true; // set up events this.link.onSuccess = this.toggle; },	toggle: function(response) { var startIndex = 0; var stopIndex = 0; this.watched = !(this.watched); if (this.watched) { this.link.url = this.link.url.replace(/action=watch/i, 'action=unwatch'); startIndex = response.indexOf(''); this.link.link.parentNode.id = 'ca-unwatch'; var i = this.link.link.title.indexOf(' ['); if (ta) { if (i != -1) this.link.link.title = ta['ca-unwatch'][1] + this.link.link.title.substr(i); else this.link.link.title = ta['ca-unwatch'][1]; }		} else { this.link.url = this.link.url.replace(/action=unwatch/i, 'action=watch'); startIndex = response.indexOf(''); this.link.link.parentNode.id = 'ca-watch'; var i = this.link.link.title.indexOf(' ['); if (ta) { if (i != -1) this.link.link.title = ta['ca-watch'][1] + this.link.link.title.substr(i); else this.link.link.title = ta['ca-watch'][1]; }		}		startIndex = response.indexOf('>', startIndex + 40) + 1; stopIndex = response.indexOf('<', startIndex); this.link.link.lastChild.nodeValue = response.substr(startIndex, stopIndex - startIndex); } };

var PatrolPage = { initialize: function { AJS.bindMethods(PatrolPage);

// init variables var elem = getElement("//td[@class='diff-ntitle']/a[contains(@href, 'action=markpatrolled')]"); if (!elem) return; this.link = new AjaxLink(elem); // set up events this.link.onError = this.failed; this.link.onSuccess = this.marked; },	marked: function(response) { var startIndex = 0; var stopIndex = 0; startIndex = response.indexOf('') + 25; stopIndex = response.indexOf(' ', startIndex);

this.link.link.lastChild.nodeValue = response.substr(startIndex, stopIndex - startIndex); this.link.image.src = Resources.TangoCheckIcon; this.link.image.alt = 'Success'; AJS.showElement(this.link.image); this.link.disable; },

failed: function(response) { var startIndex = 0; var stopIndex = 0; startIndex = response.indexOf('') + 25; stopIndex = response.indexOf(' ', startIndex);

this.link.link.lastChild.nodeValue = response.substr(startIndex, stopIndex - startIndex); this.link.image.src = Resources.TangoCrossIcon; this.link.image.alt = 'Failed'; AJS.showElement(this.link.image); } };

/******************************************************************************* **   Gallery                                                                ** *******************************************************************************/ var Gallery = { initialize: function { if (wgPageName.indexOf('Image:') == 0) return; var a = AJS.A({ href: 'javascript:void(0);', title: 'View this page\'s gallery' }, 'Gallery'); this.link = AJS.LI({ id: 'ca-gallery' }, a); Gallery.imageList.initialize; Gallery.imageList.populate(AJS.$('bodyContent')); if (Gallery.imageList.count > 0) { var actionList = getElement("//div[@id='p-cactions']/div[@class='pBody']/ul"); if (actionList) actionList.appendChild(this.link); }		AJS.AEV(a, 'click', function(event){			var fun = AJS.bind(Gallery.activate, Gallery);			fun;		}); },	activate: function { if (Gallery.imageList.count == 1) GB_showImage(Gallery.imageList.images[0].caption, Gallery.imageList.images[0].url); else if (Gallery.imageList.count > 1) GB_showImageSet(Gallery.imageList.images, 1); },	imageList: { initialize: function { this.count = 0; this.images = new Array; this.minSize = 4097; // this is in number of pixels, since we DO want to display // armors that are 64x100 (6400) pixels and such, but not // skills icons that are 64x64 pixels (4096), so add 1px ;)		},		populate: function(fromElement) {			if (!fromElement)				return;			var imgs = AJS.$bytc('img', null, fromElement);			if (imgs) {				for (var i = 0; i < imgs.length; i++) {					if (imgs[i].width * imgs[i].height >= this.minSize) {						var img = imgs[i];						this.count++;						this.images.push({ 'caption': unescape(img.parentNode.href.substr(img.parentNode.href.indexOf('Image:')).replace(/_/g, ' ')), 'url': img.src.replace(/\/thumb(\/.*?)\/[^\/]*?$/i, '$1') });					}				}			}		}	} };

AJS.AEV(window, 'load', function {	SearchBox.initialize(AJS.$('searchInput'));

WatchPage.initialize; PatrolPage.initialize;

Gallery.initialize; });

/*** * */