From 015f5bad3f6e4058d6bfeb8f6bf213de37464da7 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Sat, 20 Dec 2014 16:46:32 +0000 Subject: Exclusion; allow multiple matching rules. --- background_scripts/exclusions.coffee | 2 + pages/options.coffee | 63 +++++++--- pages/options.css | 230 +++++++++++++++++++++++++++++++++ pages/options.html | 237 +---------------------------------- pages/popup.html | 115 +++++++++-------- 5 files changed, 340 insertions(+), 307 deletions(-) create mode 100644 pages/options.css diff --git a/background_scripts/exclusions.coffee b/background_scripts/exclusions.coffee index 2b34238b..ac0e5f20 100644 --- a/background_scripts/exclusions.coffee +++ b/background_scripts/exclusions.coffee @@ -18,6 +18,8 @@ RegexpCache = # The exclusions are an array of such objects (because the order matters). root.Exclusions = Exclusions = + # Make RegexpCache, which is required on the page popup, accessible via Exclusions. + RegexpCache: RegexpCache rules: Settings.get("exclusionRules") diff --git a/pages/options.coffee b/pages/options.coffee index cd19fa37..776432d2 100644 --- a/pages/options.coffee +++ b/pages/options.coffee @@ -1,6 +1,6 @@ - $ = (id) -> document.getElementById id bgSettings = chrome.extension.getBackgroundPage().Settings +Exclusions = chrome.extension.getBackgroundPage().Exclusions # # Class hierarchy for various types of option. @@ -13,6 +13,7 @@ class Option constructor: (field,enableSaveButton) -> @field = field + @onUpdated = enableSaveButton @element = $(@field) @element.addEventListener "change", enableSaveButton @fetch() @@ -41,8 +42,10 @@ class Option # Static method. @saveOptions: -> Option.all.map (option) -> option.save() - $("saveOptions").disabled = true - $("saveOptions").innerHTML = "No Changes" + # These are only relevant on the options page; catch the exception on the popup page. + try + $("saveOptions").disabled = true + $("saveOptions").innerHTML = "No Changes" # Abstract method; only implemented in sub-classes. # Populate the option's DOM element (@element) with the setting's current value. @@ -77,8 +80,8 @@ class CheckBoxOption extends Option readValueFromElement: -> @element.checked class ExclusionRulesOption extends Option - constructor: (args...) -> - super(args...) + constructor: (field, onUpdated, @url=null) -> + super(field, onUpdated) $("exclusionAddButton").addEventListener "click", (event) => @appendRule { pattern: "", passKeys: "" } # Focus the pattern element in the new rule. @@ -91,6 +94,16 @@ class ExclusionRulesOption extends Option for rule in rules @appendRule rule + # If this is the popup page (@url is defined), then hide rules which do not match @url. If no rules + # match, then add a default rule. + if @url + haveMatch = false + for element in @element.getElementsByClassName "exclusionRuleTemplateInstance" + pattern = element.children[0].firstChild.value.trim() + unless @url.match Exclusions.RegexpCache.get pattern + element.style.display = 'none' + haveMatch = true + # Append a row for a new rule. appendRule: (rule) -> content = document.querySelector('#exclusionRuleTemplate').content @@ -100,13 +113,13 @@ class ExclusionRulesOption extends Option element = row.querySelector ".#{field}" element.value = rule[field] for event in [ "input", "change" ] - element.addEventListener event, enableSaveButton + element.addEventListener event, @onUpdated remove = row.querySelector ".exclusionRemoveButton" remove.addEventListener "click", (event) => row = event.target.parentNode.parentNode row.parentNode.removeChild row - enableSaveButton() + @onUpdated() @element.appendChild row @@ -160,12 +173,8 @@ activateHelpDialog = -> # Prevent the "show help" link from retaining the focus. document.activeElement.blur() -# -# Initialization. -document.addEventListener "DOMContentLoaded", -> - - # Populate options. The constructor adds each new object to "Option.all". - new type(name,enableSaveButton) for name, type of { +initOptions = -> + options = exclusionRules: ExclusionRulesOption filterLinkHints: CheckBoxOption hideHud: CheckBoxOption @@ -181,7 +190,10 @@ document.addEventListener "DOMContentLoaded", -> searchEngines: TextOption searchUrl: NonEmptyTextOption userDefinedLinkHintCss: TextOption - } + + # Populate options. The constructor adds each new object to "Option.all". + for name, type of options + new type(name,enableSaveButton) $("saveOptions").addEventListener "click", Option.saveOptions $("advancedOptionsLink").addEventListener "click", toggleAdvancedOptions @@ -200,3 +212,26 @@ document.addEventListener "DOMContentLoaded", -> document.activeElement.blur() if document?.activeElement?.blur Option.saveOptions() +initPopup = -> + chrome.tabs.getSelected null, (tab) -> + document.getElementById("optionsLink").setAttribute "href", chrome.runtime.getURL("pages/options.html") + updated = false + + onUpdated = -> + $("helpText").innerHTML = "Type Ctrl-Enter to save and close; Esc to cancel." + updated = true + + document.addEventListener "keyup", (event) -> + if event.ctrlKey and event.keyCode == 13 + Option.saveOptions() + window.close() + + new ExclusionRulesOption("exclusionRules", onUpdated, tab.url) + +# +# Initialization. +document.addEventListener "DOMContentLoaded", -> + switch location.pathname + when "/pages/options.html" then initOptions() + when "/pages/popup.html" then initPopup() + diff --git a/pages/options.css b/pages/options.css new file mode 100644 index 00000000..148d1330 --- /dev/null +++ b/pages/options.css @@ -0,0 +1,230 @@ +body { + font: 14px "DejaVu Sans", "Arial", sans-serif; + color: #303942; + margin: 0 auto; +} +a, a:visited { color: #15c; } +a:active { color: #052577; } +div#wrapper, #footerWrapper { + width: 540px; + margin-left: 35px; +} +header { + font-size: 18px; + font-weight: normal; + border-bottom: 1px solid #eee; + padding: 20px 0 15px 0; + width: 100%; +} +button { + -webkit-user-select: none; + -webkit-appearance: none; + background-image: -webkit-linear-gradient(#ededed, #ededed 38%, #dedede); + border: 1px solid rgba(0, 0, 0, 0.25); + border-radius: 2px; + box-shadow: 0 1px 0 rgba(0, 0, 0, 0.08), inset 0 1px 2px rgba(255, 255, 255, 0.75); + color: #444; + font: inherit; + text-shadow: 0 1px 0 #f0f0f0; + height: 24px; + font-size: 12px; + padding: 0 10px; +} +button:hover { + background-image: -webkit-linear-gradient(#f0f0f0, #f0f0f0 38%, #e0e0e0); + border-color: rgba(0, 0, 0, 0.3); + box-shadow: 0 1px 0 rgba(0, 0, 0, 0.12), inset 0 1px 2px rgba(255, 255, 255, 0.95); + color: black; +} +button:active { + background-image: -webkit-linear-gradient(#e7e7e7, #e7e7e7 38%, #d7d7d7); + box-shadow: none; + text-shadow: none; +} +button[disabled], button[disabled]:hover, button[disabled]:active { + background-image: -webkit-linear-gradient(#ededed, #ededed 38%, #dedede); + border: 1px solid rgba(0, 0, 0, 0.25); + box-shadow: 0 1px 0 rgba(0, 0, 0, 0.08), inset 0 1px 2px rgba(255, 255, 255, 0.75); + text-shadow: 0 1px 0 #f0f0f0; + color: #888; +} +input[type="checkbox"] { + -webkit-user-select: none; +} +label:hover { + color: black; +} +pre, code, .code { + font-family: Consolas, "Liberation Mono", Courier, monospace; +} +pre { + margin: 5px; + border-left: 1px solid #eee; + padding-left: 5px; + +} +input, textarea { + box-sizing: border-box; +} +textarea { + /* Horizontal resizing is pretty screwy-looking. */ + resize: vertical; +} +table#options{ + width: 100%; + font-size: 14px; + position: relative; + border-spacing: 0 23px; +} +.example { + font-size: 12px; + line-height: 16px; + color: #979ca0; + margin-left: 20px; +} +.info { + margin-left: 0px; +} +.caption { + margin-right: 10px; + min-width: 130px; + padding-top: 3px; + vertical-align: top; +} +td { padding: 0; } +div#exampleKeyMapping { + margin-left: 10px; + margin-top: 5px; +} +input#linkHintCharacters { + width: 100%; +} +input#linkHintNumbers { + width: 100%; +} +input#linkHintCharacters { + width: 100%; +} +input#scrollStepSize { + width: 40px; + margin-right: 3px; +} +textarea#userDefinedLinkHintCss, textarea#keyMappings, textarea#searchEngines { + width: 100%;; + min-height: 130px; + white-space: nowrap; +} +input#previousPatterns, input#nextPatterns { + width: 100%; +} +input#newTabUrl { + width: 100%; +} +input#searchUrl { + width: 100%; +} +#status { + margin-left: 10px; + font-size: 80%; +} +/* Make the caption in the settings table as small as possible, to pull the other fields to the right. */ +.caption { + width: 1px; + white-space: nowrap; +} +#buttonsPanel { width: 100%; } +#advancedOptions { display: none; } +#advancedOptionsLink { line-height: 24px; } +.help { + position: absolute; + right: -320px; + width: 320px; +} +input:read-only { + background-color: #eee; + color: #666; + pointer-events: none; + -webkit-user-select: none; +} +input[type="text"], textarea { + border: 1px solid #bfbfbf; + border-radius: 2px; + color: #444; + font: inherit; + padding: 3px; +} +button:focus, input[type="text"]:focus, textarea:focus { + -webkit-transition: border-color 200ms; + border-color: #4d90fe; + outline: none; +} +/* Boolean options have a tighter form representation than text options. */ +td.booleanOption { font-size: 12px; } +/* Ids and classes for rendering exclusionRules */ +#exclusionScrollBox { + overflow: scroll; + overflow-x: hidden; + overflow-y: auto; + /* Each exclusion rule is about 30px, so this allows 7 before scrolling */ + max-height: 215px; + min-height: 75px; + border-radius: 2px; + color: #444; + width: 100% +} +#exclusionRules { + width: 100%; +} +.exclusionRulePassKeys { + width: 33%; +} +.exclusionRemoveButton { + width: 1px; /* 1px; smaller than the button itself. */ +} +.exclusionRemoveButtonButton { + border: none; + background-color: #fff; + color: #979ca0; +} +.exclusionRemoveButtonButton:hover { + color: #444; +} +input.pattern, input.passKeys, .exclusionHeaderText { + width: 100%; + font-family: Consolas, "Liberation Mono", Courier, monospace; + font-size: 14px; +} +.exclusionHeaderText { + padding-left: 3px; + color: #979ca0; +} +#exclusionAddButton { + float: right; + margin-right: 0px; + margin-top: 5px; +} +#footer { + background: #f5f5f5; + border-top: 1px solid #979ca0; + position: fixed; + bottom: 0px; + z-index: 10; +} +#footer, #footerTable, #footerTableData { + width: 100%; +} +#endSpace { + /* Leave space for the fixed footer. */ + min-height: 30px; + max-height: 30px; +} +#helpText { + font-size: 12px; +} +#saveOptionsTableData { + float: right; +} +#saveOptions { + white-space: nowrap; + width: 110px; +} diff --git a/pages/options.html b/pages/options.html index 8e685304..03016d3f 100644 --- a/pages/options.html +++ b/pages/options.html @@ -1,242 +1,9 @@ Vimium Options + - - - - + diff --git a/pages/popup.html b/pages/popup.html index 775d6c07..726ecc3f 100644 --- a/pages/popup.html +++ b/pages/popup.html @@ -1,85 +1,84 @@ + - + -
-
-
-
- - - Text is added in popup.coffee. -
+ +
+ + + + + +
URL PatternsExcluded Keys
+ +
+ + + +
-
-
    -
  • - Type Ctrl-Enter to save and close. - Options -
  • -
