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-2014 Kris Maglione <maglione.k@gmail.com>
5 // This work is licensed for reuse under an MIT license. Details are
6 // given in the LICENSE.txt file included with this file.
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;
16 this.baseGroup = this.statusBar == this._statusLine ? "StatusLine " : "";
18 if (this.statusBar.localName == "toolbar" &&
19 this.statusBar.parentNode.id != "browser-bottombox")
20 overlay.overlayWindow(window, {
23 ["vbox", { id: "browser-bottombox", xmlns: "xul" },
24 ["toolbar", { id: "dactyl-addon-bar",
27 toolboxid: "navigator-toolbox",
28 toolbarname: /*L*/ "Add-on Bar",
29 class: "toolbar-primary chromeclass-toolbar",
31 iconsize: "small", defaulticonsize: "small",
33 ["statusbar", { id: "dactyl-status-bar", key: "_statusLine" }]]]
37 if (config.haveGecko("25"))
38 config.tabbrowser.getStatusPanel().hidden = true;
40 if (this.statusBar.localName == "toolbar") {
41 styles.system.add("addon-bar", config.styleableChrome, literal(/*
42 #status-bar, #dactyl-status-bar { margin-top: 0 !important; }
43 #dactyl-status-bar { min-height: 0 !important; }
44 :-moz-any(#addon-bar, #dactyl-addon-bar) > statusbar { -moz-box-flex: 1 }
45 :-moz-any(#addon-bar, #dactyl-addon-bar) > xul|toolbarspring { visibility: collapse; }
46 #addon-bar > #addonbar-closebutton { visibility: collapse; }
49 overlay.overlayWindow(window, {
51 ["statusbar", { id: this._statusLine.id, ordinal: "0" }]]
54 highlight.loadCSS(util.compileMacro(literal(/*
55 !AddonBar;#addon-bar,#dactyl-addon-bar {
56 padding-left: 0 !important;
57 padding-top: 0 !important;
58 padding-bottom: 0 !important;
59 min-height: 18px !important;
60 -moz-appearance: none !important;
63 !AddonButton;,:-moz-any(#addon-bar, #dactyl-addon-bar) xul|toolbarbutton {
64 -moz-appearance: none !important;
65 padding: 0 !important;
66 border-width: 0px !important;
67 min-width: 0 !important;
68 color: inherit !important;
70 AddonButton:not(:hover) background: transparent;
71 */))({ padding: config.OS.isMacOSX ? "padding-right: 10px !important;" : "" }));
73 if (document.getElementById("appmenu-button"))
74 highlight.loadCSS(literal(/*
75 AppmenuButton min-width: 0 !important; padding: 0 .5em !important;
79 let _commandline = "if (window.dactyl) return dactyl.modules.commandline";
81 ["button", { id: "appmenu-button", label: "", image: "chrome://branding/content/icon16.png", highlight: "AppmenuButton", xmlns: "xul" }],
82 ["toolbarbutton", { id: "appmenu-toolbar-button", label: "", image: "chrome://branding/content/icon16.png" }],
83 ["statusbar", { id: this._statusLine.id, highlight: "StatusLine", xmlns: "xul" },
84 // <!-- insertbefore="dactyl.statusBefore;" insertafter="dactyl.statusAfter;" -->
85 ["hbox", { key: "container", hidden: "false", align: "center", flex: "1" },
86 ["stack", { orient: "horizontal", align: "stretch", flex: "1", highlight: "CmdLine StatusCmdLine", class: "dactyl-container" },
87 ["hbox", { highlight: "CmdLine StatusCmdLine", class: "dactyl-container" },
88 ["label", { key: "mode", crop: "end", class: "plain", collapsed: "true" }],
89 ["stack", { id: "dactyl-statusline-stack", flex: "1", highlight: "CmdLine StatusCmdLine", class: "dactyl-container" },
90 ["textbox", { key: "url", crop: "end", flex: "1", style: "background: transparent;", class: "plain dactyl-status-field-url",
92 ["hbox", { key: "message-box" },
93 ["label", { key: "message-pre", highlight: "WarningMsg StatusWarningMsg", class: "plain", readonly: "true" }],
94 ["textbox", { key: "message", crop: "end", flex: "1", highlight: "Normal StatusNormal", class: "plain",
95 readonly: "true" }]]]]],
96 ["label", { class: "plain", key: "inputbuffer", flex: "0" }],
97 ["label", { class: "plain", key: "progress", flex: "0" }],
98 ["label", { class: "plain", key: "tabcount", flex: "0" }],
99 ["label", { class: "plain", key: "bufferposition", flex: "0" }],
100 ["label", { class: "plain", key: "zoomlevel", flex: "0" }]],
101 // just hide them since other elements expect them
102 ["statusbarpanel", { id: "statusbar-display", hidden: "true" }],
103 ["statusbarpanel", { id: "statusbar-progresspanel", hidden: "true" }]]];
106 ary.forEach(function (elem) {
107 if ("key" in elem[1])
108 elem[1].id = "dactyl-statusline-field-" + elem[1].key;
114 overlay.overlayWindow(window, {
115 objects: this.widgets = { get status() this.container },
120 this.security = content.document.dactylSecurity || "insecure";
125 cleanup: function cleanup(reason) {
126 if (reason != "unload" && "CustomizableUI" in window)
127 CustomizableUI.unregisterArea(this.statusBar.id, false);
130 get visible() !this.statusBar.collapsed && !this.statusBar.hidden,
133 "browser.locationChange": function (webProgress, request, uri) {
134 let win = webProgress.DOMWindow;
136 this.progress = uri && win && win.dactylProgress || "";
138 // if this is not delayed we get the position of the old buffer
139 this.timeout(function () {
140 this.updateBufferPosition();
141 this.updateZoomLevel();
144 "browser.overLink": function (link) {
145 switch (options["showstatuslinks"]) {
147 this.overLink = link ? _("status.link", link) : null;
148 this.status = link ? _("status.link", link) : buffer.uri;
151 this.overLink = null;
153 dactyl.echo(_("status.link", link), commandline.FORCE_SINGLELINE);
159 "browser.progressChange": function onProgressChange(webProgress, request, curSelfProgress, maxSelfProgress, curTotalProgress, maxTotalProgress) {
160 if (webProgress && webProgress.DOMWindow)
161 webProgress.DOMWindow.dactylProgress = curTotalProgress / maxTotalProgress;
162 this.progress = curTotalProgress / maxTotalProgress;
164 "browser.securityChange": function onSecurityChange(webProgress, request, state) {
166 if (state & Ci.nsIWebProgressListener.STATE_IS_BROKEN)
167 this.security = "broken";
168 else if (state & Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL)
169 this.security = "extended";
170 else if (state & Ci.nsIWebProgressListener.STATE_SECURE_HIGH)
171 this.security = "secure";
172 else // if (state & Ci.nsIWebProgressListener.STATE_IS_INSECURE)
173 this.security = "insecure";
175 if (webProgress && webProgress.DOMWindow)
176 webProgress.DOMWindow.document.dactylSecurity = this.security;
178 "browser.stateChange": function onStateChange(webProgress, request, flags, status) {
179 const L = Ci.nsIWebProgressListener;
181 if (flags & (L.STATE_IS_DOCUMENT | L.STATE_IS_WINDOW))
182 if (flags & L.STATE_START)
184 else if (flags & L.STATE_STOP)
187 if (flags & L.STATE_STOP)
190 "browser.statusChange": function onStatusChange(webProgress, request, status, message) {
191 this.timeout(function () {
192 this.status = message || buffer.uri;
195 "fullscreen": function onFullscreen(fullscreen) {
196 let go = options.get("guioptions");
198 this.wasVisible = go.has("s");
201 else if (this.wasVisible) {
208 * Update the status bar to indicate how secure the website is:
209 * extended - Secure connection with Extended Validation(EV) certificate.
210 * secure - Secure connection with valid certificate.
211 * broken - Secure connection with invalid certificate, or
213 * insecure - Insecure connection.
215 * @param {'extended'|'secure'|'broken'|'insecure'} type
218 this._security = type;
219 const highlightGroup = {
220 extended: "StatusLineExtended",
221 secure: "StatusLineSecure",
222 broken: "StatusLineBroken",
223 insecure: "StatusLineNormal"
226 highlight.highlightNode(this.statusBar, this.baseGroup + highlightGroup[type]);
228 get security() this._security,
230 // update all fields of the statusline
231 update: function update() {
233 this.inputBuffer = "";
235 this.updateTabCount();
236 this.updateBufferPosition();
237 this.updateZoomLevel();
240 unsafeURI: deprecated("util.unsafeURI", { get: function unsafeURI() util.unsafeURI }),
241 losslessDecodeURI: deprecated("util.losslessDecodeURI", function losslessDecodeURI() util.losslessDecodeURI.apply(util, arguments)),
244 * Update the URL displayed in the status line. Also displays status
245 * icons, [+-♥], when there are next and previous pages in the
246 * current tab's history, and when the current URL is bookmarked,
249 * @param {string} url The URL to display.
251 get status() this._uri,
255 if (isinstance(uri, Ci.nsIURI)) {
256 // when session information is available, add [+] when we can go
257 // backwards, [-] when we can go forwards
258 if (uri.equals(buffer.uri) && window.getWebNavigation) {
259 let sh = window.getWebNavigation().sessionHistory;
260 if (sh && sh.index > 0)
262 if (sh && sh.index < sh.count - 1)
265 modified += UTF8("❤");
268 if (modules.quickmarks)
269 modified += quickmarks.find(uri.spec.replace(/#.*/, "")).join("");
271 url = util.losslessDecodeURI(uri.spec);
274 if (url == "about:blank") {
276 url = _("buffer.noName");
279 url = url.replace(RegExp("^dactyl://help/(\\S+)#(.*)"), (m, n1, n2) => n1 + " " + decodeURIComponent(n2) + " " + _("buffer.help"))
280 .replace(RegExp("^dactyl://help/(\\S+)"), "$1 " + _("buffer.help"));
284 url += " [" + modified + "]";
286 this.widgets.url.value = url;
290 get bookmarked() this._bookmarked,
291 set bookmarked(val) {
292 this._bookmarked = val;
294 this.status = this.status;
297 updateStatus: function updateStatus() {
298 this.timeout(function () {
299 this.status = this.overLink || buffer.uri;
303 updateUrl: deprecated("statusline.status", function updateUrl(url) { this.status = url || buffer.uri; }),
306 * Set the contents of the status line's input buffer to the given
307 * string. Used primarily when a key press requires further input
308 * before being processed, including mapping counts and arguments,
309 * along with multi-key mappings.
311 * @param {string} buffer
314 get inputBuffer() this.widgets.inputbuffer.value,
315 set inputBuffer(val) this.widgets.inputbuffer.value = val == null ? "" : val,
316 updateInputBuffer: deprecated("statusline.inputBuffer", function updateInputBuffer(val) { this.inputBuffer = val; }),
319 * Update the page load progress bar.
321 * @param {string|number} progress The current progress, as follows:
322 * A string - Displayed literally.
323 * A ratio 0 < n < 1 - Displayed as a progress bar.
324 * A number n <= 0 - Displayed as a "Loading" message.
325 * Any other number - The progress is cleared.
327 progress: Modes.boundProperty({
328 get: function progress() this._progress,
329 set: function progress(progress) {
330 this._progress = progress || "";
332 if (isinstance(progress, ["String", _]))
333 this.widgets.progress.value = this._progress;
334 else if (typeof progress == "number") {
335 let progressStr = "";
336 if (this._progress <= 0)
337 progressStr = /*L*/"[ Loading... ]";
338 else if (this._progress < 1) {
339 let progress = Math.round(this._progress * 20);
341 + "===================> "
342 .substr(20 - progress, 20)
345 this.widgets.progress.value = progressStr;
349 updateProgress: deprecated("statusline.progress", function updateProgress(progress) {
350 this.progress = progress;
354 * Display the correct tabcount (e.g., [1/5]) on the status bar.
356 * @param {boolean} delayed When true, update count after a brief timeout.
357 * Useful in the many cases when an event that triggers an update is
358 * broadcast before the tab state is fully updated.
361 updateTabCount: function updateTabCount(delayed) {
362 if (dactyl.has("tabs")) {
364 this.timeout(() => { this.updateTabCount(false); }, 0);
368 this.widgets.tabcount.value = "[" + (tabs.index(null, true) + 1) + "/" + tabs.visibleTabs.length + "]";
373 * Display the main content's vertical scroll position in the status
376 * @param {number} percent The position, as a percentage.
379 updateBufferPosition: function updateBufferPosition(percent) {
380 if (percent == null) {
381 let win = document.commandDispatcher.focusedWindow;
384 win.scrollY; // intentional - see Kris
385 percent = win.scrollY == 0 ? 0 : // This prevents a forced rendering
386 win.scrollMaxY == 0 ? -1 : win.scrollY / win.scrollMaxY;
389 percent = Math.round(percent * 100);
392 var position = "All";
393 else if (percent == 0)
395 else if (percent >= 100)
397 else if (percent < 10)
398 position = " " + percent + "%";
400 position = percent + "%";
402 this.widgets.bufferposition.value = position;
406 * Display the main content's zoom level.
408 * @param {number} percent The zoom level, as a percentage. @optional
409 * @param {boolean} full True if full zoom is in operation. @optional
411 updateZoomLevel: function updateZoomLevel(percent=buffer.zoomLevel, full=buffer.fullZoom) {
413 this.widgets.zoomlevel.value = "";
415 percent = (" " + Math.round(percent)).substr(-3);
417 this.widgets.zoomlevel.value = " [" + percent + "%]";
419 this.widgets.zoomlevel.value = " (" + percent + "%)";
424 // vim: set fdm=marker sw=4 sts=4 ts=8 et: