]> git.donarmstrong.com Git - dactyl.git/blob - common/content/statusline.js
Import 1.0b7.1 supporting Firefox up to 8.*
[dactyl.git] / common / content / statusline.js
1 // Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
2 // Copyright (c) 2007-2011 by Doug Kearns <dougkearns@gmail.com>
3 // Copyright (c) 2008-2011 by Kris Maglione <maglione.k@gmail.com>
4 //
5 // This work is licensed for reuse under an MIT license. Details are
6 // given in the LICENSE.txt file included with this file.
7 "use strict";
8
9 /** @scope modules */
10
11 var StatusLine = Module("statusline", {
12     init: function init() {
13         this._statusLine = document.getElementById("status-bar");
14         this.statusBar = document.getElementById("addon-bar") || this._statusLine;
15         this.statusBar.collapsed = true;
16         this.baseGroup = this.statusBar == this._statusLine ? "StatusLine " : "";
17
18         if (this.statusBar.localName == "toolbar") {
19             styles.system.add("addon-bar", config.styleableChrome, <css><![CDATA[
20                 #status-bar { margin-top: 0 !important; }
21                 #addon-bar > statusbar { -moz-box-flex: 1 }
22                 #addon-bar > #addonbar-closebutton { visibility: collapse; }
23                 #addon-bar > xul|toolbarspring { visibility: collapse; }
24             ]]></css>);
25
26             util.overlayWindow(window, { append: <><statusbar id="status-bar" ordinal="0"/></> });
27
28             highlight.loadCSS(util.compileMacro(<![CDATA[
29                 !AddonBar;#addon-bar {
30                     /* The Add-on Bar */
31                     padding-left: 0 !important;
32                     min-height: 18px !important;
33                     -moz-appearance: none !important;
34                     <padding>
35                 }
36                 !AddonButton;#addon-bar xul|toolbarbutton {
37                     /* An Add-on Bar button */
38                     -moz-appearance: none !important;
39                     padding: 0 !important;
40                     border-width: 0px !important;
41                     min-width: 0 !important;
42                     color: inherit !important;
43                 }
44                 AddonButton:not(:hover)  background: transparent;
45             ]]>)({ padding: util.OS.isMacOSX ? "padding-right: 10px !important;" : "" }));
46
47             if (document.getElementById("appmenu-button"))
48                 highlight.loadCSS(<![CDATA[
49                     AppmenuButton       /* The app-menu button */ \
50                                         min-width: 0 !important; padding: 0 .5em !important;
51                 ]]>);
52         }
53
54         XML.ignoreWhitespace = true;
55         let _commandline = "if (window.dactyl) return dactyl.modules.commandline";
56         let prepend = <e4x xmlns={XUL} xmlns:dactyl={NS}>
57             <button id="appmenu-button" label="" image="chrome://branding/content/icon16.png" highlight="AppmenuButton" />
58             <toolbarbutton id="appmenu-toolbar-button" label="" image="chrome://branding/content/icon16.png" />
59             <statusbar id="status-bar" highlight="StatusLine">
60                 <!-- insertbefore="dactyl.statusBefore;" insertafter="dactyl.statusAfter;" -->
61                 <hbox key="container" hidden="false" align="center"  flex="1">
62                     <stack orient="horizontal"       align="stretch" flex="1" highlight="CmdLine StatusCmdLine" class="dactyl-container">
63                         <hbox                                                 highlight="CmdLine StatusCmdLine" class="dactyl-container">
64                             <label key="mode"          crop="end"                                               class="plain" collapsed="true"/>
65                             <stack  id="dactyl-statusline-stack"     flex="1" highlight="CmdLine StatusCmdLine" class="dactyl-container">
66                                 <textbox key="url"     crop="end"    flex="1" style="background: transparent;"  class="plain dactyl-status-field-url" readonly="true"/>
67                                 <textbox key="message" crop="end"    flex="1" highlight="Normal StatusNormal"   class="plain"                         readonly="true"/>
68                             </stack>
69                         </hbox>
70                     </stack>
71                     <label class="plain" key="inputbuffer"    flex="0"/>
72                     <label class="plain" key="progress"       flex="0"/>
73                     <label class="plain" key="tabcount"       flex="0"/>
74                     <label class="plain" key="bufferposition" flex="0"/>
75                     <label class="plain" key="zoomlevel"      flex="0"/>
76                 </hbox>
77                 <!-- just hide them since other elements expect them -->
78                 <statusbarpanel id="statusbar-display"       hidden="true"/>
79                 <statusbarpanel id="statusbar-progresspanel" hidden="true"/>
80             </statusbar>
81         </e4x>;
82
83         for each (let attr in prepend..@key)
84             attr.parent().@id = "dactyl-statusline-field-" + attr;
85
86         util.overlayWindow(window, {
87             objects: this.widgets = { get status() this.container },
88             prepend: prepend.elements()
89         });
90
91         try {
92             this.security = content.document.dactylSecurity || "insecure";
93         }
94         catch (e) {}
95     },
96
97     get visible() !this.statusBar.collapsed && !this.statusBar.hidden,
98
99     signals: {
100         "browser.locationChange": function (webProgress, request, uri) {
101             let win = webProgress.DOMWindow;
102             this.status = uri;
103             this.progress = uri && win && win.dactylProgress || "";
104
105             // if this is not delayed we get the position of the old buffer
106             this.timeout(function () {
107                 this.updateBufferPosition();
108                 this.updateZoomLevel();
109             }, 500);
110         },
111         "browser.overLink": function (link) {
112             switch (options["showstatuslinks"]) {
113             case "status":
114                 this.status = link ? _("status.link", link) : buffer.uri;
115                 break;
116             case "command":
117                 if (link)
118                     dactyl.echo(_("status.link", link), commandline.FORCE_SINGLELINE);
119                 else
120                     commandline.clear();
121                 break;
122             }
123         },
124         "browser.progressChange": function onProgressChange(webProgress, request, curSelfProgress, maxSelfProgress, curTotalProgress, maxTotalProgress) {
125             if (webProgress && webProgress.DOMWindow)
126                 webProgress.DOMWindow.dactylProgress = curTotalProgress / maxTotalProgress;
127             this.progress = curTotalProgress / maxTotalProgress;
128         },
129         "browser.securityChange": function onSecurityChange(webProgress, request, state) {
130
131             if (state & Ci.nsIWebProgressListener.STATE_IS_BROKEN)
132                 this.security = "broken";
133             else if (state & Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL)
134                 this.security = "extended";
135             else if (state & Ci.nsIWebProgressListener.STATE_SECURE_HIGH)
136                 this.security = "secure";
137             else // if (state & Ci.nsIWebProgressListener.STATE_IS_INSECURE)
138                 this.security = "insecure";
139
140             if (webProgress && webProgress.DOMWindow)
141                 webProgress.DOMWindow.document.dactylSecurity = this.security;
142         },
143         "browser.stateChange": function onStateChange(webProgress, request, flags, status) {
144             if (flags & Ci.nsIWebProgressListener.STATE_START)
145                 this.progress = 0;
146             if (flags & Ci.nsIWebProgressListener.STATE_STOP) {
147                 this.progress = "";
148                 this.updateStatus();
149             }
150         },
151         "browser.statusChange": function onStatusChange(webProgress, request, status, message) {
152             this.timeout(function () {
153                 this.status = message || buffer.uri;
154             });
155         }
156     },
157
158     /**
159      * Update the status bar to indicate how secure the website is:
160      * extended - Secure connection with Extended Validation(EV) certificate.
161      * secure -   Secure connection with valid certificate.
162      * broken -   Secure connection with invalid certificate, or
163      *            mixed content.
164      * insecure - Insecure connection.
165      *
166      * @param {'extended'|'secure'|'broken'|'insecure'} type
167      */
168     set security(type) {
169         this._security = type;
170         const highlightGroup = {
171             extended: "StatusLineExtended",
172             secure:   "StatusLineSecure",
173             broken:   "StatusLineBroken",
174             insecure: "StatusLineNormal"
175         };
176
177         highlight.highlightNode(this.statusBar, this.baseGroup + highlightGroup[type]);
178     },
179     get security() this._security,
180
181     // update all fields of the statusline
182     update: function update() {
183         this.updateStatus();
184         this.inputBuffer = "";
185         this.progress = "";
186         this.updateTabCount();
187         this.updateBufferPosition();
188         this.updateZoomLevel();
189     },
190
191     unsafeURI: deprecated("util.unsafeURI", { get: function unsafeURI() util.unsafeURI }),
192     losslessDecodeURI: deprecated("util.losslessDecodeURI", function losslessDecodeURI() util.losslessDecodeURI.apply(util, arguments)),
193
194     /**
195      * Update the URL displayed in the status line. Also displays status
196      * icons, [+-♥], when there are next and previous pages in the
197      * current tab's history, and when the current URL is bookmarked,
198      * respectively.
199      *
200      * @param {string} url The URL to display.
201      */
202     get status() this._uri,
203     set status(uri) {
204         let modified = "";
205         let url = uri;
206         if (isinstance(uri, Ci.nsIURI)) {
207             // when session information is available, add [+] when we can go
208             // backwards, [-] when we can go forwards
209             if (uri.equals(buffer.uri) && window.getWebNavigation) {
210                 let sh = window.getWebNavigation().sessionHistory;
211                 if (sh && sh.index > 0)
212                     modified += "-";
213                 if (sh && sh.index < sh.count - 1)
214                     modified += "+";
215                 if (this.bookmarked)
216                     modified += UTF8("❤");
217             }
218
219             if (modules.quickmarks)
220                 modified += quickmarks.find(uri.spec.replace(/#.*/, "")).join("");
221
222             url = util.losslessDecodeURI(uri.spec);
223         }
224
225         if (url == "about:blank") {
226             if (!buffer.title)
227                 url = _("buffer.noName");
228         }
229         else {
230             url = url.replace(RegExp("^dactyl://help/(\\S+)#(.*)"), function (m, n1, n2) n1 + " " + decodeURIComponent(n2) + " " + _("buffer.help"))
231                      .replace(RegExp("^dactyl://help/(\\S+)"), "$1 " + _("buffer.help"));
232         }
233
234         if (modified)
235             url += " [" + modified + "]";
236
237         this.widgets.url.value = url;
238         this._status = uri;
239     },
240
241     get bookmarked() this._bookmarked,
242     set bookmarked(val) {
243         this._bookmarked = val;
244         if (this.status)
245             this.status = this.status;
246     },
247
248     updateStatus: function updateStatus() {
249         this.timeout(function () {
250             this.status = buffer.uri;
251         });
252     },
253
254     updateUrl: deprecated("statusline.status", function updateUrl(url) { this.status = url || buffer.uri }),
255
256     /**
257      * Set the contents of the status line's input buffer to the given
258      * string. Used primarily when a key press requires further input
259      * before being processed, including mapping counts and arguments,
260      * along with multi-key mappings.
261      *
262      * @param {string} buffer
263      * @optional
264      */
265     get inputBuffer() this.widgets.inputbuffer.value,
266     set inputBuffer(val) this.widgets.inputbuffer.value = val == null ? "" : val,
267     updateInputBuffer: deprecated("statusline.inputBuffer", function updateInputBuffer(val) { this.inputBuffer = val; }),
268
269     /**
270      * Update the page load progress bar.
271      *
272      * @param {string|number} progress The current progress, as follows:
273      *    A string          - Displayed literally.
274      *    A ratio 0 < n < 1 - Displayed as a progress bar.
275      *    A number n <= 0   - Displayed as a "Loading" message.
276      *    Any other number  - The progress is cleared.
277      */
278     progress: Modes.boundProperty({
279         get: function progress() this._progress,
280         set: function progress(progress) {
281             this._progress = progress || "";
282
283             if (isinstance(progress, ["String", _]))
284                 this.widgets.progress.value = this._progress;
285             else if (typeof progress == "number") {
286                 let progressStr = "";
287                 if (this._progress <= 0)
288                     progressStr = /*L*/"[ Loading...         ]";
289                 else if (this._progress < 1) {
290                     let progress = Math.round(this._progress * 20);
291                     progressStr = "["
292                         + "===================>                    "
293                             .substr(20 - progress, 20)
294                         + "]";
295                 }
296                 this.widgets.progress.value = progressStr;
297             }
298         }
299     }),
300     updateProgress: deprecated("statusline.progress", function updateProgress(progress) {
301         this.progress = progress;
302     }),
303
304     /**
305      * Display the correct tabcount (e.g., [1/5]) on the status bar.
306      *
307      * @param {boolean} delayed When true, update count after a brief timeout.
308      *     Useful in the many cases when an event that triggers an update is
309      *     broadcast before the tab state is fully updated.
310      * @optional
311      */
312     updateTabCount: function updateTabCount(delayed) {
313         if (dactyl.has("tabs")) {
314             if (delayed) {
315                 this.timeout(function () this.updateTabCount(false), 0);
316                 return;
317             }
318
319             this.widgets.tabcount.value = "[" + (tabs.index(null, true) + 1) + "/" + tabs.visibleTabs.length + "]";
320         }
321     },
322
323     /**
324      * Display the main content's vertical scroll position in the status
325      * bar.
326      *
327      * @param {number} percent The position, as a percentage.
328      * @optional
329      */
330     updateBufferPosition: function updateBufferPosition(percent) {
331         if (percent == null) {
332             let win = document.commandDispatcher.focusedWindow;
333             if (!win)
334                 return;
335             win.scrollY; // intentional - see Kris
336             percent = win.scrollY    == 0 ?  0 : // This prevents a forced rendering
337                       win.scrollMaxY == 0 ? -1 : win.scrollY / win.scrollMaxY;
338         }
339
340         percent = Math.round(percent * 100);
341
342         if (percent < 0)
343             var position = "All";
344         else if (percent == 0)
345             position = "Top";
346         else if (percent >= 100)
347             position = "Bot";
348         else if (percent < 10)
349             position = " " + percent + "%";
350         else
351             position = percent + "%";
352
353         this.widgets.bufferposition.value = position;
354     },
355
356     /**
357      * Display the main content's zoom level.
358      *
359      * @param {number} percent The zoom level, as a percentage. @optional
360      * @param {boolean} full True if full zoom is in operation. @optional
361      */
362     updateZoomLevel: function updateZoomLevel(percent, full) {
363         if (arguments.length == 0)
364             [percent, full] = [buffer.zoomLevel, buffer.fullZoom];
365
366         if (percent == 100)
367             this.widgets.zoomlevel.value = "";
368         else {
369             percent = ("  " + Math.round(percent)).substr(-3);
370             if (full)
371                 this.widgets.zoomlevel.value = " [" + percent + "%]";
372             else
373                 this.widgets.zoomlevel.value = " (" + percent + "%)";
374         }
375     }
376 });
377
378 // vim: set fdm=marker sw=4 ts=4 et: