MediaWiki:LAPI.js

// ';				var rules = { position: 'absolute', visibility: 'hidden', top: 0, left: 0, margin: 0, border: 0, width: '1px', height: '1px' };				Object.merge(rules, container.style);

container.innerHTML = html; body.insertBefore(container, body.firstChild); var innerDiv = container.firstChild; var checkDiv = innerDiv.firstChild; var td = innerDiv.nextSibling.firstChild.firstChild;

data.doesNotAddBorder = (checkDiv.offsetTop !== 5); data.doesAddBorderForTableAndCells = (td.offsetTop === 5);

innerDiv.style.overflow = 'hidden'; innerDiv.style.position = 'relative'; data.subtractsBorderForOverflowNotVisible = (checkDiv.offsetTop === -5);

var bodyMarginTop = body.style.marginTop; body.style.marginTop = '1px'; data.doesNotIncludeMarginInBodyOffset = (body.offsetTop === 0); body.style.marginTop = bodyMarginTop;

body.removeChild(container); }

function jQuery_offset(node) { if (node === node.ownerDocument.body) return jQuery_bodyOffset(node); if (node.getBoundingClientRect) { var box = node.getBoundingClientRect; var scroll = LAPI.Pos.scrollOffset; return { x: (box.left + scroll.x), y: (box.top + scroll.y)					}; }				if (!data) jQuery_init; var elem = node; var offsetParent = elem.offsetParent; var prevOffsetParent = elem; var doc = elem.ownerDocument; var prevComputedStyle = doc.defaultView.getComputedStyle(elem, null); var computedStyle;

var top = elem.offsetTop; var left = elem.offsetLeft;

while ((elem = elem.parentNode) && elem !== doc.body && elem !== doc.documentElement) { computedStyle = doc.defaultView.getComputedStyle(elem, null); top -= elem.scrollTop; left -= elem.scrollLeft; if (elem === offsetParent) { top += elem.offsetTop; left += elem.offsetLeft; if (data.doesNotAddBorder && !(data.doesAddBorderForTableAndCells && /^t(able|d|h)$/i.test(elem.tagName))) { top += parseInt(computedStyle.borderTopWidth, 10) || 0; left += parseInt(computedStyle.borderLeftWidth, 10) || 0; }						prevOffsetParent = offsetParent; offsetParent = elem.offsetParent; }					if (data.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== 'visible') { top += parseInt(computedStyle.borderTopWidth, 10) || 0; left += parseInt(computedStyle.borderLeftWidth, 10) || 0; }					prevComputedStyle = computedStyle; }

if (prevComputedStyle.position === 'relative' || prevComputedStyle.position === 'static') { top += doc.body.offsetTop; left += doc.body.offsetLeft; }				if (prevComputedStyle.position === 'fixed') { top += Math.max(doc.documentElement.scrollTop, doc.body.scrollTop); left += Math.max(doc.documentElement.scrollLeft, doc.body.scrollLeft); }				return { x: left, y: top };			}

function jQuery_bodyOffset(body) { if (!data) jQuery_init; var top = body.offsetTop, left = body.offsetLeft; if (data.doesNotIncludeMarginInBodyOffset) { top += parseInt(LAPI.DOM.currentStyle(body, 'margin-top'), 10) || 0; left += parseInt(LAPI.DOM.currentStyle(body, 'margin-left'), 10) || 0; }				return { x: left, y: top };			}

return jQuery_offset; }),

isWithin: function (node, x, y) { if (!node || !node.parentNode) return false; var pos = LAPI.Pos.position(node); return (x == null || x > pos.x && x < pos.x + node.offsetWidth) && (y == null || y > pos.y && y < pos.y + node.offsetHeight); },

// Private:

// IE has some strange offset... mouse_offset: function { if (LAPI.Browser.is_ie) { var doc_elem = document.documentElement; if (doc_elem) { if (typeof (doc_elem.getBoundingClientRect) == 'function') { var tmp = doc_elem.getBoundingClientRect; return { x: tmp.left, y: tmp.top };					} else { return { x: doc_elem.clientLeft, y: doc_elem.clientTop };					}				}			}			return { x: 0, y: 0 };		}

}; // end LAPI.Pos

} // end if (guard)

