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