Use 'max-height' (vs. fixed 'height') to allow partially collapsed quotes
[QuoteCollapse.git] / src / chrome / content / quotecollapse / quotecollapse.js
blobffe343807a5b23e0fc1f98cd8c977cad016f6d7c
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  * Contributor(s): Michael J Gruber  http://quotecollapse.mozdev.org/
15  *
16  * Alternatively, the contents of this file may be used under the terms of
17  * either the GNU General Public License Version 2 or later (the "GPL"), or
18  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
19  * in which case the provisions of the GPL or the LGPL are applicable instead
20  * of those above. If you wish to allow use of your version of this file only
21  * under the terms of either the GPL or the LGPL, and not to allow others to
22  * use your version of this file under the terms of the MPL, indicate your
23  * decision by deleting the provisions above and replace them with the notice
24  * and other provisions required by the GPL or the LGPL. If you do not delete
25  * the provisions above, a recipient may use your version of this file under
26  * the terms of any one of the MPL, the GPL or the LGPL.
27  *
28  * ***** END LICENSE BLOCK ***** */
31 var QuoteCollapse = {
32 //  _URIFixup : Components.classes["@mozilla.org/docshell/urifixup;1"].getService(Components.interfaces.nsIURIFixup),
33 //  _pref : Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService).getBranch(null),
36   // Taken from LinkVisitor.
37   // This function is invoked in window (?) context,
38   // so use 'QuoteCollapse' instead of 'this'.
39   // event.originalTarget is the loaded document.
41   messagePane : null,
43   onMailWindowLoad : function(event) {
44     QuoteCollapse._messagePane = document.getElementById('messagepane'); // browser parenting the document
45     // messagePane.addEventListener("click", QuoteCollapse._onClick, false); // cpould also reg. on doc
46     QuoteCollapse._messagePane.addEventListener("load", QuoteCollapse._onLoad, true); // wait for doc to be loaded
47   },
49  // this is called when loading the document; time to insert style
50   _onLoad: function(event) {
51     var messageDocument = QuoteCollapse._messagePane.contentDocument; 
52     if( ! messageDocument.getElementsByTagName("blockquote").item(0) ) return; // nothing to be done
53     messageDocument.addEventListener("click", QuoteCollapse._onClick, false);
54     messageDocument.getElementsByTagName("body").item(0).className='mailview'; // class for customizing
56     // the following is inspired by code from quotecolors
57     var StyleElement = messageDocument.createElement("style");
58     StyleElement.type = "text/css";
59     // we don't need a BODY.mailview qualifier here
60     var stylecontent='\
61 blockquote[type="cite"] {\n\
62  background-image: url("chrome://quotecollapse/skin/twisty-clsd.png");\n\
63  background-repeat: no-repeat;\n\
64  background-position: top left;\n\
65  max-height: 2.25ex;\n\
66  padding-bottom: 0px ! important;\n\
67  overflow: -moz-hidden-unscrollable;\n\
68 }\n\
69 \n\
70 blockquote[type="cite"][qctoggled="true"] {\n\
71  background-image: url("chrome://quotecollapse/skin/twisty-open.png");\n\
72  max-height: none;\n\
73  overflow: visible;\n\
74 }\n\
76     var styletext = document.createTextNode(stylecontent);
77     StyleElement.appendChild(styletext);
78     messageDocument.getElementsByTagName("head").item(0).appendChild(StyleElement);
79   },
81   _getState: function(node) {
82     return (node.getAttribute("qctoggled")=="true");
83   },
85   _setState: function(node, state) {
86     if(state)
87       node.setAttribute("qctoggled","true");
88     else
89       node.setAttribute("qctoggled","false");
90   },
92   _setSubTree: function(node, state) {
93     if(node.nodeName == 'BLOCKQUOTE')
94       QuoteCollapse._setState(node, state);
95    
96     for (var i=0; i<node.childNodes.length; i++) {
97       QuoteCollapse._setSubTree(node.childNodes.item(i), state);
98     }
99   },
101   _setSubTreeLevel: function(node, state, level) {
102     if(node.nodeName == 'BLOCKQUOTE') {
103       if(level<=0) {
104         QuoteCollapse._setState(node, state);
105         return; // no need to go deeper
106       }
107       level--; // only BQs count for the level magic
108     } 
109     for (var i=0; i<node.childNodes.length; i++) {
110       QuoteCollapse._setSubTreeLevel(node.childNodes.item(i), state, level);
111     }
112   },
114   // we could use subtree on BODY, but the following is more efficient
115   _setTree : function(doc, newstate) {
116     var tree =  doc.getElementsByTagName("blockquote");
117     for(var i=0; i<tree.length; i++)
118       QuoteCollapse._setState(tree.item(i), newstate);
119   },
120   
121   _setLevel : function(target, newstate) {
122     var level=0;
123     var node=target;
124     do {
125       node = node.parentNode;
126       if(node.nodeName == 'BLOCKQUOTE')
127         level++;
128     } while(node.nodeName != 'BODY');
129     QuoteCollapse._setSubTreeLevel(node, newstate, level); // node is the BODY element
130   },
131   
132  // this is called by a click event
133   _onClick: function(event) {
134     var target = event.target;
135     if(target.nodeName == 'SPAN')
136       target = target.parentNode; // cite-tags span?
137     if(target.nodeName == 'PRE')
138       target = target.parentNode; // PRE inside; don't walk all the way up
139     if(target.nodeName != 'BLOCKQUOTE')
140       return true;
142     var newstate= ! QuoteCollapse._getState(target);
145 // react only to active spot (leave rest for copy etc.)
146     if( (event.pageX > target.offsetLeft+12) || (event.pageY > target.offsetTop+12) ) return true;
147     
148     if(event.shiftKey)
149       if(event.ctrlKey || event.metaKey)
150         QuoteCollapse._setTree(target.ownerDocument, newstate);
151       else
152        QuoteCollapse._setSubTree(target, newstate);
153     else
154       if(event.ctrlKey || event.metaKey)
155         QuoteCollapse._setLevel(target, newstate);
156       else
157         QuoteCollapse._setState(target, newstate);
158     return true;
159   },
165 // register listener so that we can update the popup
166 // This is done from the corresponding xul 
167 // window.addEventListener("load", QuoteCollapse.onWindowLoad, true);