options.coffee | |
---|---|
Template | |
Private constants | |
Regular expression used to validate template keys. | R_VALID_KEY = /^[A-Z0-9]*\.[A-Z0-9]*$/i |
Regular expression used to validate keyboard shortcut inputs. | R_VALID_SHORTCUT = /[A-Z0-9]/i |
Source URL of the user feedback widget script. | WIDGET_SOURCE = "https://widget.uservoice.com/RSRS5SpMkMxvKOCs27g.js" |
Private variables | |
Easily accessible reference to the extension controller. | {ext} = chrome.extension.getBackgroundPage() |
Indicate whether or not the user feedback feature has been added to the page. | feedbackAdded = no |
Load functions | |
Bind an event of the specified | bindSaveEvent = (selector, type, option, evaluate, callback) ->
log.trace()
$(selector).on type, ->
$this = $ this
key = ''
value = null
store.modify option, (data) ->
key = $this.attr('id').match(new RegExp("^#{option}(\\S*)"))[1]
key = key[0].toLowerCase() + key.substr 1
data[key] = value = evaluate.call $this, key
callback? $this, key, value |
Bind an event of the specified | bindTemplateSaveEvent = (selector, type, assign, callback) ->
log.trace()
$(selector).on type, ->
$this = $ this
key = ''
templates = $ '#templates'
option = templates.find 'option:selected'
if option.length and templates.data('quiet') isnt 'true'
key = $this.attr('id').match(/^template_(\S*)/)[1]
key = key[0].toLowerCase() + key.substr 1
assign.call $this, option, key
callback? $this, option, key |
Update the options page with the values from the current settings. | load = ->
log.trace()
anchor = store.get 'anchor'
menu = store.get 'menu'
shortcuts = store.get 'shortcuts'
$('#analytics').attr 'checked', 'checked' if store.get 'analytics'
$('#anchorTarget').attr 'checked', 'checked' if anchor.target
$('#anchorTitle').attr 'checked', 'checked' if anchor.title
$('#menuEnabled').attr 'checked', 'checked' if menu.enabled
$('#menuOptions').attr 'checked', 'checked' if menu.options
$('#menuPaste').attr 'checked', 'checked' if menu.paste
$('#shortcutsEnabled').attr 'checked', 'checked' if shortcuts.enabled
$('#shortcutsPaste').attr 'checked', 'checked' if shortcuts.paste
loadControlEvents()
loadSaveEvents()
loadImages()
loadTemplates()
loadNotifications()
loadToolbar()
loadUrlShorteners()
loadDeveloperTools() |
Bind the event handlers required for controlling general changes. | loadControlEvents = ->
log.trace()
$('#menuEnabled').change( ->
groups = $('#menuOptions').parents('.control-group').first()
groups = groups.add $('#menuPaste').parents('.control-group').first()
if $(this).is ':checked' then groups.slideDown() else groups.slideUp()
).change()
$('#shortcutsEnabled').change( ->
groups = $('#shortcutsPaste').parents('.control-group').first()
if $(this).is ':checked' then groups.slideDown() else groups.slideUp()
).change() |
Update the developer tools section of the options page with the current settings. | loadDeveloperTools = ->
log.trace()
loadLogger() |
Create an | loadImages = ->
log.trace()
imagePreview = $ '#template_image_preview'
images = $ '#template_image'
images.append $ '<option/>',
text: icons.getMessage()
value: ''
images.append $ '<option/>',
disabled: 'disabled'
text: '---------------'
for image in icons.ICONS
images.append $ '<option/>',
text: image.getMessage()
value: image.name
images.change ->
opt = images.find 'option:selected'
imagePreview.attr 'class', icons.getClass opt.val()
images.change() |
Update the logging section of the options page with the current settings. | loadLogger = ->
log.trace()
logger = store.get 'logger'
$('#loggerEnabled').attr 'checked', 'checked' if logger.enabled
loggerLevel = $ '#loggerLevel'
loggerLevel.find('option').remove()
for level in log.LEVELS
option = $ '<option/>',
text: i18n.get "opt_logger_level_#{level.name}_text"
value: level.value
option.attr 'selected', 'selected' if level.value is logger.level
loggerLevel.append option |
Ensure debug level is selected if configuration currently matches none. | unless loggerLevel.find('option[selected]').length
loggerLevel.find("option[value='#{log.DEBUG}']").attr 'selected',
'selected'
loadLoggerSaveEvents() |
Bind the event handlers required for persisting logging changes. | loadLoggerSaveEvents = ->
log.trace()
bindSaveEvent '#loggerEnabled, #loggerLevel', 'change', 'logger', (key) ->
value = if key is 'level' then @val() else @is ':checked'
log.debug "Changing logging #{key} to '#{value}'"
value
, (jel, key, value) ->
logger = store.get 'logger'
chrome.extension.getBackgroundPage().log.config = log.config = logger
analytics.track 'Logging', 'Changed', key[0].toUpperCase() + key.substr(1),
Number value |
Update the notification section of the options page with the current settings. | loadNotifications = ->
log.trace()
notifications = store.get 'notifications'
$('#notifications').attr 'checked', 'checked' if notifications.enabled
$('#notificationDuration').val if notifications.duration > 0
notifications.duration * .001
else
0
loadNotificationSaveEvents() |
Bind the event handlers required for persisting notification changes. | loadNotificationSaveEvents = ->
log.trace()
$('#notificationDuration').on 'input', ->
store.modify 'notifications', (notifications) =>
ms = $(this).val()
ms = if ms? then parseInt(ms, 10) * 1000 else 0
log.debug "Changing notification duration to #{ms} milliseconds"
notifications.duration = ms
analytics.track 'Notifications', 'Changed', 'Duration', ms
$('#notifications').change ->
store.modify 'notifications', (notifications) =>
enabled = $(this).is ':checked'
log.debug "Changing notifications to '#{enabled}'"
notifications.enabled = enabled
analytics.track 'Notifications', 'Changed', 'Enabled', Number enabled |
Bind the event handlers required for persisting general changes. | loadSaveEvents = ->
log.trace()
$('#analytics').change ->
enabled = $(this).is ':checked'
log.debug "Changing analytics to '#{enabled}'"
if enabled
store.set 'analytics', yes
chrome.extension.getBackgroundPage().analytics.add()
analytics.add()
analytics.track 'General', 'Changed', 'Analytics', 1
else
analytics.track 'General', 'Changed', 'Analytics', 0
analytics.remove()
chrome.extension.getBackgroundPage().analytics.remove()
store.set 'analytics', no
bindSaveEvent '#anchorTarget, #anchorTitle', 'change', 'anchor', (key) ->
value = @is ':checked'
log.debug "Changing anchor #{key} to '#{value}'"
value
, (jel, key, value) ->
key = key[0].toUpperCase() + key.substr 1
analytics.track 'Anchors', 'Changed', key, Number value
bindSaveEvent '#menuEnabled, #menuOptions, #menuPaste', 'change', 'menu',
(key) ->
value = @is ':checked'
log.debug "Changing context menu #{key} to '#{value}'"
value
, (jel, key, value) ->
ext.updateContextMenu()
key = key[0].toUpperCase() + key.substr 1
analytics.track 'Context Menu', 'Changed', key, Number value
bindSaveEvent '#shortcutsEnabled, #shortcutsPaste', 'change', 'shortcuts',
(key) ->
value = @is ':checked'
log.debug "Changing keyboard shortcuts #{key} to '#{value}'"
value
, (jel, key, value) ->
key = key[0].toUpperCase() + key.substr 1
analytics.track 'Keyboard Shortcuts', 'Changed', key, Number value |
Update the templates section of the options page with the current settings. | loadTemplates = ->
log.trace()
templates = $ '#templates' |
Start from a clean slate. | templates.remove 'option' |
Create and insert options representing each template. | templates.append loadTemplate template for template in ext.templates |
Load all of the event handlers required for managing the templates. | loadTemplateControlEvents()
loadTemplateImportEvents()
loadTemplateExportEvents()
loadTemplateSaveEvents() |
Create an | loadTemplate = (template) ->
log.trace()
log.debug 'Creating an option for the following template...', template
option = $ '<option/>',
text: template.title
value: template.key
option.data 'content', template.content
option.data 'enabled', "#{template.enabled}"
option.data 'image', template.image
option.data 'readOnly', "#{template.readOnly}"
option.data 'shortcut', template.shortcut
option.data 'usage', "#{template.usage}"
option |
Bind the event handlers required for controlling the templates. | loadTemplateControlEvents = ->
log.trace()
templates = $ '#templates' |
Whenever the selected option changes we want all the controls to represent the current selection (where possible). | templates.change ->
$this = $ this
opt = $this.find 'option:selected'
clearErrors()
templates.data 'quiet', 'true'
if opt.length is 0 |
Reset all controls and fields as no option is selected. | lastSelectedTemplate = {}
i18n.content '#add_btn', 'opt_add_button'
$('#moveUp_btn, #moveDown_btn').attr 'disabled', 'disabled'
$('.toggle-disabled').attr 'disabled', 'disabled'
$('.toggle-readonly').removeAttr 'readonly'
$('#template_content, #template_shortcut, #template_title').val ''
$('#template_enabled').attr 'checked', 'checked'
$('#template_image option:first-child').attr 'selected', 'selected'
$('#template_image').change()
else |
An option is selected; start cooking. | i18n.content '#add_btn', 'opt_add_new_button' |
Disable the Up control since the selected option is at the top of the list. | if opt.is ':first-child'
$('#moveUp_btn').attr 'disabled', 'disabled'
else
$('#moveUp_btn').removeAttr 'disabled' |
Disable the Down control since the selected option is at the bottom of the list. | if opt.is ':last-child'
$('#moveDown_btn').attr 'disabled', 'disabled'
else
$('#moveDown_btn').removeAttr 'disabled' |
Update the fields and controls to reflect selected option. | imgOpt = $ "#template_image option[value='#{opt.data 'image'}']"
if imgOpt.length is 0
$('#template_image option:first-child').attr 'selected', 'selected'
else
imgOpt.attr 'selected', 'selected'
$('#template_content').val opt.data 'content'
$('#template_image').change()
$('#template_shortcut').val opt.data 'shortcut'
$('#template_title').val opt.text()
if opt.data('enabled') is 'true'
$('#template_enabled').attr 'checked', 'checked'
else
$('#template_enabled').removeAttr 'checked'
if opt.data('readOnly') is 'true'
$('.toggle-disabled').attr 'disabled', 'disabled'
$('.toggle-readonly').attr 'readonly', 'readonly'
else
$('.toggle-disabled').removeAttr 'disabled'
$('.toggle-readonly').removeAttr 'readonly'
templates.data 'quiet', 'false'
templates.change() |
Add a new order to the select based on the input values. | $('#add_btn').click ->
opt = templates.find 'option:selected'
if opt.length |
Template was selected; clear that selection and allow creation. | templates.val([]).change()
$('#template_title').focus()
else |
User submitted new template so check it out already. | opt = loadTemplate
content: $('#template_content').val()
enabled: String $('#template_enabled').is ':checked'
image: $('#template_image option:selected').val()
key: utils.keyGen()
readOnly: no
shortcut: $('#template_shortcut').val().trim().toUpperCase()
title: $('#template_title').val().trim()
usage: 0 |
Wipe any pre-existing error messages. | clearErrors() |
Confirm that the template meets the criteria. | errors = validateTemplate opt, yes
if errors.length is 0
log.debug 'Adding the following option...', opt
templates.append opt
opt.attr 'selected', 'selected'
updateToolbarTemplates()
templates.change().focus()
saveTemplates()
analytics.track 'Templates', 'Added', opt.text()
else |
Show the error message(s) to the user. | showErrors errors |
Prompt the user to confirm removal of the selected template. | $('#delete_btn').click ->
$('.delete_hdr').html i18n.get 'opt_delete_wizard_header',
templates.find('option:selected').text()
$('#delete_con').modal 'show' |
Cancel the template removal process. | $('.delete_no_btn').click -> $('#delete_con').modal 'hide' |
Finalize the template removal. | $('.delete_yes_btn').click ->
opt = templates.find 'option:selected'
if opt.data('readOnly') isnt 'true'
title = opt.text()
opt.remove()
templates.scrollTop(0).change().focus()
saveTemplates()
log.debug "Deleted #{title} template"
analytics.track 'Templates', 'Deleted', title
$('#delete_con').modal 'hide'
updateToolbarTemplates() |
Move the selected option down once when the Down control is clicked. | $('#moveDown_btn').click ->
opt = templates.find 'option:selected'
opt.insertAfter opt.next()
templates.change().focus()
saveTemplates() |
Move the selected option up once when the Up control is clicked. | $('#moveUp_btn').click ->
opt = templates.find 'option:selected'
opt.insertBefore opt.prev()
templates.change().focus()
saveTemplates() |
Bind the event handlers required for exporting templates. | loadTemplateExportEvents = ->
log.trace()
templates = $ '#templates' |
Simulate alert closing without removing alert from the DOM. | $('.export_error .close').on 'click', ->
$('.export_error').find('span').html(' ').end().hide() |
Restore the previous view in the export process. | $('.export_back_btn').click ->
log.info 'Going back to previous export stage'
$('.export_con_stage1').show()
$('.export_con_stage2').hide() |
Prompt the user to selected the templates to be exported. | $('#export_btn').click ->
log.info 'Launching export wizard'
list = $ '.export_con_list'
list.find('option').remove()
updateTemplate templates.find 'option:selected'
templates.val([]).change()
$('.export_yes_btn').attr 'disabled', 'disabled'
$('.export_con_stage1').show()
$('.export_con_stage2').hide()
$('.export_content').val ''
$('.export_error').find('span').html(' ').end().hide()
templates.find('option').each ->
opt = $ this
list.append $ '<option/>',
text: opt.text()
value: opt.val()
$('#export_con').modal 'show' |
Enable/disable the continue button depending on whether or not any templates are currently selected. | $('.export_con_list').change ->
if $(this).find('option:selected').length > 0
$('.export_yes_btn').removeAttr 'disabled'
else
$('.export_yes_btn').attr 'disabled', 'disabled' |
Copy the text area contents to the system clipboard. | $('.export_copy_btn').click (event) ->
$this = $ this
ext.copy $('.export_content').val(), yes
$this.text i18n.get 'opt_export_wizard_copy_alt_button'
$this.delay 800
$this.queue ->
$this.text i18n.get 'opt_export_wizard_copy_button'
$this.dequeue()
event.preventDefault() |
Deselect all of the templates in the list. | $('.export_deselect_all_btn').click ->
$('.export_con_list option').removeAttr('selected').parent().focus()
$('.export_yes_btn').attr 'disabled', 'disabled' |
Cancel the export process. | $('.export_no_btn, .export_close_btn').click (event) ->
$('#export_con').modal 'hide'
event.preventDefault() |
Prompt the user to select a file location to save the exported data. | $('.export_save_btn').click ->
$this = $ this
str = $this.parents('.export_con_stage2').find('.export_content').val() |
Export-specific error handler for dealing with the FileSystem API. | exportErrorHandler = fileErrorHandler (message) ->
log.error message
$('.export_error').find('span').text(message).end().show() |
Write the contents of the text area in to a temporary file and then prompt the user to download it. | window.webkitRequestFileSystem window.TEMPORARY, 1024 * 1024, (fs) ->
fs.root.getFile 'export.json', create: yes, (fileEntry) ->
fileEntry.createWriter (fileWriter) ->
builder = new WebKitBlobBuilder()
done = no
builder.append str
fileWriter.onerror = exportErrorHandler
fileWriter.onwriteend = ->
if done
$('.export_error').find('span').html(' ').end().hide()
window.location.href = fileEntry.toURL()
else
done = yes
fileWriter.write builder.getBlob 'application/json'
fileWriter.truncate 0
, exportErrorHandler
, exportErrorHandler |
Select all of the templates in the list. | $('.export_select_all_btn').click ->
$('.export_con_list option').attr('selected', 'selected').parent().focus()
$('.export_yes_btn').removeAttr 'disabled' |
Create the exported data based on the selected templates. | $('.export_yes_btn').click ->
$this = $ this
items = $this.parents('.export_con_stage1').find '.export_con_list option'
keys = []
items.filter(':selected').each -> keys.push $(this).val()
$('.export_content').val createExport keys
$('.export_error').find('span').html(' ').end().hide()
$('.export_con_stage1').hide()
$('.export_con_stage2').show() |
Bind the event handlers required for importing templates. | loadTemplateImportEvents = ->
log.trace()
data = null
templates = $ '#templates' |
Simulate alert closing without removing alert from the DOM. | $('.import_error .close').on 'click', ->
$('.import_error').find('span').html(' ').end().hide() |
Restore the previous view in the import process. | $('.import_back_btn').click ->
log.info 'Going back to previous import stage'
$('.import_con_stage1').show()
$('.import_con_stage2, .import_con_stage3').hide() |
Prompt the user to input/load the data to be imported. | $('#import_btn').click ->
log.info 'Launching import wizard'
updateTemplate templates.find 'option:selected'
templates.val([]).change()
$('.import_con_stage1').show()
$('.import_con_stage2, .import_con_stage3').hide()
$('.import_content').val ''
$('.import_error').find('span').html(' ').end().hide()
$('.import_file_btn').val ''
$('#import_con').modal 'show' |
Enable/disable the finalize button depending on whether or not any templates are currently selected. | $('.import_con_list').change ->
if $(this).find('option:selected').length > 0
$('.import_final_btn').removeAttr 'disabled'
else
$('.import_final_btn').attr 'disabled', 'disabled' |
Deselect all of the templates in the list. | $('.import_deselect_all_btn').click ->
$('.import_con_list option').removeAttr('selected').parent().focus()
$('.import_final_btn').attr 'disabled', 'disabled' |
Read the contents of the loaded file in to the text area and perform simple error handling. | $('.import_file_btn').change (event) ->
file = event.target.files[0]
reader = new FileReader()
reader.onerror = fileErrorHandler (message) ->
log.error message
$('.import_error').find('span').text(message).end().show()
reader.onload = (evt) ->
result = evt.target.result
log.debug 'Following contents were read from the file...', result
$('.import_error').find('span').html(' ').end().hide()
$('.import_content').val result
reader.readAsText file |
Finalize the import process. | $('.import_final_btn').click ->
$this = $ this
list = $this.parents('.import_con_stage2').find '.import_con_list'
list.find('option:selected').each ->
opt = $ this
existingOpt = templates.find "option[value='#{opt.val()}']"
opt.removeAttr 'selected'
if existingOpt.length is 0
templates.append opt
else
existingOpt.replaceWith opt
$('#import_con').modal 'hide'
updateToolbarTemplates()
templates.focus()
saveTemplates yes
if data?
analytics.track 'Templates', 'Imported', data.version,
data.templates.length |
Cancel the import process. | $('.import_no_btn, .import_close_btn').click -> $('#import_con').modal 'hide' |
Paste the contents of the system clipboard in to the text area. | $('.import_paste_btn').click ->
$this = $ this
$('.import_file_btn').val ''
$('.import_content').val ext.paste()
$this.text i18n.get 'opt_import_wizard_paste_alt_button'
$this.delay 800
$this.queue ->
$this.text i18n.get 'opt_import_wizard_paste_button'
$this.dequeue() |
Select all of the templates in the list. | $('.import_select_all_btn').click ->
$('.import_con_list option').attr('selected', 'selected').parent().focus()
$('.import_final_btn').removeAttr 'disabled' |
Read the imported data and attempt to extract any valid templates and list the changes to user for them to check and finalize. | $('.import_yes_btn').click ->
$this = $(this).attr 'disabled', 'disabled'
list = $ '.import_con_list'
str = $this.parents('.import_con_stage1').find('.import_content').val()
$('.import_error').find('span').html(' ').end().hide()
try
importData = createImport str
catch error
log.error error
$('.import_error').find('span').text(error).end().show()
if importData
data = readImport importData
if data.templates.length is 0
$('.import_con_stage3').show()
$('.import_con_stage1, .import_con_stage2').hide()
else
list.find('option').remove()
$('.import_count').text data.templates.length
list.append loadTemplate template for template in data.templates
$('.import_final_btn').attr 'disabled', 'disabled'
$('.import_con_stage2').show()
$('.import_con_stage1, .import_con_stage3').hide()
$this.removeAttr 'disabled' |
Bind the event handlers required for persisting template changes. | loadTemplateSaveEvents = ->
log.trace()
bindTemplateSaveEvent '#template_enabled, #template_image', 'change',
(opt, key) ->
value = switch key
when 'enabled' then "#{@is ':checked'}"
when 'image' then @val()
log.debug "Changing template #{key} to '#{value}'"
opt.data key, value
, saveTemplates
bindTemplateSaveEvent "#template_content, #template_shortcut
, #template_title", 'input', (opt, key) ->
switch key
when 'content' then opt.data key, @val()
when 'shortcut'
value = @val().toUpperCase().trim()
if value and not isShortcutValid value
opt.data 'error', i18n.get 'opt_template_shortcut_invalid'
else
log.debug "Changing template #{key} to '#{value}'"
opt.data key, value
when 'title'
value = @val().trim()
if value.length is 0
opt.data 'error', i18n.get 'opt_template_title_invalid'
else
opt.text value
, (jel, opt, key) ->
clearErrors selector = "##{jel.attr 'id'}"
if opt.data 'error'
showErrors [
message: opt.data 'error'
selector: selector
]
opt.removeData 'error'
else
saveTemplates() |
Update the toolbar behaviour section of the options page with the current settings. | loadToolbar = ->
log.trace()
toolbar = store.get 'toolbar'
if toolbar.popup
$('#toolbarPopupYes').addClass 'active'
else
$('#toolbarPopupNo').addClass 'active'
$('#toolbarClose').attr 'checked', 'checked' if toolbar.close
$('#toolbarOptions').attr 'checked', 'checked' if toolbar.options
updateToolbarTemplates()
loadToolbarControlEvents()
loadToolbarSaveEvents() |
Bind the event handlers required for controlling toolbar behaviour changes. | loadToolbarControlEvents = ->
log.trace() |
Bind a click event to listen for changes to the button selection. | $('#toolbarPopup .btn').click( ->
$(".#{$(this).attr 'id'}").show().siblings().hide()
).filter('.active').click() |
Bind the event handlers required for persisting toolbar behaviour changes. | loadToolbarSaveEvents = ->
log.trace()
$('#toolbarPopup .btn').click ->
popup = not $('#toolbarPopupYes').hasClass 'active'
store.modify 'toolbar', (toolbar) -> toolbar.popup = popup
ext.updateToolbar()
log.debug "Toolbar popup enabled: #{popup}"
analytics.track 'Toolbars', 'Changed', 'Behaviour', Number popup
bindSaveEvent '#toolbarClose, #toolbarKey, #toolbarOptions', 'change',
'toolbar', (key) ->
value = if key is 'key' then @val() else @is ':checked'
log.debug "Changing toolbar #{key} to '#{value}'"
value
, (jel, key, value) ->
ext.updateToolbar()
key = key[0].toUpperCase() + key.substr 1
if key is 'Key'
analytics.track 'Toolbars', 'Changed', key
else
analytics.track 'Toolbars', 'Changed', key, Number value |
Update the URL shorteners section of the options page with the current settings. | loadUrlShorteners = ->
log.trace()
$('input[name="enabled_shortener"]').each ->
$this = $ this
$this.attr 'checked', 'checked' if store.get "#{$this.attr 'id'}.enabled"
yes
yourls = store.get 'yourls'
$('#yourlsAuthentication' + (switch yourls.authentication
when 'advanced' then 'Advanced'
when 'basic' then 'Basic'
else 'None'
)).addClass 'active'
$('#yourlsPassword').val yourls.password
$('#yourlsSignature').val yourls.signature
$('#yourlsUrl').val yourls.url
$('#yourlsUsername').val yourls.username
loadUrlShortenerAccounts()
loadUrlShortenerControlEvents()
loadUrlShortenerSaveEvents() |
Update the accounts in the URL shorteners section of the options pages with current state of their OAuth objects. | loadUrlShortenerAccounts = ->
log.trace() |
Retrieve all URL shortener services that use OAuth and iterate over the results. | for shortener in ext.queryUrlShortener ((shortener) -> shortener.oauth?), no
do (shortener) -> |
Bind the event handler required for logging in and out of accounts and then reflect the current login state in the button. | button = $ "##{shortener.name}Account"
button.click ->
$this = $(this).blur()
if $this.data('oauth') isnt 'true'
$this.tooltip 'hide'
log.debug "Attempting to authorize #{shortener.name}"
shortener.oauth.authorize ->
log.debug "Authorization response for #{shortener.name}...",
arguments
if success = shortener.oauth.hasAccessToken()
$this.addClass('btn-danger').removeClass 'btn-success'
$this.data 'oauth', 'true'
$this.html i18n.get 'opt_url_shortener_logout_button'
analytics.track 'Shorteners', 'Login', shortener.title,
Number success
else
log.debug "Removing authorization for #{shortener.name}"
shortener.oauth.clearAccessToken()
if $this.attr('id') is 'bitlyAccount'
shortener.oauth.clear 'apiKey'
shortener.oauth.clear 'login'
$this.addClass('btn-success').removeClass 'btn-danger'
$this.data 'oauth', 'false'
$this.html i18n.get 'opt_url_shortener_login_button'
analytics.track 'Shorteners', 'Logout', shortener.title
if shortener.oauth.hasAccessToken()
button.addClass('btn-danger').removeClass 'btn-success'
button.data 'oauth', 'true'
button.html i18n.get 'opt_url_shortener_logout_button'
else
button.addClass('btn-success').removeClass 'btn-danger'
button.data 'oauth', 'false'
button.html i18n.get 'opt_url_shortener_login_button' |
Bind the event handlers required for controlling URL shortener configuration changes. | loadUrlShortenerControlEvents = ->
log.trace() |
Bind a click event to listen for changes to the button selection. | $('#yourlsAuthentication button').click( ->
$(".#{$(this).attr 'id'}").show().siblings().hide()
).filter('.active').click() |
Bind the event handlers required for persisting URL shortener configuration changes. | loadUrlShortenerSaveEvents = ->
log.trace()
$('input[name="enabled_shortener"]').change ->
store.modify 'bitly', 'googl', 'yourls', (data, key) ->
if data.enabled = $("##{key}").is ':checked'
shortener = ext.queryUrlShortener (shortener) -> shortener.name is key
log.debug "Enabling #{shortener.title} URL shortener"
analytics.track 'Shorteners', 'Enabled', shortener.title
$('#yourlsAuthentication button').click ->
$this = $ this
auth = $this.attr('id').match(/yourlsAuthentication(.*)/)[1]
store.modify 'yourls', (yourls) ->
yourls.authentication = if auth is 'None' then '' else auth.toLowerCase()
log.debug "YOURLS authentication changed: #{auth}"
analytics.track 'Shorteners', 'Changed', 'YOURLS Authentication',
$this.index()
bindSaveEvent "#yourlsPassword, #yourlsSignature, #yourlsUrl
, #yourlsUsername", 'input', 'yourls', -> @val().trim() |
Save functions | |
Update the settings with the values from the template section of the options page. | saveTemplates = (updateUI) ->
log.trace() |
Validate all the | errors = []
templates = [] |
Update the settings for each template based on their corresponding options. | $('#templates option').each ->
$this = $ this
template = deriveTemplate $this
errors = validateTemplate template, no
return templates.push template if errors.length is 0 |
Show the user which template failed validation. | if updateUI
$this.attr 'selected', 'selected'
$('#templates').change().focus()
no |
Determine whether or not any validation errors were encountered. | if errors.length is 0
clearErrors() if updateUI |
Ensure the data for all changes to templates are persisted. | store.set 'templates', templates
ext.updateTemplates()
else
showErrors errors if updateUI |
Update the existing template with information extracted from the imported
template provided. | updateImportedTemplate = (template, existing) ->
log.trace()
log.debug 'Updating existing template with the following imported data...',
template |
Ensure that read-only templates are protected. | unless existing.readOnly
existing.content = template.content |
Only allow valid titles. | existing.title = template.title if 0 < template.title.length <= 32
existing.enabled = template.enabled |
Only allow existing images or None. | if template.image is '' or icons.exists template.image
existing.image = template.image |
Only allow valid keyboard shortcuts or empty. | if template.shortcut is '' or isShortcutValid template.shortcut
existing.shortcut = template.shortcut
existing.usage = template.usage
log.debug 'Updated the following template...', existing
existing |
Update the specified | updateTemplate = (option) ->
log.trace()
if option.length
option.data 'content', $('#template_content').val()
option.data 'enabled', String $('#template_enabled').is ':checked'
option.data 'image', $('#template_image option:selected').val()
option.data 'shortcut', $('#template_shortcut').val().trim().toUpperCase()
option.text $('#template_title').val().trim()
log.debug 'Updated the following option with field values...', option
option |
Update the selection of templates in the toolbar behaviour section to reflect those available in the templates section. | updateToolbarTemplates = ->
log.trace()
templates = []
toolbarKey = store.get 'toolbar.key'
toolbarTemplates = $ '#toolbarKey'
lastSelectedTemplate = toolbarTemplates.find 'option:selected'
lastSelectedTemplateKey = ''
if lastSelectedTemplate.length
lastSelectedTemplateKey = lastSelectedTemplate.val()
toolbarTemplates.find('option').remove()
$('#templates option').each ->
$this = $ this
template =
key: $this.val()
selected: no
title: $this.text()
if lastSelectedTemplateKey
template.selected = yes if template.key is lastSelectedTemplateKey
else if template.key is toolbarKey
template.selected = yes
templates.push template
for template in templates
option = $ '<option/>',
text: template.title
value: template.key
option.attr 'selected', 'selected' if template.selected
toolbarTemplates.append option |
Validation functions | |
Remove all validation errors from the fields. | clearErrors = (selector) ->
log.trace()
if selector?
log.debug "Clearing displayed validation errors for '#{selector}'"
group = $(selector).parents('.control-group').first()
group.removeClass('error').find('.controls .error-message').remove()
else
log.debug 'Clearing all displayed validation errors'
$('.control-group.error').removeClass 'error'
$('.error-message').remove() |
Indicate whether or not the specified | isKeyNew = (key, additionalKeys = []) ->
log.trace()
log.debug "Checking if template key '#{key}' is new"
available = yes
$('#templates option').each -> available = no if $(this).val() is key
available and key not in additionalKeys |
Indicate whether or not the specified | isKeyValid = (key) ->
log.trace()
log.debug "Validating template key '#{key}'"
R_VALID_KEY.test key |
Indicate whether or not the specified keyboard | isShortcutValid = (shortcut) ->
log.trace()
log.debug "Validating keyboard shortcut '#{shortcut}'"
R_VALID_SHORTCUT.test shortcut |
Display all of the specified | showErrors = (errors) ->
log.trace()
log.debug 'Creating an alert for the following errors...', errors
for error in errors
group = $(error.selector).focus().parents('.control-group').first()
group.addClass('error').find('.controls').append $ '<p/>',
class: 'error-message help-block'
html: error.message |
Indicate whether or not | validateImportedTemplate = (template) ->
log.trace()
log.debug 'Validating property types of the following template...', template
'object' is typeof template and
'string' is typeof template.content and
'boolean' is typeof template.enabled and
'string' is typeof template.image and
'string' is typeof template.key and
'string' is typeof template.shortcut and
'string' is typeof template.title and
'number' is typeof template.usage |
Validate the specified | validateTemplate = (object, isNew) ->
log.trace()
errors = []
template = if $.isPlainObject object then object else deriveTemplate object
log.debug 'Validating the following template...', template |
Only validate the key and title of user-defined templates. | unless template.readOnly |
Only validate keys of new templates. | if isNew and not isKeyValid template.key
errors.push message: i18n.get 'opt_template_key_invalid' |
Title is missing but is required. | if template.title.length is 0
errors.push
message: i18n.get 'opt_template_title_invalid'
selector: '#template_title' |
Validate whether or not the shortcut is valid. | if template.shortcut and not isShortcutValid template.shortcut
errors.push
message: i18n.get 'opt_template_shortcut_invalid'
selector: '#template_shortcut' |
Indicate whether or not any validation errors were encountered. | log.debug 'Following validation errors were found...', errors
errors |
Miscellaneous functions | |
Create a template from the information from the imported | addImportedTemplate = (template) ->
log.trace()
log.debug 'Creating a new template with the following details...', template
if isKeyValid template.key
newTemplate =
content: template.content
enabled: template.enabled
image: ''
key: template.key
readOnly: no
shortcut: ''
title: i18n.get 'untitled'
usage: template.usage |
Only allow existing images. | newTemplate.image = template.image if icons.exists template.image |
Only allow valid keyboard shortcuts. | if isShortcutValid template.shortcut
newTemplate.shortcut = template.shortcut |
Only allow valid titles. | newTemplate.title = template.title if 0 < template.title.length <= 32
log.debug 'Following template was created...', newTemplate
newTemplate |
Create a JSON string to export the templates with the specified | createExport = (keys) ->
log.trace()
log.debug 'Creating an export string for the following keys...', keys
data = templates: [], version: ext.version
for key in keys
data.templates.push deriveTemplate $ "#templates option[value='#{key}']"
if data.templates.length
analytics.track 'Templates', 'Exported', ext.version, data.templates.length
log.debug 'Following export data has been created...', data
JSON.stringify data |
Create a JSON object from the imported string specified. | createImport = (str) ->
log.trace()
log.debug 'Parsing the following import string...', str
try
data = JSON.parse str
catch error
throw i18n.get 'error_import_data'
if not $.isArray(data.templates) or data.templates.length is 0 or
typeof data.version isnt 'string'
throw i18n.get 'error_import_invalid'
log.debug 'Following data was created from the string...', data
data |
Create a template with the information derived from the specified | deriveTemplate = (option) ->
log.trace()
log.debug 'Deriving a template from the following option...', option
if option.length > 0
template =
content: option.data 'content'
enabled: option.data('enabled') is 'true'
image: option.data 'image'
index: option.parent().find('option').index option
key: option.val()
readOnly: option.data('readOnly') is 'true'
shortcut: option.data 'shortcut'
title: option.text()
usage: parseInt option.data('usage'), 10
log.debug 'Following template was derived from the option...', template
template |
Add the user feedback feature to the page. | feedback = ->
unless feedbackAdded |
Temporary workaround for Content Security Policy issues with UserVoice's
use of inline JavaScript. | uvwDialogClose = $ '#uvw-dialog-close[onclick]'
uvwDialogClose.live 'hover', ->
$(this).removeAttr 'onclick'
uvwDialogClose.die 'hover'
$(uvwDialogClose.selector.replace('[onclick]', '')).live 'click', (e) ->
UserVoice.hidePopupWidget()
e.preventDefault()
uvTabLabel = $ '#uvTabLabel[href^="javascript:"]'
uvTabLabel.live 'hover', ->
$(this).removeAttr 'href'
uvTabLabel.die 'hover' |
Continue with normal process of loading Widget. | window.uvOptions = {}
uv = document.createElement 'script'
uv.async = 'async'
uv.src = WIDGET_SOURCE
script = document.getElementsByTagName('script')[0]
script.parentNode.insertBefore uv, script
feedbackAdded = yes |
Error handler to be used when dealing with the FileSystem API that passes a
localized error message to | fileErrorHandler = (callback) ->
(e) ->
callback i18n.get switch e.code
when FileError.NOT_FOUND_ERR then 'error_file_not_found'
when FileError.ABORT_ERR then 'error_file_aborted'
else 'error_file_default' |
Read the imported data created by | readImport = (importData) ->
log.trace()
log.debug 'Importing the following data...', importData
data = templates: []
keys = []
for template in importData.templates
existing = {} |
Ensure templates imported from previous versions are valid for 1.0.0+. | if importData.version < '1.0.0'
template.image = if template.image > 0
icons.fromLegacy(template.image - 1)?.name or ''
else
''
template.key = ext.getKeyForName template.name
template.usage = 0
else if importData.version < '1.1.0'
template.image = icons.fromLegacy(template.image)?.name or ''
if validateImportedTemplate template
if isKeyNew template.key, keys |
Attempt to create and add the new template. | template = addImportedTemplate template
if template
data.templates.push template
keys.push template.key
else |
Attempt to update the previously imported template. | for imported, i in data.templates
if imported.key is template.key
existing = updateImportedTemplate template, imported
data.templates[i] = existing
break
unless existing.key |
Attempt to derive the existing template from the available options. | existing = deriveTemplate $ "#templates
option[value='#{template.key}']" |
Attempt to update the derived template. | if existing
existing = updateImportedTemplate template, existing
data.templates.push existing
keys.push existing.key
log.debug 'Following data was derived from that imported...', data
data |
Options page setup | options = window.options = new class Options extends utils.Class |
Public functions | |
Initialize the options page. | init: ->
log.trace()
log.info 'Initializing the options page' |
Add support for analytics if the user hasn't opted out. | analytics.add() if store.get 'analytics' |
Add the user feedback feature to the page. | feedback() |
Begin initialization. | i18n.init
bitlyAccount: opt_url_shortener_account_title: i18n.get 'shortener_bitly'
googlAccount: opt_url_shortener_account_title: i18n.get 'shortener_googl'
version_definition: opt_guide_standard_version_text: ext.version
$('.year-repl').html "#{new Date().getFullYear()}" |
Bind tab selection event to all tabs. | initialTabChange = yes
$('a[tabify]').click ->
target = $(this).attr 'tabify'
nav = $ "#navigation a[tabify='#{target}']"
parent = nav.parent 'li'
unless parent.hasClass 'active'
parent.siblings().removeClass 'active'
parent.addClass 'active'
$(target).show().siblings('.tab').hide()
store.set 'options_active_tab', id = nav.attr 'id'
unless initialTabChange
id = id.match(/(\S*)_nav$/)[1]
id = id[0].toUpperCase() + id.substr 1
log.debug "Changing tab to #{id}"
analytics.track 'Tabs', 'Changed', id
initialTabChange = no
$(document.body).scrollTop 0 |
Reflect the persisted tab. | store.init 'options_active_tab', 'general_nav'
optionsActiveTab = store.get 'options_active_tab'
$("##{optionsActiveTab}").click()
log.debug "Initially displaying tab for #{optionsActiveTab}" |
Bind Developer Tools wizard events to their corresponding elements. | $('#tools_nav').click -> $('#tools_wizard').modal 'show'
$('.tools_close_btn').click -> $('#tools_wizard').modal 'hide' |
Ensure that form submissions don't reload the page. | $('form').submit -> no |
Load the current option values. | load()
$('#template_shortcut_modifier').html if ext.isThisPlatform 'mac'
ext.SHORTCUT_MAC_MODIFIERS
else
ext.SHORTCUT_MODIFIERS |
Initialize all popovers, tooltips and go-to links. | $('[popover]').each ->
$this = $ this
trigger = $this.attr 'data-trigger'
trigger = if trigger? then trigger.trim().toLowerCase() else 'hover'
$this.popover
content: -> i18n.get $this.attr 'popover'
trigger: trigger
if trigger is 'manual'
$this.click -> $this.popover 'toggle'
$('[title]').each ->
$this = $ this
placement = $this.attr 'data-placement'
placement = if placement? then placement.trim().toLowerCase() else 'top'
$this.tooltip placement: placement
$('[data-goto]').click ->
goto = $ $(this).attr 'data-goto'
log.debug "Relocating view to include '#{goto.selector}'"
$(window).scrollTop goto.position()?.top or 0 |
Initialize | utils.ready -> options.init()
|