User:Galil/Sandbox/ajaxifier.js

From Guild Wars Wiki
Jump to navigationJump to search

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Press Ctrl-F5.
 /****
  * 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:
  * [http://orangoo.com/labs/AJS/ AJS], [http://orangoo.com/labs/AJS/ AJS.fx], [http://orangoo.com/labs/GreyBox/ GreyBox] <nowiki>
 */



/*******************************************************************************
 **    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('<li id="ca-unwatch" class="selected">');
      
			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('<li id="ca-watch" class="selected">');
  
			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('<h1 class="firstHeading">') + 25;
		stopIndex = response.indexOf('</h1>', 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('<h1 class="firstHeading">') + 25;
		stopIndex = response.indexOf('</h1>', 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();  
});

/***
 * </nowiki>
 */