if (typeof (LAPI.Evt) == 'undefined') {

LAPI.Evt = { listenTo: function (object, node, evt, f, capture) { var listener = LAPI.Evt.makeListener(object, f); LAPI.Evt.attach(node, evt, listener, capture); },

attach: function (node, evt, f, capture) { if (node.attachEvent) node.attachEvent('on' + evt, f); else if (node.addEventListener) node.addEventListener(evt, f, capture); else node['on' + evt] = f;		},

remove: function (node, evt, f, capture) { if (node.detachEvent) node.detachEvent('on' + evt, f); else if (node.removeEventListener) node.removeEventListener(evt, f, capture); else node['on' + evt] = null; },

makeListener: function (obj, listener) { // Some hacking around to make sure 'this' is set correctly var object = obj, f = listener; return function (evt) { return f.apply(object, [evt || window.event]); };			// Alternative implementation: // var f = listener.bind (obj); // return function (evt) { return f (evt || window.event); }; },

kill: function (evt) { if (typeof (evt.preventDefault) == 'function') { evt.stopPropagation; evt.preventDefault; // Don't follow the link } else if (typeof (evt.cancelBubble) != 'undefined') { // IE... evt.cancelBubble = true; }			return false; // Don't follow the link (IE) }

}; // end LAPI.Evt

} // end if (guard)

if (typeof (LAPI.Edit) == 'undefined') {

LAPI.Edit = function { this.initialize.apply(this, arguments); };

LAPI.Edit.SAVE = 1; LAPI.Edit.PREVIEW = 2; LAPI.Edit.REVERT = 4; LAPI.Edit.CANCEL = 8;

LAPI.Edit.prototype = { initialize: function (initial_text, columns, rows, labels, handlers) { var my_labels = { box: null, preview: null, save: 'Save', cancel: 'Cancel', nullsave: null, revert: null, post: null };			if (labels) my_labels = Object.merge(labels, my_labels); this.labels = my_labels; this.timestamp = (new Date).getTime; this.id = 'simpleedit_' + this.timestamp; this.view = LAPI.make('div', {				id: this.id			}, {				marginRight: '1em'			}); // Somehow, the textbox extends beyond the bounding box of the view. Don't know why, but // adding a small margin fixes the layout more or less. this.form = LAPI.make(				'form', {				id: this.id + '_form',				action: "",				onsubmit: (function {})			}); if (my_labels.box) { var label = LAPI.make('div'); label.appendChild(LAPI.DOM.makeLabel(this.id + '_label', my_labels.box, this.id + '_text')); this.form.appendChild(label); }			this.textarea = LAPI.make(				'textarea', {				id: this.id + '_text',				cols: columns,				rows: rows,				value: (initial_text ? initial_text.toString : "")			}); LAPI.Evt.attach(this.textarea, 'keyup', LAPI.Evt.makeListener(this, this.text_changed)); // Catch cut/copy/paste through the context menu. Some browsers support oncut, oncopy, // onpaste events for this, but since that's only IE, FF 3, Safari 3, and Chrome, we // cannot rely on this. Instead, we check again as soon as we leave the textarea. Only // minor catch is that on FF 3, the next focus target is determined before the blur event // fires. Since in practice save will always be enabled, this shouldn't be a problem. LAPI.Evt.attach(this.textarea, 'mouseout', LAPI.Evt.makeListener(this, this.text_changed)); LAPI.Evt.attach(this.textarea, 'blur', LAPI.Evt.makeListener(this, this.text_changed)); this.form.appendChild(this.textarea); this.form.appendChild(LAPI.make('br')); this.preview_section = LAPI.make('div', null, {				borderBottom: '1px solid #8888aa',				display: 'none'			}); this.view.insertBefore(this.preview_section, this.view.firstChild); this.save = LAPI.DOM.makeButton(this.id + '_save', my_labels.save, LAPI.Evt.makeListener(this, this.do_save)); this.form.appendChild(this.save); if (my_labels.preview) { this.preview = LAPI.DOM.makeButton(this.id + '_preview', my_labels.preview, LAPI.Evt.makeListener(this, this.do_preview)); this.form.appendChild(this.preview); }			this.cancel = LAPI.DOM.makeButton(this.id + '_cancel', my_labels.cancel, LAPI.Evt.makeListener(this, this.do_cancel)); this.form.appendChild(this.cancel); this.view.appendChild(this.form); if (my_labels.post) { this.post_text = LAPI.DOM.setContent(LAPI.make('div'), my_labels.post); this.view.appendChild(this.post_text); }			if (handlers) Object.merge(handlers, this); if (typeof (this.ongettext) != 'function') this.ongettext = function (text) { return text; }; // Default: no modifications this.current_mask = LAPI.Edit.SAVE + LAPI.Edit.PREVIEW + LAPI.Edit.REVERT + LAPI.Edit.CANCEL; if ((!initial_text || initial_text.trim.length == 0) && this.preview) this.preview.disabled = true; if (my_labels.revert) { this.revert = LAPI.DOM.makeButton(this.id + '_revert', my_labels.revert, LAPI.Evt.makeListener(this, this.do_revert)); this.form.insertBefore(this.revert, this.cancel); }			this.original_text = ""; },

getView: function { return this.view; },

getText: function { return this.ongettext(this.textarea.value); },

setText: function (text) { this.textarea.value = text; this.original_text = text; this.text_changed; },

changeText: function (text) { this.textarea.value = text; this.text_changed; },

hidePreview: function { this.preview_section.style.display = 'none'; if (this.onpreview) this.onpreview(this); },

showPreview: function { this.preview_section.style.display = ""; if (this.onpreview) this.onpreview(this); },

setPreview: function (html) { if (html.nodeName) { LAPI.DOM.removeChildren(this.preview_section); this.preview_section.appendChild(html); } else { this.preview_section.innerHTML = html; }		},

busy: function (show) { if (show) LAPI.Ajax.injectSpinner(this.cancel, this.id + '_spinner'); else LAPI.Ajax.removeSpinner(this.id + '_spinner'); },

do_save: function (evt) { if (this.onsave) this.onsave(this); return true; },

do_revert: function (evt) { this.changeText(this.original_text); return true; },

do_cancel: function (evt) { if (this.oncancel) this.oncancel(this); return true; },

do_preview: function (evt) { var self = this; this.busy(true); LAPI.Ajax.parseWikitext(				this.getText, function (text, failureFunc) {				self.busy(false);				self.setPreview(text);				self.showPreview;			}, function (req, json_result) {				// Error. TODO: user feedback?				self.busy(false);			}, true, mw.config.get('wgUserLanguage') || null, mw.config.get('wgPageName') || null); return true; },

enable: function (bit_set) { var call_text_changed = false; this.current_mask = bit_set; this.save.disabled = ((bit_set & LAPI.Edit.SAVE) == 0); this.cancel.disabled = ((bit_set & LAPI.Edit.CANCEL) == 0); if (this.preview) { if ((bit_set & LAPI.Edit.PREVIEW) == 0) this.preview.disabled = true; else call_text_changed = true; }			if (this.revert) { if ((bit_set & LAPI.Edit.REVERT) == 0) this.revert.disabled = true; else call_text_changed = true; }			if (call_text_changed) this.text_changed; },

text_changed: function (evt) { var text = this.textarea.value; text = text.trim; var length = text.length; if (this.preview && (this.current_mask & LAPI.Edit.PREVIEW) != 0) { // Preview is basically enabled this.preview.disabled = (length <= 0); }			if (this.labels.nullsave) { if (length > 0) { this.save.value = this.labels.save; } else { this.save.value = this.labels.nullsave; }			}			if (this.revert) { this.revert.disabled = (text == this.original_text || this.textarea.value == this.original_text); }			return true; }

}; // end LAPI.Edit

} // end if (guard)

//