root/trunk/chrome/content/integration/elements.js

Revision 826, 15.8 kB (checked in by MaierMan, 3 years ago)

#555: Allow Menu Editor to do (some) of its dirty work.
Needs testing!

Line 
1 /* ***** BEGIN LICENSE BLOCK *****
2  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3  *
4  * The contents of this file are subject to the Mozilla Public License Version
5  * 1.1 (the "License"); you may not use this file except in compliance with
6  * the License. You may obtain a copy of the License at
7  * http://www.mozilla.org/MPL/
8  *
9  * Software distributed under the License is distributed on an "AS IS" basis,
10  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11  * for the specific language governing rights and limitations under the
12  * License.
13  *
14  * The Original Code is DownThemAll!
15  *
16  * The Initial Developers of the Original Code are Stefano Verna and Federico Parodi
17  * Portions created by the Initial Developers are Copyright (C) 2004-2007
18  * the Initial Developers. All Rights Reserved.
19  *
20  * Contributor(s):
21  *    Stefano Verna
22  *    Federico Parodi <f.parodi@tiscali.it>
23  *    Nils Maier <MaierMan@web.de>
24  *
25  * Alternatively, the contents of this file may be used under the terms of
26  * either the GNU General Public License Version 2 or later (the "GPL"), or
27  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28  * in which case the provisions of the GPL or the LGPL are applicable instead
29  * of those above. If you wish to allow use of your version of this file only
30  * under the terms of either the GPL or the LGPL, and not to allow others to
31  * use your version of this file under the terms of the MPL, indicate your
32  * decision by deleting the provisions above and replace them with the notice
33  * and other provisions required by the GPL or the LGPL. If you do not delete
34  * the provisions above, a recipient may use your version of this file under
35  * the terms of any one of the MPL, the GPL or the LGPL.
36  *
37  * ***** END LICENSE BLOCK ***** */
38  
39 // DTA context overlay
40 var DTA_ContextOverlay = {
41
42   _str: Components.classes['@mozilla.org/intl/stringbundle;1']
43     .getService(Components.interfaces.nsIStringBundleService)
44     .createBundle('chrome://dta/locale/menu.properties'),
45  
46   getString: function(n) {
47     try {
48       return this._str.GetStringFromName(n);
49     } catch (ex) {
50       DTA_debug.log("locale error: " + n, ex);
51       return '<error>';
52     }
53   },
54  
55   trim: function(t) {
56     return t.replace(/^[ \t_]+|[ \t_]+$/gi, '').replace(/(_){2,}/g, "_");
57   },
58  
59   addLinksToArray: function(lnks, urls, doc) {
60     if (!lnks || !lnks.length) {
61       return;
62     }
63    
64     var ref = DTA_AddingFunctions.getRef(doc);
65    
66     for (var i = 0; i < lnks.length; ++i) {
67       // remove anchor from url
68       var link = lnks[i];
69       // if it's valid and it's new
70       if (!DTA_AddingFunctions.isLinkOpenable(link.href)) {
71         continue;
72       }
73        
74       /// XXX: title is also parsed by extractDescription
75       /// XXX: is this instance necessary?
76       var udesc = '';
77       if (link.hasAttribute('title')) {
78         udesc = this.trim(link.getAttribute('title'));
79       }
80       urls.push({
81         'url': new DTA_URL(link.href, doc.characterSet),
82         'referrer': ref,
83         'description': this.extractDescription(link),
84         'ultDescription': udesc
85       });
86      
87       var ml = DTA_getLinkPrintMetalink(link.hash);
88       if (ml) {
89         urls.push({
90           'url': new DTA_URL(ml, doc.characterSet),
91           'referrer': ref,
92           'description': '[metalink] http://www.metalinker.org/',
93           'ultDescription': '',
94           'metalink': true
95         });
96       }
97     }
98   },
99  
100   addImagesToArray: function(lnks, images, doc) {
101     if (!lnks || !lnks.length) {
102       return;
103     }
104
105     var ref = DTA_AddingFunctions.getRef(doc);
106
107     for (var i = 0; i < lnks.length; ++i) {
108       var src = lnks[i].src;
109       if (!DTA_AddingFunctions.isLinkOpenable(src)) {
110         try {
111           src = DTA_AddingFunctions.composeURL(doc, src);
112         }
113         catch (ex) {
114           DTA_debug.log("failed to compose: " + src, ex);
115           continue;
116         }
117       }
118       // if it's valid and it's new
119       // better double check :p
120       if (!DTA_AddingFunctions.isLinkOpenable(src)) {
121         continue;
122       }
123       var desc = '';
124       if (lnks[i].hasAttribute('alt')) {
125         desc = this.trim(lnks[i].getAttribute('alt'));
126       }
127       else if (lnks[i].hasAttribute('title')) {
128         desc = this.trim(lnks[i].getAttribute('title'));
129       }
130       images.push({
131         'url': new DTA_URL(src, doc.characterSet),
132         'referrer': ref,
133         'description': desc
134       });
135     }
136   },
137  
138   // recursively add stuff.
139   addLinks: function(aWin, aURLs, aImages, honorSelection) {
140
141     function filterElements(nodes, set) {
142       var filtered = [];
143       for (var i = 0, e = nodes.length; i < e; ++i) {
144         if (set.containsNode(nodes[i], true)) {
145           filtered.push(nodes[i]);
146         }
147       }
148       return filtered;
149     }
150  
151     try {
152      
153       var links = aWin.document.links;
154       var images = aWin.document.images;
155       var embeds = aWin.document.embeds;
156       var rawInputs = aWin.document.getElementsByTagName('input');
157       var inputs = [];
158       for (var i = 0; i < rawInputs.length; ++i) {
159         var rit = rawInputs[i].getAttribute('type');
160         if (!rit || rit.toLowerCase() != 'image') {
161           continue;
162         }
163         inputs.push(rawInputs[i]);
164       }
165      
166       var sel = aWin.getSelection();
167       if (honorSelection && sel && !sel.isCollapsed) {
168         DTA_debug.logString("selection only");
169         [links, images, embeds, inputs] = [links, images, embeds, inputs].map(
170           function(e) {
171             return filterElements(e, sel);
172           }
173         );
174       }
175       else {
176         // we were asked to honor the selection, but we didn't actually have one.
177         // so reset this flag so that we can continue processing frames below.
178         honorSelection = false;
179       }
180      
181       this.addLinksToArray(links, aURLs, aWin.document);
182       [images, embeds, inputs].forEach(
183         function(e) {
184           this.addImagesToArray(e, aImages, aWin.document);
185         },
186         this
187       );
188     }
189     catch (ex) {
190       DTA_debug.log('addLinks', ex);
191     }
192    
193     // do not process further as we just filtered the selection
194     if (honorSelection) {
195       return;
196     }
197    
198     // recursively process any frames
199     if (aWin.frames) {
200       for (var i = 0, e = aWin.frames.length; i < e; ++i) {
201         this.addLinks(aWin.frames[i], aURLs, aImages);
202       }
203     }
204   },
205  
206   findWindowsNavigator: function(all) {
207     var windows = [];
208     if (!all) {
209       var sel = document.commandDispatcher.focusedWindow.getSelection();
210       if (sel.isCollapsed) {
211         windows.push(gBrowser.selectedBrowser.contentWindow.top);
212       }
213       else {
214         windows.push(document.commandDispatcher.focusedWindow);
215       }
216     }
217     else {
218       gBrowser.browsers.forEach(
219         function(e) {
220           windows.push(e.contentWindow.top);
221         }
222       );
223     }
224     return windows;
225   },
226   findWindowsMail: function(all) {
227     var windows = [];
228     if (document.documentElement.getAttribute('windowtype') == 'mail:3pane') {
229       windows.push(document.getElementById('messagepane').contentWindow);
230     }
231     else if (!all) {
232       windows.push(document.commandDispatcher.focusedWindow);
233     }
234     else {
235       windows = DTA_Mediator
236         .getAllByType('mail:messageWindow')
237         .map(function(w) {
238           return w.content;
239         });
240     }
241     return windows;
242   },
243   _types: {
244     'mail:3pane': 'findWindowsMail',
245     'mail:messageWindow': 'findWindowsMail'
246   },
247  
248   findLinks: function(turbo, all) {
249     try {
250       if (all == undefined && turbo && DTA_preferences.getDTA('rememberoneclick', false)) {
251         all = DTA_preferences.getDTA('lastalltabs', false);
252       }
253       if (turbo && all != undefined) {
254         DTA_preferences.setDTA('lastalltabs', all);
255       }
256      
257       function makeUnique(i) {
258         var known = {};
259         return i.filter(
260           function(e) {
261             var url = e.url.url;
262             if (url in known) {
263               return false;
264             }
265             known[url] = null;
266             return true;
267           }
268         );
269       }   
270      
271       if (turbo) {
272         DTA_debug.logString("findLinks(): DtaOneClick request from the user");
273       } else {
274         DTA_debug.logString("findLinks(): DtaStandard request from the user");
275       }
276
277       var wt = document.documentElement.getAttribute('windowtype');
278       if (wt in this._types) {
279         var windows = this[this._types[wt]](all);
280       }
281       else {
282         var windows = this.findWindowsNavigator(all);
283       }
284      
285       var urls = [];
286       var images = [];
287       windows.forEach(
288         function(win) {
289           this.addLinks(win, urls, images, !all);
290         },
291         this
292       );
293       urls = makeUnique(urls);
294       images = makeUnique(images);
295
296       if (!urls.length && !images.length) {
297         DTA_alert(this.getString('error'), this.getString('errornolinks'));
298         return;
299       }
300      
301       if (turbo) {
302         try {
303           DTA_AddingFunctions.saveLinkArray(true, urls, images);
304           return;
305         } catch (ex) {
306           DTA_debug.log('findLinks', ex);
307           DTA_alert(this.getString('error'), this.getString('errorinformation'));
308         }
309       }
310       DTA_AddingFunctions.saveLinkArray(false, urls, images);
311     }
312     catch(ex) {
313       DTA_debug.log('findLinks', ex);
314     }
315   },
316  
317   findSingleLink: function(turbo) {
318     try {
319       var win = document.commandDispatcher.focusedWindow.top;
320       var ctx = this.contextMenu;
321
322       var cur = ctx.target;
323      
324       var tofind = ctx.onLink ? /^a$/i : /^img$/i;
325    
326       while (!("tagName" in cur) || !tofind.test(cur.tagName)) {
327         cur = cur.parentNode;
328       }
329       var url = ctx.onLink ? cur.href : cur.src;
330      
331       if (!DTA_AddingFunctions.isLinkOpenable(url)) {
332         DTA_alert(this.getString('error'), this.getError('errornodownload'));
333         return;
334       }
335      
336       var ml = DTA_getLinkPrintMetalink(url);
337       url = new DTA_URL(ml ? ml : url, win.document.characterSet);
338      
339       var ref = DTA_AddingFunctions.getRef(document.commandDispatcher.focusedWindow.document);
340       var desc = this.extractDescription(cur);
341       if (turbo) {
342         try {
343           DTA_AddingFunctions.saveSingleLink(true, url, ref, desc);
344           return;
345         }
346         catch (ex) {
347           DTA_debug.log('findSingleLink', ex);
348           DTA_alert(this.getString('error'), this.getString('errorinformation'));
349         }
350       }
351       DTA_AddingFunctions.saveSingleLink(false, url, ref, desc);
352     }
353     catch (ex) {
354       DTA_debug.log('findSingleLink: ', ex);
355     }
356   },
357  
358   init: function() {
359     try {
360       this.direct = {};
361       this.compact = {};
362      
363       var ctxItem = document.getElementById("dtaCtxCompact");
364       var ctx = ctxItem.parentNode;
365       var cont = document.getElementById('dtaCtxSubmenu');
366
367       ['SepBack', 'Pref', 'SepPref', 'TDTA', 'DTA', 'SaveT', 'Save', 'SepFront'].forEach(
368         function(id) {
369           this.compact[id] = document.getElementById('dtaCtx' + id);
370           var node = document.getElementById('dtaCtx' + id).cloneNode(true);
371           node.setAttribute('id', node.id + "-direct");
372           ctx.insertBefore(node, ctxItem.nextSibling);
373           this.direct[id] = node;
374         },
375         this
376       );
377       // intitalize those to have Menu Editor pick up "good" text
378       [this.direct, this.compact].forEach(
379         function(m) {
380           m.Save.label = this.getString('dtasavelink');
381           m.SaveT.label = this.getString('turbosavelink');
382         },
383         this
384       );
385
386       var menu = document.getElementById("dtaToolsMenu").parentNode;
387       ctx.addEventListener("popupshowing", function (evt) { DTA_ContextOverlay.onContextShowing(evt); }, false);
388       menu.addEventListener("popupshowing", function (evt) { DTA_ContextOverlay.onToolsShowing(evt); }, false);
389
390       this.ctxBase = document.getElementById('dtaCtxCompact');
391      
392       // prepare tools
393       this.tools = {};
394       ['DTA', 'TDTA', 'Manager'].forEach(
395         function (e) {
396           this.tools[e] = document.getElementById('dtaTools' + e);
397         },
398         this
399       );
400       this.toolsBase = document.getElementById('dtaToolsMenu');
401       this.toolsMenu = document.getElementById('dtaToolsPopup');
402       this.toolsSep = document.getElementById('dtaToolsSep');
403      
404     }
405     catch (ex) {
406       Components.utils.reportError(ex);
407       DTA_debug.log("DCO::init()", ex);
408     }
409   },
410   get contextMenu() {
411       if (window.gContextMenu !=  null) {
412         return gContextMenu;
413       }
414       var cm = {
415         onLink: false,
416         onImage: false,
417         target: document.popupNode,
418         fake: true
419       };
420       if (cm.target) {
421         var node = cm.target;
422         if (node instanceof Components.interfaces.nsIImageLoadingContent && node.currentURI) {
423           cm.onImage = true;
424         }
425         while (node && !cm.onLink) {
426           if (node instanceof HTMLAnchorElement && node.href) {
427             cm.onLink = true;
428           }       
429           node = node.parentNode;
430         }
431       }
432       return cm;
433   },
434   onContextShowing: function(evt) {
435     try {
436       var ctx = this.contextMenu;
437       // get settings
438       var items = DTA_preferences.getDTA("ctxmenu", "1,1,0").split(",").map(function(e){return parseInt(e);});
439       var compact = DTA_preferences.getDTA("ctxcompact", false);
440      
441       var menu;
442       if (compact) {
443         this.ctxBase.hidden = false;
444         menu = this.compact;
445         }
446       else {
447         this.ctxBase.hidden = true;
448         menu = this.direct;
449       }
450      
451       // hide all
452       for (var i in menu) {
453         this.direct[i].hidden = true;
454         this.compact[i].hidden = true;
455       }
456      
457       // setup menu items
458       // show will hold those that will be shown
459       var show = [];
460      
461       // hovering an image or link
462       if (ctx && (ctx.onLink || ctx.onImage)) {
463         if (items[0]) {
464           show.push(menu.Save);
465         }
466         if (items[1]) {
467           show.push(menu.SaveT);
468         }
469         menu.Save.label = this.getString('dtasave' + (ctx.onLink ? 'link' : 'image'));
470         menu.SaveT.label = this.getString('turbosave' + (ctx.onLink ? 'link' : 'image'));
471       }
472       // regular
473       if (ctx && (ctx.fake || !(ctx.onLink || ctx.onImage))) {
474         if (items[0]) {
475           show.push(menu.DTA);
476         }
477         if (items[1]) {
478           show.push(menu.TDTA);
479         }
480         var sel = document.commandDispatcher.focusedWindow.getSelection();
481         sel = sel && !sel.isCollapsed;
482         menu.DTA.label = this.getString('dta' + (sel ? 'selection' : 'regular'));
483         menu.TDTA.label = this.getString('turbo' + (sel ? 'selection' : 'regular'));
484       }
485      
486       // prefs
487       if (items[2]) {
488         show.push(menu.Pref);
489         if (compact && (items[0] || items[1])) {
490           show.push(menu.SepPref);
491         }
492       }
493      
494       // show the seperators, if required.
495       var n = menu.SepFront;
496       while ((n = n.previousSibling)) {
497         if (n.hidden) {
498           continue;
499         }
500         if (n.nodeName != 'menuseparator') {
501           show.push(menu.SepFront);
502         }
503         break;
504         }
505       n = menu.SepBack;
506       while ((n = n.nextSibling)) {
507         if (n.hidden) {
508           continue;
509       }
510         if (n.nodeName != 'menuseparator') {
511           show.push(menu.SepBack);
512         }
513         break;
514         }
515      
516       show.forEach(
517         function (node) {
518           node.hidden = false;
519         }
520       );
521
522     } catch(ex) {
523       DTA_debug.log("DTAContext(): ", ex);
524     }   
525   },
526  
527   onToolsShowing : function(evt) {
528     try {
529      
530       // get settings
531       var menu = DTA_preferences.getDTA("toolsmenu", "1,1,1").split(",").map(function(e){return parseInt(e);});
532      
533       // all hidden...
534       var hidden = DTA_preferences.getDTA("toolshidden", false);
535       for (var i in this.tools) {
536         this.tools[i].hidden = hidden;
537       }
538       this.toolsBase.hidden = hidden;
539       if (hidden) {
540         return;
541       }
542
543       var compact = menu.indexOf(0) != -1;
544      
545       // setup menu items
546       // show will hold those that will be shown
547       var show = [];
548      
549       if (menu[0]) {
550         show.push('DTA');
551       }
552       if (menu[1]) {
553         show.push('TDTA');
554       }
555       // prefs
556       if (menu[2]) {
557         show.push('Manager');
558       }
559       this.toolsSep.hidden = menu.indexOf(0) == -1;
560       this.toolsBase.setAttribute('label', this.getString(menu.indexOf(1) != -1 ? 'moredtatools' : 'simpledtatools'));
561    
562       // show the items.
563       for (var i in this.tools) {
564         var cur = this.tools[i];
565         if (show.indexOf(i) == -1) {
566           this.toolsMenu.insertBefore(cur, this.toolsSep);
567         }
568         else {
569           this.toolsBase.parentNode.insertBefore(cur, this.toolsBase);
570         }
571       }
572     } catch(ex) {
573       DTA_debug.log("DTATools(): ", ex);
574     }
575   },
576  
577   extractDescription: function(child) {
578     var rv = "";
579     try {
580       var fmt = function(s) {
581         try {
582           return s.replace(/(\n){1,}/gi, " ").replace(/(\s){2,}/gi, " ") + " ";
583         } catch (ex) { /* no-op */ }
584         return "";
585       };
586       for (var i = 0, e = child.childNodes.length; i < e; ++i) {
587         var c = child.childNodes[i];
588
589         if (c.nodeValue && c.nodeValue != "") {
590           rv += fmt(c.nodeValue);
591         }
592
593         if (c.nodeType == 1) {
594           rv += this.extractDescription(c);
595         }
596
597         if (c.hasAttribute)
598         {
599           if (c.hasAttribute('title')) {
600             rv += fmt(c.getAttribute('title'));
601           }
602           else if (c.hasAttribute('alt')) {
603             rv += fmt(c.getAttribute('alt'));
604           }
605         }
606       }
607     }
608     catch(ex) {
609       DTA_debug.log('extractDescription', ex);
610     }
611     return this.trim(rv);
612   }
613 }
614
615 addEventListener("load", function() {DTA_ContextOverlay.init();}, false);
Note: See TracBrowser for help on using the browser.