Benutzer:Wilhans/monobook.js
aus Wikipedia, der freien Enzyklopädie
Hinweis: Leere nach dem Speichern den Browser-Cache, um die Änderungen zu sehen: Mozilla/Firefox: Shift-Strg-R, Internet Explorer: Strg-F5, Opera: F5, Safari: ⌘-R, Konqueror: Strg-R.
/* <pre> */ //====================================================================== //## functions.js /** finds an element in document by id */ function $(id) { return document.getElementById(id); } /** finds descendants of an ancestor by tagName, className and index */ function descendants(ancestor, tagName, className, index) { if (ancestor == null) return null; if (ancestor.constructor == String) { ancestor = document.getElementById(ancestor); } var elements = ancestor.getElementsByTagName(tagName ? tagName : "*"); if (className) { var tmp = new Array(); for (var i=0; i<elements.length; i++) { if (elements[i].className == className) { tmp.push(elements[i]); } } elements = tmp; } if (typeof index == "undefined") return elements; if (index >= elements.length) return null; return elements[index]; } /** add an OnLoad event handler */ function doOnLoad(callback) { //.. gecko, safari, konqueror and standard if (typeof window.addEventListener != 'undefined') window.addEventListener('load', callback, false); //.. opera 7 else if (typeof document.addEventListener != 'undefined') document.addEventListener('load', callback, false); //.. win/ie else if (typeof window.attachEvent != 'undefined') window.attachEvent('onload', callback); // mac/ie5 and other crap fails here. on purpose. } //====================================================================== //## Page.js //@depends functions.js /** represents the current Page */ function Page() { //------------------------------------------------------------------------------ //## public URL generation /** compute an URL in action form which may have a title parameter */ this.actionURL = function(args) { var url = this.wiki.site + this.wiki.actionPath; return appendArgs(url, args); } /** compute an URL in the read form without a title parameter */ this.readURL = function(lemma, args, target) { var url = this.wiki.site + this.wiki.readPath + encodeTitle(lemma) + (target ? "/" + encodeTitle(target) : ""); return appendArgs(url, args); } //------------------------------------------------------------------------------ //## url encoding and decoding /** appends arguments to an url with "?" or "&" depending on context */ function appendArgs(url, args) { var code = encodeArgs(args); if (code == "") return url; return url + (url.indexOf("?") == -1 ? "?" : "&") + code; } /** encodes an Object or Array into URL parameters. */ function encodeArgs(args) { if (!args) return ""; var query = ""; for (arg in args) { var key = encodeURIComponent(arg); var raw = args[arg]; if (raw == null) continue; var value = arg == "title" || arg == "target" ? encodeTitle(raw) : encodeURIComponent(raw.toString()); query += "&" + key + "=" + value; } if (query == "") return ""; return query.substring(1); } /** convert URL-parameters into an Array. */ function decodeArgs(search) { var out = new Object(); if (search == "") return out; var split = search.split("&"); for (i=0; i<split.length; i++) { var parts = split[i].split("="); var key = decodeURIComponent(parts[0]); var value = key == "title" || key == "target" ? decodeTitle(parts[1]) : decodeURIComponent(parts[1]); out[key] = value; } return out; } /** encode a MediaWiki title into URL form */ function encodeTitle(title) { return encodeURIComponent(title) .replace(/%3[aA]/g, ":") .replace(/%2[fF]/g, "/") .replace(/ /g, "_"); } /** decode a MediaWiki title from an URL */ function decodeTitle(title) { // MediaWiki allows "+" for "+" instead of " " since 05jan06 title = title.replace(/\+/g, "%2b"); return decodeURIComponent(title) .replace(/_/g, " "); } //------------------------------------------------------------------------------ //## page info function locationSite() { return location.protocol + "//" + location.host + (location.port ? ":" + location.port : ""); } function locationArgs() { if (!location.search) return new Object(); return decodeArgs(location.search.substring(1)) } function searchformAction(site) { var search = $('searchform').action; if (search.substring(0, site.length) == site) { search = search.substring(site.length); } var match = /(.*\/)(.*):.*/(search); return { readPath: match[1], specialNS: decodeTitle(match[2]) }; } // readPath is needed later, but this.wiki is not accessible within an ordinary function :/ var site = locationSite(); var search = searchformAction(site); var readPath = search.readPath; var specialNS = search.specialNS; var params = locationArgs(); function lemma() { if (params.title) return params.title; var split = location.pathname.split(readPath); if (split.length < 2) return null; return decodeTitle(split[1]); } function user() { // ca-nstab-user ??? var userPage = $('pt-userpage'); if (userPage == null) return null; var href = userPage.getElementsByTagName("a")[0].attributes.href.value; var split = href.split(readPath); if (split.length < 2) return null; var full = decodeTitle(split[1]); return full.split("/")[0]; } function perma() { var link = $('t-permalink'); if (!link) return null; var a = link.getElementsByTagName("a")[0]; if (!a) return null; return a.href; // to get the oldid use this: // .replace(/^.*&oldid=([0-9]+).*/, "$1"); } //------------------------------------------------------------------------------ //## public info this.lemma = lemma(); this.perma = perma(); this.user = user(); this.params = params; this.wiki = { site: site, actionPath: "/w/index.php", //### generalize readPath: readPath, specialNS: specialNS, } } //====================================================================== //## Ajax.js /** ajax helper functions */ var Ajax = { /** headers preset for POSTs */ urlEncoded: function(charset) { return { "Content-Type": "application/x-www-form-urlencoded; charset=" + charset }}, /** headers preset for POSTs */ multipartFormData: function(boundary, charset) { return { "Content-Type": "multipart/form-data; boundary=" + boundary + "; charset=" + charset }}, /** encodes an Object or Array into URL parameters. */ encodeArgs: function(args) { if (!args) return ""; var query = ""; for (var arg in args) { var key = encodeURIComponent(arg); var raw = args[arg]; if (raw == null) continue; var value = encodeURIComponent(raw.toString()); query += "&" + key + "=" + value; } if (query == "") return ""; return query.substring(1); }, /** encodes form data as multipart/form-data */ encodeFormData: function(boundary, data) { var out = ""; for (name in data) { var raw = data[name]; if (raw == null) continue; out += '--' + boundary + '\r\n'; out += 'Content-Disposition: form-data; name="' + name + '"\r\n\r\n'; out += raw.toString() + '\r\n'; } out += '--' + boundary + '--'; return out; }, /** create a XMLHttpRequest with named parameters */ call: function(args) { // create var client = new XMLHttpRequest(); client.args = args; // open client.open( args.method ? args.method : "GET", args.url, args.async ? args.async == true : true ); // set headers if (args.headers) { for (var name in args.headers) { client.setRequestHeader(name, args.headers[name]); } } // handle state changes client.onreadystatechange = function() { if (args.state) args.state(client, args); if (client.readyState != 4) return; if (args.state4) args.state4(client, args); } // debug status client.debug = function() { return client.status + " " + client.statusText + "\n" + client.getAllResponseHeaders() + "\n\n" + client.responseText; } // and start client.send(args.body ? args.body : null); return client; }, /** parses text into an XML DOM */ parseXML: function(text) { var parser = new DOMParser(); return parser.parseFromString(text, "text/xml"); }, } //====================================================================== //## AjaxEditor.js /** ajax functions for MediaWiki */ function AjaxEditor(page, progress) { /** prepend a page with a given text */ this.prependText = function(title, text, summary) { action( { title: title, action: "edit", section: 0 }, 200, "editform", function(f) { return { wpSection: f.wpSection.value, wpStarttime: f.wpStarttime.value, wpEdittime: f.wpEdittime.value, wpScrolltop: f.wpScrolltop.value, wpSummary: summary, wpWatchthis: f.wpWatchthis.checked ? "1" : null, wpMinoredit: f.wpMinoredit.checked ? "1" : null, wpSave: f.wpSave.value, wpEditToken: f.wpEditToken.value, wpTextbox1: text + f.wpTextbox1.value, }}, 200, progress ); } /** append a page with a given text */ this.appendText = function(title, text, summary) { action( { title: title, action: "edit", // inserts the summary as a headline //section: "new" }, 200, "editform", function(f) { return { wpSection: f.wpSection.value, wpStarttime: f.wpStarttime.value, wpEdittime: f.wpEdittime.value, wpScrolltop: f.wpScrolltop.value, wpSummary: summary, wpWatchthis: f.wpWatchthis.checked ? "1" : null, wpMinoredit: f.wpMinoredit.checked ? "1" : null, wpSave: f.wpSave.value, wpEditToken: f.wpEditToken.value, wpTextbox1: f.wpTextbox1.value + text, }}, 200, progress ); } /** delete a page */ this.deletePage = function(title, reason) { action( { title: title, action: "delete" }, 200, "deleteconfirm", function(f) { return { wpReason: (reason == null ? "" : reason != "" ? reason + " - " : "") + f.wpReason.value, wpConfirmB: f.wpConfirmB.value, wpEditToken: f.wpEditToken.value, }}, 200, progress ); } /** block a user */ this.blockUser = function(user, duration, reason) { action( { title: page.wiki.specialNS + ":" + "Blockip", target: user, }, 200, "blockip", function(f) { return { wpBlockAddress: user, // title encode? wpBlockReason: reason, wpBlockExpiry: f.wpBlockExpiry.value, wpBlockOther: duration, wpEditToken: f.wpEditToken.value, wpBlock: f.wpBlock.value, }}, 200, progress ); } /** move a page */ this.movePage = function(oldTitle, newTitle, reason, withDisk) { action( { title: page.wiki.specialNS + ":" + "Movepage", target: oldTitle, }, 200, "movepage", function(f) { return { wpOldTitle: oldTitle, // title encode? wpNewTitle: newTitle, // title encode? wpReason: reason, wpMovetalk: withDisk ? "1" : null, wpEditToken: f.wpEditToken.value, wpMove: f.wpMove.value, }}, 200, progress ); } //------------------------------------------------------------------------------ if (!progress) progress = null; /** * get a form, change it, post it. * * makeData gets form.elements to create a Map * progress may have methods * start(formName, url), execute(source), done(source) * and error(source [, expectedStatus]) */ function action( actionArgs, getStatus, formName, makeData, postStatus, progress) { function phase1() { var url = page.actionURL(actionArgs) if (progress.start) progress.start(formName, url); Ajax.call({ method: "GET", // default value, but needed in the ProgressArea :/ url: url, state4: phase2, }); } function phase2(source) { if (getStatus && source.status != getStatus) { if (progress.error) { progress.error(source, getStatus); return; } else throw "status unexpected: " + status + "\n" + source.debug(); } if (progress.execute) progress.execute(source); var doc = Ajax.parseXML(source.responseText); var form = findForm(doc, formName); if (form == null) { if (progress.error) { progress.error(source, "form not found: " + formName); return; } else throw "form not found: " + formName + "\n" + source.debug(); } var data = makeData(form.elements); //### BÄH! //var boundary = "134j5hkvgnarw4t82raflfjl3aklsjdfhsdlkhflkqe"; Ajax.call({ method: "POST", url: form.action, headers: Ajax.urlEncoded("UTF-8"), body: Ajax.encodeArgs(data), //headers: Ajax.multipartFormData(boundary, "UTF-8"), //body: Ajax.encodeFormData(boundary, data), state4: phase3, }); } function phase3(source) { if (postStatus && source.status != postStatus) { if (progress.error) { progress.error(source, postStatus); return; } else throw "status unexpected: " + status + "\n" + source.debug(); } if (progress.done) progress.done(source); } if (!progress) progress = {}; phase1(); } /** finds a HTMLForm within an XMLDocument (!) */ function findForm(doc, name) { // firefox does _not_ provide document.forms, // but within the form we get proper HTMLInputElements var forms = doc.getElementsByTagName("form"); for (var i=0; i<forms.length; i++) { var form = forms[i]; if (elementName(form) == name) return form; } return null; } /** finds the name or id of an element */ function elementName(element) { return element.name ? element.name : element.id ? element.id : null; } } //====================================================================== //## ProgressArea.js //@depends messageArea.css /** uses a messageArea to display ajax progress */ function ProgressArea() { //------------------------------------------------------------------------------ //## Progress interface this.start = function(formName, url) { var ma = messageArea(); ma.display("form: " + formName + ", url: " + url); //ma.fade(6000); } this.execute = function(source) { var ma = messageArea(); ma.display("method: " + source.args.method + ", status: " + source.status + " " + source.statusText + ", url: " + source.args.url); //ma.fade(6000); } this.done = function(source) { var ma = messageArea(); ma.display("method: " + source.args.method + ", status: " + source.status + " " + source.statusText + ", url: " + source.args.url); ma.fade(2000); } this.error = function(source, expectedStatus) { var ma = messageArea(); ma.display("expected status: " + expectedStatus + ", received status: " + source.status + " " + source.statusText + ", url: " + source.args.url); //ma.fade(12000); } //------------------------------------------------------------------------------ //## helper function status(client) { return client.status + " " + client.statusText; } /** returns an additional message area div */ function messageArea() { var div = $('messageArea'); if (div) return div; div = document.createElement("div"); div.id = 'messageArea'; div.className = "messageArea"; var bc = $('bodyContent'); bc.insertBefore(div, bc.firstChild); /** display a text in the div */ div.display = function(text) { div.textContent = text; } /** show the div until a timeout */ div.fade = function(timeout) { if (div.timer) clearTimeout(div.timer); div.timer = setTimeout( function() { div.style.display = "none"; }, timeout ); div.style.display = null; } return div; } } //====================================================================== //## QuickBar.js //@depends actionLink.css /** creates portlet content for a QuickBar */ function QuickBar(page) { /** adds a new list item */ this.line = function() { this.li = document.createElement("li"); this.ul.appendChild(this.li); return this; } /** appends a label to the current line */ this.label = function(text) { if (this.li == null) { alert("error: call line first"); return; } var b = document.createElement("b"); var t = document.createTextNode(label); b.appendChild(t); this.li.appendChild(b); return this; } /** adds space to the current line */ this.space = function() { var s = document.createTextNode(" "); this.li.appendChild(s); return this; } /** adds an action link calling a function to the current line */ this.action = function(func, label) { if (this.li == null) { alert("error: call line first"); return; } var a = document.createElement("a"); a.className = "actionLink"; a.onclick = func; this.li.appendChild(a); //a.textContent = label; var t = document.createTextNode(label); a.appendChild(t); return this; } /** prompts for a reason, then calls a function with the reason as argument */ this.actionPrompt = function(func, label, query) { return this.action(function() { var reason = prompt(query, ""); if (reason) func(reason); else if (reason == "") func(reason); return false; }, label); } /** creates closure over a function call with a single argument */ this.actionFixed = function(func, label, reason) { if (!reason) reason = label; return this.action(function() { func(reason); return false; }, label); } /** creates a link element displaying a user page */ this.userPage = function(subTitle) { var title = page.user + "/" + subTitle; var a = document.createElement("a"); a.href = page.readURL(title); a.textContent = subTitle; this.li.appendChild(a); return this; } /** adds arbitray content to the current line */ this.append = function(content) { if (this.li == null) { alert("error: call line first"); return; } if (content.length) { for (var i=0; i<content.length; i++) { this.li.appendChild(content[i]); } } else { this.li.appendChild(content); } return this; } // private this.ul = document.createElement("ul"); this.li = null; // semi-public for the SideBar this.component = this.ul; } //====================================================================== //## SideBar.js //@depends functions.js, Page.js, QuickBar.js //@depends leanSideBar.css, fixSideBar.css, leftPersonal.css, bottomLinks.css /** encapsulates column-one */ function SideBar(page) { //------------------------------------------------------------------------------ //## public methods /** changes labels of item links. data is an Array of name/label Arrays */ this.labelItems = function(data) { function sa(action, text) { var el = $(action); if (!el) return; var a = el.getElementsByTagName("a")[0]; if (!a) return; a.textContent = text; } for (var i=0; i<data.length; i++) { sa(data[i][0], data[i][1]); } } /** create a QuickBar, add it in a new portlet and return it to add content */ this.quickBar = function(id, label) { var quickBar = new QuickBar(page); var barPortlet = portlet(id, label, quickBar.component); appendPortlet(barPortlet); return quickBar; } /** move together tools */ this.meltTools = function() { var pNavigation = $('p-navigation'); var pMitmachen = $('p-Mitmachen'); var pTb = $('p-tb'); var tool = document.createElement("ul"); var myTool = portlet("p-my-tools", "Tools", tool); moveListItems(pNavigation, tool); moveListItems(pMitmachen, tool); moveListItems(pTb, tool); appendPortlet(myTool); } /** move p-personal into a new portlet */ this.leftPersonal = function() { var pPersonal = $('p-personal'); var pers = document.createElement("ul"); var myPers = portlet("p-my-personal", "Personal", pers); moveListItems(pPersonal, pers, true); appendPortlet(myPers); } /** move p-personal out of the column-one so it does not inherit its position:fixes */ this.unfixPersonal = function() { var pPersonal = $('p-personal'); var columnContent = $('column-content'); pPersonal.parentNode.removeChild(pPersonal); columnContent.insertBefore(pPersonal, columnContent.firstChild); } /** move p-cactions out of the column-one so it does not inherit its position:fixes */ this.unfixCactions = function() { var pCactions = $('p-cactions'); var columnContent = $('column-content'); pCactions.parentNode.removeChild(pCactions); columnContent.insertBefore(pCactions, columnContent.firstChild); } /** insert a select box to replace the pLang replacement */ this.leanLang = function() { var pLang = $('p-lang'); if (!pLang) return; var lang = langSelect(pLang); var myLang = portlet("p-my-lang", "Languages", lang); appendPortlet(myLang); } /** duplicates the cactions */ this.bottomLinks = function() { var tabs = $('p-cactions').cloneNode(true); tabs.id = 'mytabs'; var items = tabs.getElementsByTagName('LI'); for (i=0; i<items.length; i++) { if (items[i].id) items[i].id = 'mytabs-' + items[i].id; } $('column-content').appendChild(tabs); } //------------------------------------------------------------------------------ //## helper /** create a portlet */ function portlet(id, title, content) { var outer = document.createElement("div"); outer.id = id; outer.className = "portlet"; var header = document.createElement("h5"); var headerText = document.createTextNode(title); var body = document.createElement("div"); body.className = "pBody"; outer.appendChild(header); header.appendChild(headerText); outer.appendChild(body); body.appendChild(content); return outer; } /** append a portlet to column-one */ function appendPortlet(portlet) { if (!portlet) return; var columnOne = $('column-one'); columnOne.appendChild(portlet); // navigation.parentNode.insertBefore(search, navigation); } /** moves li tags into another ul */ function moveListItems(from, to, reverse) { if (!from) return; var list = from.getElementsByTagName("li"); for (var i=list.length-1; i>=0; i--) { var li = list[i]; li.parentNode.removeChild(li); if (reverse && to.firstChild) to.insertBefore(li, to.firstChild); else to.appendChild(li); } } /** create a select for the language */ function langSelect(pLang) { var select = document.createElement("select"); select.id = "langSelect"; select.options[0] = new Option("auswählen", ""); var list = pLang.getElementsByTagName("a"); for (var i=0; i<list.length; i++) { var a = list[i]; //### test textContent on safari! select.options[i+1] = new Option(a.firstChild.textContent, a.href); } select.onchange = function() { var selected = this.options[this.selectedIndex].value; if (selected == "") return; location.href = selected; } return select; } } //====================================================================== //## Bookmark.js //@depends Page.js, AjaxEditor.js /** manages a personal bookmarks page */ function Bookmark(page, editor) { var PAGE = "bookmarks"; /** adds a bookmark on a user's bookmark page */ function add(remark) { var lemma = page.lemma; var mode = "perma"; var perma = page.perma; if (!perma) { var params = page.params; var oldid = params["oldid"]; var target = params["target"]; if (oldid) { var diff = params["diff"]; if (diff) { if (diff == "prev") mode = "prev"; else if (diff == "next") mode = "next"; else mode = "diff"; perma = page.actionURL({ title: lemma, oldid: oldid, diff: diff}); } else { mode = "old"; perma = page.actionURL({ title: lemma, oldid: oldid}); } } else if (target) { // for Special:Contributions lemma += "/" + target; } } var text = "*[[:" + lemma + "]]"; if (perma) text += " <small>[" + perma + " " + mode + "]</small>"; if (remark) text += " " + remark text += "\n"; var title = page.user + "/" + PAGE; editor.prependText(title, text, ""); return false; } // public this.add = add; this.page = PAGE; this.full = page.readURL(page.user + "/" + PAGE); } //====================================================================== //## Template.js //@depends Page.js, AjaxEditor.js /** puts templates into the current page */ function Template(page, editor) { /** puts an SLA template into the current article */ function sla(reason) { var TEMPLATE = "löschen"; stamp(TEMPLATE, reason); return false; } /** puts an LA template into the current article */ function la(reason) { var TEMPLATE = "subst:Löschantrag"; var LISTPAGE = "Wikipedia:Löschkandidaten"; stamp(TEMPLATE, reason); enlist(TEMPLATE, reason, LISTPAGE); return false; } /** puts an QS template into the current article */ function qs(reason) { var TEMPLATE = "subst:QS"; var LISTPAGE = "Wikipedia:Qualitätssicherung"; stamp(TEMPLATE, reason); enlist(TEMPLATE, reason, LISTPAGE); return false; } /** puts an Test template into the current article */ function test() { var TEMPLATE = "subst:Test"; var r = renderer; // rendered differently editor.prependText( page.lemma, r.get(r.template(TEMPLATE), r.sp, r.signature, r.lf, r.line, r.lf), r.get(r.template(TEMPLATE)) ); return false; } //------------------------------------------------------------------------------ //## helper /** put template in the current page */ function stamp(template, reason) { var r = renderer; editor.prependText( page.lemma, r.get(r.template(template), r.sp, reason, r.sp, r.dash, r.sp, r.signature, r.lf, r.line, r.lf), r.get(r.template(template), r.sp, reason) ); } /** list current page on a list page */ function enlist(template, reason, listPage) { var r = renderer; editor.appendText( listPage + "/" + currentDate(), r.get(r.lf, r.header(r.link(page.lemma)), r.lf, reason, r.sp, r.dash, r.sp, r.signature, r.lf), r.get(r.template(template), r.sp, r.link(page.lemma), r.sp, reason) ); } /** returns the current date in the format the LKs are organized */ function currentDate() { var months = [ "Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember" ]; var now = new Date(); var year = now.getYear(); if (year < 999) year += 1900; return now.getDate() + ". " + months[now.getMonth()] + " " + year; } /** helps rendering mediawiki text */ var renderer = { template: function(name) { return "{" + "{" + name + "}" + "}"; }, link: function(title) { return "[" + "[" + title + "]" + "]"; }, link2: function(title, label) { return "[" + "[" + title + "|" + label + "]" + "]"; }, header: function(text) { return "==" + text + "=="; }, dash: "--", signature: "~~" + "~~", line: "----", sp: " ", lf: "\n", get: function() { var out = ""; for (var i=0; i<arguments.length; i++) { var arg = arguments[i]; while (arg.constructor == Function) { arg = arg.apply(this); } out += arg.toString(); } return out; } } //------------------------------------------------------------------------------ // public this.la = la; this.sla = sla; this.qs = qs; this.test = test; } //====================================================================== //## foldHeaders.js //@depends functions.js, foldButton.css /** provides an icon for each heading to fold or unfold it */ function foldHeaders() { //------------------------------------------------------------------------------ //## folder /** attaches folders to all header tags of a given depth */ function attachDepthFolders(element, depth) { var tagName = "H" + depth; var headers = element.getElementsByTagName(tagName); var folders = new Array(); for (var i=0; i<headers.length; i++) { var header = headers[i]; // skip some headers we do not want to modify if (header.id == "siteSub") continue; if (header.parentNode.id == "toctitle") continue; if (header.folder) continue; var end = i < headers.length-1 ? findEnd(headers[i+1]) : element.lastChild; var folder = attachSingleFolder(header, end); folders.push(folder); if (depth < 6) attachDepthFolders(folder, depth+1); } return folders; } /** finds a (inclusive) endpoint for folder content */ function findEnd(next) { var maybe = next; for (;;) { maybe = maybe.previousSibling; if (maybe.nodeName != "#text") break; } if (maybe.nodeName != "P") { return next.previousSibling; } for (;;) { maybe = maybe.previousSibling; if (maybe.nodeName != "#text") break; } if (maybe.nodeName != "DIV" || maybe.className != "editsection") { return next.previousSibling; } return maybe.previousSibling; } /** attaches a folder span to a header, end is inclusive. returns the folder span. */ function attachSingleFolder(header, end) { // add a button to the header toggling the display of the content span var button = createButton(); buttonState(button, true); header.insertBefore(button, header.firstChild); // move content between start and end into a span after the header var range = document.createRange(); range.setStartAfter(header); range.setEndAfter(end); var contents = range.extractContents(); var folder = document.createElement("span"); folder.appendChild(contents); header.parentNode.insertBefore(folder, header.nextSibling); // add state and methods to the folder folder.button = button; folder.open = true; folder.update = function() { buttonState(this.button, this.open); folderState(this, this.open); } folder.fold = function(open) { this.open = open; this.update(); } folder.flip = function() { this.open = !this.open; this.update(); } // make the button react on clicks button.folder = folder; button.onclick = function() { this.folder.flip(); } // give the header a fold method, too header.folder = folder; header.fold = function(open) { //### HACK: minimize height header.style.marginBottom = open ? null : "1px"; this.folder.fold(open); } return folder; } /** creates a button element for buttonState */ function createButton() { // uses monobook.css var button = document.createElement("span"); button.className = "foldButton"; return button; }; /** changes the open state of a button */ function buttonState(button, open) { // character look: arrows button.innerHTML = open ? "⋁" : "≻"; } /** changes the open state of a folder */ function folderState(folder, open) { folder.style.display = open ? null : "none"; // "inline" : "none"; } //------------------------------------------------------------------------------ //## helper /** makes an element invisible */ function invisible(id) { var element = $(id); if (!element) return; if (!element.style) return; element.style.display = "none"; } //------------------------------------------------------------------------------ //## installation var bodyContent = $('bodyContent'); // prevent double installation if (bodyContent.foldersInstalled) return; bodyContent.foldersInstalled = true; // install a folder on every header var topFolders = attachDepthFolders(bodyContent, 2); // autocollapse everything for (var i=0; i<topFolders.length; i++) { topFolders[i].fold(false); } // hide toc invisible("toc"); return false; } //====================================================================== //## historyUsermessage.js /** modifies the new usermessages to contain only a link to the history of the talkpage */ function historyUsermessage() { var um = descendants('bodyContent', "div", "usermessage", 0); var a = descendants(um, "a", null, 1); if (!a) return; a.href = a.href.replace(/&diff=cur$/, "&action=history"); a.textContent = "History"; } //====================================================================== //## _public.js //@depends functions.js, Page.js, Ajax.js, AjaxEditor.js //@depends SideBar.js, Bookmark.js, Template.js //@depende foldHeaders.js, historyUsermessage.js /** onload hook */ function initialize() { // replace diff=cur with action=history historyUsermessage(); // setup objects var page = new Page(); var progress = new ProgressArea(); var editor = new AjaxEditor(page, progress); var bookmark = new Bookmark(page, editor); var template = new Template(page, editor); var sideBar = new SideBar(page); // add portlet to the toolbar sideBar.quickBar("quickTools", "QuickTools") .line() .action(foldHeaders, "Falten") .line() .action(template.test, "Test") .space().actionPrompt(template.qs, "QS", "QS - Begründung?") .space().actionPrompt(template.la, "LA", "LA - Begründung?") .space().actionPrompt(template.sla, "SLA", "SLA - Begründung?") .line() .userPage(bookmark.page) .space().actionPrompt(bookmark.add, "add", "Bookmark - Kommentar?") ; } doOnLoad(initialize); /* </pre> */