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;
197 var prefManager = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch);
200 fireforg.timeout = window.setTimeout("",10);
201 fireforg.timeoutCount = 0;
203 // add onclick event handler for status bar icon
204 document.getElementById("fireforg_spi").onclick = fireforg.onStatusbarIconClicked;
206 // add appcontent hook
207 var appcontent = window.document.getElementById("appcontent");
208 appcontent.addEventListener("DOMContentLoaded", fireforg.onLoadSite, false);
209 appcontent.addEventListener("DOMContentLoaded", fireforg.onUrlSwitch, false);
211 // add tab change listener
212 var container = gBrowser.tabContainer;
213 container.addEventListener("TabSelect", fireforg.onUrlSwitch, false);
215 // try to listen to url changes. Found this somewhere on the web. Doesn't work.
216 /*var wnd = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
217 .getInterface(Components.interfaces.nsIWebNavigation)
218 .QueryInterface(Components.interfaces.nsIDocShell)
219 .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
220 .getInterface(Components.interfaces.nsIURIContentListener);
221 wnd.parentContentListener = fireforg.onUrlSwitch;
223 // document.getElementById('urlbar').onchange = fireforg.onUrlSwitch;
225 this.initialized = true;
226 this.strings = document.getElementById("fireforg-strings");
228 if( prefManager.getBoolPref("extensions.fireforg.injectZotero") )
229 window.setTimeout( fireforg.injectZoteroAccordingToPref ,5000);
233 onStatusbarIconClicked: function (ev) {
235 fireforg.showLinkListPopup();
237 document.getElementById('fireforg_popup_menu').openPopup( document.getElementById("fireforg_spi"),"before_end",0,0,false,null);
240 showLinkListPopup: function () {
242 if( fireforg.currentLinkRegistryEntry ) {
243 var popupMenu = document.getElementById('fireforg_popup_dynamic');
245 fireforg.populateMenuWithAnnotations( popupMenu, fireforg.currentLinkRegistryEntry );
247 popupMenu.openPopup( document.getElementById("fireforg_spi"),"before_end",0,0,false,null);
251 orgProtocolAcknowledgeResponse: function (id) {
252 fireforg.orgProtocolSendURL("fireforg-acknowledge://" + encodeURIComponent(id));
254 orgProtocolShowAnnotation: function (file, heading, encoded) {
257 file = encodeURIComponent( file );
258 heading = encodeURIComponent( heading ); }
259 fireforg.orgProtocolSendURL("fireforg-show-annotation://" + file + "/" + heading);
261 setStatusBarIconNormal: function (matches) {
264 document.getElementById('fireforg_spi_image').setAttribute("src","chrome://fireforg/skin/org-mode-unicorn_16.png");
265 document.getElementById('fireforg_spi_label').setAttribute("value","");
267 document.getElementById('fireforg_spi_image').setAttribute("src","chrome://fireforg/skin/org-mode-unicorn_16_highlighted.png");
268 document.getElementById('fireforg_spi_label').setAttribute("value","(" + matches + ")");
271 setStatusBarIconError: function () {
272 document.getElementById('fireforg_spi_label').setAttribute("value","Error");
273 document.getElementById('fireforg_spi_label_tags').setAttribute("value","");
275 setStatusBarWaiting: function () {
276 document.getElementById('fireforg_spi_label').setAttribute("value","...");
277 document.getElementById('fireforg_spi_label_tags').setAttribute("value","");
279 setStatusBarTags: function (tagString) {
280 document.getElementById('fireforg_spi_label_tags').setAttribute("value",tagString);
282 orgProtocolStoreLink: function () {
283 fireforg.orgProtocolSendURL("store-link://" + encodeURIComponent(window.content.document.URL) + "/" + encodeURIComponent(document.title));
285 orgProtocolRemember: function () {
286 fireforg.orgProtocolSendURL("remember://" + encodeURIComponent(window.content.document.URL) + "/" + encodeURIComponent(document.title) + "/" + encodeURIComponent(window.getSelection()));
288 orgProtocolSendURL: function (url) {
289 var prefManager = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch);
291 if( prefManager.getBoolPref("extensions.fireforg.macWorkaround") ) { // Workaround
292 var tmpFileName = prefManager.getCharPref("extensions.fireforg.macWorkaroundFile");
293 var file = Components.classes["@mozilla.org/file/local;1"]
294 .createInstance(Components.interfaces.nsILocalFile);
295 file.initWithPath( tmpFileName );
296 if(file.exists() == false) {
297 file.create( Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 420);
300 var stream = Components.classes["@mozilla.org/network/file-output-stream;1"]
301 .createInstance(Components.interfaces.nsIFileOutputStream);
303 stream.init(file, 0x02 | 0x08 | 0x20, 0666, 0);
304 var finalString = "org-protocol://" + url;
305 stream.write( finalString, finalString.length);
309 var req = new XMLHttpRequest();
311 req.open('POST', "org-protocol://" + url,true);
316 contextMenuItemShowing: function (e) {
317 // alert("ContextMenuItemShowing");
319 var contextMenuEntry = document.getElementById('fireforg_ctx_menu_fireforg_popup');
321 // remove all children
322 while(contextMenuEntry.hasChildNodes()){
323 contextMenuEntry.removeChild(contextMenuEntry.lastChild);}
325 if( gContextMenu.onLink ) {
326 var url = gContextMenu.link;
328 var registryEntry = fireforg.getRegistryEntryForLink( url );
330 fireforg.populateMenuWithAnnotations( contextMenuEntry, registryEntry );
336 populateMenuWithAnnotations: function (menu, registryEntry ) {
337 // remove all children
338 while(menu.hasChildNodes()){
339 menu.removeChild(menu.lastChild);}
341 if( registryEntry ) {
342 fireforg.jq( registryEntry ).children()
345 // alert( "This: " + this);
346 var headingJQ = fireforg.jq( this );
347 var file = headingJQ.attr("file");
348 var headingText = headingJQ.attr("text");
349 var tags = headingJQ.attr("tags");
351 var tmpItem = document.createElement("menuitem");
352 tmpItem.setAttribute("class","fireforg-popupmenu");
353 tmpItem.setAttribute("label", headingText + " " + tags );
354 tmpItem.setAttribute("onclick","fireforg.orgProtocolShowAnnotation(\"" + encodeURIComponent(file) + "\",\"" + encodeURIComponent(headingText) + "\",true)");
355 menu.appendChild( tmpItem );
361 injectZoteroAccordingToPref: function () {
362 var prefManager = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch);
363 if( prefManager.getBoolPref("extensions.fireforg.injectZotero") ) {
364 if( Zotero.Translate ) {
365 if( !Zotero.Translate.prototype.fireforg_runHandler ) {
366 Zotero.Translate.prototype.fireforg_runHandler = Zotero.Translate.prototype.runHandler;
367 Zotero.Translate.prototype.runHandler = function(type, argument) {
368 if( type == "itemDone") {
369 fireforg.zoteroItemDoneHandler( argument );
371 return this.fireforg_runHandler(type, argument);
373 // alert("Zotero translator handler injected.");
376 } else { // remove injection
377 if( Zotero.Translate() ) {
378 if( Zotero.Translate.prototype.fireforg_runHandler ) {
379 Zotero.Translate.prototype.runHandler = Zoter.Translate.prototype.fireforg_runHandler;
380 //alert("Zotero injection removed");
385 // additional "itemDone" handler for Zoteros translator
386 // item : Zotero.item that has been processed
387 zoteroItemDoneHandler: function (item) {
389 /*alert("itemDoneHandler called with: \n" +
390 "type: " + Zotero.ItemTypes.getName(item.getType()) + "\n" +
391 "title: " + item.getDisplayTitle(true) + "\n");*/
392 window.setTimeout( function () {
393 //alert( "item.attachments : " + item.getAttachments());
395 var translatorObj = new Zotero.Translate("export"); // create Translator for export
396 translatorObj.setItems( [ item ]);
397 translatorObj.setTranslator( "9cb70025-a888-4a29-a210-93ec52da40d4" ); // set Translator to use the BibTex translator
398 translatorObj.setHandler("done", fireforg._zoteroTranslationDone);
399 // deinject runHandler to avoid recursion
400 translatorObj.runHandler = translatorObj.fireforg_runHandler;
401 translatorObj.translate(); }
406 _zoteroTranslationDone: function (obj, worked) {
408 alert("Fireforg: Zotero BibTex export failed!");
410 var bibtex = obj.output.replace(/\r\n/g, "\n");
411 //alert("BibTex: " + bibtex );
414 fireforg.orgProtocolSendURL("fireforg-bibtex-entry://" + encodeURIComponent( bibtex ) );
420 window.addEventListener("load", function(e) { fireforg.onLoad(e); }, false);