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 @@
| URL Patterns | +Excluded Keys | +
|
These are the matching rules for this page.
+
+
Vimium Options. |
- - - |