| /* A few useful utility functions. */ |
| |
| // Capture a method on an object. |
| function method(obj, name) { |
| return function() {obj[name].apply(obj, arguments);}; |
| } |
| |
| // The value used to signal the end of a sequence in iterators. |
| var StopIteration = {toString: function() {return "StopIteration"}}; |
| |
| // Apply a function to each element in a sequence. |
| function forEach(iter, f) { |
| if (iter.next) { |
| try {while (true) f(iter.next());} |
| catch (e) {if (e != StopIteration) throw e;} |
| } |
| else { |
| for (var i = 0; i < iter.length; i++) |
| f(iter[i]); |
| } |
| } |
| |
| // Map a function over a sequence, producing an array of results. |
| function map(iter, f) { |
| var accum = []; |
| forEach(iter, function(val) {accum.push(f(val));}); |
| return accum; |
| } |
| |
| // Create a predicate function that tests a string againsts a given |
| // regular expression. No longer used but might be used by 3rd party |
| // parsers. |
| function matcher(regexp){ |
| return function(value){return regexp.test(value);}; |
| } |
| |
| // Test whether a DOM node has a certain CSS class. Much faster than |
| // the MochiKit equivalent, for some reason. |
| function hasClass(element, className){ |
| var classes = element.className; |
| return classes && new RegExp("(^| )" + className + "($| )").test(classes); |
| } |
| |
| // Insert a DOM node after another node. |
| function insertAfter(newNode, oldNode) { |
| var parent = oldNode.parentNode; |
| parent.insertBefore(newNode, oldNode.nextSibling); |
| return newNode; |
| } |
| |
| function removeElement(node) { |
| if (node.parentNode) |
| node.parentNode.removeChild(node); |
| } |
| |
| function clearElement(node) { |
| while (node.firstChild) |
| node.removeChild(node.firstChild); |
| } |
| |
| // Check whether a node is contained in another one. |
| function isAncestor(node, child) { |
| while (child = child.parentNode) { |
| if (node == child) |
| return true; |
| } |
| return false; |
| } |
| |
| // The non-breaking space character. |
| var nbsp = "\u00a0"; |
| var matching = {"{": "}", "[": "]", "(": ")", |
| "}": "{", "]": "[", ")": "("}; |
| |
| // Standardize a few unportable event properties. |
| function normalizeEvent(event) { |
| if (!event.stopPropagation) { |
| event.stopPropagation = function() {this.cancelBubble = true;}; |
| event.preventDefault = function() {this.returnValue = false;}; |
| } |
| if (!event.stop) { |
| event.stop = function() { |
| this.stopPropagation(); |
| this.preventDefault(); |
| }; |
| } |
| |
| if (event.type == "keypress") { |
| event.code = (event.charCode == null) ? event.keyCode : event.charCode; |
| event.character = String.fromCharCode(event.code); |
| } |
| return event; |
| } |
| |
| // Portably register event handlers. |
| function addEventHandler(node, type, handler, removeFunc) { |
| function wrapHandler(event) { |
| handler(normalizeEvent(event || window.event)); |
| } |
| if (typeof node.addEventListener == "function") { |
| node.addEventListener(type, wrapHandler, false); |
| if (removeFunc) return function() {node.removeEventListener(type, wrapHandler, false);}; |
| } |
| else { |
| node.attachEvent("on" + type, wrapHandler); |
| if (removeFunc) return function() {node.detachEvent("on" + type, wrapHandler);}; |
| } |
| } |
| |
| function nodeText(node) { |
| return node.textContent || node.innerText || node.nodeValue || ""; |
| } |
| |
| function nodeTop(node) { |
| var top = 0; |
| while (node.offsetParent) { |
| top += node.offsetTop; |
| node = node.offsetParent; |
| } |
| return top; |
| } |
| |
| function isBR(node) { |
| var nn = node.nodeName; |
| return nn == "BR" || nn == "br"; |
| } |
| function isSpan(node) { |
| var nn = node.nodeName; |
| return nn == "SPAN" || nn == "span"; |
| } |