Version 0.7.0
[minimalist.git] / content / minimalist.js
blobe7ade8f85337bb779c4e32e6ad17bc808365cfd0
1 // Providing shortcuts for many otherwise lengthy XPCOM interfaces and constants
2 const PREF_STRING = Components.interfaces.nsIPrefBranch.PREF_STRING
3 const nsIPrefBranch = Components.interfaces.nsIPrefBranch
4 const nsIPromptService = Components.interfaces.nsIPromptService
5 const nsIURI = Components.interfaces.nsIURI
7 // Generic constants
8 const settings = Components.classes['@zelgadis.jp/minimalist;1'].getService().wrappedJSObject
9 const preferenceManager = Components.classes['@mozilla.org/preferences-service;1'].getService(nsIPrefBranch)
10 const promptService = Components.classes['@mozilla.org/embedcomp/prompt-service;1'].getService(nsIPromptService)
11 const sourceURL = Components.classes['@mozilla.org/network/standard-url;1'].getService(nsIURI)
12 const destinationURL = sourceURL.clone()
14 // Various regular expressions. They are automatically compiled once on startup due to the literal notation
15 const extractRefreshURL = /url=(.+)/i
18 /* Purpose: Gets called for every new document and does everything we can't do within the XPCOM code
20 const onDocumentLoaded = function(event) {
21 // This sub-function scans for meta refresh tags and immediately follows their target
22 if (settings.skipRefresh) {
23 var metaTags = event.target.getElementsByTagName('meta')
25 for each (var metaTag in metaTags) {
26 if (metaTag.httpEquiv.toLowerCase() == 'refresh') {
27 if (match = extractRefreshURL.exec(metaTag.content)) {
28 sourceURL.spec = event.target.location.href
29 destinationURL.spec = srcURL.resolve(match[1])
31 // Make sure we're not loading this document again, else we'd end up in an infinite loop
32 if (!sourceURL.equals(destinationURL)) {
33 event.target.location.replace(dstURL.spec) } } } } } }
35 getBrowser().addEventListener('DOMContentLoaded', onDocumentLoaded, false)
38 /* Purpose: Gets called every time the user clicks on the statusbar panel and once on startup.
39 * It acts differently depending on which mouse button was clicked
41 const onButtonClicked = function(self, button, behavior) {
42 switch (button) {
43 // Filter level button
44 case 0:
45 var cycleAmount = 1
46 switch (behavior) {
47 case 4:
48 // This is used once at startup. It doesn't change the filter level, but sets the icon
49 cycleAmount = 0
51 case 0:
52 // Left mouse button was clicked. Cycle to the next filter level
53 preferenceManager.setIntPref('extensions.minimalist.level', (settings.level + cycleAmount) & 3)
54 self.src = 'chrome://minimalist/content/L' + settings.level + '.png'
55 break
57 case 2:
58 // Right mouse button was clicked. Show a list of blocked sites for this tab
59 var popupMenu = document.getElementById('minimalist-filter-popup')
61 // Remove all previous entries
62 while (popupMenu.lastChild) {
63 popupMenu.removeChild(popupMenu.lastChild) }
65 // <\Skip redirection delays>: Insert into menu list
66 var settingsItem1 = document.createElement('menuitem')
67 settingsItem1.onclick = onChangeSetting
68 settingsItem1.setAttribute('label', 'Skip redirection delays')
69 settingsItem1.setAttribute('closemenu', 'none')
70 settingsItem1.setAttribute('type', 'checkbox')
71 settingsItem1.setAttribute('checked', settings.skipRefresh)
73 // <\Global embed whitelist>: Insert into menu list
74 var settingsMenu1 = document.createElement('menu')
75 var settingsPopup1 = document.createElement('menupopup')
76 settingsMenu1.setAttribute('label', 'Global embed whitelist')
78 // <\Whitelisted domains>: Insert into menu list
79 var settingsMenu2 = document.createElement('menu')
80 var settingsPopup2 = document.createElement('menupopup')
81 settingsMenu2.setAttribute('label', 'Whitelisted domains')
83 // <\Blocked resources>: Insert into menu list
84 var settingsMenu3 = document.createElement('menu')
85 var settingsPopup3 = document.createElement('menupopup')
86 settingsMenu3.setAttribute('label', 'Blocked resources')
88 // <\Blocked resources>: Prepare deeper menu level
89 var currentWindow = gBrowser.selectedBrowser.contentWindow.location.href
90 if (currentWindow in settings.blockedSites) {
91 var destinationHosts = settings.blockedSites[currentWindow]
92 for (var destinationHost in destinationHosts) {
93 // <\Blocked resources\[hosts]>: Build contents
94 var menuForHost = document.createElement('menu')
95 menuForHost.setAttribute('label', destinationHost)
97 // <\Blocked resources\[hosts]>: Prepare deeper menu level
98 var menuForPaths = document.createElement('menupopup')
99 var destinationPaths = destinationHosts[destinationHost]
100 for (var destinationPath in destinationPaths) {
101 var blockedResource = destinationPaths[destinationPath]
103 // <\Blocked resources\[hosts]\[paths]>: Build contents
104 var pathMenuItem = document.createElement('menuitem')
105 pathMenuItem.onclick = onChangeWhitelistEntry
106 pathMenuItem.setUserData( 'responsibleTag', blockedResource.responsibleTag, null)
107 pathMenuItem.setUserData( 'sourceDomain', blockedResource.sourceDomain, null)
108 pathMenuItem.setUserData('destinationDomain', blockedResource.destinationDomain, null)
109 pathMenuItem.setAttribute('label',
110 '[L' + blockedResource.filterLevel + ':' + blockedResource.responsibleTag + '] ' +
111 destinationPath)
113 menuForPaths.appendChild(pathMenuItem) }
114 menuForHost.appendChild(menuForPaths)
115 settingsPopup3.appendChild(menuForHost) } }
117 // <\Blocked resources\Empty>: Insert into menu list (if necessary)
118 if (!settingsPopup3.firstChild) {
119 var emptyMenuItem = document.createElement('menuitem')
120 emptyMenuItem.setAttribute('label', 'Empty')
121 emptyMenuItem.setAttribute('disabled', true)
122 settingsPopup3.appendChild(emptyMenuItem) }
124 // <\Global embed whitelist>: Prepare deeper menu level
125 var settingPath ='extensions.minimalist.embed'
126 var whiteList = preferenceManager.getCharPref(settingPath).split(',')
127 if (whiteList.length > 1 || whiteList[0] != '') {
128 // <\Global embed whitelist\[domains]>: Build contents
129 for each (destinationDomain in whiteList) {
130 var listMenuItem = document.createElement('menuitem')
131 listMenuItem.onclick = onChangeWhitelistEntry
132 listMenuItem.setUserData('domainList', whiteList, null)
133 listMenuItem.setUserData('settingPath', settingPath, null)
134 listMenuItem.setAttribute('label', destinationDomain)
135 listMenuItem.setAttribute('value', '<embeds>')
136 settingsPopup1.appendChild(listMenuItem) } }
137 else {
138 // <\Global embed whitelist\Empty>: Insert into menu list
139 var listMenuItem = document.createElement('menuitem')
140 listMenuItem.setAttribute('label', 'Empty')
141 listMenuItem.setAttribute('disabled', true)
142 settingsPopup1.appendChild(listMenuItem) }
144 // <\Whitelisted domains>: Prepare deeper menu level
145 var currentDomain = settings.getDomainName(currentWindow)
146 settingPath = 'extensions.minimalist.whitelist.' + currentDomain
147 if (preferenceManager.getPrefType(settingPath) == PREF_STRING) {
148 whiteList = preferenceManager.getCharPref(settingPath).split(',')
150 // <\Whitelisted domains\[domains]>: Build contents
151 if (whiteList.length > 1 || whiteList[0] != '') {
152 for each (destinationDomain in whiteList) {
153 var listMenuItem = document.createElement('menuitem')
154 listMenuItem.onclick = onChangeWhitelistEntry
155 listMenuItem.setUserData('domainList', whiteList, null)
156 listMenuItem.setUserData('settingPath', settingPath, null)
157 listMenuItem.setAttribute('label', destinationDomain)
158 listMenuItem.setAttribute('value', currentDomain)
159 settingsPopup2.appendChild(listMenuItem) } } }
161 // <\Whitelisted domains\Empty>: Insert into menu list (if necessary)
162 if (!settingsPopup2.firstChild) {
163 var listMenuItem = document.createElement('menuitem')
164 listMenuItem.setAttribute('label', 'Empty')
165 listMenuItem.setAttribute('disabled', true)
166 settingsPopup2.appendChild(listMenuItem) }
168 // Put everything together and attach it to the XUL document popup node
169 settingsMenu1.appendChild(settingsPopup1)
170 settingsMenu2.appendChild(settingsPopup2)
171 settingsMenu3.appendChild(settingsPopup3)
172 popupMenu.appendChild(settingsItem1)
173 popupMenu.appendChild(settingsMenu1)
174 popupMenu.appendChild(settingsMenu2)
175 popupMenu.appendChild(settingsMenu3)
176 self.parentNode.appendChild(popupMenu)
178 // Display the popup
179 popupMenu.openPopup(self, 'before_end', 0, 0, true, false)
180 break }
181 break
183 // Cookie Button
184 case 1:
185 break } }
188 /* Purpose: Gets called when a menu entry in the blocked sites or whitelisted domains popup list is clicked.
189 * Asks the user whether she wants to add/remove it to/from the whitelist and acts accordingly
191 const onChangeWhitelistEntry = function(event) {
192 if (event.target.value) {
193 // This mode removes a domain from the whitelist. Only left button mouse clicks are processed here
194 if (event.button == 0) {
195 var domainList = event.target.getUserData('domainList')
196 var settingPath = event.target.getUserData('settingPath')
198 // Find and remove the clicked domain name from the whitelist
199 for (var index in domainList) {
200 if (domainList[index] == event.target.label) {
201 domainList.splice(index, 1)
202 break } }
204 // Make a question
205 var queryText = 'Remove domain <' + event.target.label + '> from '
206 if (event.target.value == '<embeds>') {
207 queryText = queryText + 'the global embed whitelist?' }
208 else {
209 queryText = queryText + '<' + event.target.value + '>\'s whitelist?' }
211 // For convenience, automatically reloads the tab's content after rewriting the whitelist
212 if (promptService.confirm(null, 'Confirm your choice', queryText)) {
213 if (domainList.length == 0 || domainList[0] == '') {
214 preferenceManager.clearUserPref(settingPath)
215 if (preferenceManager.getPrefType(settingPath) == PREF_STRING) {
216 preferenceManager.setCharPref(settingPath, '') } }
217 else {
218 preferenceManager.setCharPref(settingPath, domainList.join(',')) }
219 gBrowser.selectedBrowser.contentWindow.location.reload() } } }
220 else {
221 // This mode adds a domain to the whitelist
222 var currentDomain = event.target.getUserData('sourceDomain')
223 var domainToAdd = event.target.getUserData('destinationDomain')
225 // Make a question
226 var settingPath, queryText = 'Add domain <' + domainToAdd + '> to '
227 if (event.button == 2) {
228 var responsibleTag = event.target.getUserData('responsibleTag')
229 if (responsibleTag in settings.embedTagClass) {
230 settingPath = 'extensions.minimalist.embed'
231 queryText = queryText + 'the global embed whitelist?' }
232 else {
233 return } }
234 else {
235 settingPath = 'extensions.minimalist.whitelist.' + currentDomain
236 queryText = queryText + '<' + currentDomain + '>\'s whitelist?' }
238 // Append the new domain to the existing list of domains - or create a new list if it doesn't exist
239 var currentList = ''
240 if (preferenceManager.getPrefType(settingPath) == PREF_STRING) {
241 currentList = currentList + preferenceManager.getCharPref(settingPath)
242 if (currentList != '') {
243 currentList = currentList + ',' } }
244 currentList = currentList + domainToAdd
246 // For convenience, automatically reloads the tab's content after adding the whitelist entry
247 if (promptService.confirm(null, 'Confirm your choice', queryText)) {
248 preferenceManager.setCharPref(settingPath, currentList)
249 gBrowser.selectedBrowser.contentWindow.location.reload() } } }
252 /* Purpose: Gets called when the user clicks a menu item for changing a setting
254 const onChangeSetting = function(event) {
255 if (event.target.getAttribute('type')) {
256 // This mode simply toggles skipRefresh. The menu item is toggled automatically
257 var settingPath = 'extensions.minimalist.skipRefresh'
258 var newValue = !preferenceManager.getBoolPref(settingPath)
260 preferenceManager.setBoolPref(settingPath, newValue) } }
263 /* Purpose: These two functions delete all blocked sites entries for a tab when they become invalid
265 const onLocationChange = function(browser) {
266 delete settings.blockedSites[browser.contentWindow.location.href] }
268 const onTabClosed = function(event) {
269 delete settings.blockedSites[event.target.linkedBrowser.contentWindow.location.href] }
272 /* Purpose: Gets called once the XUL overlay is loaded. It sets up everything we need for our statusbar panel
274 const onOverlayLoaded = function() {
275 // Track every location change in a tab so we can clear its current blocked sites list
276 gBrowser.addTabsProgressListener({
277 onLocationChange: onLocationChange,
278 onStateChange: function() {},
279 onProgressChange: function() {},
280 onStatusChange: function() {},
281 onSecurityChange: function() {}})
283 // Closing the tab also makes its blocked sites list invalid
284 gBrowser.tabContainer.addEventListener('TabClose', onTabClosed, false)
286 // Set initial values for statusbar panel
287 onButtonClicked(document.getElementById('minimalist-filterLevel'), 0, 4) }
289 window.addEventListener('load', onOverlayLoaded, false)