1 // Fireforg - a Firefox extension for org mode interaction
3 // Copyright 2009 Andreas Burtzlaff
5 // Author: Andreas Burtzlaff < andreas at burtz[REMOVE]laff dot de >
8 // This file is not part of GNU Emacs.
10 // This includes a copy of jquery (http://jquery.com/) under its GPL license
11 // (http://docs.jquery.com/Licensing)
13 // This program is free software; you can redistribute it and/or
14 // modify it under the terms of the GNU General Public License as
15 // published by the Free Software Foundation; either version 2, or (at
16 // your option) any later version.
18 // This program is distributed in the hope that it will be useful, but
19 // WITHOUT ANY WARRANTY; without even the implied warranty of
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 // General Public License for more details.
23 // You should have received a copy of the GNU General Public License
24 // along with this program; if not, write to the Free Software
25 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 // define jquery convenience functions
29 jq: function (a) { return $mb( a, window.content.document); },
33 lookupAndModifyLinks: function () {
35 // remove existing annotations;
36 fireforg.jq(".orgNote").remove();
38 var prefManager = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch);
39 var annotationLinkStyle = prefManager.getCharPref("extensions.fireforg.annotationLinkStyle");
40 var annotationLinkTooltip = prefManager.getBoolPref("extensions.fireforg.annotationLinkTooltip");
42 var linksJQ = fireforg.jq("a");
44 linksJQ.each( function () {
47 var objectJQ = fireforg.jq(this);
48 var url = objectJQ.attr("href");
50 var registryEntry = fireforg.getRegistryEntryForLink( url );
53 // add class orgNoteLink in order to be able to select the modified elements later (no css info connected with this class)
54 // objectJQ.attr("class", objectJQ.attr("class") + " orgNoteLink");
56 objectJQ.attr("style", objectJQ.attr("style") + annotationLinkStyle);
57 // in order to be able to remove the inserted style string, it has to be stored separately
58 objectJQ.attr("fireforgStyle", annotationLinkStyle);
60 // generate tooltip text from entry (BUG: Newline doesn't work here. Rewrite with proper menu on mouseover.)
61 if( annotationLinkTooltip ) {
63 fireforg.jq( registryEntry ).children().each( function () { tooltipText = tooltipText + fireforg.jq( this ).attr("text") + "\n"; });
64 objectJQ.attr("title", tooltipText );
70 var pos = fireforg.jq(this).offset();
71 // var posRoot = $mb(window.content.document, window.content.document).offset();
73 // var annotation = document.createElement('div');
74 //annotation.textContent = "OrgTest!";
76 var annotation = document.createElement('div');
77 annotation.class += "orgNote";
78 // var img = document.createElement('img');
79 //img.src = "chrome://fireforg/skin/org-mode-unicorn_16.png";
80 //annotation.appendChild( img );
81 // fireforg.orgProtocolSendURL("fireforg-get-annotations://" + encodeURIComponent(fireforg.requestid) + "/" + encodeURIComponent( window.content.document.URL));
82 annotation.textContent = "Org!";
83 fireforg.jq( annotation ).css({"border-style":"solid", "border-width" : "1px","background-color" : "gray"});
85 fireforg.jq("body").append( annotation );
86 var annotationWidth = fireforg.jq("body").children().eq(".orgNote").width();
87 var annotationHeight = fireforg.jq(annotation).height();
88 //alert(annotationWidth);
89 fireforg.jq(annotation).css( { "position" : "absolute", "left" : "" + (pos.left-40) + "px", "top" : "" + pos.top + "px" ,"z-index" : "3"});
90 //$mb( annotation, window.content.document ).css( { "position" : "absolute", "left" : "-10px", "top" : "-10px" ,"z-index" : "3"});
95 onLoadSite: function () {
97 if( fireforg.loadRegistryFromFile() ) {
98 var prefManager = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch);
99 if( prefManager.getBoolPref("extensions.fireforg.lookupLinksOnLoad") )
100 fireforg.lookupAndModifyLinks();
102 fireforg.setStatusBarIconError();
106 onUrlSwitch: function() {
108 if( fireforg.loadRegistryFromFile() ) {
112 fireforg.setStatusBarWaiting();
115 var url = window.content.document.URL;
116 fireforg.currentLink = url;
118 if( !fireforg.registryDOM ) {
119 fireforg.setStatusBarIconNormal(0);
122 // get all heading for url
123 fireforg.currentLinkRegistryEntry = fireforg.registryDOM.evaluate("//link[@url=\"" + url +"\"]", fireforg.registryDOM, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
124 if( fireforg.currentLinkRegistryEntry ) {
125 fireforg.setStatusBarIconNormal( fireforg.jq( fireforg.currentLinkRegistryEntry ).children().length );
127 fireforg.setStatusBarIconNormal( 0 );
129 // get tags for url, remove duplicates
131 fireforg.jq( fireforg.currentLinkRegistryEntry ).children().each( function () { var t = fireforg.jq( this ).attr("tags"); if( t != "") { tags = tags + t }; });
133 var tagList = tags.replace("::",":").replace(/^:/,"").replace(/:$/,"").split(":").sort();
134 if( tagList.length > 0 ) {
135 var tagNoDup = ":" + tagList[0] + ":";
136 for( var i = 1 ; i < tagList.length ; i++ ) {
137 if( tagList[i] != "" && tagList[i] != tagList[i-1] )
138 tagNoDup = tagNoDup + tagList[i] + ":";
140 fireforg.setStatusBarTags( tagNoDup );
142 fireforg.setStatusBarTags("");
145 fireforg.setStatusBarTags("");
150 fireforg.setStatusBarIconError();
156 loadRegistryFromFile: function () {
157 var file = Components.classes["@mozilla.org/file/local;1"]
158 .createInstance(Components.interfaces.nsILocalFile);
159 file.initWithPath( Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch).getCharPref("extensions.fireforg.registryFile") );
160 if ( !file.exists() ) {
163 var is = Components.classes["@mozilla.org/network/file-input-stream;1"]
164 .createInstance( Components.interfaces.nsIFileInputStream );
165 is.init( file,0x01, 00004, null);
166 var sis = Components.classes["@mozilla.org/scriptableinputstream;1"]
167 .createInstance( Components.interfaces.nsIScriptableInputStream );
169 var output = sis.read( sis.available() );
171 var parser = new DOMParser();
172 fireforg.registryDOM = parser.parseFromString( output , "text/xml");
173 var registry = fireforg.registryDOM.childNodes;
177 fireforg.domEntry = document.getElementById("fireforg_registry");
178 if( fireforg.domEntry ) {
179 document.removeChild( fireforg.domEntry );
181 fireforg.domEntry = document.createElement("registry");
182 fireforg.domEntry.id = "fireforg_registry";
188 getRegistryEntryForLink: function (url) {
190 // the xpath query may be invalid for certain url's
191 return fireforg.registryDOM.evaluate("//link[@url=\"" + url +"\"]", fireforg.registryDOM, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
198 fireforg.timeout = window.setTimeout("",10);
199 fireforg.timeoutCount = 0;
201 // add onclick event handler for status bar icon
202 document.getElementById("fireforg_spi").onclick = fireforg.onStatusbarIconClicked;
204 // add appcontent hook
205 var appcontent = window.document.getElementById("appcontent");
206 appcontent.addEventListener("DOMContentLoaded", fireforg.onLoadSite, false);
207 appcontent.addEventListener("DOMContentLoaded", fireforg.onUrlSwitch, false);
209 // add tab change listener
210 var container = gBrowser.tabContainer;
211 container.addEventListener("TabSelect", fireforg.onUrlSwitch, false);
213 // try to listen to url changes. Found this somewhere on the web. Doesn't work.
214 /*var wnd = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
215 .getInterface(Components.interfaces.nsIWebNavigation)
216 .QueryInterface(Components.interfaces.nsIDocShell)
217 .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
218 .getInterface(Components.interfaces.nsIURIContentListener);
219 wnd.parentContentListener = fireforg.onUrlSwitch;
221 // document.getElementById('urlbar').onchange = fireforg.onUrlSwitch;
223 this.initialized = true;
224 this.strings = document.getElementById("fireforg-strings");
226 // try to add "itemDone" handler to Zoteros translator
227 // if( Zotero.Translate ) {
228 // alert("Zotero Translate handler injected.");
229 // Zotero.Translate.prototype.setHandler("itemDone", fireforg.zoteroItemDoneHandler );
231 window.setTimeout( function () { if( Zotero.Translate ) {
232 alert("Zotero translator handler injected.");
233 //(new Zotero.Translate()).setHandler("itemDone", fireforg.zoteroItemDoneHandler );
234 Zotero.Translate.prototype.fireforg_runHandler = Zotero.Translate.prototype.runHandler;
235 Zotero.Translate.prototype.runHandler = function(type, argument) {
236 if( type == "itemDone") {
237 // alert("Zotero hijacked!");
238 fireforg.zoteroItemDoneHandler( argument );
241 return this.fireforg_runHandler(type, argument);
247 onStatusbarIconClicked: function (ev) {
249 fireforg.showLinkListPopup();
251 document.getElementById('fireforg_popup_menu').openPopup( document.getElementById("fireforg_spi"),"before_end",0,0,false,null);
255 showLinkListPopup: function () {
257 if( fireforg.currentLinkRegistryEntry ) {
258 var popupMenu = document.getElementById('fireforg_popup_dynamic');
260 fireforg.populateMenuWithAnnotations( popupMenu, fireforg.currentLinkRegistryEntry );
262 popupMenu.openPopup( document.getElementById("fireforg_spi"),"before_end",0,0,false,null);
266 orgProtocolAcknowledgeResponse: function (id) {
267 fireforg.orgProtocolSendURL("fireforg-acknowledge://" + encodeURIComponent(id));
269 orgProtocolShowAnnotation: function (file, heading, encoded) {
272 file = encodeURIComponent( file );
273 heading = encodeURIComponent( heading ); }
274 fireforg.orgProtocolSendURL("fireforg-show-annotation://" + file + "/" + heading);
276 setStatusBarIconNormal: function (matches) {
279 document.getElementById('fireforg_spi_image').setAttribute("src","chrome://fireforg/skin/org-mode-unicorn_16.png");
280 document.getElementById('fireforg_spi_label').setAttribute("value","");
282 document.getElementById('fireforg_spi_image').setAttribute("src","chrome://fireforg/skin/org-mode-unicorn_16_highlighted.png");
283 document.getElementById('fireforg_spi_label').setAttribute("value","(" + matches + ")");
286 setStatusBarIconError: function () {
287 document.getElementById('fireforg_spi_label').setAttribute("value","Error");
288 document.getElementById('fireforg_spi_label_tags').setAttribute("value","");
290 setStatusBarWaiting: function () {
291 document.getElementById('fireforg_spi_label').setAttribute("value","...");
292 document.getElementById('fireforg_spi_label_tags').setAttribute("value","");
294 setStatusBarTags: function (tagString) {
295 document.getElementById('fireforg_spi_label_tags').setAttribute("value",tagString);
297 orgProtocolStoreLink: function () {
298 fireforg.orgProtocolSendURL("store-link://" + encodeURIComponent(window.content.document.URL),true);// + "/" + encodeURIComponent(document.title), true);
300 orgProtocolRemember: function () {
301 fireforg.orgProtocolSendURL("remember://" + encodeURIComponent(window.content.document.URL) + "/" + encodeURIComponent(document.title) + "/" + encodeURIComponent(window.getSelection()));
303 orgProtocolSendURL: function (url) {
304 var req = new XMLHttpRequest();
306 req.open('POST', "org-protocol://" + url,true);
310 contextMenuItemShowing: function (e) {
311 // alert("ContextMenuItemShowing");
313 var contextMenuEntry = document.getElementById('fireforg_ctx_menu_fireforg_popup');
315 // remove all children
316 while(contextMenuEntry.hasChildNodes()){
317 contextMenuEntry.removeChild(contextMenuEntry.lastChild);}
319 if( gContextMenu.onLink ) {
320 var url = gContextMenu.link;
322 var registryEntry = fireforg.getRegistryEntryForLink( url );
324 fireforg.populateMenuWithAnnotations( contextMenuEntry, registryEntry );
330 populateMenuWithAnnotations: function (menu, registryEntry ) {
331 // remove all children
332 while(menu.hasChildNodes()){
333 menu.removeChild(menu.lastChild);}
335 if( registryEntry ) {
336 fireforg.jq( registryEntry ).children()
339 // alert( "This: " + this);
340 var headingJQ = fireforg.jq( this );
341 var file = headingJQ.attr("file");
342 var headingText = headingJQ.attr("text");
343 var tags = headingJQ.attr("tags");
345 var tmpItem = document.createElement("menuitem");
346 tmpItem.setAttribute("class","fireforg-popupmenu");
347 tmpItem.setAttribute("label", headingText + " " + tags );
348 tmpItem.setAttribute("onclick","fireforg.orgProtocolShowAnnotation(\"" + encodeURIComponent(file) + "\",\"" + encodeURIComponent(headingText) + "\",true)");
349 menu.appendChild( tmpItem );
354 // additional "itemDone" handler for Zoteros translator
355 // item : Zotero.item that has been processed
356 zoteroItemDoneHandler: function (item) {
358 alert("itemDoneHandler called with: \n" +
359 "type: " + Zotero.ItemTypes.getName(item.getType()) + "\n" +
360 "title: " + item.getDisplayTitle(true) + "\n");
361 window.setTimeout( function () {
362 var translatorObj = new Zotero.Translate("export"); // create Translator for export
363 translatorObj.setItems( [ item ]);
364 translatorObj.setTranslator( "9cb70025-a888-4a29-a210-93ec52da40d4" ); // set Translator to use the BibTex translator
365 translatorObj.setHandler("done", fireforg._zoteroTranslationDone);
366 // deinject runHandler to avoid recursion
367 translatorObj.runHandler = translatorObj.fireforg_runHandler;
368 translatorObj.translate(); }
373 _zoteroTranslationDone: function (obj, worked) {
375 alert("Fireforg: Zotero BibTex export failed!");
377 alert("BibTex: " + obj.output.replace(/\r\n/g, "\n") );
385 window.addEventListener("load", function(e) { fireforg.onLoad(e); }, false);