+ -- cgit v1.2.3 From e2fe06c25ad0d478a602f77d3ea052ac0e886233 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Sun, 21 Dec 2014 05:28:02 +0000 Subject: Exclusion; minor refactoring. --- background_scripts/exclusions.coffee | 2 +- pages/options.coffee | 126 +++++++++++++++++------------------ pages/popup.html | 2 +- 3 files changed, 64 insertions(+), 66 deletions(-) diff --git a/background_scripts/exclusions.coffee b/background_scripts/exclusions.coffee index ac0e5f20..fd74e7da 100644 --- a/background_scripts/exclusions.coffee +++ b/background_scripts/exclusions.coffee @@ -18,7 +18,7 @@ RegexpCache = # The exclusions are an array of such objects (because the order matters). root.Exclusions = Exclusions = - # Make RegexpCache, which is required on the page popup, accessible via Exclusions. + # Make RegexpCache, which is required on the page popup, accessible via the Exclusions object. RegexpCache: RegexpCache rules: Settings.get("exclusionRules") diff --git a/pages/options.coffee b/pages/options.coffee index 776432d2..956c3e4e 100644 --- a/pages/options.coffee +++ b/pages/options.coffee @@ -42,10 +42,6 @@ class Option # Static method. @saveOptions: -> Option.all.map (option) -> option.save() - # These are only relevant on the options page; catch the exception on the popup page. - try - $("saveOptions").disabled = true - $("saveOptions").innerHTML = "No Changes" # Abstract method; only implemented in sub-classes. # Populate the option's DOM element (@element) with the setting's current value. @@ -137,43 +133,63 @@ class ExclusionRulesOption extends Option flatten = (rule) -> if rule and rule.pattern then rule.pattern + "\n" + rule.passKeys else "" a.map(flatten).join("\n") == b.map(flatten).join("\n") -# -# Operations for page elements. -enableSaveButton = -> - $("saveOptions").removeAttribute "disabled" - $("saveOptions").innerHTML = "Save Changes" - -# Display either "linkHintNumbers" or "linkHintCharacters", depending upon "filterLinkHints". -maintainLinkHintsView = -> - hide = (el) -> el.parentNode.parentNode.style.display = "none" - show = (el) -> el.parentNode.parentNode.style.display = "table-row" - if $("filterLinkHints").checked - hide $("linkHintCharacters") - show $("linkHintNumbers") - else - show $("linkHintCharacters") - hide $("linkHintNumbers") - -toggleAdvancedOptions = - do (advancedMode=false) -> - (event) -> - if advancedMode - $("advancedOptions").style.display = "none" - $("advancedOptionsLink").innerHTML = "Show advanced options…" - else - $("advancedOptions").style.display = "table-row-group" - $("advancedOptionsLink").innerHTML = "Hide advanced options" - advancedMode = !advancedMode - event.preventDefault() - # Prevent the "advanced options" link from retaining the focus. - document.activeElement.blur() - -activateHelpDialog = -> - showHelpDialog chrome.extension.getBackgroundPage().helpDialogHtml(true, true, "Command Listing"), frameId - # Prevent the "show help" link from retaining the focus. - document.activeElement.blur() - -initOptions = -> +initOptionsPage = -> + enableSaveButton = -> + $("saveOptions").removeAttribute "disabled" + $("saveOptions").innerHTML = "Save Changes" + + # Display either "linkHintNumbers" or "linkHintCharacters", depending upon "filterLinkHints". + maintainLinkHintsView = -> + hide = (el) -> el.parentNode.parentNode.style.display = "none" + show = (el) -> el.parentNode.parentNode.style.display = "table-row" + if $("filterLinkHints").checked + hide $("linkHintCharacters") + show $("linkHintNumbers") + else + show $("linkHintCharacters") + hide $("linkHintNumbers") + + toggleAdvancedOptions = + do (advancedMode=false) -> + (event) -> + if advancedMode + $("advancedOptions").style.display = "none" + $("advancedOptionsLink").innerHTML = "Show advanced options…" + else + $("advancedOptions").style.display = "table-row-group" + $("advancedOptionsLink").innerHTML = "Hide advanced options" + advancedMode = !advancedMode + event.preventDefault() + # Prevent the "advanced options" link from retaining the focus. + document.activeElement.blur() + + activateHelpDialog = -> + showHelpDialog chrome.extension.getBackgroundPage().helpDialogHtml(true, true, "Command Listing"), frameId + # Prevent the "show help" link from retaining the focus when clicked. + document.activeElement.blur() + + saveOptions = -> + Option.saveOptions() + $("saveOptions").disabled = true + $("saveOptions").innerHTML = "No Changes" + + $("saveOptions").addEventListener "click", saveOptions + $("advancedOptionsLink").addEventListener "click", toggleAdvancedOptions + $("showCommands").addEventListener "click", activateHelpDialog + $("filterLinkHints").addEventListener "click", maintainLinkHintsView + + for element in document.getElementsByClassName "nonEmptyTextOption" + element.className = element.className + " example info" + element.innerHTML = "Leave empty to reset this option." + + maintainLinkHintsView() + window.onbeforeunload = -> "You have unsaved changes to options." unless $("saveOptions").disabled + + document.addEventListener "keyup", (event) -> + if event.ctrlKey and event.keyCode == 13 + document.activeElement.blur() if document?.activeElement?.blur + saveOptions() + options = exclusionRules: ExclusionRulesOption filterLinkHints: CheckBoxOption @@ -195,31 +211,13 @@ initOptions = -> for name, type of options new type(name,enableSaveButton) - $("saveOptions").addEventListener "click", Option.saveOptions - $("advancedOptionsLink").addEventListener "click", toggleAdvancedOptions - $("showCommands").addEventListener "click", activateHelpDialog - $("filterLinkHints").addEventListener "click", maintainLinkHintsView - - for element in document.getElementsByClassName "nonEmptyTextOption" - element.className = element.className + " example info" - element.innerHTML = "Leave empty to reset this option." - - maintainLinkHintsView() - window.onbeforeunload = -> "You have unsaved changes to options." unless $("saveOptions").disabled - - document.addEventListener "keyup", (event) -> - if event.ctrlKey and event.keyCode == 13 - document.activeElement.blur() if document?.activeElement?.blur - Option.saveOptions() - -initPopup = -> +initPopupPage = -> chrome.tabs.getSelected null, (tab) -> document.getElementById("optionsLink").setAttribute "href", chrome.runtime.getURL("pages/options.html") - updated = false onUpdated = -> - $("helpText").innerHTML = "Type Ctrl-Enter to save and close; Esc to cancel." - updated = true + $("helpText").innerHTML = + "Type Ctrl-Enter to save and close; Esc to cancel." document.addEventListener "keyup", (event) -> if event.ctrlKey and event.keyCode == 13 @@ -232,6 +230,6 @@ initPopup = -> # Initialization. document.addEventListener "DOMContentLoaded", -> switch location.pathname - when "/pages/options.html" then initOptions() - when "/pages/popup.html" then initPopup() + when "/pages/options.html" then initOptionsPage() + when "/pages/popup.html" then initPopupPage() diff --git a/pages/popup.html b/pages/popup.html index 726ecc3f..6c2dc2d0 100644 --- a/pages/popup.html +++ b/pages/popup.html @@ -69,7 +69,7 @@ - These are the matching exclusion rules for this page. + These are the matching rules for this page.
Vimium Options.
-- cgit v1.2.3 From 681bc13783d1f1b8579f810dbbc68174a84c1a10 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Sun, 21 Dec 2014 06:39:46 +0000 Subject: Exclusion; multi-rule matching. --- background_scripts/exclusions.coffee | 24 +++++++----------------- background_scripts/main.coffee | 19 ++----------------- pages/options.coffee | 35 ++++++++++++++++++++++++++++------- 3 files changed, 37 insertions(+), 41 deletions(-) diff --git a/background_scripts/exclusions.coffee b/background_scripts/exclusions.coffee index fd74e7da..db86e583 100644 --- a/background_scripts/exclusions.coffee +++ b/background_scripts/exclusions.coffee @@ -23,11 +23,14 @@ root.Exclusions = Exclusions = rules: Settings.get("exclusionRules") - # Return the first exclusion rule matching the URL, or null. + # Merge the matching rules for URL, or null. getRule: (url) -> - for rule in @rules - return rule if url.match(RegexpCache.get(rule.pattern)) - return null + matching = (rule for rule in @rules when url.match(RegexpCache.get(rule.pattern))) + if matching.length + pattern: (rule.pattern for rule in matching).join " | " # Not used; for documentation/debugging only. + passKeys: (rule.passKeys for rule in matching).join "" + else + null setRules: (rules) -> # Callers map a rule to null to have it deleted, and rules without a pattern are useless. @@ -37,19 +40,6 @@ root.Exclusions = Exclusions = postUpdateHook: (rules) -> @rules = rules - # Update an existing rule or add a new rule. - updateOrAdd: (newRule) -> - seen = false - @rules.push(newRule) - @setRules @rules.map (rule) -> - if rule.pattern == newRule.pattern - if seen then null else seen = newRule - else - rule - - remove: (pattern) -> - @setRules(@rules.filter((rule) -> rule and rule.pattern != pattern)) - # Development and debug only. # Enable this (temporarily) to restore legacy exclusion rules from backup. if false and Settings.has("excludedUrlsBackup") diff --git a/background_scripts/main.coffee b/background_scripts/main.coffee index 647923c0..cebb38ca 100644 --- a/background_scripts/main.coffee +++ b/background_scripts/main.coffee @@ -75,26 +75,10 @@ getCurrentTabUrl = (request, sender) -> sender.tab.url root.isEnabledForUrl = isEnabledForUrl = (request) -> rule = Exclusions.getRule(request.url) { - rule: rule isEnabledForUrl: not rule or rule.passKeys passKeys: rule?.passKeys or "" } -# Called by the popup UI. -# If the URL pattern matches an existing rule, then the existing rule is updated. Otherwise, a new rule is created. -root.addExclusionRule = (pattern,passKeys) -> - if pattern = pattern.trim() - Exclusions.updateOrAdd({ pattern: pattern, passKeys: passKeys }) - chrome.tabs.query({ windowId: chrome.windows.WINDOW_ID_CURRENT, active: true }, - (tabs) -> updateActiveState(tabs[0].id)) - -# Called by the popup UI. Remove all existing exclusion rules with this pattern. -root.removeExclusionRule = (pattern) -> - if pattern = pattern.trim() - Exclusions.remove(pattern) - chrome.tabs.query({ windowId: chrome.windows.WINDOW_ID_CURRENT, active: true }, - (tabs) -> updateActiveState(tabs[0].id)) - saveHelpDialogSettings = (request) -> Settings.set("helpDialog_showAdvancedCommands", request.showAdvancedCommands) @@ -353,7 +337,8 @@ setBrowserActionIcon = (tabId,path) -> # Updates the browserAction icon to indicate whether Vimium is enabled or disabled on the current page. # Also propagates new enabled/disabled/passkeys state to active window, if necessary. # This lets you disable Vimium on a page without needing to reload. -updateActiveState = (tabId) -> +# Exported via root because it's called from the page popup. +root.updateActiveState = updateActiveState = (tabId) -> enabledIcon = "icons/browser_action_enabled.png" disabledIcon = "icons/browser_action_disabled.png" partialIcon = "icons/browser_action_partial.png" diff --git a/pages/options.coffee b/pages/options.coffee index 956c3e4e..1cd0661f 100644 --- a/pages/options.coffee +++ b/pages/options.coffee @@ -1,6 +1,18 @@ $ = (id) -> document.getElementById id bgSettings = chrome.extension.getBackgroundPage().Settings -Exclusions = chrome.extension.getBackgroundPage().Exclusions +bgExclusions = chrome.extension.getBackgroundPage().Exclusions + +# Generate a default exclusion-rule pattern from a URL. +generateDefaultPattern = (url) -> + if /^https?:\/\/./.test url + # The common use case is to disable Vimium at the domain level. + # Generate "https?://www.example.com/*" from "http://www.example.com/path/to/page.html". + "https?:/" + url.split("/",3)[1..].join("/") + "/*" + else if /^[a-z]{3,}:\/\/./.test url + # Anything else which seems to be a URL. + url.split("/",3).join("/") + "/*" + else + url + "*" # # Class hierarchy for various types of option. @@ -76,12 +88,17 @@ class CheckBoxOption extends Option readValueFromElement: -> @element.checked class ExclusionRulesOption extends Option - constructor: (field, onUpdated, @url=null) -> + constructor: (field, onUpdated, @url="") -> super(field, onUpdated) $("exclusionAddButton").addEventListener "click", (event) => - @appendRule { pattern: "", passKeys: "" } - # Focus the pattern element in the new rule. - @element.children[@element.children.length-1].children[0].children[0].focus() + @addRule() + + addRule: -> + @appendRule { pattern: (if @url then generateDefaultPattern(@url) else ""), passKeys: "" } + # On the options page, focus the pattern; on the page popup (where we already have a pattern), focus the + # passKeys. + focus = if @url then 1 else 0 + @element.children[@element.children.length-1].children[focus].children[0].focus() # Scroll the new rule into view. exclusionScrollBox = $("exclusionScrollBox") exclusionScrollBox.scrollTop = exclusionScrollBox.scrollHeight @@ -96,9 +113,11 @@ class ExclusionRulesOption extends Option haveMatch = false for element in @element.getElementsByClassName "exclusionRuleTemplateInstance" pattern = element.children[0].firstChild.value.trim() - unless @url.match Exclusions.RegexpCache.get pattern - element.style.display = 'none' + if @url.match bgExclusions.RegexpCache.get pattern haveMatch = true + else + element.style.display = 'none' + @addRule() unless haveMatch # Append a row for a new rule. appendRule: (rule) -> @@ -222,6 +241,8 @@ initPopupPage = -> document.addEventListener "keyup", (event) -> if event.ctrlKey and event.keyCode == 13 Option.saveOptions() + chrome.tabs.query { windowId: chrome.windows.WINDOW_ID_CURRENT, active: true }, (tabs) -> + chrome.extension.getBackgroundPage().updateActiveState(tabs[0].id) window.close() new ExclusionRulesOption("exclusionRules", onUpdated, tab.url) -- cgit v1.2.3 From ae3794d10f11f61d7d78c523afc3abc4d5f72467 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Sun, 21 Dec 2014 07:19:37 +0000 Subject: Exclusion; focus last rule. --- pages/options.coffee | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pages/options.coffee b/pages/options.coffee index 1cd0661f..c3f619ca 100644 --- a/pages/options.coffee +++ b/pages/options.coffee @@ -107,14 +107,15 @@ class ExclusionRulesOption extends Option for rule in rules @appendRule rule - # If this is the popup page (@url is defined), then hide rules which do not match @url. If no rules - # match, then add a default rule. + # If this is the popup page (@url is truthy), then hide rules which do not match @url. If no rules + # match, then add a default rule. Focus the passKeys field in the last (most recent) rule. if @url haveMatch = false for element in @element.getElementsByClassName "exclusionRuleTemplateInstance" pattern = element.children[0].firstChild.value.trim() if @url.match bgExclusions.RegexpCache.get pattern haveMatch = true + element.children[1].firstChild.focus() else element.style.display = 'none' @addRule() unless haveMatch -- cgit v1.2.3 From 0409bcdc8ee753e8ff3832cb852f604f8d347688 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Sun, 21 Dec 2014 07:50:13 +0000 Subject: Exclusion; rework layout. --- pages/options.css | 2 ++ pages/popup.html | 23 +++++++---------------- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/pages/options.css b/pages/options.css index 148d1330..5b098c8f 100644 --- a/pages/options.css +++ b/pages/options.css @@ -1,3 +1,5 @@ +/* NOTE: This stylesheet is included in both options.html and popup.html. So changes here affect + both of these. */ body { font: 14px "DejaVu Sans", "Arial", sans-serif; color: #303942; diff --git a/pages/popup.html b/pages/popup.html index 6c2dc2d0..c84845f5 100644 --- a/pages/popup.html +++ b/pages/popup.html @@ -7,36 +7,27 @@ padding: 0px; } - #endSpace { - width: 450px; - } - #helpText, #optionsLink { font-family : "Helvetica Neue", "Helvetica", "Arial", sans-serif; font-size: 12px; } - #helpText { - color: #979ca0; - } + #helpText { color: #979ca0; } #exclusionAddButton { width: 80px; } - /* Styles overwridden or changed from options.css. */ + /* These are overridden from options.css. */ - #footerWrapper { - width: 450px; /* As for #endSpace */ - margin-left: 0px; - } + #endSpace, #footerWrapper { width: 450px; } - #endSpace { - /* Leave space for the fixed footer. */ - min-height: 36px; - max-height: 36px; + #endSpace { /* Leave space for the fixed footer. */ + min-height: 46px; + max-height: 46px; } + #footerWrapper { margin-left: 0px; } -- cgit v1.2.3 From eacf2802cf46eefefed7904f7138d840bd07805b Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Sun, 21 Dec 2014 08:35:17 +0000 Subject: Exclusion; add Save button. --- pages/options.coffee | 18 +++++++++++++++--- pages/options.html | 2 ++ pages/popup.html | 14 ++++++++------ 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/pages/options.coffee b/pages/options.coffee index c3f619ca..db9c3287 100644 --- a/pages/options.coffee +++ b/pages/options.coffee @@ -118,7 +118,9 @@ class ExclusionRulesOption extends Option element.children[1].firstChild.focus() else element.style.display = 'none' - @addRule() unless haveMatch + unless haveMatch + @addRule() + @onUpdated() # Append a row for a new rule. appendRule: (rule) -> @@ -236,8 +238,18 @@ initPopupPage = -> document.getElementById("optionsLink").setAttribute "href", chrome.runtime.getURL("pages/options.html") onUpdated = -> - $("helpText").innerHTML = - "Type Ctrl-Enter to save and close; Esc to cancel." + $("helpText").innerHTML = "Type Ctrl-Enter to save and close." + $("saveOptions").removeAttribute "disabled" + $("saveOptions").innerHTML = "Save Changes" + + + $("saveOptions").addEventListener "click", -> + Option.saveOptions() + $("helpText").innerHTML = "Rules saved." + $("saveOptions").innerHTML = "No Changes" + $("saveOptions").disabled = true + chrome.tabs.query { windowId: chrome.windows.WINDOW_ID_CURRENT, active: true }, (tabs) -> + chrome.extension.getBackgroundPage().updateActiveState(tabs[0].id) document.addEventListener "keyup", (event) -> if event.ctrlKey and event.keyCode == 13 diff --git a/pages/options.html b/pages/options.html index 03016d3f..51a53634 100644 --- a/pages/options.html +++ b/pages/options.html @@ -23,6 +23,8 @@
+
diff --git a/pages/popup.html b/pages/popup.html index c84845f5..6dcc7a8b 100644 --- a/pages/popup.html +++ b/pages/popup.html @@ -13,21 +13,24 @@ } #helpText { color: #979ca0; } + #exclusionAddButton { width: 80px; } - #exclusionAddButton { - width: 80px; + #saveOptions { + margin-top: 5px; /* Match exclusionAddButton */ + margin-left: 5px; + float: right; } /* These are overridden from options.css. */ #endSpace, #footerWrapper { width: 450px; } + #footerWrapper { margin-left: 0px; } #endSpace { /* Leave space for the fixed footer. */ min-height: 46px; max-height: 46px; } - #footerWrapper { margin-left: 0px; } @@ -61,13 +64,12 @@ -
These are the matching rules for this page. + +
Vimium Options.
- -
-- cgit v1.2.3 From ef41858c387cb6157c59907d6eec7b0324f13ce2 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Sun, 21 Dec 2014 08:43:13 +0000 Subject: Exclusion; enable save button on add rule. --- pages/options.coffee | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pages/options.coffee b/pages/options.coffee index db9c3287..ad192142 100644 --- a/pages/options.coffee +++ b/pages/options.coffee @@ -102,6 +102,7 @@ class ExclusionRulesOption extends Option # Scroll the new rule into view. exclusionScrollBox = $("exclusionScrollBox") exclusionScrollBox.scrollTop = exclusionScrollBox.scrollHeight + @onUpdated() populateElement: (rules) -> for rule in rules @@ -120,7 +121,6 @@ class ExclusionRulesOption extends Option element.style.display = 'none' unless haveMatch @addRule() - @onUpdated() # Append a row for a new rule. appendRule: (rule) -> @@ -242,7 +242,6 @@ initPopupPage = -> $("saveOptions").removeAttribute "disabled" $("saveOptions").innerHTML = "Save Changes" - $("saveOptions").addEventListener "click", -> Option.saveOptions() $("helpText").innerHTML = "Rules saved." -- cgit v1.2.3 From d715bf897dd72bfad8aa6573e1919afc66b02e3a Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Sun, 21 Dec 2014 09:24:37 +0000 Subject: Exclusion; absolute exclusion rules taje priority. --- background_scripts/exclusions.coffee | 5 ++++- pages/options.coffee | 11 ++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/background_scripts/exclusions.coffee b/background_scripts/exclusions.coffee index db86e583..a8d65d62 100644 --- a/background_scripts/exclusions.coffee +++ b/background_scripts/exclusions.coffee @@ -26,8 +26,11 @@ root.Exclusions = Exclusions = # Merge the matching rules for URL, or null. getRule: (url) -> matching = (rule for rule in @rules when url.match(RegexpCache.get(rule.pattern))) + # An absolute exclusion rule (with no passKeys) takes priority. + for rule in matching + return rule unless rule.passKeys if matching.length - pattern: (rule.pattern for rule in matching).join " | " # Not used; for documentation/debugging only. + pattern: (rule.pattern for rule in matching).join " | " # Not used; for debugging only. passKeys: (rule.passKeys for rule in matching).join "" else null diff --git a/pages/options.coffee b/pages/options.coffee index ad192142..460f2789 100644 --- a/pages/options.coffee +++ b/pages/options.coffee @@ -229,7 +229,7 @@ initOptionsPage = -> searchUrl: NonEmptyTextOption userDefinedLinkHintCss: TextOption - # Populate options. The constructor adds each new object to "Option.all". + # Populate options. The constructor adds each new object to "Option.all". for name, type of options new type(name,enableSaveButton) @@ -242,7 +242,7 @@ initPopupPage = -> $("saveOptions").removeAttribute "disabled" $("saveOptions").innerHTML = "Save Changes" - $("saveOptions").addEventListener "click", -> + saveOptions = -> Option.saveOptions() $("helpText").innerHTML = "Rules saved." $("saveOptions").innerHTML = "No Changes" @@ -250,13 +250,14 @@ initPopupPage = -> chrome.tabs.query { windowId: chrome.windows.WINDOW_ID_CURRENT, active: true }, (tabs) -> chrome.extension.getBackgroundPage().updateActiveState(tabs[0].id) + $("saveOptions").addEventListener "click", saveOptions + document.addEventListener "keyup", (event) -> if event.ctrlKey and event.keyCode == 13 - Option.saveOptions() - chrome.tabs.query { windowId: chrome.windows.WINDOW_ID_CURRENT, active: true }, (tabs) -> - chrome.extension.getBackgroundPage().updateActiveState(tabs[0].id) + saveOptions() window.close() + # Populate options. Just one, here. new ExclusionRulesOption("exclusionRules", onUpdated, tab.url) # -- cgit v1.2.3 From 1602338bbde44f1e38a6bf09c57cef51b7939a59 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Sun, 21 Dec 2014 10:29:02 +0000 Subject: Exclusion; exclusion rules box smaller on popup. --- pages/popup.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pages/popup.html b/pages/popup.html index 6dcc7a8b..2e59d471 100644 --- a/pages/popup.html +++ b/pages/popup.html @@ -25,6 +25,9 @@ #endSpace, #footerWrapper { width: 450px; } #footerWrapper { margin-left: 0px; } + /* Make exclusionScrollBox smaller than on the options page, because there are likely to be fewer + matching rules, and the popup obscures the underlying page. */ + #exclusionScrollBox { max-height: 124px; } #endSpace { /* Leave space for the fixed footer. */ min-height: 46px; -- cgit v1.2.3 From eb5750642c54e4c6ad6e12d348f0ce3d40bac0e1 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Sun, 21 Dec 2014 11:10:40 +0000 Subject: Exclusion; highlight mismatched patterns. --- pages/options.coffee | 21 +++++++++++++++++++-- pages/popup.html | 11 ++++++++--- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/pages/options.coffee b/pages/options.coffee index 460f2789..cd932ed8 100644 --- a/pages/options.coffee +++ b/pages/options.coffee @@ -98,7 +98,9 @@ class ExclusionRulesOption extends Option # On the options page, focus the pattern; on the page popup (where we already have a pattern), focus the # passKeys. focus = if @url then 1 else 0 - @element.children[@element.children.length-1].children[focus].children[0].focus() + element = @element.children[@element.children.length-1] + element.children[focus].children[0].focus() + @activatePatternWatcher element if @url # Scroll the new rule into view. exclusionScrollBox = $("exclusionScrollBox") exclusionScrollBox.scrollTop = exclusionScrollBox.scrollHeight @@ -108,11 +110,14 @@ class ExclusionRulesOption extends Option for rule in rules @appendRule rule + elements = @element.getElementsByClassName "exclusionRuleTemplateInstance" + @activatePatternWatcher element for element in elements if @url + # If this is the popup page (@url is truthy), then hide rules which do not match @url. If no rules # match, then add a default rule. Focus the passKeys field in the last (most recent) rule. if @url haveMatch = false - for element in @element.getElementsByClassName "exclusionRuleTemplateInstance" + for element in elements pattern = element.children[0].firstChild.value.trim() if @url.match bgExclusions.RegexpCache.get pattern haveMatch = true @@ -122,6 +127,18 @@ class ExclusionRulesOption extends Option unless haveMatch @addRule() + # On the popup page, provide visual feedback when a pattern does not match the current page. This assumes + # that @url is not empty. + activatePatternWatcher: (element) -> + computedStyle = window.getComputedStyle(element) + originalColor = computedStyle.getPropertyValue("color") + patternElement = element.children[0].firstChild + patternElement.addEventListener "keyup", => + if @url.match bgExclusions.RegexpCache.get patternElement.value + patternElement.style.color = originalColor + else + patternElement.style.color = "red" + # Append a row for a new rule. appendRule: (rule) -> content = document.querySelector('#exclusionRuleTemplate').content diff --git a/pages/popup.html b/pages/popup.html index 2e59d471..0b4de131 100644 --- a/pages/popup.html +++ b/pages/popup.html @@ -25,9 +25,14 @@ #endSpace, #footerWrapper { width: 450px; } #footerWrapper { margin-left: 0px; } + /* Make exclusionScrollBox smaller than on the options page, because there are likely to be fewer - matching rules, and the popup obscures the underlying page. */ - #exclusionScrollBox { max-height: 124px; } + matching rules, and the popup obscures the underlying page. + */ + #exclusionScrollBox { + max-height: 124px; + min-height: 124px; + } #endSpace { /* Leave space for the fixed footer. */ min-height: 46px; @@ -70,7 +75,7 @@
- Vimium Options. + Vimium Options -- cgit v1.2.3 From 98fd49a6cfe660c363aecb6ebe0a4cb69342808d Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Sun, 21 Dec 2014 11:49:13 +0000 Subject: Exclusion; show state. --- background_scripts/exclusions.coffee | 6 +++--- pages/options.coffee | 18 +++++++++++++++--- pages/popup.html | 26 +++++++++++++++----------- 3 files changed, 33 insertions(+), 17 deletions(-) diff --git a/background_scripts/exclusions.coffee b/background_scripts/exclusions.coffee index a8d65d62..12d86f41 100644 --- a/background_scripts/exclusions.coffee +++ b/background_scripts/exclusions.coffee @@ -23,9 +23,9 @@ root.Exclusions = Exclusions = rules: Settings.get("exclusionRules") - # Merge the matching rules for URL, or null. - getRule: (url) -> - matching = (rule for rule in @rules when url.match(RegexpCache.get(rule.pattern))) + # Merge the matching rules for URL, or null. If rules are provided, match against those. + getRule: (url, rules=@rules) -> + matching = (rule for rule in rules when rule.pattern and url.match(RegexpCache.get(rule.pattern))) # An absolute exclusion rule (with no passKeys) takes priority. for rule in matching return rule unless rule.passKeys diff --git a/pages/options.coffee b/pages/options.coffee index cd932ed8..80c9ae44 100644 --- a/pages/options.coffee +++ b/pages/options.coffee @@ -161,8 +161,8 @@ class ExclusionRulesOption extends Option readValueFromElement: -> rules = for element in @element.getElementsByClassName "exclusionRuleTemplateInstance" - pattern = element.children[0].firstChild.value.trim() - passKeys = element.children[1].firstChild.value.trim() + pattern = element.children[0].firstChild.value.trim().split(/\s+/).join "" + passKeys = element.children[1].firstChild.value.trim().split(/\s+/).join "" { pattern: pattern, passKeys: passKeys } rules.filter (rule) -> rule.pattern @@ -275,7 +275,19 @@ initPopupPage = -> window.close() # Populate options. Just one, here. - new ExclusionRulesOption("exclusionRules", onUpdated, tab.url) + exclusions = new ExclusionRulesOption("exclusionRules", onUpdated, tab.url) + + document.addEventListener "keyup", (event) -> + rules = exclusions.readValueFromElement() + isEnabled = bgExclusions.getRule tab.url, rules + console.log isEnabled + $("state").innerHTML = + if isEnabled and isEnabled.passKeys + "Excluded: #{isEnabled.passKeys}" + else if isEnabled + "Disabled" + else + "Enabled" # # Initialization. diff --git a/pages/popup.html b/pages/popup.html index 0b4de131..370751ff 100644 --- a/pages/popup.html +++ b/pages/popup.html @@ -7,12 +7,12 @@ padding: 0px; } - #helpText, #optionsLink { + #helpText, #optionsLink, #state { font-family : "Helvetica Neue", "Helvetica", "Arial", sans-serif; font-size: 12px; } - #helpText { color: #979ca0; } + #helpText, #stateLine { color: #979ca0; } #exclusionAddButton { width: 80px; } #saveOptions { @@ -67,16 +67,20 @@