ZeroOmega/omega-target-chromium-extension/src/options.coffee
2014-11-28 20:45:20 +08:00

252 lines
8.1 KiB
CoffeeScript

OmegaTarget = require('omega-target')
OmegaPac = OmegaTarget.OmegaPac
Promise = OmegaTarget.Promise
xhr = Promise.promisify(require('xhr'))
url = require('url')
chromeApiPromisifyAll = require('./chrome_api')
proxySettings = chromeApiPromisifyAll(chrome.proxy.settings)
parseExternalProfile = require('./parse_external_profile')
class ChromeOptions extends OmegaTarget.Options
parseExternalProfile: (details) ->
parseExternalProfile(details, @_options, @_fixedProfileConfig.bind(this))
fetchUrl: (dest_url, opt_bypass_cache) ->
if opt_bypass_cache
parsed = url.parse(dest_url, true)
parsed.search = undefined
parsed.query['_'] = Date.now()
dest_url = url.format(parsed)
xhr(dest_url).get(1)
updateProfile: (args...) ->
super(args...).then (results) ->
error = false
for own profileName, result of results
if result instanceof Error
error = true
break
if error
# TODO(catus): Find a better way to notify the user.
###
@setBadge(
text: '!'
color: '#faa732'
title: chrome.i18n.getMessage('browserAction_titleDownloadFail')
)
###
return results
_proxyNotControllable: null
proxyNotControllable: -> @_proxyNotControllable
setProxyNotControllable: (reason) ->
@_proxyNotControllable = reason
if reason
@_state.set({'proxyNotControllable': reason})
@setBadge()
else
@_state.remove(['proxyNotControllable'])
@clearBadge()
_badgeTitle: null
setBadge: (options) ->
if not options
options =
if @_proxyNotControllable
text: '='
color: '#da4f49'
else
text: '?'
color: '#49afcd'
chrome.browserAction.setBadgeText(text: options.text)
chrome.browserAction.setBadgeBackgroundColor(color: options.color)
if options.title
@_badgeTitle = options.title
chrome.browserAction.setTitle(title: options.title)
else
@_badgeTitle = null
clearBadge: ->
if @_badgeTitle
@currentProfileChanged('clearBadge')
if @_proxyNotControllable
@setBadge()
else
chrome.browserAction.setBadgeText(text: '')
return
_fixedProfileConfig: (profile) ->
config = {}
config['mode'] = 'fixed_servers'
rules = {}
protocols = ['proxyForHttp', 'proxyForHttps', 'proxyForFtp']
protocolProxySet = false
for protocol in protocols when profile[protocol]?
rules[protocol] = profile[protocol]
protocolProxySet = true
if profile.fallbackProxy
if profile.fallbackProxy.scheme == 'http'
# Chromium does not allow HTTP proxies in 'fallbackProxy'.
if not protocolProxySet
# Use 'singleProxy' if no proxy is configured for other protocols.
rules['singleProxy'] = profile.fallbackProxy
else
# Try to set the proxies of all possible protocols.
for protocol in protocols
rules[protocol] ?= profile.fallbackProxy
else
rules['fallbackProxy'] = profile.fallbackProxy
else if not protocolProxySet
config['mode'] = 'direct'
if config['mode'] != 'direct'
rules['bypassList'] = profile.bypassList.map((b) -> b.pattern)
config['rules'] = rules
return config
_proxyChangeWatchers: []
_proxyChangeListener: null
watchProxyChange: (callback) ->
if not @_proxyChangeListener?
@_proxyChangeListener = (details) =>
for watcher in @_proxyChangeWatchers
watcher(details)
chrome.proxy.settings.onChange.addListener @_proxyChangeListener
@_proxyChangeWatchers.push(callback)
applyProfileProxy: (profile, meta) ->
meta ?= profile
if profile.profileType == 'SystemProfile'
# Clear proxy settings, returning proxy control to Chromium.
return proxySettings.clearAsync({}).then =>
chrome.proxy.settings.get {}, @_proxyChangeListener
return
config = {}
if profile.profileType == 'DirectProfile'
config['mode'] = 'direct'
else if profile.profileType == 'PacProfile'
config['mode'] = 'pac_script'
config['pacScript'] = if profile.pacUrl
url: profile.pacUrl
mandatory: true
else
data: profile.pacScript
mandatory: true
else if profile.profileType == 'FixedProfile'
config = @_fixedProfileConfig(profile)
else
config['mode'] = 'pac_script'
config['pacScript'] =
data: null
mandatory: true
setPacScript = @pacForProfile(profile).then (script) ->
profileName = OmegaPac.PacGenerator.ascii(JSON.stringify(meta.name))
profileName = profileName.replace(/\*/g, '\\u002a')
profileName = profileName.replace(/\\/g, '\\u002f')
prefix = "/*OmegaProfile*#{profileName}*#{meta.revision}*/"
config['pacScript'].data = prefix + script
return
setPacScript ?= Promise.resolve()
setPacScript.then(->
proxySettings.setAsync({value: config})
).then =>
chrome.proxy.settings.get {}, @_proxyChangeListener
return
_quickSwitchInit: false
setQuickSwitch: (quickSwitch) ->
if quickSwitch
chrome.browserAction.setPopup({popup: ''})
if not @_quickSwitchInit
@_quickSwitchInit = true
chrome.browserAction.onClicked.addListener (tab) =>
@clearBadge()
profiles = @_options['-quickSwitchProfiles']
index = profiles.indexOf(@_currentProfileName)
index = (index + 1) % profiles.length
@applyProfile(profiles[index]).then =>
if @_options['-refreshOnProfileChange']
if tab.url and tab.url.indexOf('chrome') != 0
chrome.tabs.reload(tab.id)
else
chrome.browserAction.setPopup({popup: 'popup.html'})
Promise.resolve()
_alarms: null
schedule: (name, periodInMinutes, callback) ->
name = 'omega.' + name
if not _alarms?
@_alarms = {}
chrome.alarms.onAlarm.addListener (alarm) =>
@_alarms[alarm.name]?()
if periodInMinutes < 0
delete @_alarms[name]
chrome.alarms.clear(name)
else
@_alarms[name] = callback
chrome.alarms.create(name, {
periodInMinutes: periodInMinutes
})
Promise.resolve()
printFixedProfile: (profile) ->
return unless profile.profileType == 'FixedProfile'
result = ''
for scheme in OmegaPac.Profiles.schemes when profile[scheme.prop]
pacResult = OmegaPac.Profiles.pacResult(profile[scheme.prop])
if scheme.scheme
result += "#{scheme.scheme}: #{pacResult}\n"
else
result += "#{pacResult}\n"
result ||= chrome.i18n.getMessage(
'browserAction_profileDetails_DirectProfile')
return result
printProfile: (profile) ->
type = profile.profileType
if type.indexOf('RuleListProfile') >= 0
type = 'RuleListProfile'
if type == 'FixedProfile'
@printFixedProfile(profile)
else if type == 'PacProfile' and profile.pacUrl
profile.pacUrl
else
chrome.i18n.getMessage('browserAction_profileDetails_' + type) || null
upgrade: (options, changes) ->
super(options).catch (err) =>
return Promise.reject err if options?['schemaVersion']
getOldOptions = if @switchySharp
@switchySharp.getOptions().timeout(1000)
else
Promise.reject()
getOldOptions = getOldOptions.catch =>
if options?['config']
Promise.resolve options
else if localStorage['config']
Promise.resolve localStorage
else
Promise.reject new @NoOptionsError()
getOldOptions.then (oldOptions) =>
i18n = {
upgrade_profile_auto: chrome.i18n.getMessage('upgrade_profile_auto')
}
try
# Upgrade from SwitchySharp.
upgraded = require('./upgrade')(oldOptions, i18n)
catch ex
@log.error(ex)
return Promise.reject ex
if localStorage['config']
Object.getPrototypeOf(localStorage).clear.call(localStorage)
@_state.set({'firstRun': 'upgrade'})
return this && super(upgraded, upgraded)
onFirstRun: (reason) ->
chrome.tabs.create url: chrome.extension.getURL('options.html')
module.exports = ChromeOptions