From 9d6c70c656052e1c4f7df7a255c4f493d56f08c8 Mon Sep 17 00:00:00 2001 From: Matěj Cepl Date: Sat, 7 May 2011 00:53:06 +0200 Subject: Another massive cleanup and simplification of tweak scripts. bugs on bugzilla.mozilla.org now work as well, although the history is still not shown inline, which remains for 1.1. Fixes #88 --- data/tweaks/addNewLinks.js | 10 +- data/tweaks/bug-page-mod.js | 260 ++++++++++++++++---------------------------- data/tweaks/viewSource.js | 58 ++++------ 3 files changed, 115 insertions(+), 213 deletions(-) (limited to 'data/tweaks') diff --git a/data/tweaks/addNewLinks.js b/data/tweaks/addNewLinks.js index b8e7bd2..05d5ffb 100644 --- a/data/tweaks/addNewLinks.js +++ b/data/tweaks/addNewLinks.js @@ -35,13 +35,13 @@ * * ***** END LICENSE BLOCK ***** */ -function addNewLinks(d) { - var product = d +function addNewLinks() { + var product = document .querySelector("#field_container_product option[selected]"); - var component = d.querySelector("#component option[selected]"); + var component = document.querySelector("#component option[selected]"); if (product) { - var label = d.getElementById('field_container_product'); + var label = document.getElementById('field_container_product'); var url = 'enter_bug.cgi?product=' + encodeURIComponent(product.value); if (label) { @@ -51,7 +51,7 @@ function addNewLinks(d) { } if (product && component) { - var select = d.querySelector("select#component"); + var select = document.querySelector("select#component"); var label = select.parentNode; var url = 'enter_bug.cgi?product=' + encodeURIComponent(product.value) + '&component=' diff --git a/data/tweaks/bug-page-mod.js b/data/tweaks/bug-page-mod.js index 482108e..688d3f9 100644 --- a/data/tweaks/bug-page-mod.js +++ b/data/tweaks/bug-page-mod.js @@ -71,38 +71,22 @@ function collectHistory(rpcURL) { })); } -function tweakBugzilla(d, cData) { - // run on both bugzilla.m.o and bugzilla-stage-tip.m.o - // if (!onBugzillaPage(d.URL)) - // return; +function tweakBugzilla(atts, cData) { + // Add links for creating new bug in the same product + // and same component + addNewLinks(); - // Put the quicksearch text in the quicksearch boxes - quicksearchHandler(d); - - if (!d.getElementById("comments")) // don't process the mid-air collision - // pages - return; - - // Make the comment box bigger - var commentBox = d.querySelector("#comment"); - if (commentBox) - commentBox.rows=20; - - addNewLinks(d); - - attachmentDiffLinkify(d); - - viewAttachmentSource(d); + viewAttachmentSource(atts); // Mark up history along right hand edge - var historyLink = d.querySelector("link[title='Bug Activity']"); + var historyLink = document.querySelector("link[title='Bug Activity']"); if (!historyLink) return; // Add our own style for bugzilla-tweaks - var style = d.createElement("style"); + var style = document.createElement("style"); style.setAttribute("type", "text/css"); - style.appendChild(d.createTextNode( + style.appendChild(document.createTextNode( ".bztw_history { border: none; font-weight: normal; width: 58em; margin-left: 5em; }" + ".bztw_inlinehistory { font-weight: normal; width: 56em; }" + ".bztw_history .old, .bztw_inlinehistory .old { text-decoration: line-through; }" + @@ -111,35 +95,20 @@ function tweakBugzilla(d, cData) { "tr.bz_tr_obsolete.bztw_plusflag { display: table-row !important; }" + '.bztw_historyitem + .bztw_historyitem:before { content: "; "; }' )); - d.getElementsByTagName("head")[0].appendChild(style); - style = d.createElement("style"); + document.getElementsByTagName("head")[0].appendChild(style); + style = document.createElement("style"); style.setAttribute("type", "text/css"); style.id = "bztw_cc"; - style.appendChild(d.createTextNode( + style.appendChild(document.createTextNode( ".bztw_cc { display: none; }" + '.bztw_historyitem.bztw_cc + .bztw_historyitem:before { content: ""; }' + '.bztw_historyitem:not([class~="bztw_cc"]) ~ .bztw_historyitem.bztw_cc + .bztw_historyitem:before { content: "; "; }' )); - d.getElementsByTagName("head")[0].appendChild(style); - - // UserNameCache - var userNameCache = {}; - function getUserName(email) { - if (email in userNameCache) { - return userNameCache[email]; - } - var emailLink = d.querySelectorAll("a.email"); - for (var i = 0; i < emailLink.length; ++i) { - if (emailLink[i].href == "mailto:" + email) { - return userNameCache[email] = htmlEncode(trimContent(emailLink[i])); - } - } - return email; - } + document.getElementsByTagName("head")[0].appendChild(style); // collect the flag names var flagNames = [], flags = {}, flagOccurrences = {}; - var flagRows = d.querySelectorAll("#flags tr"); + var flagRows = document.querySelectorAll("#flags tr"); for (var i = 0; i < flagRows.length; ++i) { var item = flagRows[i].querySelectorAll("td"); if (!item[1]) @@ -148,7 +117,7 @@ function tweakBugzilla(d, cData) { flagNames.push(name); flags[name] = item[1]; } - flagRows = d.querySelectorAll(".field_label[id^=field_label_cf_]"); + flagRows = document.querySelectorAll(".field_label[id^=field_label_cf_]"); for (var i = 0; i < flagRows.length; ++i) { var name = trimContent(flagRows[i]).replace(/\:$/, '') .replace('\u2011', '-', 'g'); @@ -188,26 +157,26 @@ function tweakBugzilla(d, cData) { return lookup(trimContent(item[base])); } - var DataStore = new DataStoreCtor(d); + var DataStore = new DataStoreCtor(); var AttachmentFlagHandler = new AttachmentFlagHandlerCtor(); - AttachmentFlagHandler.determineInterestingFlags(d); + AttachmentFlagHandler.determineInterestingFlags(); var CheckinComment = new CheckinCommentCtor(); - CheckinComment.initialize(d, AttachmentFlagHandler._interestingFlags); + CheckinComment.initialize(AttachmentFlagHandler._interestingFlags); - if (d.location.hostname in cData.XMLRPCData) { - var XMLRPCUrl = cData.XMLRPCData[d.location.hostname].url; + if (document.location.hostname in cData.XMLRPCData) { + var XMLRPCUrl = cData.XMLRPCData[document.location.hostname].url; collectHistory(XMLRPCUrl); } - tbplbotSpamCollapser(d); + tbplbotSpamCollapser(); } // =================================================== function processHistory(history) { // FIXME Remove remaining code to special function ... callback -// preprocessDuplicateMarkers(d, iframe.contentDocument); +// preprocessDuplicateMarkers(document, iframe.contentDocument); /* This is an example of the history we get: @@ -318,13 +287,28 @@ This is an example of the history we get: } */ + // UserNameCache + var userNameCache = {}; + function getUserName(email) { + if (email in userNameCache) { + return userNameCache[email]; + } + var emailLink = document.querySelectorAll("a.email"); + for (var i = 0; i < emailLink.length; ++i) { + if (emailLink[i].href == "mailto:" + email) { + return userNameCache[email] = htmlEncode(trimContent(emailLink[i])); + } + } + return email; + } + if (history) { // console.log("processHistory: history = " + history.toSource()); return ; } // var historyItems = iframe.contentDocument.querySelectorAll('#bugzilla-body tr'); -// var cmtTimes = d.querySelectorAll('.bz_comment_time'); +// var cmtTimes = document.querySelectorAll('.bz_comment_time'); // Sometimes the history will stack several changes together, // and we'll want to append the data from the Nth item to the @@ -344,7 +328,7 @@ This is an example of the history we get: // + flags[flagName].innerHTML + ''; // } -// AttachmentFlagHandler.setupLinks(d); +// AttachmentFlagHandler.setupLinks(document); // END OF load event handler } @@ -375,7 +359,7 @@ function processHistoryItem(commentTimes, itemRaw) { var time = trimContent(item[1]); var inline = (mainUser == user && time == mainTime); - var currentDiv = d.createElement("div"); + var currentDiv = document.createElement("div"); var userPrefix = ''; if (inline) { // assume that the change was made by the same user @@ -412,7 +396,7 @@ function processHistoryItem(commentTimes, itemRaw) { var flag = flagsFound[idx]; flagOccurrences[flag] = 'flag' + flagCounter; if (inline) { - var anchor = d.createElement("a"); + var anchor = document.createElement("a"); anchor.setAttribute("name", "flag" + flagCounter); commentHead.insertBefore(anchor, commentHead.firstChild); } else { @@ -424,7 +408,7 @@ function processHistoryItem(commentTimes, itemRaw) { var attachmentFlagAnchors = AttachmentFlagHandler.handleItem(user, item); if (inline) { for (var idx = 0; idx < attachmentFlagAnchors.length; ++idx) { - var anchor = d.createElement("a"); + var anchor = document.createElement("a"); anchor.setAttribute("name", attachmentFlagAnchors[idx]); commentHead.insertBefore(anchor, commentHead.firstChild); } @@ -438,10 +422,10 @@ function processHistoryItem(commentTimes, itemRaw) { ccSuffix = ''; var html = userPrefix + ccPrefix + - transformType(trimContent(item[2]), d, trimContent(item[3]), + transformType(trimContent(item[2]), trimContent(item[3]), trimContent(item[4])) + ": " + formatTransition(trimContent(item[3]), trimContent(item[4]), - trimContent(item[2]), d, iframe.contentDocument); + trimContent(item[2]), iframe.contentDocument); var nextItemsCount = item[0].rowSpan; for (var k = 1; k < nextItemsCount; ++k) { @@ -458,7 +442,7 @@ function processHistoryItem(commentTimes, itemRaw) { var flag = flagsFound[idx]; flagOccurrences[flag] = 'flag' + flagCounter; if (inline) { - var anchor = d.createElement("a"); + var anchor = document.createElement("a"); anchor.setAttribute("name", "flag" + flagCounter); commentHead.insertBefore(anchor, commentHead.firstChild); } else { @@ -470,7 +454,7 @@ function processHistoryItem(commentTimes, itemRaw) { var attachmentFlagAnchors = AttachmentFlagHandler.handleItem(user, item); if (inline) { for (var idx = 0; idx < attachmentFlagAnchors.length; ++idx) { - var anchor = d.createElement("a"); + var anchor = document.createElement("a"); anchor.setAttribute("name", attachmentFlagAnchors[idx]); commentHead.insertBefore(anchor, commentHead.firstChild); } @@ -479,10 +463,10 @@ function processHistoryItem(commentTimes, itemRaw) { } html += prefix + - transformType(trimContent(item[0]), d, trimContent(item[1]), + transformType(trimContent(item[0]), trimContent(item[1]), trimContent(item[2])) + ": " + formatTransition(trimContent(item[1]), trimContent(item[2]), - trimContent(item[0]), d, iframe.contentDocument); + trimContent(item[0]), iframe.contentDocument); } html += ccSuffix; if (ccOnly) { @@ -509,7 +493,7 @@ var TransformValues = { linkifyBugNumbers: function (str) { return str.replace(/(bug (\d+))/gi, '$1'); }, - linkifyDependencies: function (str, type, doc, histDoc) { + linkifyDependencies: function (str, type, histDoc) { switch (type) { case "Blocks": case "Depends on": @@ -546,18 +530,18 @@ var TransformValues = { // =============================================================================== -function transform(str, type, doc, histDoc) { +function transform(str, type, histDoc) { for (var funcname in TransformValues) { var func = TransformValues[funcname]; - str = func.call(null, str, type, doc, histDoc); + str = func.call(null, str, type, histDoc); } return str } var TransformTypes = { - linkifyAttachments: function (str, doc) { + linkifyAttachments: function (str) { return str.replace(/(Attachment #(\d+))/g, function (str, x, id) { - var link = doc.querySelector("a[href='attachment.cgi?id=" + id + "']"); + var link = document.querySelector("a[href='attachment.cgi?id=" + id + "']"); if (link) { var class_ = ''; if (/bz_obsolete/i.test(link.className)) { @@ -577,7 +561,7 @@ var TransformTypes = { return str; }); }, - changeDependencyLinkTitles: function (str, doc, old, new_) { + changeDependencyLinkTitles: function (str, old, new_) { switch (str) { case "Blocks": case "Depends on": @@ -592,18 +576,18 @@ var TransformTypes = { // ======================================================================= -function transformType(str, doc, old, new_) { +function transformType(str, old, new_) { for (var funcname in TransformTypes) { var func = TransformTypes[funcname]; - str = func.call(null, str, doc, old, new_); + str = func.call(null, str, old, new_); } return str; } // new is a keyword, which makes this function uglier than I'd like -function formatTransition(old, new_, type, doc, histDoc) { +function formatTransition(old, new_, type, histDoc) { if (old.length) { - old = transform(htmlEncode(old), type, doc, histDoc); + old = transform(htmlEncode(old), type, histDoc); var setOldStyle = true; switch (type) { case "Blocks": @@ -616,7 +600,7 @@ function formatTransition(old, new_, type, doc, histDoc) { } } if (new_.length) { - new_ = '' + transform(htmlEncode(new_), type, doc, histDoc) + ''; + new_ = '' + transform(htmlEncode(new_), type, histDoc) + ''; } var mid = ''; if (old.length && new_.length) { @@ -652,59 +636,13 @@ var reviewBoardUrlBase = "http://reviews.visophyte.org/"; // =============================================================================== -/** - * Whenever we find a patch with a diff, insert an additional link to asuth's - * review board magic. - */ -function attachmentDiffLinkify(doc) { - var bug_id = getBugNumber(doc); - - var table = doc.getElementById("attachment_table"); - if (!table) - return; - var rows = table.querySelectorAll("tr"); - for (var i = 0; i < rows.length; ++i) { - var item = rows[i].querySelectorAll("td"); - if (item.length != 3) - continue; - // get the ID of the attachment - var links = item[2].querySelectorAll("a"); - if (links.length != 2) - continue; - var match = reAttachmentDiff.exec(links[1].href); - if (match) { - var attach_id = match[1]; - var parentNode = links[1].parentNode; - parentNode.appendChild(doc.createTextNode(" | ")); - var linkNode = doc.createElement("a"); - linkNode.href = reviewBoardUrlBase + "r/bzpatch/bug" + bug_id + "/attach" + attach_id + "/"; - linkNode.textContent = "Review"; - parentNode.appendChild(linkNode); - } - } -} - -function quicksearchHandler(doc) { - var win = doc.defaultView; - var match = /quicksearch=([^&]+)/i.exec(win.location.search); - if (match) { - var quicksearch = unescape(match[1].replace('+', ' ', 'g')); - var quicksearchBox = doc.querySelectorAll("input[name=quicksearch]"); - if (quicksearchBox) { - for (var i = 0; i < quicksearchBox.length; ++i) { - quicksearchBox[i].value = quicksearch; - } - } - } -} - function AttachmentFlagHandlerCtor() { this._db = {}; this._interestingFlags = {}; } AttachmentFlagHandlerCtor.prototype = { - determineInterestingFlags: function (doc) { - var table = doc.getElementById("attachment_table"); + determineInterestingFlags: function () { + var table = document.getElementById("attachment_table"); if (!table) return; var rows = table.querySelectorAll("tr"); @@ -765,10 +703,10 @@ AttachmentFlagHandlerCtor.prototype = { if (match) { el.textContent = match[1]; if (match[3]) { - var textNode = doc.createTextNode(match[3]); + var textNode = document.createTextNode(match[3]); el.parentNode.insertBefore(textNode, el.nextSibling); } - var span = doc.createElement("span"); + var span = document.createElement("span"); span.textContent = match[2]; el.parentNode.insertBefore(span, el.nextSibling); @@ -811,13 +749,13 @@ AttachmentFlagHandlerCtor.prototype = { } return anchorsCreated; }, - setupLinks: function (doc) { + setupLinks: function () { for (var id in this._interestingFlags) { for (var i = 0; i < this._interestingFlags[id].length; ++i) { var flag = this._interestingFlags[id][i]; if ("placeholder" in flag && "anchor" in flag) { - var link = doc.createElement("a"); + var link = document.createElement("a"); link.href = "#" + flag.anchor; link.textContent = flag.placeholder.textContent; flag.placeholder.replaceChild(link, flag.placeholder.firstChild); @@ -862,9 +800,9 @@ function CheckinCommentCtor() { this.checkinFlags = ""; } CheckinCommentCtor.prototype = { - initialize: function(doc, flags) { - this.bugNumber = getBugNumber(doc); - var summarySpan = doc.getElementById("short_desc_nonedit_display"); + initialize: function(flags) { + this.bugNumber = getBugNo(); + var summarySpan = document.getElementById("short_desc_nonedit_display"); if (summarySpan) { this.summary = summarySpan.textContent; } @@ -930,11 +868,11 @@ CheckinCommentCtor.prototype = { } this.checkinFlags = checkinFlags.join(" "); if (this.isValid()) { - var div = doc.createElement("div"); + var div = document.createElement("div"); div.setAttribute("style", "display: none;"); div.id = "__bz_tw_checkin_comment"; - div.appendChild(doc.createTextNode(this.toString())); - doc.body.appendChild(div); + div.appendChild(document.createTextNode(this.toString())); + document.body.appendChild(div); } }, isValid: function() { @@ -953,8 +891,8 @@ CheckinCommentCtor.prototype = { } }; -function DataStoreCtor(doc) { - this.storage = doc.defaultView.localStorage; +function DataStoreCtor() { + this.storage = document.defaultView.localStorage; this.data = {}; this.bugNumber = null; function visualizeStoredData() { @@ -976,25 +914,25 @@ function DataStoreCtor(doc) { alert("You don't have any data stored about your bugs"); } } - var script = doc.createElement("script"); - script.appendChild(doc.createTextNode(visualizeStoredData.toSource() + + var script = document.createElement("script"); + script.appendChild(document.createTextNode(visualizeStoredData.toSource() + clearStoredData.toSource() + htmlEncode.toSource())); - doc.body.appendChild(script); - this.initialize(doc); + document.body.appendChild(script); + this.initialize(); } DataStoreCtor.prototype = { - initialize: function(doc) { - this.bugNumber = getBugNumber(doc); + initialize: function() { + this.bugNumber = getBugNo(); var data = this._ensureEntry(this.bugNumber, this.data); // last visited date data.visitedTime = (new Date()).getTime(); // last comment count - data.commentCount = doc.querySelectorAll(".bz_comment").length; + data.commentCount = document.querySelectorAll(".bz_comment").length; // last status of bug flags var flags = this._ensureEntry("flags", data); - var flagRows = doc.querySelectorAll("#flags tr"); + var flagRows = document.querySelectorAll("#flags tr"); for (var i = 0; i < flagRows.length; ++i) { var flagCols = flagRows[i].querySelectorAll("td"); if (flagCols.length != 3) { @@ -1009,7 +947,7 @@ DataStoreCtor.prototype = { } flags[flagName] = flagValue; } - flagRows = doc.querySelectorAll(".field_label[id^=field_label_cf_]"); + flagRows = document.querySelectorAll(".field_label[id^=field_label_cf_]"); for (var i = 0; i < flagRows.length; ++i) { var flagName = trimContent(flagRows[i]).replace(/:$/, ""); var flagValue = flagRows[i].parentNode.querySelector("select"); @@ -1021,7 +959,7 @@ DataStoreCtor.prototype = { flags[flagName] = flagValue; } // last attachments - var attachmentTable = doc.getElementById("attachment_table"); + var attachmentTable = document.getElementById("attachment_table"); var attachmentRows = attachmentTable.querySelectorAll("tr"); for (var i = 0; i < attachmentRows.length; ++i) { var attachmentCells = attachmentRows[i].querySelectorAll("td"); @@ -1084,26 +1022,10 @@ DataStoreCtor.prototype = { _reAttachmentHref: /attachment\.cgi\?id=(\d+)$/i }; -function getBugNumber(doc) { - var idField = doc.querySelector("form[name=changeform] input[name=id]"); - if (idField) { - return idField.value; - } - return null; -} - -function getUserName(doc) { - var links = doc.querySelectorAll("#header .links li"); - var last = links[links.length - 1]; - if (last.innerHTML.indexOf("logout") >= 0) { - return trimContent(last.lastChild); - } - return null; -} -function handleEmptyCollapsedBoxes(doc) { +function handleEmptyCollapsedBoxes() { // first, try to get the display style of a CC field (any would do) - var historyBoxes = doc.querySelectorAll(".bztw_history"); + var historyBoxes = document.querySelectorAll(".bztw_history"); for (var i = 0; i < historyBoxes.length; ++i) { var box = historyBoxes[i]; for (var j = 0; j < box.childNodes.length; ++j) { @@ -1135,17 +1057,17 @@ function htmlEncode(str) { .replace('"', '"', 'g'); } -function tbplbotSpamCollapser(d) { - var collapseExpandBox = d.querySelector(".bz_collapse_expand_comments"); +function tbplbotSpamCollapser() { + var collapseExpandBox = document.querySelector(".bz_collapse_expand_comments"); if (!collapseExpandBox) { return; } - var a = d.createElement("a"); + var a = document.createElement("a"); a.href = "#"; a.addEventListener("click", function(e) { e.preventDefault(); - var win = d.defaultView.wrappedJSObject; - var comments = d.querySelectorAll(".bz_comment"); + var win = document.defaultView.wrappedJSObject; + var comments = document.querySelectorAll(".bz_comment"); for (var i = 0; i < comments.length; ++i) { var comment = comments[i]; try { @@ -1160,8 +1082,8 @@ function tbplbotSpamCollapser(d) { } return false; }, false); - a.appendChild(d.createTextNode("Collapse All tbplbot Comments")); - var li = d.createElement("li"); + a.appendChild(document.createTextNode("Collapse All tbplbot Comments")); + var li = document.createElement("li"); li.appendChild(a); collapseExpandBox.appendChild(li); } diff --git a/data/tweaks/viewSource.js b/data/tweaks/viewSource.js index fd47cec..d2a9fd1 100644 --- a/data/tweaks/viewSource.js +++ b/data/tweaks/viewSource.js @@ -37,55 +37,33 @@ var reAttachmentType = /,\s+([^ )]*)[;)]/; -function viewAttachmentSource(doc) { - function addLink(elem, title, href) { - if (elem.textContent.match(/[\S]/)) { - elem.appendChild(doc.createTextNode(" | ")); - } - var link = doc.createElement("a"); - link.href = href; - link.textContent = title; - elem.appendChild(link); - } - var table = doc.getElementById("attachment_table"); - if (!table) - return; - var rows = table.querySelectorAll("tr"); - for ( var i = 0; i < rows.length; ++i) { - var items = rows[i].querySelectorAll("td"); - if (items.length != 3) - continue; - var links = items[0].querySelectorAll("a"); - if (links.length == 0) - continue; - var attachHref = links[0].href; - // get the type of the attachment - var span = items[0].querySelector(".bz_attach_extra_info"); - if (!span) - continue; - var typeName = null; - try { - // Match mime type followed by ";" (charset) or ")" (no charset) - typeName = span.textContent.match(reAttachmentType)[1]; - typeName = typeName.split(";")[0]; // ignore charset following type - } - catch (e) { +function viewAttachmentSource(attachments) { + attachments.forEach(function (att) { + if (att.length < 1) { + return ; } + var typeName = att[2]; + var elem = att[4]; + var id = att[1]; + var attachHref = elem.getAttribute("href"); + if (typeName == "application/java-archive" || typeName == "application/x-jar") { // Due to the fix for bug 369814, only zip files with this special // mime type can be used with the jar: protocol. // http://hg.mozilla.org/mozilla-central/rev/be54f6bb9e1e - addLink(items[2], "JAR Contents", "jar:" + attachHref - + "!/"); + // signature of addLink is addLink(elem, title, href) + // // https://bugzilla.mozilla.org/show_bug.cgi?id=369814#c5 has more // possible mime types for zips? + createDeadLink("viewSourceJAR_" + id, "JAR Contents", elem, + "jar:" + attachHref + "!/", [], "pipe", null, null); } else if (typeName == "application/zip" || typeName == "application/x-zip-compressed" || typeName == "application/x-xpinstall") { - addLink(items[2], "Static ZIP Contents", "jar:" - + attachHref + "!/"); + createDeadLink("viewSourceZIP_" + id, "Static ZIP Contents", elem, + "jar:" + attachHref + "!/", [], "pipe", null, null); } else if (typeName != "text/plain" && typeName != "patch" && // Other types that Gecko displays like text/plain @@ -98,7 +76,9 @@ function viewAttachmentSource(doc) { // Binary image types for which the "source" is not useful typeName != "image/gif" && typeName != "image/png" && typeName != "image/jpeg") { - addLink(items[2], "Source", "view-source:" + attachHref); + createDeadLink("viewSourcePlain_" + id, "Source", elem, + "view-source:" + attachHref, [], "pipe", null, null); + } } - } + ) } -- cgit