I submitted the Sticky Notes and Snip-Edit Plugin to the 2008 Codegeist. Although I spent more time that I had expected (too much fun) I turned out to be very straight forward.
The plugin relies heavily on jQuery which dramatically simplifies the way you can integrate HTML with Javascript.
Atlassian introduced it in Confluence 2.8 (but could be included in older versions) and it is going to make life way easier in theming and extending Confluence's capabilities. The Sticky Notes and Snip-Edit Plugin is an example.
Sticky Notes
Here's a simplified explanation of how the plugin works. You can get the source code here.
How it works
The plugin allows you to add notes on every heading (h1 to h5) of a document.
Each comment added is identified by a noteId which is based on the heading caption, and a commentId, which is generated when storing the comment.
when the HTML is loaded, the noteIds are generated, the existing notes marked, and the proper assigned to hover, adding or deleting notes:
jQuery(document).ready(initStickyNotes);
function initStickyNotes() {
jQuery(".wiki-content").find("h1, h2, h3, h4, h5").each(setSectionsIds);
jQuery(".existing-note").each(setExistingNote);
jQuery(".sticky-action").each(setStickyAction);
}
SetSectionsIds will generate the noteId and will append a <span> element on which the sticky notes HTML will be included:
function setSectionsIds() {
var jthis = jQuery(this);
var content = jthis.text();
var id;
id = 'sticky_' + content.replace(/\W/g, '').toLowerCase();
jthis.append("<div class='sticky-action' id='" + id + "'><span class='sticky-icon'></span></div>");
}
setExistingNote will set the proper icon for existing notes. The existing notes are rendered in the HTML generated by the {stickynotes} macro. For each existing note we are rendering the noteId as "<span class="existing-note">$noteId</span> in a hidden div.
function setExistingNote() {
var noteid = jQuery(this).html();
toggleActionIcon(jQuery("#" + noteid), true);
}
function toggleActionIcon(section, hasNotes) {
if (hasNotes) {
section.addClass("sticky-notes");
section.find(".sticky-icon").addClass("has-notes");
} else {
section.removeClass("sticky-notes");
section.find(".sticky-icon").removeClass("has-notes");
}
}
setStickyAction will then load the existing notes, and set the proper actions for clicking and hovering over the icons:
function setStickyAction() {
setActionIcon(jQuery(this), true);
}
function setActionIcon(section, pinNote) {
if (section.hasClass("sticky-notes")) {
section.hover(hoverInNotes, hoverOutNotes);
section.find(".sticky-icon").click(pinStickyNote);
} else {
section.find(".sticky-icon").click(addStickyNote);
section.hover(function() {}, function() {});
}
}
When the sticky notes icons are hovered, and if they have notes, then they are loaded and display. If they hovered out, they are closed:
function hoverInNotes() {
var jthis = jQuery(this);
if (! jthis.find(".sticky").length) {
openNote(jthis);
}
}
function openNote(section) {
var id = section.attr("id");
var popup = createPopup(section);
var uri = makeUri("get") + "¬eId=" + id;
popup.load(uri, function() {
if (stickyReadOnly) {
jQuery(this).sticky().find(".sticky-add").hide();
jQuery(this).find(".sticky-delete").hide();
} else {
jQuery(this).sticky().find(".sticky-add").click(addStickyNote);
jQuery(this).find(".sticky-delete").click(deleteStickyNote);
}
});
}
function hoverOutNotes() {
var popup = jQuery(this).find(".sticky-popup");
if (! popup.find(".sticky-pinned").length) {
popup.remove();
}
}
addStickyNote and deleteStickyNote would just display the proper html and actions when the buttons are clicked.
What happens in the server is plain old Confluence plugin code.
Of course there is actually more happening in the plugin (the {stickable} macro, the action icon, handling cookies to keep sticky notes closed, pinning notes) but it a nutshell this is how it works.
Add Comment