├── omega-web
├── .gitignore
├── .gitattributes
├── Gruntfile.coffee
├── img
│ └── icons
│ │ ├── omega-128.png
│ │ ├── omega-48.png
│ │ ├── omega-64.png
│ │ ├── omega-action-16.png
│ │ ├── omega-action-19.png
│ │ ├── omega-action-24.png
│ │ ├── omega-action-32.png
│ │ ├── omega-action.svg
│ │ ├── omega-128.svg
│ │ └── draw_omega.js
├── grunt
│ ├── bower.coffee
│ ├── ngAnnotate.coffee
│ ├── mochaTest.coffee
│ ├── autoprefixer.coffee
│ ├── less.coffee
│ ├── aliases.coffee
│ ├── coffee.coffee
│ ├── jade.coffee
│ ├── copy.coffee
│ ├── coffeelint.coffee
│ └── watch.coffee
├── src
│ ├── partials
│ │ ├── profile_unsupported.jade
│ │ ├── reset_options_confirm.jade
│ │ ├── input_group_clear.jade
│ │ ├── apply_options_confirm.jade
│ │ ├── delete_profile.jade
│ │ ├── rule_reset_confirm.jade
│ │ ├── cannot_delete_profile.jade
│ │ ├── delete_attached.jade
│ │ ├── options_welcome.jade
│ │ ├── rule_remove_confirm.jade
│ │ ├── profile_virtual.jade
│ │ ├── replace_profile.jade
│ │ ├── omega_profile_select.jade
│ │ ├── rename_profile.jade
│ │ ├── general.jade
│ │ ├── profile_rule_list.jade
│ │ ├── about.jade
│ │ ├── profile.jade
│ │ ├── fixed_auth_edit.jade
│ │ ├── profile_fixed.jade
│ │ ├── profile_pac.jade
│ │ ├── ui.jade
│ │ ├── io.jade
│ │ └── new_profile.jade
│ ├── omega
│ │ ├── controllers
│ │ │ ├── rule_list_profile.coffee
│ │ │ ├── about.coffee
│ │ │ ├── quick_switch.coffee
│ │ │ ├── pac_profile.coffee
│ │ │ ├── io.coffee
│ │ │ ├── fixed_profile.coffee
│ │ │ └── profile.coffee
│ │ ├── filters.coffee
│ │ ├── directives.coffee
│ │ └── app.coffee
│ ├── coffee
│ │ ├── log_error.coffee
│ │ ├── options_guide.coffee
│ │ ├── options.coffee
│ │ ├── switch_profile_guide.coffee
│ │ └── omega_decoration.coffee
│ ├── popup
│ │ ├── js
│ │ │ ├── i18n.js
│ │ │ ├── loader.js
│ │ │ ├── keyboard_help.js
│ │ │ ├── index.js
│ │ │ ├── proxy_not_controllable.js
│ │ │ └── keyboard.js
│ │ ├── proxy_not_controllable.html
│ │ ├── index.html
│ │ └── css
│ │ │ ├── dialog.css
│ │ │ └── index.css
│ ├── less
│ │ ├── common.less
│ │ └── popup.less
│ └── options.jade
├── package.json
└── bower.json
├── .gitignore
├── omega-pac
├── .gitattributes
├── .gitignore
├── Gruntfile.coffee
├── uglifyjs-shim.js
├── grunt
│ ├── aliases.coffee
│ ├── mochaTest.coffee
│ ├── watch.coffee
│ ├── coffeelint.coffee
│ └── browserify.coffee
├── index.coffee
├── test
│ ├── shexp_utils.coffee
│ ├── utils.coffee
│ └── pac_generator.coffee
├── package.json
└── src
│ ├── shexp_utils.coffee
│ ├── utils.coffee
│ └── pac_generator.coffee
├── omega-target
├── .gitignore
├── omega_pac_shim.js
├── src
│ ├── utils.coffee
│ ├── errors.coffee
│ ├── default_options.coffee
│ ├── browser_storage.coffee
│ ├── log.coffee
│ └── storage.coffee
├── Gruntfile.coffee
├── grunt
│ ├── aliases.coffee
│ ├── mochaTest.coffee
│ ├── watch.coffee
│ ├── coffeelint.coffee
│ └── browserify.coffee
├── index.coffee
└── package.json
├── omega-target-chromium-extension
├── omega_target_shim.js
├── index.coffee
├── .gitignore
├── grunt
│ ├── coffee.coffee
│ ├── mochaTest.coffee
│ ├── aliases.coffee
│ ├── compress.coffee
│ ├── po2crx.coffee
│ ├── coffeelint.coffee
│ ├── copy.coffee
│ ├── watch.coffee
│ └── browserify.coffee
├── Gruntfile.coffee
├── src
│ ├── module
│ │ ├── index.coffee
│ │ ├── proxy
│ │ │ ├── index.coffee
│ │ │ ├── proxy_impl.coffee
│ │ │ ├── proxy_auth.coffee
│ │ │ ├── proxy_impl_listener.coffee
│ │ │ └── proxy_impl_script.coffee
│ │ ├── chrome_api.coffee
│ │ ├── switchysharp.coffee
│ │ ├── external_api.coffee
│ │ ├── inspect.coffee
│ │ ├── chrome_port.coffee
│ │ ├── fetch_url.coffee
│ │ ├── tabs.coffee
│ │ └── storage.coffee
│ ├── coffee
│ │ ├── background_preload.coffee
│ │ └── omega_debug.coffee
│ └── js
│ │ ├── omega_target_popup.js
│ │ └── omega_webext_proxy_script.js
├── overlay
│ ├── background.html
│ └── manifest.json
├── package.json
└── grunt-po2crx.coffee
├── .tern-project
├── omega-build
├── package.json
└── Gruntfile.coffee
├── AUTHORS
├── .github
├── PULL_REQUEST_TEMPLATE.md
└── ISSUE_TEMPLATE.md
└── .circleci
└── config.yml
/omega-web/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /tmp
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | bower_components
3 |
--------------------------------------------------------------------------------
/omega-web/.gitattributes:
--------------------------------------------------------------------------------
1 | lib/* linguist-vendored
2 |
--------------------------------------------------------------------------------
/omega-pac/.gitattributes:
--------------------------------------------------------------------------------
1 | uglifyjs.js linguist-vendored
2 |
--------------------------------------------------------------------------------
/omega-pac/.gitignore:
--------------------------------------------------------------------------------
1 | /index.js
2 | /omega_pac.min.js
3 |
--------------------------------------------------------------------------------
/omega-target/.gitignore:
--------------------------------------------------------------------------------
1 | /index.js
2 | /omega_target.min.js
3 |
--------------------------------------------------------------------------------
/omega-target/omega_pac_shim.js:
--------------------------------------------------------------------------------
1 | module.exports = OmegaPac;
2 |
--------------------------------------------------------------------------------
/omega-target/src/utils.coffee:
--------------------------------------------------------------------------------
1 | exports.Promise = require('bluebird')
2 |
--------------------------------------------------------------------------------
/omega-pac/Gruntfile.coffee:
--------------------------------------------------------------------------------
1 | module.exports = require('load-grunt-config')
2 |
--------------------------------------------------------------------------------
/omega-web/Gruntfile.coffee:
--------------------------------------------------------------------------------
1 | module.exports = require('load-grunt-config')
2 |
--------------------------------------------------------------------------------
/omega-target/Gruntfile.coffee:
--------------------------------------------------------------------------------
1 | module.exports = require('load-grunt-config')
2 |
--------------------------------------------------------------------------------
/omega-pac/uglifyjs-shim.js:
--------------------------------------------------------------------------------
1 | require('uglify-js-real');
2 | module.exports = UglifyJS;
3 |
--------------------------------------------------------------------------------
/omega-target-chromium-extension/omega_target_shim.js:
--------------------------------------------------------------------------------
1 | module.exports = OmegaTarget
2 |
--------------------------------------------------------------------------------
/omega-target-chromium-extension/index.coffee:
--------------------------------------------------------------------------------
1 | module.exports = require('./src/module')
2 |
--------------------------------------------------------------------------------
/omega-web/img/icons/omega-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reference/SwitchyOmega/master/omega-web/img/icons/omega-128.png
--------------------------------------------------------------------------------
/omega-web/img/icons/omega-48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reference/SwitchyOmega/master/omega-web/img/icons/omega-48.png
--------------------------------------------------------------------------------
/omega-web/img/icons/omega-64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reference/SwitchyOmega/master/omega-web/img/icons/omega-64.png
--------------------------------------------------------------------------------
/omega-web/img/icons/omega-action-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reference/SwitchyOmega/master/omega-web/img/icons/omega-action-16.png
--------------------------------------------------------------------------------
/omega-web/img/icons/omega-action-19.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reference/SwitchyOmega/master/omega-web/img/icons/omega-action-19.png
--------------------------------------------------------------------------------
/omega-web/img/icons/omega-action-24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reference/SwitchyOmega/master/omega-web/img/icons/omega-action-24.png
--------------------------------------------------------------------------------
/omega-web/img/icons/omega-action-32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reference/SwitchyOmega/master/omega-web/img/icons/omega-action-32.png
--------------------------------------------------------------------------------
/.tern-project:
--------------------------------------------------------------------------------
1 | {
2 | "libs": [
3 | "chai"
4 | ],
5 | "plugins": {
6 | "node": {},
7 | "coffee": {}
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/omega-pac/grunt/aliases.coffee:
--------------------------------------------------------------------------------
1 | module.exports =
2 | default: [
3 | 'coffeelint'
4 | 'browserify'
5 | ]
6 | test: ['mochaTest']
7 |
--------------------------------------------------------------------------------
/omega-target-chromium-extension/.gitignore:
--------------------------------------------------------------------------------
1 | /index.js
2 | /omega_target_*.min.js
3 |
4 | /tmp
5 | /build
6 | /release.zip
7 | /web-ext-artifacts
8 |
--------------------------------------------------------------------------------
/omega-target/grunt/aliases.coffee:
--------------------------------------------------------------------------------
1 | module.exports =
2 | default: [
3 | 'coffeelint'
4 | 'browserify'
5 | ]
6 | test: ['mochaTest']
7 |
--------------------------------------------------------------------------------
/omega-web/grunt/bower.coffee:
--------------------------------------------------------------------------------
1 | module.exports =
2 | install:
3 | options:
4 | copy: true
5 | targetDir: 'build/lib/'
6 | layout: 'byComponent'
7 |
--------------------------------------------------------------------------------
/omega-web/src/partials/profile_unsupported.jade:
--------------------------------------------------------------------------------
1 | .lead
2 | | {{'options_profileUnsupported' | tr:profile.profileType}}
3 | p {{'options_profileUnsupportedHelp' | tr}}
4 |
--------------------------------------------------------------------------------
/omega-web/grunt/ngAnnotate.coffee:
--------------------------------------------------------------------------------
1 | module.exports =
2 | options:
3 | singleQuotes: true
4 | app:
5 | files:
6 | 'build/js/omega.ngmin.js': 'build/js/omega.js'
7 |
--------------------------------------------------------------------------------
/omega-pac/grunt/mochaTest.coffee:
--------------------------------------------------------------------------------
1 | module.exports =
2 | test:
3 | options:
4 | reporter: 'spec'
5 | require: 'coffee-script/register'
6 | src: ['test/**/*.coffee']
7 |
--------------------------------------------------------------------------------
/omega-web/grunt/mochaTest.coffee:
--------------------------------------------------------------------------------
1 | module.exports =
2 | test:
3 | options:
4 | reporter: 'spec'
5 | require: 'coffee-script/register'
6 | src: ['test/**/*.coffee']
7 |
--------------------------------------------------------------------------------
/omega-target/grunt/mochaTest.coffee:
--------------------------------------------------------------------------------
1 | module.exports =
2 | test:
3 | options:
4 | reporter: 'spec'
5 | require: 'coffee-script/register'
6 | src: ['test/**/*.coffee']
7 |
--------------------------------------------------------------------------------
/omega-web/src/omega/controllers/rule_list_profile.coffee:
--------------------------------------------------------------------------------
1 | angular.module('omega').controller 'RuleListProfileCtrl', ($scope) ->
2 | $scope.ruleListFormats = OmegaPac.Profiles.ruleListFormats
3 |
--------------------------------------------------------------------------------
/omega-web/grunt/autoprefixer.coffee:
--------------------------------------------------------------------------------
1 | module.exports =
2 | options:
3 | map: false
4 | unprefixed:
5 | expand: true
6 | cwd: 'tmp/css'
7 | src: '**/*.css'
8 | dest: 'build/css/'
9 |
--------------------------------------------------------------------------------
/omega-target-chromium-extension/grunt/coffee.coffee:
--------------------------------------------------------------------------------
1 | module.exports =
2 | coffee:
3 | expand: true
4 | cwd: 'src/coffee'
5 | src: ['**/*.coffee']
6 | dest: 'build/js/'
7 | ext: '.js'
8 |
--------------------------------------------------------------------------------
/omega-target-chromium-extension/grunt/mochaTest.coffee:
--------------------------------------------------------------------------------
1 | module.exports =
2 | test:
3 | options:
4 | reporter: 'spec'
5 | require: 'coffee-script/register'
6 | src: ['test/**/*.coffee']
7 |
--------------------------------------------------------------------------------
/omega-web/grunt/less.coffee:
--------------------------------------------------------------------------------
1 | module.exports =
2 | web_options:
3 | files:
4 | 'tmp/css/options.css': 'src/less/options.less'
5 | web_popup:
6 | files:
7 | 'tmp/css/popup.css': 'src/less/popup.less'
8 |
--------------------------------------------------------------------------------
/omega-web/grunt/aliases.coffee:
--------------------------------------------------------------------------------
1 | module.exports =
2 | default: [
3 | 'copy'
4 | 'jade'
5 | 'less'
6 | 'autoprefixer'
7 | 'coffeelint'
8 | 'coffee'
9 | 'bower'
10 | ]
11 | test: ['mochaTest']
12 |
--------------------------------------------------------------------------------
/omega-web/grunt/coffee.coffee:
--------------------------------------------------------------------------------
1 | module.exports =
2 | web:
3 | expand: true
4 | cwd: 'src/coffee'
5 | src: ['**/*.coffee']
6 | dest: 'build/js/'
7 | ext: '.js'
8 | web_omega:
9 | files:
10 | 'build/js/omega.js': 'src/omega/**/*.coffee'
11 |
--------------------------------------------------------------------------------
/omega-pac/grunt/watch.coffee:
--------------------------------------------------------------------------------
1 | module.exports =
2 | grunt:
3 | options:
4 | reload: true
5 | files:
6 | 'grunt/*'
7 | tasks: ['coffeelint:tasks', 'default']
8 | src:
9 | files: ['src/**/*.coffee', 'test/**/*.coffee']
10 | tasks: ['default']
11 |
--------------------------------------------------------------------------------
/omega-target-chromium-extension/grunt/aliases.coffee:
--------------------------------------------------------------------------------
1 | module.exports =
2 | default: [
3 | 'coffeelint'
4 | 'browserify'
5 | 'coffee'
6 | 'copy'
7 | 'po2crx'
8 | ]
9 | test: ['mochaTest']
10 | release: ['default', 'chromium-manifest', 'compress']
11 |
--------------------------------------------------------------------------------
/omega-target/grunt/watch.coffee:
--------------------------------------------------------------------------------
1 | module.exports =
2 | grunt:
3 | options:
4 | reload: true
5 | files:
6 | 'grunt/*'
7 | tasks: ['coffeelint:tasks', 'default']
8 | src:
9 | files: ['src/**/*.coffee', 'test/**/*.coffee']
10 | tasks: ['default']
11 |
--------------------------------------------------------------------------------
/omega-web/src/coffee/log_error.coffee:
--------------------------------------------------------------------------------
1 | window.onerror = (message, url, line, col, err) ->
2 | log = localStorage['log'] || ''
3 | if err?.stack
4 | log += err.stack + '\n\n'
5 | else
6 | log += "#{url}:#{line}:#{col}:\t#{message}\n\n"
7 | localStorage['log'] = log
8 | return
9 |
--------------------------------------------------------------------------------
/omega-pac/index.coffee:
--------------------------------------------------------------------------------
1 | module.exports =
2 | Conditions: require('./src/conditions')
3 | PacGenerator: require('./src/pac_generator')
4 | Profiles: require('./src/profiles')
5 | RuleList: require('./src/rule_list')
6 | ShexpUtils: require('./src/shexp_utils')
7 |
8 | for name, value of require('./src/utils.coffee')
9 | module.exports[name] = value
10 |
--------------------------------------------------------------------------------
/omega-web/img/icons/omega-action.svg:
--------------------------------------------------------------------------------
1 |
2 |
13 |
--------------------------------------------------------------------------------
/omega-web/img/icons/omega-128.svg:
--------------------------------------------------------------------------------
1 |
2 |
13 |
--------------------------------------------------------------------------------
/omega-target-chromium-extension/Gruntfile.coffee:
--------------------------------------------------------------------------------
1 | module.exports = (grunt) ->
2 | require('load-grunt-config')(grunt)
3 | require('./grunt-po2crx')(grunt)
4 |
5 | grunt.registerTask 'chromium-manifest', ->
6 | manifest = grunt.file.readJSON('overlay/manifest.json')
7 | manifest.permissions = manifest.permissions.filter (p) -> p != 'downloads'
8 | grunt.file.write('tmp/manifest.json', JSON.stringify(manifest))
9 |
--------------------------------------------------------------------------------
/omega-target-chromium-extension/grunt/compress.coffee:
--------------------------------------------------------------------------------
1 | module.exports =
2 | options:
3 | archive: './release.zip'
4 | mode: 'zip'
5 | build:
6 | files: [
7 | {
8 | cwd: 'build'
9 | src: ['**', '!manifest.json']
10 | expand: true
11 | filter: 'isFile'
12 | }
13 | {
14 | cwd: 'tmp/'
15 | src: 'manifest.json'
16 | expand: true
17 | }
18 | ]
19 |
--------------------------------------------------------------------------------
/omega-web/grunt/jade.coffee:
--------------------------------------------------------------------------------
1 | module.exports =
2 | options:
3 | pretty: true
4 | web:
5 | files: [
6 | {
7 | expand: true
8 | dest: 'build/'
9 | cwd: 'src/'
10 | ext: '.html'
11 | src: '*.jade'
12 | }
13 | {
14 | expand: true
15 | dest: 'build/partials/'
16 | cwd: 'src/partials'
17 | ext: '.html'
18 | src: ['*.jade']
19 | }
20 | ]
21 |
--------------------------------------------------------------------------------
/omega-web/grunt/copy.coffee:
--------------------------------------------------------------------------------
1 | module.exports =
2 | pac:
3 | files:
4 | 'build/js/omega_pac.min.js': 'node_modules/omega-pac/omega_pac.min.js'
5 | lib:
6 | expand: true
7 | cwd: 'lib'
8 | src: ['**/*']
9 | dest: 'build/lib/'
10 | img:
11 | expand: true
12 | cwd: 'img'
13 | src: ['**/*']
14 | dest: 'build/img/'
15 | popup:
16 | expand: true
17 | cwd: 'src/popup'
18 | src: ['**/*']
19 | dest: 'build/popup/'
20 |
--------------------------------------------------------------------------------
/omega-target/index.coffee:
--------------------------------------------------------------------------------
1 | module.exports =
2 | Log: require('./src/log')
3 | Storage: require('./src/storage')
4 | BrowserStorage: require('./src/browser_storage')
5 | Options: require('./src/options')
6 | OptionsSync: require('./src/options_sync')
7 | OmegaPac: require('omega-pac')
8 |
9 | for name, value of require('./src/utils.coffee')
10 | module.exports[name] = value
11 |
12 | for name, value of require('./src/errors.coffee')
13 | module.exports[name] = value
14 |
--------------------------------------------------------------------------------
/omega-target-chromium-extension/src/module/index.coffee:
--------------------------------------------------------------------------------
1 | module.exports =
2 | Storage: require('./storage')
3 | Options: require('./options')
4 | ChromeTabs: require('./tabs')
5 | SwitchySharp: require('./switchysharp')
6 | ExternalApi: require('./external_api')
7 | WebRequestMonitor: require('./web_request_monitor')
8 | Inspect: require('./inspect')
9 | Url: require('url')
10 | proxy: require('./proxy')
11 |
12 | for name, value of require('omega-target')
13 | module.exports[name] ?= value
14 |
--------------------------------------------------------------------------------
/omega-target-chromium-extension/src/module/proxy/index.coffee:
--------------------------------------------------------------------------------
1 | ListenerProxyImpl = require('./proxy_impl_listener')
2 | SettingsProxyImpl = require('./proxy_impl_settings')
3 | ScriptProxyImpl = require('./proxy_impl_script')
4 |
5 | exports.proxyImpls = [ListenerProxyImpl, ScriptProxyImpl, SettingsProxyImpl]
6 | exports.getProxyImpl = (log) ->
7 | for Impl in exports.proxyImpls
8 | if Impl.isSupported()
9 | return new Impl(log)
10 | throw new Error('Your browser does not support proxy settings!')
11 |
--------------------------------------------------------------------------------
/omega-web/src/partials/reset_options_confirm.jade:
--------------------------------------------------------------------------------
1 | .modal-header
2 | button.close(type='button' ng-click='$dismiss()')
3 | span(aria-hidden='true') ×
4 | span.sr-only {{'dialog_close' | tr}}
5 | h4.modal-title {{'options_modalHeader_resetOptions' | tr}}
6 | .modal-body
7 | p.text-danger {{'options_resetOptionsConfirm' | tr}}
8 | .modal-footer
9 | button.btn.btn-default(ng-click='$dismiss()') {{'dialog_cancel' | tr}}
10 | button.btn.btn-danger(type='button' ng-click='$close("ok")') {{'options_reset' | tr}}
11 |
--------------------------------------------------------------------------------
/omega-web/src/omega/controllers/about.coffee:
--------------------------------------------------------------------------------
1 | angular.module('omega').controller 'AboutCtrl', ($scope, $rootScope,
2 | $modal, omegaDebug) ->
3 |
4 | $scope.downloadLog = omegaDebug.downloadLog
5 | $scope.reportIssue = omegaDebug.reportIssue
6 |
7 | $scope.showResetOptionsModal = ->
8 | $modal.open(templateUrl: 'partials/reset_options_confirm.html').result
9 | .then -> omegaDebug.resetOptions()
10 |
11 | try
12 | $scope.version = omegaDebug.getProjectVersion()
13 | catch _
14 | $scope.version = '?.?.?'
15 |
--------------------------------------------------------------------------------
/omega-web/src/partials/input_group_clear.jade:
--------------------------------------------------------------------------------
1 | .input-group
2 | input.form-control(ng-model='model' ng-attr-type='{{type}}' ng-pattern='ngPattern || catchAll'
3 | placeholder='{{placeholder}}' ng-change='modelChange()')
4 | span.input-group-btn
5 | button.btn.btn-default.input-group-clear-btn(type='button' ng-click='toggleClear()'
6 | ng-disabled='!model && !oldModel' title="{{'inputClear_' + (oldModel ? 'restore' : 'clear') | tr}}")
7 | span.glyphicon(ng-class='{"glyphicon-remove": !oldModel, "glyphicon-repeat": !!oldModel}')
8 |
--------------------------------------------------------------------------------
/omega-web/src/partials/apply_options_confirm.jade:
--------------------------------------------------------------------------------
1 | .modal-header
2 | button.close(type='button' ng-click='$dismiss()')
3 | span(aria-hidden='true') ×
4 | span.sr-only {{'dialog_close' | tr}}
5 | h4.modal-title {{'options_modalHeader_applyOptions' | tr}}
6 | .modal-body
7 | p {{'options_applyOptionsRequired' | tr}}
8 | p {{'options_applyOptionsConfirm' | tr}}
9 | .modal-footer
10 | button.btn.btn-default(ng-click='$dismiss()') {{'dialog_cancel' | tr}}
11 | button.btn.btn-primary(type='button' ng-click='$close("ok")') {{'options_apply' | tr}}
12 |
--------------------------------------------------------------------------------
/omega-web/src/popup/js/i18n.js:
--------------------------------------------------------------------------------
1 | $script.ready('om-page-info', function() {
2 | document.querySelector('#js-direct .om-profile-name').textContent =
3 | OmegaTargetPopup.getMessage('profile_direct');
4 | document.querySelector('#js-system .om-profile-name').textContent =
5 | OmegaTargetPopup.getMessage('profile_system');
6 | document.querySelector('#js-addrule-label').textContent =
7 | OmegaTargetPopup.getMessage('popup_addCondition');
8 | document.querySelector('#js-option-label').textContent =
9 | OmegaTargetPopup.getMessage('popup_showOptions');
10 | });
11 |
--------------------------------------------------------------------------------
/omega-web/img/icons/draw_omega.js:
--------------------------------------------------------------------------------
1 | var drawOmega = function (ctx, outerCircleColor, innerCircleColor) {
2 | ctx.globalCompositeOperation = "source-over";
3 | ctx.fillStyle = outerCircleColor;
4 | ctx.beginPath();
5 | ctx.arc(0.5, 0.5, 0.5, 0, Math.PI * 2, true);
6 | ctx.closePath();
7 | ctx.fill();
8 |
9 | if (innerCircleColor != null) {
10 | ctx.fillStyle = innerCircleColor;
11 | } else {
12 | ctx.globalCompositeOperation = "destination-out";
13 | }
14 |
15 | ctx.beginPath();
16 | ctx.arc(0.5, 0.5, 0.25, 0, Math.PI * 2, true);
17 | ctx.closePath();
18 | ctx.fill();
19 | };
20 |
--------------------------------------------------------------------------------
/omega-web/src/partials/delete_profile.jade:
--------------------------------------------------------------------------------
1 | .modal-header
2 | button.close(type='button' ng-click='$dismiss()')
3 | span(aria-hidden='true') ×
4 | span.sr-only {{'dialog_close' | tr}}
5 | h4.modal-title {{'options_modalHeader_deleteProfile' | tr}}
6 | .modal-body
7 | p {{'options_deleteProfileConfirm' | tr}}
8 | .well
9 | span(omega-profile-inline='profile' options='options' disp-name='dispNameFilter')
10 | .modal-footer
11 | button.btn.btn-default(ng-click='$dismiss()') {{'dialog_cancel' | tr}}
12 | button.btn.btn-danger(type='button' ng-click='$close("ok")') {{'options_deleteProfile' | tr}}
13 |
--------------------------------------------------------------------------------
/omega-web/src/partials/rule_reset_confirm.jade:
--------------------------------------------------------------------------------
1 | .modal-header
2 | button.close(type='button' ng-click='$dismiss()')
3 | span(aria-hidden='true') ×
4 | span.sr-only {{'dialog_close' | tr}}
5 | h4.modal-title {{'options_modalHeader_resetRules' | tr}}
6 | .modal-body
7 | p {{'options_resetRulesConfirm' | tr}}
8 | .well
9 | span(omega-profile-inline='ruleProfile' disp-name='dispNameFilter' options='options')
10 | .modal-footer
11 | button.btn.btn-default(ng-click='$dismiss()') {{'dialog_cancel' | tr}}
12 | button.btn.btn-warning(type='button' ng-click='$close("ok")') {{'options_resetRules' | tr}}
13 |
--------------------------------------------------------------------------------
/omega-build/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "omega-build",
3 | "version": "0.0.1",
4 | "private": true,
5 | "devDependencies": {
6 | "grunt": "~0.4.1",
7 | "grunt-hub": "^0.7.0"
8 | },
9 | "scripts": {
10 | "deps": "npm install && (cd ../omega-pac && npm install) && (cd ../omega-target && npm install) && (cd ../omega-web && npm install && bower install) && (cd ../omega-target-chromium-extension/ && npm install)",
11 | "dev": "(cd ../omega-pac && npm run dev) && (cd ../omega-target && npm run dev) && (cd ../omega-web && npm run dev) && (cd ../omega-target-chromium-extension/ && npm run dev)"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/omega-web/src/partials/cannot_delete_profile.jade:
--------------------------------------------------------------------------------
1 | .modal-header
2 | button.close(type='button' ng-click='$dismiss()')
3 | span(aria-hidden='true') ×
4 | span.sr-only {{'dialog_close' | tr}}
5 | h4.modal-title {{'options_modalHeader_cannotDeleteProfile' | tr}}
6 | .modal-body
7 | p {{'options_profileReferredBy' | tr}}
8 | .well
9 | ul.list-style-none
10 | li(ng-repeat='p in refs')
11 | span(omega-profile-inline='p' options='options' disp-name='dispNameFilter')
12 | p {{'options_modifyReferringProfiles' | tr}}
13 | .modal-footer
14 | button.btn.btn-default(ng-click='$dismiss()') {{'dialog_cancel' | tr}}
15 |
--------------------------------------------------------------------------------
/omega-web/src/omega/controllers/quick_switch.coffee:
--------------------------------------------------------------------------------
1 | angular.module('omega').controller 'QuickSwitchCtrl', ($scope, $filter) ->
2 | $scope.sortableOptions =
3 | tolerance: 'pointer'
4 | axis: 'y'
5 | forceHelperSize: true
6 | forcePlaceholderSize: true
7 | connectWith: '.cycle-profile-container'
8 | containment: '#quick-switch-settings'
9 |
10 | $scope.$watchCollection 'options', (options) ->
11 | return unless options?
12 | $scope.notCycledProfiles =
13 | for profile in $filter('profiles')(options, 'all') when (
14 | options["-quickSwitchProfiles"].indexOf(profile.name) < 0)
15 | profile.name
16 |
--------------------------------------------------------------------------------
/AUTHORS:
--------------------------------------------------------------------------------
1 | # SwitchyOmega authors:
2 |
3 | FelisCatus
4 |
5 | # SwitchyOmega translators:
6 |
7 | Filip
8 | Michal Čihař
9 | masoud Rahmani
10 |
11 | # SwitchyOmega includes or links to (unchanged):
12 | # * jQuery UI (custom build)
13 | # => omega-web/lib/jquery-ui-*.js
14 | # => Copyright 2014 jQuery Foundation and other contributors; Licensed MIT
15 | # * Many npm packages and bower packages
16 | # => **/node_modules, **/bower_components
17 | # => Please refer to their project homepages or npm package pages for the
18 | # copyright and license information of each package.
19 |
--------------------------------------------------------------------------------
/omega-build/Gruntfile.coffee:
--------------------------------------------------------------------------------
1 | module.exports = (grunt) ->
2 | submodules = ['omega-pac', 'omega-target', 'omega-web', 'omega-target-*']
3 | hubConfig =
4 | all:
5 | options:
6 | concurrent: Infinity
7 | src: "../*/Gruntfile.*"
8 | for module in submodules
9 | hubConfig[module] =
10 | src: "../#{module}/Gruntfile.*"
11 |
12 | hubAll = (task) -> "hub:#{module}:#{task}" for module in submodules
13 |
14 | grunt.initConfig {
15 | hub: hubConfig
16 | }
17 |
18 | grunt.loadNpmTasks 'grunt-hub'
19 |
20 | grunt.registerTask 'default', hubAll('default')
21 | grunt.registerTask 'test', hubAll('test')
22 | grunt.registerTask 'watch', ['hub:all:watch']
23 |
--------------------------------------------------------------------------------
/omega-web/grunt/coffeelint.coffee:
--------------------------------------------------------------------------------
1 | module.exports =
2 | options:
3 | arrow_spacing: level: 'error'
4 | colon_assignment_spacing:
5 | level: 'error'
6 | spacing:
7 | left: 0
8 | right: 1
9 | missing_fat_arrows: level: 'warn'
10 | no_empty_functions: level: 'error'
11 | no_empty_param_list: level: 'error'
12 | no_interpolation_in_single_quotes: level: 'error'
13 | no_stand_alone_at: level: 'error'
14 | space_operators: level: 'error'
15 | # https://github.com/clutchski/coffeelint/issues/525
16 | indentation: level: 'ignore'
17 |
18 | gruntfile: ['Gruntfile.coffee']
19 | tasks: ['grunt/**/*.coffee']
20 | src: ['src/**/*.coffee']
21 |
--------------------------------------------------------------------------------
/omega-target-chromium-extension/src/module/chrome_api.coffee:
--------------------------------------------------------------------------------
1 | OmegaTarget = require('omega-target')
2 | Promise = OmegaTarget.Promise
3 |
4 | exports.chromeApiPromisify = (target, method) ->
5 | return (args...) ->
6 | new Promise (resolve, reject) ->
7 | callback = (callbackArgs...) ->
8 | if chrome.runtime.lastError?
9 | error = new Error(chrome.runtime.lastError.message)
10 | error.original = chrome.runtime.lastError
11 | return reject(error)
12 | if callbackArgs.length <= 1
13 | resolve(callbackArgs[0])
14 | else
15 | resolve(callbackArgs)
16 |
17 | args.push(callback)
18 | target[method].apply(target, args)
19 |
--------------------------------------------------------------------------------
/omega-web/src/partials/delete_attached.jade:
--------------------------------------------------------------------------------
1 | .modal-header
2 | button.close(type='button' ng-click='$dismiss()')
3 | span(aria-hidden='true') ×
4 | span.sr-only {{'dialog_close' | tr}}
5 | h4.modal-title {{'options_modalHeader_deleteAttached' | tr}}
6 | .modal-body
7 | p {{'options_deleteAttachedConfirm' | tr}}
8 | .well
9 | span(omega-profile-icon='attached' options='options')
10 | = ' '
11 | | {{attached.sourceUrl || ('options_ruleListLineCount' | tr:[attached.ruleList.split('\n').length])}}
12 | .modal-footer
13 | button.btn.btn-default(ng-click='$dismiss()') {{'dialog_cancel' | tr}}
14 | button.btn.btn-danger(type='button' ng-click='$close("ok")') {{'options_deleteAttached' | tr}}
15 |
--------------------------------------------------------------------------------
/omega-pac/grunt/coffeelint.coffee:
--------------------------------------------------------------------------------
1 | module.exports =
2 | options:
3 | arrow_spacing: level: 'error'
4 | colon_assignment_spacing:
5 | level: 'error'
6 | spacing:
7 | left: 0
8 | right: 1
9 | missing_fat_arrows: level: 'warn'
10 | no_empty_functions: level: 'error'
11 | no_empty_param_list: level: 'error'
12 | no_interpolation_in_single_quotes: level: 'error'
13 | no_stand_alone_at: level: 'error'
14 | space_operators: level: 'error'
15 | # https://github.com/clutchski/coffeelint/issues/525
16 | indentation: level: 'ignore'
17 |
18 | gruntfile: ['Gruntfile.coffee']
19 | tasks: ['grunt/**/*.coffee']
20 | src: ['src/**/*.coffee', 'test/**/*.coffee']
21 |
--------------------------------------------------------------------------------
/omega-web/src/partials/options_welcome.jade:
--------------------------------------------------------------------------------
1 | .modal-header
2 | button.close(type='button' ng-click='$dismiss()')
3 | span(aria-hidden='true') ×
4 | span.sr-only {{'dialog_close' | tr}}
5 | h4.modal-title {{'options_modalHeader_welcome' | tr}}
6 | .modal-body
7 | p(ng-show="upgrade") {{'options_welcomeUpgrade' | tr}}
8 | p(ng-show="upgrade") {{'options_welcomeUpgradeGuide' | tr}}
9 | p(ng-show="!upgrade") {{'options_welcomeNormal' | tr}}
10 | p(ng-show="!upgrade") {{'options_welcomeNormalGuide' | tr}}
11 | .modal-footer
12 | button.btn.btn-default(ng-click='$close("skip")') {{'options_guideSkip' | tr}}
13 | button.btn.btn-primary(type='button' ng-click='$close("show")') {{'options_guideNext' | tr}}
14 |
--------------------------------------------------------------------------------
/omega-target/grunt/coffeelint.coffee:
--------------------------------------------------------------------------------
1 | module.exports =
2 | options:
3 | arrow_spacing: level: 'error'
4 | colon_assignment_spacing:
5 | level: 'error'
6 | spacing:
7 | left: 0
8 | right: 1
9 | missing_fat_arrows: level: 'warn'
10 | no_empty_functions: level: 'error'
11 | no_empty_param_list: level: 'error'
12 | no_interpolation_in_single_quotes: level: 'error'
13 | no_stand_alone_at: level: 'error'
14 | space_operators: level: 'error'
15 | # https://github.com/clutchski/coffeelint/issues/525
16 | indentation: level: 'ignore'
17 |
18 | gruntfile: ['Gruntfile.coffee']
19 | tasks: ['grunt/**/*.coffee']
20 | src: ['src/**/*.coffee', 'test/**/*.coffee']
21 |
--------------------------------------------------------------------------------
/omega-target-chromium-extension/grunt/po2crx.coffee:
--------------------------------------------------------------------------------
1 | module.exports =
2 | locales:
3 | files:
4 | 'build/_locales/en/messages.json':
5 | '../omega-locales/en_US/LC_MESSAGES/omega-web.po'
6 | 'build/_locales/zh/messages.json':
7 | '../omega-locales/zh_CN/LC_MESSAGES/omega-web.po'
8 | 'build/_locales/cs/messages.json':
9 | '../omega-locales/cs/LC_MESSAGES/omega-web.po'
10 | 'build/_locales/fa/messages.json':
11 | '../omega-locales/fa/LC_MESSAGES/omega-web.po'
12 | 'build/_locales/zh_CN/messages.json':
13 | '../omega-locales/zh_CN/LC_MESSAGES/omega-web.po'
14 | 'build/_locales/zh_TW/messages.json':
15 | '../omega-locales/zh_TW/LC_MESSAGES/omega-web.po'
16 |
--------------------------------------------------------------------------------
/omega-target-chromium-extension/grunt/coffeelint.coffee:
--------------------------------------------------------------------------------
1 | module.exports =
2 | options:
3 | arrow_spacing: level: 'error'
4 | colon_assignment_spacing:
5 | level: 'error'
6 | spacing:
7 | left: 0
8 | right: 1
9 | missing_fat_arrows: level: 'warn'
10 | no_empty_functions: level: 'error'
11 | no_empty_param_list: level: 'error'
12 | no_interpolation_in_single_quotes: level: 'error'
13 | no_stand_alone_at: level: 'error'
14 | space_operators: level: 'error'
15 | # https://github.com/clutchski/coffeelint/issues/525
16 | indentation: level: 'ignore'
17 |
18 | gruntfile: ['Gruntfile.coffee']
19 | tasks: ['grunt/**/*.coffee']
20 | src: ['*.coffee', 'src/**/*.coffee', 'test/**/*.coffee']
21 |
--------------------------------------------------------------------------------
/omega-target-chromium-extension/overlay/background.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SwitchyOmega Background
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/omega-target-chromium-extension/grunt/copy.coffee:
--------------------------------------------------------------------------------
1 | module.exports =
2 | web:
3 | expand: true
4 | cwd: '../omega-web/build'
5 | src: ['**/*']
6 | dest: 'build/'
7 | target:
8 | files:
9 | 'build/js/omega_target.min.js':
10 | 'node_modules/omega-target/omega_target.min.js'
11 | target_self:
12 | src: 'omega_target_chromium_extension.min.js'
13 | dest: 'build/js/'
14 | target_popup:
15 | expand: true
16 | cwd: 'src/js'
17 | src: 'omega_target_popup.js'
18 | dest: 'build/js/'
19 | overlay:
20 | expand: true
21 | cwd: 'overlay'
22 | src: ['**/*']
23 | dest: 'build/'
24 | docs:
25 | expand: true
26 | cwd: '..'
27 | src: ['COPYING', 'AUTHORS']
28 | dest: 'build/'
29 |
--------------------------------------------------------------------------------
/omega-web/src/partials/rule_remove_confirm.jade:
--------------------------------------------------------------------------------
1 | .modal-header
2 | button.close(type='button' ng-click='$dismiss()')
3 | span(aria-hidden='true') ×
4 | span.sr-only {{'dialog_close' | tr}}
5 | h4.modal-title {{'options_modalHeader_deleteRule' | tr}}
6 | .modal-body
7 | p {{'options_deleteRuleConfirm' | tr}}
8 | div.well
9 | span.label.label-info {{'condition_' + rule.condition.conditionType | tr}}
10 | = ' '
11 | | {{rule.condition.pattern}}
12 | span.pull-right
13 | span(omega-profile-inline='ruleProfile' disp-name='dispNameFilter' options='options')
14 | .modal-footer
15 | button.btn.btn-default(ng-click='$dismiss()') {{'dialog_cancel' | tr}}
16 | button.btn.btn-danger(type='button' ng-click='$close("ok")') {{'options_deleteRule' | tr}}
17 |
--------------------------------------------------------------------------------
/omega-web/src/popup/js/loader.js:
--------------------------------------------------------------------------------
1 | window.OmegaPopup = {};
2 | $script(['js/index.js', 'js/profiles.js', 'js/keyboard.js'], 'om-main');
3 | $script(['js/i18n.js']);
4 | $script('../js/omega_target_popup.js', 'om-target', function() {
5 | OmegaTargetPopup.getActivePageInfo(function(err, info) {
6 | window.OmegaPopup.pageInfo = info;
7 | $script.done('om-page-info');
8 | });
9 | OmegaTargetPopup.getState([
10 | 'availableProfiles',
11 | 'currentProfileName',
12 | 'validResultProfiles',
13 | 'isSystemProfile',
14 | 'currentProfileCanAddRule',
15 | 'proxyNotControllable',
16 | 'externalProfile',
17 | 'showExternalProfile',
18 | ], function(err, state) {
19 | window.OmegaPopup.state = state;
20 | $script.done('om-state');
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/omega-pac/grunt/browserify.coffee:
--------------------------------------------------------------------------------
1 | module.exports =
2 | index:
3 | files:
4 | 'index.js': 'index.coffee'
5 | options:
6 | transform: ['coffeeify']
7 | exclude: ['uglify-js', 'ip-address']
8 | browserifyOptions:
9 | extensions: '.coffee'
10 | builtins: []
11 | standalone: 'index.coffee'
12 | debug: true
13 | browser:
14 | files:
15 | 'omega_pac.min.js': './index.coffee'
16 | options:
17 | alias: [
18 | './index.coffee:OmegaPac'
19 | ]
20 | transform: ['coffeeify']
21 | plugin:
22 | if process.env.BUILD == 'release'
23 | [['minifyify', {map: false}]]
24 | else
25 | []
26 | browserifyOptions:
27 | extensions: '.coffee'
28 | standalone: 'OmegaPac'
29 |
--------------------------------------------------------------------------------
/omega-web/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "omega-web",
3 | "version": "0.0.1",
4 | "private": true,
5 | "devDependencies": {
6 | "chai": "~1.9.1",
7 | "coffeelint": "^1.16.0",
8 | "grunt": "^0.4.5",
9 | "grunt-autoprefixer": "^1.0.1",
10 | "grunt-bower-task": "^0.5.0",
11 | "grunt-coffeelint": "^0.0.13",
12 | "grunt-contrib-coffee": "^0.11.1",
13 | "grunt-contrib-concat": "^0.5.0",
14 | "grunt-contrib-copy": "^0.5.0",
15 | "grunt-contrib-jade": "^0.12.0",
16 | "grunt-contrib-less": "^0.11.4",
17 | "grunt-contrib-watch": "^0.6.1",
18 | "grunt-mocha-test": "~0.11.0",
19 | "grunt-ng-annotate": "^0.3.2",
20 | "load-grunt-config": "^0.13.1",
21 | "omega-pac": "../omega-pac"
22 | },
23 | "scripts": {
24 | "dev": "npm link omega-pac && npm link"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/omega-target/grunt/browserify.coffee:
--------------------------------------------------------------------------------
1 | module.exports =
2 | index:
3 | files:
4 | 'index.js': 'index.coffee'
5 | options:
6 | transform: ['coffeeify']
7 | exclude: ['bluebird', 'jsondiffpatch', 'omega-pac']
8 | browserifyOptions:
9 | extensions: '.coffee'
10 | builtins: []
11 | standalone: 'index.coffee'
12 | debug: true
13 | browser:
14 | files:
15 | 'omega_target.min.js': 'index.coffee'
16 | options:
17 | alias: [
18 | './index.coffee:OmegaTarget'
19 | ]
20 | transform: ['coffeeify']
21 | plugin:
22 | if process.env.BUILD == 'release'
23 | [['minifyify', {map: false}]]
24 | else
25 | []
26 | browserifyOptions:
27 | extensions: '.coffee'
28 | standalone: 'OmegaTarget'
29 |
--------------------------------------------------------------------------------
/omega-pac/test/shexp_utils.coffee:
--------------------------------------------------------------------------------
1 | chai = require 'chai'
2 | should = chai.should()
3 |
4 | describe 'ShexpUtils', ->
5 | ShexpUtils = require '../src/shexp_utils'
6 | describe '#escapeSlash', ->
7 | it 'should escape all forward slashes', ->
8 | regex = ShexpUtils.escapeSlash '/test/'
9 | regex.should.equal '\\/test\\/'
10 | it 'should not escape slashes that are already escaped', ->
11 | regex = ShexpUtils.escapeSlash '\\/test\\/'
12 | regex.should.equal '\\/test\\/'
13 | it 'should know the difference between escaped and unescaped slashes', ->
14 | regex = ShexpUtils.escapeSlash '\\\\/\\/test\\/'
15 | regex.should.equal '\\\\\\/\\/test\\/'
16 | describe '#shExp2RegExp', ->
17 | it 'should escape regex meta chars and back slashes', ->
18 | regex = ShexpUtils.shExp2RegExp 'this.is|a\\test+'
19 | regex.should.equal '^this\\.is\\|a\\\\test\\+$'
20 |
--------------------------------------------------------------------------------
/omega-web/src/popup/proxy_not_controllable.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SwitchyOmega Popup
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/omega-web/grunt/watch.coffee:
--------------------------------------------------------------------------------
1 | module.exports =
2 | grunt:
3 | options:
4 | reload: true
5 | files:
6 | 'grunt/*'
7 | tasks: ['coffeelint:tasks', 'default']
8 | copy_pac:
9 | files:
10 | 'node_modules/omega-pac/omega_pac.min.js'
11 | tasks: 'copy:pac'
12 | copy_lib:
13 | files:
14 | 'lib/**/*'
15 | tasks: 'copy:lib'
16 | copy_img:
17 | files:
18 | 'img/**/*'
19 | tasks: 'copy:img'
20 | copy_popup:
21 | files:
22 | 'src/popup/**/*'
23 | tasks: 'copy:popup'
24 | jade:
25 | files: ['src/**/*.jade']
26 | tasks: 'jade'
27 | less:
28 | files:
29 | 'src/less/**/*.less'
30 | tasks: ['less', 'autoprefixer']
31 | coffeelint:
32 | files: 'src/**/*.coffee'
33 | tasks: ['coffeelint']
34 | coffee:
35 | files: [
36 | 'src/coffee/**/*.coffee'
37 | 'src/omega/**/*.coffee'
38 | ]
39 | tasks: ['coffee']
40 |
--------------------------------------------------------------------------------
/omega-web/src/partials/profile_virtual.jade:
--------------------------------------------------------------------------------
1 | div
2 | section.settings-group
3 | h3 {{'options_group_virtualProfile' | tr}}
4 | p.help-block
5 | | {{'options_virtualProfileTargetHelp' | tr}}
6 | .form-group
7 | label {{'options_virtualProfileTarget' | tr}}
8 | = ' '
9 | div(omega-profile-select='options | profiles:profile' ng-model='profile.defaultProfileName'
10 | disp-name='dispNameFilter' style='display: inline-block;' options='options')
11 | section.settings-group
12 | h3 {{'options_group_virtualProfileReplace' | tr}}
13 | p.help-block(omega-html='"options_virtualProfileReplaceHelp" | tr:[$profile("profileByName(profile.defaultProfileName)")]')
14 | .form-group
15 | button.btn.btn-default(ng-click='replaceProfile(profile.defaultProfileName, profile.name)')
16 | span.glyphicon.glyphicon-search
17 | = ' '
18 | | {{'options_virtualProfileReplace' | tr}}
19 |
--------------------------------------------------------------------------------
/omega-target/src/errors.coffee:
--------------------------------------------------------------------------------
1 | class NetworkError extends Error
2 | constructor: (err) ->
3 | super
4 | this.cause = err
5 | this.name = 'NetworkError'
6 |
7 | class HttpError extends NetworkError
8 | constructor: ->
9 | super
10 | this.statusCode = this.cause?.statusCode
11 | this.name = 'HttpError'
12 |
13 | class HttpNotFoundError extends HttpError
14 | constructor: ->
15 | super
16 | this.name = 'HttpNotFoundError'
17 |
18 | class HttpServerError extends HttpError
19 | constructor: ->
20 | super
21 | this.name = 'HttpServerError'
22 |
23 | class ContentTypeRejectedError extends Error
24 | constructor: ->
25 | super
26 | this.name = 'ContentTypeRejectedError'
27 |
28 | module.exports =
29 | NetworkError: NetworkError
30 | HttpError: HttpError
31 | HttpNotFoundError: HttpNotFoundError
32 | HttpServerError: HttpServerError
33 | ContentTypeRejectedError: ContentTypeRejectedError
34 |
--------------------------------------------------------------------------------
/omega-pac/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "omega-pac",
3 | "version": "0.0.1",
4 | "private": true,
5 | "main": "./index.js",
6 | "devDependencies": {
7 | "chai": "~1.9.1",
8 | "coffee-script": "^1.7.1",
9 | "coffeeify": "^0.7.0",
10 | "coffeelint": "^1.16.0",
11 | "grunt": "^0.4.5",
12 | "grunt-browserify": "^3.0.0",
13 | "grunt-coffeelint": "^0.0.13",
14 | "grunt-contrib-coffee": "^0.11.1",
15 | "grunt-contrib-watch": "^0.6.1",
16 | "grunt-mocha-test": "~0.11.0",
17 | "load-grunt-config": "^0.13.1",
18 | "lolex": "^1.4.0",
19 | "minifyify": "^4.1.1"
20 | },
21 | "dependencies": {
22 | "ip-address": "^4.0.0",
23 | "tldjs": "^1.5.2",
24 | "uglify-js": "^2.4.15"
25 | },
26 | "browser": {
27 | "uglify-js": "./uglifyjs-shim.js",
28 | "uglify-js-real": "./uglifyjs.js"
29 | },
30 | "scripts": {
31 | "dev": "npm link",
32 | "test": "TZ=Europe/London grunt test"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/omega-web/src/popup/js/keyboard_help.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | var keyForId = {
3 | 'js-direct': '0',
4 | 'js-system': 'S',
5 | 'js-external': 'E',
6 | 'js-addrule': 'A',
7 | 'js-temprule': 'T',
8 | 'js-option': 'O',
9 | 'js-reqinfo': 'R'
10 | }
11 | Object.keys(keyForId).forEach(function (id) {
12 | showHelp(id, keyForId[id]);
13 | });
14 |
15 | for (var i = 1; i <= 9; i++) {
16 | showHelp('js-profile-' + i, '' + i);
17 | }
18 |
19 | return;
20 |
21 | function showHelp(id, key) {
22 | var element = document.getElementById(id);
23 | if (!element) return;
24 | if (!element.querySelector('.om-keyboard-help')) {
25 | var span = document.createElement('span');
26 | span.classList.add('om-keyboard-help');
27 | span.textContent = key;
28 | var reference = element.querySelector('.glyphicon');
29 | reference.parentNode.insertBefore(span, reference.nextSibling);
30 | }
31 | }
32 | })();
33 |
--------------------------------------------------------------------------------
/omega-target-chromium-extension/grunt/watch.coffee:
--------------------------------------------------------------------------------
1 | module.exports =
2 | grunt:
3 | options:
4 | reload: true
5 | files:
6 | 'grunt/*'
7 | tasks: ['coffeelint:tasks', 'default']
8 | po2crx_locales:
9 | files: ['../omega-locales/**/*']
10 | tasks: ['po2crx:locales']
11 | copy_web:
12 | files: ['node_modules/omega-web/build/**/*']
13 | tasks: ['copy:web']
14 | copy_target:
15 | files: ['node_modules/omega-target/omega_target.min.js']
16 | tasks: ['copy:target']
17 | copy_overlay:
18 | files: ['overlay/**/*']
19 | tasks: ['copy:overlay']
20 | copy_target_popup:
21 | files: ['src/js/omega_target_popup.js']
22 | tasks: ['copy:target_popup']
23 | coffee:
24 | files: ['src/**/*.coffee']
25 | tasks: ['coffeelint:src', 'browserify', 'coffee', 'copy:target_self']
26 | browserify_omega_webext_proxy_script:
27 | files: ['src/js/omega_webext_proxy_script.js']
28 | tasks: ['browserify:omega_webext_proxy_script']
29 |
--------------------------------------------------------------------------------
/omega-target/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "omega-target",
3 | "version": "0.0.1",
4 | "private": true,
5 | "main": "./index.js",
6 | "devDependencies": {
7 | "chai": "^1.10.0",
8 | "coffee-script": "^1.8.0",
9 | "coffeeify": "^0.7.0",
10 | "coffeelint": "^1.16.0",
11 | "grunt": "^0.4.5",
12 | "grunt-browserify": "^3.0.0",
13 | "grunt-coffeelint": "^0.0.13",
14 | "grunt-contrib-coffee": "^0.11.1",
15 | "grunt-contrib-watch": "^0.6.1",
16 | "grunt-mocha-test": "~0.11.0",
17 | "load-grunt-config": "^0.13.1",
18 | "minifyify": "^4.1.1",
19 | "sinon": "^1.12.2",
20 | "sinon-chai": "^2.6.0"
21 | },
22 | "dependencies": {
23 | "bluebird": "^2.3.2",
24 | "jsondiffpatch": "^0.1.8",
25 | "limiter": "^1.0.5",
26 | "omega-pac": "../omega-pac"
27 | },
28 | "browser": {
29 | "omega-pac": "./omega_pac_shim.js"
30 | },
31 | "scripts": {
32 | "dev": "npm link omega-pac && npm link",
33 | "test": "grunt test"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ### What does this PR do?
2 | - [ ] Bug fix
3 | - [ ] Improvement
4 | - [ ] New feature
5 |
6 | (Note: Translations and typo fixes should be done on https://hosted.weblate.org/projects/switchyomega/ instead of PR.)
7 |
8 | Please explain the changes in details here...
9 |
10 | #### Compatibility
11 |
12 | Is this PR compatible with old versions? Can users simply upgrade the extension?
13 | Please describe any possible breaking changes (or surprising UX differences).
14 |
15 | #### Screenshots (if applicable)
16 |
17 | ---
18 |
19 | After creating the PR:
20 |
21 | - Please make sure the CircleCI test passes. Feel free to add more commits for
22 | bug or style fixes.
23 | - Any merge conflicts should be fixed on *your* side. Prefer rebasing to merging.
24 | - Allow some time for project maintainers to review and merge the change.
25 | - New features & behavior changes are subject to discussion. Please understand
26 | that project maintainers may reject new features, or request changes.
27 |
--------------------------------------------------------------------------------
/omega-web/src/partials/replace_profile.jade:
--------------------------------------------------------------------------------
1 | .modal-header
2 | button.close(type='button' ng-click='$dismiss()')
3 | span(aria-hidden='true') ×
4 | span.sr-only {{'dialog_close' | tr}}
5 | h4.modal-title {{'options_modalHeader_replaceProfile' | tr}}
6 | .modal-body
7 | p(omega-html='"options_replaceProfileConfirm" | tr:[profileSelect("fromName"), profileSelect("toName")]')
8 | .well
9 | span(omega-profile-inline='profileByName(fromName)' options='options' disp-name='dispNameFilter')
10 | = ' '
11 | span.glyphicon.glyphicon-chevron-right
12 | = ' '
13 | span(omega-profile-inline='profileByName(toName)' options='options' disp-name='dispNameFilter')
14 | .help-block(omega-html="'options_replaceProfileHelp' | tr:[$profile('profileByName(fromName)'), $profile('profileByName(toName)')]")
15 | .modal-footer
16 | button.btn.btn-default(ng-click='$dismiss()') {{'dialog_cancel' | tr}}
17 | button.btn.btn-warning(type='button' ng-click='$close({fromName: fromName, toName: toName})') {{'options_replaceProfile' | tr}}
18 |
--------------------------------------------------------------------------------
/omega-web/src/partials/omega_profile_select.jade:
--------------------------------------------------------------------------------
1 | .btn-group.omega-profile-select(dropdown on-toggle="toggled(open)")
2 | button.btn.btn-default.dropdown-toggle(dropdown-toggle type='button' aria-expanded='false'
3 | role='listbox' aria-haspopup='true')
4 | span(omega-profile-icon='selectedProfile' options='options' icon='selectedProfile ? undefined : "glyphicon-time"')
5 | = ' '
6 | span(ng-show='!!profileName') {{getName(selectedProfile)}}
7 | span(ng-show='!profileName') {{defaultText}}
8 | = ' '
9 | span.caret
10 | ul.dropdown-menu(role='listbox')
11 | li(role='option' ng-if='!!defaultText' ng-class='{active: profileName == ""}')
12 | a(ng-click='setProfileName("")')
13 | span.glyphicon.glyphicon-time
14 | = ' {{defaultText}}'
15 | li(role='option' ng-repeat='profile in dispProfiles' ng-class='{active: profileName == profile.name}')
16 | a(ng-click='setProfileName(profile.name)')
17 | span(omega-profile-icon='profile' options='options')
18 | = ' {{getName(profile)}}'
19 |
--------------------------------------------------------------------------------
/omega-target-chromium-extension/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "omega-target-chromium-extension",
3 | "version": "0.0.1",
4 | "private": true,
5 | "main": "./index",
6 | "devDependencies": {
7 | "chai": "~1.9.1",
8 | "coffee-script": "^1.7.1",
9 | "coffeeify": "^0.7.0",
10 | "coffeelint": "^1.16.0",
11 | "grunt": "^0.4.5",
12 | "grunt-browserify": "^3.0.0",
13 | "grunt-coffeelint": "^0.0.13",
14 | "grunt-contrib-coffee": "^0.11.1",
15 | "grunt-contrib-compress": "^0.12.0",
16 | "grunt-contrib-copy": "^0.5.0",
17 | "grunt-contrib-watch": "^0.6.1",
18 | "grunt-mocha-test": "~0.11.0",
19 | "load-grunt-config": "^0.13.1",
20 | "minifyify": "^4.1.1",
21 | "po2json": "^0.3.2"
22 | },
23 | "dependencies": {
24 | "heap": "^0.2.6",
25 | "omega-target": "../omega-target",
26 | "omega-web": "../omega-web",
27 | "omega-pac": "../omega-pac",
28 | "xhr": "^1.16.0"
29 | },
30 | "browser": {
31 | "omega-target": "./omega_target_shim.js"
32 | },
33 | "scripts": {
34 | "dev": "npm link omega-target && npm link omega-web && npm link omega-pac"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/omega-pac/test/utils.coffee:
--------------------------------------------------------------------------------
1 | chai = require 'chai'
2 | should = chai.should()
3 | Utils = require '../src/utils'
4 |
5 | describe 'getBaseDomain', ->
6 | {getBaseDomain} = Utils
7 | it 'should return domains with zero level unchanged', ->
8 | getBaseDomain('someinternaldomain').should.equal('someinternaldomain')
9 | it 'should return domains with one level unchanged', ->
10 | getBaseDomain('example.com').should.equal('example.com')
11 | getBaseDomain('e.test').should.equal('e.test')
12 | getBaseDomain('a.b').should.equal('a.b')
13 | it 'should treat two-segment TLD as one component', ->
14 | getBaseDomain('images.google.co.uk').should.equal('google.co.uk')
15 | getBaseDomain('images.google.co.jp').should.equal('google.co.jp')
16 | getBaseDomain('example.com.cn').should.equal('example.com.cn')
17 | it 'should not mistake short domains with two-segment TLDs', ->
18 | getBaseDomain('a.bc.com').should.equal('bc.com')
19 | getBaseDomain('i.t.co').should.equal('t.co')
20 | it 'should not try to modify IP address literals', ->
21 | getBaseDomain('127.0.0.1').should.equal('127.0.0.1')
22 | getBaseDomain('[::1]').should.equal('[::1]')
23 | getBaseDomain('::f').should.equal('::f')
24 |
--------------------------------------------------------------------------------
/omega-target-chromium-extension/src/coffee/background_preload.coffee:
--------------------------------------------------------------------------------
1 | window.UglifyJS_NoUnsafeEval = true
2 | localStorage['log'] = ''
3 | localStorage['logLastError'] = ''
4 |
5 | window.OmegaContextMenuQuickSwitchHandler = -> null
6 |
7 | if chrome.contextMenus?
8 | # We don't need this API. However its presence indicates that Chrome >= 35
9 | # which provides info.checked we need in contextMenu callback.
10 | # https://developer.chrome.com/extensions/contextMenus
11 | if chrome.i18n.getUILanguage?
12 | # We must create the menu item here before others to make it first in menu.
13 | chrome.contextMenus.create({
14 | id: 'enableQuickSwitch'
15 | title: chrome.i18n.getMessage('contextMenu_enableQuickSwitch')
16 | type: 'checkbox'
17 | checked: false
18 | contexts: ["browser_action"]
19 | onclick: (info) -> window.OmegaContextMenuQuickSwitchHandler(info)
20 | })
21 |
22 | chrome.contextMenus.create({
23 | title: chrome.i18n.getMessage('popup_reportIssues')
24 | contexts: ["browser_action"]
25 | onclick: OmegaDebug.reportIssue
26 | })
27 |
28 | chrome.contextMenus.create({
29 | title: chrome.i18n.getMessage('popup_errorLog')
30 | contexts: ["browser_action"]
31 | onclick: OmegaDebug.downloadLog
32 | })
33 |
--------------------------------------------------------------------------------
/omega-web/src/omega/filters.coffee:
--------------------------------------------------------------------------------
1 | angular.module('omega').filter 'profiles', (builtinProfiles, profileOrder,
2 | isProfileNameHidden, isProfileNameReserved) ->
3 |
4 | charCodePlus = '+'.charCodeAt(0)
5 | builtinProfileList = (profile for _, profile of builtinProfiles)
6 | (options, filter) ->
7 | result = []
8 | for name, value of options when name.charCodeAt(0) == charCodePlus
9 | result.push value
10 | if (typeof filter == 'object' or (
11 | typeof filter == 'string' and filter.charCodeAt(0) == charCodePlus))
12 | if typeof filter == 'string'
13 | filter = filter.substr(1)
14 | result = OmegaPac.Profiles.validResultProfilesFor(filter, options)
15 | if filter == 'all'
16 | result = result.filter (profile) -> !isProfileNameHidden(profile.name)
17 | result = result.concat builtinProfileList
18 | else
19 | result = result.filter (profile) -> !isProfileNameReserved(profile.name)
20 | if filter == 'sorted'
21 | result.sort profileOrder
22 | result
23 |
24 | angular.module('omega').filter 'tr', (omegaTarget) -> omegaTarget.getMessage
25 | angular.module('omega').filter 'dispName', (omegaTarget) ->
26 | (name) ->
27 | if typeof name == 'object'
28 | name = name.name
29 | omegaTarget.getMessage('profile_' + name) || name
30 |
--------------------------------------------------------------------------------
/omega-target-chromium-extension/grunt/browserify.coffee:
--------------------------------------------------------------------------------
1 | path = require('path')
2 | module.exports =
3 | index:
4 | files:
5 | 'index.js': 'index.coffee'
6 | options:
7 | transform: ['coffeeify']
8 | exclude: ['bluebird', 'omega-pac', 'omega-target']
9 | browserifyOptions:
10 | extensions: '.coffee'
11 | builtins: []
12 | standalone: 'index.coffee'
13 | debug: true
14 | browser:
15 | files:
16 | 'omega_target_chromium_extension.min.js': 'index.coffee'
17 | options:
18 | alias: [
19 | './index.coffee:OmegaTargetChromium'
20 | ]
21 | transform: ['coffeeify']
22 | plugin:
23 | if process.env.BUILD == 'release'
24 | [['minifyify', {map: false}]]
25 | else
26 | []
27 | browserifyOptions:
28 | extensions: '.coffee'
29 | standalone: 'OmegaTargetChromium'
30 | omega_webext_proxy_script:
31 | files:
32 | 'build/js/omega_webext_proxy_script.min.js':
33 | 'src/js/omega_webext_proxy_script.js'
34 | options:
35 | alias:
36 | 'omega-pac': 'omega-pac/omega_pac.min.js'
37 | plugin:
38 | if process.env.BUILD == 'release'
39 | [['minifyify', {map: false}]]
40 | else
41 | []
42 | browserifyOptions:
43 | noParse: [require.resolve('omega-pac/omega_pac.min.js')]
44 |
--------------------------------------------------------------------------------
/omega-target/src/default_options.coffee:
--------------------------------------------------------------------------------
1 | module.exports = ->
2 | schemaVersion: 2
3 | "-enableQuickSwitch": false
4 | "-refreshOnProfileChange": true
5 | "-startupProfileName": ""
6 | "-quickSwitchProfiles": []
7 | "-revertProxyChanges": true
8 | "-confirmDeletion": true
9 | "-showInspectMenu": true
10 | "-addConditionsToBottom": false
11 | "-showExternalProfile": true
12 | "-downloadInterval": 1440
13 | "+proxy":
14 | bypassList: [
15 | {
16 | pattern: "127.0.0.1"
17 | conditionType: "BypassCondition"
18 | }
19 | {
20 | pattern: "::1"
21 | conditionType: "BypassCondition"
22 | }
23 | {
24 | pattern: "localhost"
25 | conditionType: "BypassCondition"
26 | }
27 | ]
28 | profileType: "FixedProfile"
29 | name: "proxy"
30 | color: "#99ccee"
31 | fallbackProxy:
32 | port: 8080
33 | scheme: "http"
34 | host: "proxy.example.com"
35 |
36 | "+auto switch":
37 | profileType: "SwitchProfile"
38 | rules: [
39 | {
40 | condition:
41 | pattern: "internal.example.com"
42 | conditionType: "HostWildcardCondition"
43 |
44 | profileName: "direct"
45 | }
46 | {
47 | condition:
48 | pattern: "*.example.com"
49 | conditionType: "HostWildcardCondition"
50 |
51 | profileName: "proxy"
52 | }
53 | ]
54 | name: "auto switch"
55 | color: "#99dd99"
56 | defaultProfileName: "direct"
57 |
--------------------------------------------------------------------------------
/omega-web/src/partials/rename_profile.jade:
--------------------------------------------------------------------------------
1 | form(ng-submit='renameProfile.$valid && $close(newName)' name='renameProfile')
2 | .modal-header
3 | button.close(type='button' ng-click='$dismiss()')
4 | span(aria-hidden='true') ×
5 | span.sr-only {{'dialog_close' | tr}}
6 | h4.modal-title {{'options_modalHeader_renameProfile' | tr}}
7 | .modal-body
8 | .form-group(ng-class='{"has-error": !renameProfile.profileNewName.$valid}')
9 | label(for='profile-new-name') {{'options_renameProfileName' | tr}}
10 | input#profile-new-name.form-control(type='text' name='profileNewName' required ng-model='newName'
11 | ui-validate='validateProfileName' ng-init='newName = fromName')
12 | .help-block(ng-show='renameProfile.profileNewName.$error.required') {{'options_profileNameEmpty' | tr}}
13 | .help-block(ng-show='renameProfile.profileNewName.$error.reserved') {{'options_profileNameReserved' | tr}}
14 | .help-block(ng-show='!renameProfile.profileNewName.$error.reserved && renameProfile.profileNewName.$error.conflict')
15 | | {{'options_profileNameConflict' | tr}}
16 | .help-block(ng-show='renameProfile.profileNewName.$valid && newName && isProfileNameHidden(newName)')
17 | .text-info
18 | span.glyphicon.glyphicon-info-sign
19 | = ' '
20 | | {{'options_profileNameHidden' | tr}}
21 | .modal-footer
22 | button.btn.btn-default(type='button' ng-click='$dismiss()') {{'dialog_cancel' | tr}}
23 | button.btn.btn-primary(type='submit' ng-disabled='!renameProfile.$valid') {{'options_renameProfile' | tr}}
24 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
25 |
26 | ### SwitchyOmega version / SwitchyOmega 版本
27 |
28 |
29 | ### Browser version & OS version / 浏览器名称、版本及操作系统版本
30 |
31 |
32 | ### Problem description / 问题描述
33 |
34 | (Please provide as much detail as possible. We recommend the following format.)
35 | (请尽可能多提供一些细节。我们推荐使用下面的格式。)
36 |
37 | #### Steps to reproduce issue / 重现错误所需步骤
38 |
39 | (What did you do? / 你做了什么?)
40 |
41 | 1.
42 | 2.
43 | 3.
44 |
45 | #### Expected behavior / 期望发生的情况
46 |
47 |
48 | #### Actual (or suggested) behavior / 实际发生的情况(或建议修改后的行为)
49 |
50 |
--------------------------------------------------------------------------------
/omega-target/src/browser_storage.coffee:
--------------------------------------------------------------------------------
1 | Storage = require('./storage')
2 | Promise = require('bluebird')
3 |
4 | class BrowserStorage extends Storage
5 | constructor: (@storage, @prefix = '') ->
6 | @proto = Object.getPrototypeOf(@storage)
7 |
8 | get: (keys) ->
9 | map = {}
10 | if typeof keys == 'string'
11 | map[keys] = undefined
12 | else if Array.isArray(keys)
13 | for key in keys
14 | map[key] = undefined
15 | else if typeof keys == 'object'
16 | map = keys
17 | for own key of map
18 | try
19 | value = JSON.parse(@proto.getItem.call(@storage, @prefix + key))
20 | map[key] = value if value?
21 | if typeof map[key] == 'undefined'
22 | delete map[key]
23 | Promise.resolve map
24 |
25 | set: (items) ->
26 | for own key, value of items
27 | value = JSON.stringify(value)
28 | @proto.setItem.call(@storage, @prefix + key, value)
29 | Promise.resolve items
30 |
31 | remove: (keys) ->
32 | if not keys?
33 | if not @prefix
34 | @proto.clear.call(@storage)
35 | else
36 | index = 0
37 | while true
38 | key = @proto.key.call(index)
39 | break if key == null
40 | if @key.substr(0, @prefix.length) == @prefix
41 | @proto.removeItem.call(@storage, @prefix + keys)
42 | else
43 | index++
44 | if typeof keys == 'string'
45 | @proto.removeItem.call(@storage, @prefix + keys)
46 | for key in keys
47 | @proto.removeItem.call(@storage, @prefix + key)
48 |
49 | Promise.resolve()
50 |
51 | module.exports = BrowserStorage
52 |
--------------------------------------------------------------------------------
/omega-web/src/partials/general.jade:
--------------------------------------------------------------------------------
1 | .page-header
2 | h2 {{'options_tab_general' | tr}}
3 | section.settings-group
4 | h3 {{'options_group_networkRequests' | tr}}
5 | div.checkbox
6 | label
7 | input#revert-proxy-changes(type='checkbox' ng-model='options["-monitorWebRequests"]')
8 | span {{'options_monitorWebRequests' | tr}}
9 | p.help-block(omega-html="'options_monitorWebRequestsHelp' | tr")
10 | section.settings-group.width-limit
11 | h3 {{'options_downloadOptions' | tr}}
12 | p.help-block {{'options_downloadOptionsHelp' | tr}}
13 | .form-group
14 | label(for='download-interval') {{'options_downloadInterval' | tr}}
15 | select#download-interval.form-control.inline-form-control(ng-model='options["-downloadInterval"]'
16 | ng-options='interval as (downloadIntervalI18n(interval) | tr) for interval in downloadIntervals')
17 | section.settings-group.width-limit
18 | h3 {{'options_group_conflicts' | tr}}
19 | p {{'options_conflicts_introduction' | tr}}
20 | p.help-text.text-danger
21 | span(style='padding: 1px 4px; background: #da4f49; color: #fff; box-shadow: #ccc 1px 1px 1px 1px;') =
22 | = ' '
23 | | {{'options_conflicts_lowerPriority' | tr}}
24 | p.help-text.text-info
25 | span.glyphicon.glyphicon-info-sign
26 | = ' '
27 | span(omega-html="'options_conflicts_higherPriority' | tr:[$profile('systemProfile')]")
28 |
29 | div.checkbox
30 | label
31 | input#revert-proxy-changes(type='checkbox' ng-model='options["-showExternalProfile"]')
32 | span {{'options_showExternalProfile' | tr}}
33 | p.help-block(omega-html="'options_showExternalProfileHelp' | tr:[$profile('systemProfile'), $profile('externalProfile')]")
34 |
--------------------------------------------------------------------------------
/omega-web/src/less/common.less:
--------------------------------------------------------------------------------
1 | /* angular */
2 |
3 | .ng-hide {
4 | display: none !important;
5 | }
6 |
7 | /* helpers */
8 |
9 | .clear-padding {
10 | padding: 0 !important;
11 | }
12 |
13 | /* basic */
14 |
15 | a[role="button"] {
16 | cursor: pointer;
17 | }
18 |
19 | .form-control.ng-invalid {
20 | border-color: #a94442;
21 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
22 |
23 | &:hover {
24 | border-color: #843534;
25 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 6px #CE8483;
26 | }
27 | }
28 |
29 | .disabled .btn {
30 | background-color: #ddd !important;
31 | border-color: #ccc !important;
32 | cursor: not-allowed !important;
33 | pointer-events: none !important;
34 | opacity: .65 !important;
35 | box-shadow: none !important;
36 | }
37 |
38 | /* profile */
39 |
40 | .virtual-profile-icon {
41 | border: dotted 1px;
42 | margin: -1px;
43 | }
44 |
45 | .profile-inline {
46 | background-color: #eee;
47 | color: #333;
48 | padding: 0 5px;
49 | }
50 |
51 | /* omega-profile-select */
52 |
53 | .omega-profile-select {
54 | width: 100%;
55 | .dropdown-menu>li>a {
56 | max-width: none !important;
57 | }
58 |
59 | .btn {
60 | width: 100%;
61 | display: block;
62 | text-align: left;
63 | text-align: initial;
64 | position: relative;
65 | padding-right: 25px;
66 |
67 | .caret {
68 | position: absolute;
69 | top: 50%;
70 | right: 12px;
71 | margin-top: -2px;
72 | vertical-align: middle;
73 | }
74 | }
75 |
76 | .dropdown-menu {
77 | width: 100%;
78 | cursor: pointer;
79 | }
80 | }
81 |
82 | .monospace {
83 | font-family: Menlo,Monaco,Consolas,"Courier New",monospace !important;
84 | }
85 |
--------------------------------------------------------------------------------
/omega-pac/src/shexp_utils.coffee:
--------------------------------------------------------------------------------
1 | module.exports = exports =
2 | regExpMetaChars: do ->
3 | chars = '''\\[\^$.|?*+(){}/'''
4 | set = {}
5 | for i in [0...chars.length]
6 | set[chars.charCodeAt(i)] = true
7 | set
8 | escapeSlash: (pattern) ->
9 | charCodeSlash = 47 # /
10 | charCodeBackSlash = 92 # \
11 | escaped = false
12 | start = 0
13 | result = ''
14 | for i in [0...pattern.length]
15 | code = pattern.charCodeAt(i)
16 | if code == charCodeSlash and not escaped
17 | result += pattern.substring start, i
18 | result += '\\'
19 | start = i
20 | escaped = (code == charCodeBackSlash and not escaped)
21 | result += pattern.substr start
22 | shExp2RegExp: (pattern, options) ->
23 | trimAsterisk = options?.trimAsterisk || false
24 | start = 0
25 | end = pattern.length
26 | charCodeAsterisk = 42 # '*'
27 | charCodeQuestion = 63 # '?'
28 | if trimAsterisk
29 | while start < end && pattern.charCodeAt(start) == charCodeAsterisk
30 | start++
31 | while start < end && pattern.charCodeAt(end - 1) == charCodeAsterisk
32 | end--
33 | if end - start == 1 && pattern.charCodeAt(start) == charCodeAsterisk
34 | return ''
35 | regex = ''
36 | if start == 0
37 | regex += '^'
38 | for i in [start...end]
39 | code = pattern.charCodeAt(i)
40 | switch code
41 | when charCodeAsterisk then regex += '.*'
42 | when charCodeQuestion then regex += '.'
43 | else
44 | if exports.regExpMetaChars[code] >= 0
45 | regex += '\\'
46 | regex += pattern[i]
47 |
48 | if end == pattern.length
49 | regex += '$'
50 |
51 | return regex
52 |
--------------------------------------------------------------------------------
/omega-web/src/coffee/options_guide.coffee:
--------------------------------------------------------------------------------
1 | $script 'lib/tether/tether.js', ->
2 | $script 'lib/shepherd.js/shepherd.min.js', ->
3 | tr = chrome.i18n.getMessage.bind(chrome.i18n)
4 | tour = new Shepherd.Tour
5 | defaults:
6 | classes: 'shepherd-theme-arrows'
7 | scrollTo: true
8 |
9 | targetAnchorClick =
10 | selector: '.shepherd-target a'
11 | event: 'click'
12 |
13 | tour.addStep('fixed-profile-step',
14 | text: tr('options_guide_fixedProfileStep')
15 | attachTo: '.nav-profile[data-profile-type="FixedProfile"] right'
16 | scrollTo: false
17 | advanceOn: targetAnchorClick
18 | buttons: [
19 | text: tr('options_guideNext')
20 | action: tour.next
21 | ]
22 | )
23 |
24 | tour.addStep 'fixed-servers-step',
25 | text: tr('options_guide_fixedServersStep')
26 | attachTo: '.fixed-servers top'
27 | scrollTo: false
28 | buttons: [
29 | text: tr('options_guideNext')
30 | action: tour.next
31 | ]
32 |
33 | tour.addStep 'auto-switch-profile-step',
34 | text: tr('options_guide_autoSwitchProfileStep')
35 | attachTo: '.nav-profile[data-profile-type="SwitchProfile"] right'
36 | scrollTo: false
37 | advanceOn: targetAnchorClick
38 | buttons: [
39 | text: tr('options_guideNext')
40 | action: tour.next
41 | ]
42 |
43 | tour.addStep 'add-more-profiles-step',
44 | text: tr('options_guide_addMoreProfilesStep')
45 | attachTo: '.nav-new-profile right'
46 | scrollTo: false
47 | advanceOn: targetAnchorClick
48 | buttons: [
49 | text: tr('options_guideDone')
50 | action: tour.next
51 | ]
52 |
53 | tour.start()
54 |
--------------------------------------------------------------------------------
/omega-target-chromium-extension/grunt-po2crx.coffee:
--------------------------------------------------------------------------------
1 | module.exports = (grunt) ->
2 | taskDesc = 'Convert gettext PO files to Chromium Extension messages format.'
3 | # coffeelint: disable=missing_fat_arrows
4 | grunt.registerMultiTask 'po2crx', taskDesc, ->
5 | for f in this.files
6 | result = {}
7 | for src in f.src
8 | json = require('po2json').parseFileSync(src)
9 | for own key, value of json when key
10 | message = value[1]
11 | refs = []
12 | matchCount = 0
13 | message = message.replace /\$(\d+:)?(\w+)\$/g, (_, order, ref) ->
14 | matchCount++
15 | if order
16 | order = parseInt(order)
17 | else
18 | order = matchCount
19 | ### TODO(catus): Shall we enable this warning?
20 | if matchCount > 1
21 | grunt.log.writeln("In this message: #{key}=#{message}")
22 | grunt.log.writeln(
23 | 'Order not specified for two or more refs in same message.')
24 | ###
25 | refs[order] = ref
26 | return '$' + ref + '$'
27 |
28 | if not matchCount
29 | placeholders = undefined
30 | else
31 | placeholders = {}
32 | for i in [0...refs.length]
33 | placeholder = refs[i] ? ('_unused_' + i)
34 | placeholders[placeholder] = {content: '$' + i}
35 | if message == ' '
36 | message = ''
37 | result[key] =
38 | message: message
39 | placeholders: placeholders
40 |
41 | grunt.file.write(f.dest, JSON.stringify(result))
42 | grunt.log.writeln("File \"#{f.dest}\" created.")
43 | # coffeelint: enable=missing_fat_arrows
44 |
--------------------------------------------------------------------------------
/omega-web/src/partials/profile_rule_list.jade:
--------------------------------------------------------------------------------
1 | div(ng-controller='RuleListProfileCtrl')
2 | section.settings-group
3 | h3 {{'options_group_ruleListConfig' | tr}}
4 | .form-group
5 | label {{'options_ruleListMatchProfile' | tr}}
6 | = ' '
7 | div(omega-profile-select='options | profiles:profile' ng-model='profile.matchProfileName'
8 | disp-name='dispNameFilter' options='options' style='display: inline-block;')
9 | .form-group
10 | label {{'options_ruleListDefaultProfile' | tr}}
11 | = ' '
12 | div(omega-profile-select='options | profiles:profile' ng-model='profile.defaultProfileName'
13 | disp-name='dispNameFilter' options='options' style='display: inline-block;')
14 | form.form-group
15 | label {{'options_ruleListFormat' | tr}}
16 | .radio.inline-form-control.no-min-width(ng-repeat='format in ruleListFormats')
17 | label
18 | input(type='radio' name='formatInput' value='{{format}}' ng-model='profile.format')
19 | | {{'ruleListFormat_' + format | tr}}
20 | section.settings-group
21 | h3 {{'options_group_ruleListUrl' | tr}}
22 | .width-limit(input-group-clear type='url' model='profile.sourceUrl' ng-if='profile')
23 | p.help-block {{'options_ruleListUrlHelp' | tr}}
24 | section.settings-group
25 | h3 {{'options_group_ruleListText' | tr}}
26 | p
27 | button.btn.btn-default(ng-disabled='!profile.sourceUrl' ng-click='updateProfile(profile.name)'
28 | ladda='updatingProfile[profile.name]' data-spinner-color="#000000")
29 | | #[span.glyphicon.glyphicon-download-alt] {{'options_downloadProfileNow' | tr}}
30 | textarea.monospace.form-control.width-limit(ng-model='profile.ruleList' rows=20
31 | ng-disabled='!!profile.sourceUrl')
32 |
33 |
--------------------------------------------------------------------------------
/omega-web/src/partials/about.jade:
--------------------------------------------------------------------------------
1 | .page-header
2 | h2 {{'about_title' | tr}}
3 | section.omega-experimental(ng-show='isExperimental')
4 | p.alert.alert-warning
5 | span.glyphicon.glyphicon-warning-sign
6 | = ' '
7 | span {{'about_experimental_warning_moz' | tr}}
8 | section
9 | .media(style='margin: 1em 0')
10 | .media-left
11 | img.media-object(src='img/icons/omega-action-32.png')
12 | .media-body
13 | h4.media-heading {{'appNameShort' | tr}}
14 | p {{'about_app_description' | tr}}
15 | section
16 | p
17 | button.btn.btn-info(ng-click='reportIssue()')
18 | span.glyphicon.glyphicon-comment
19 | = ' '
20 | | {{'popup_reportIssues' | tr}}
21 | = ' '
22 | button.btn.btn-default(ng-click='downloadLog()')
23 | span.glyphicon.glyphicon-download
24 | = ' '
25 | | {{'popup_errorLog' | tr}}
26 | = ' '
27 | button.btn.btn-danger(ng-click='showResetOptionsModal()')
28 | span.glyphicon.glyphicon-alert
29 | = ' '
30 | | {{'options_reset' | tr}}
31 | section
32 | p
33 | | {{'about_version' | tr:[version]}}
34 | p.text-warning
35 | span.glyphicon.glyphicon-info-sign
36 | = ' '
37 | span(ng-bind-html='"about_disclaimer_networkService" | tr')
38 | p.text-success
39 | span.glyphicon.glyphicon-eye-close
40 | = ' '
41 | span(ng-bind-html='"about_disclaimer_privacy" | tr')
42 | p.text-info
43 | span.glyphicon.glyphicon-question-sign
44 | = ' '
45 | span(ng-bind-html='"about_help" | tr')
46 |
47 | section(style='margin-top: 7em')
48 | p
49 | | {{'appNameShort' | tr}}
50 | br
51 | span(ng-bind-html='"about_copyright" | tr')
52 | br
53 | span(ng-bind-html='"about_license" | tr')
54 | br
55 | span(ng-bind-html='"about_credits" | tr')
56 |
--------------------------------------------------------------------------------
/omega-target-chromium-extension/src/coffee/omega_debug.coffee:
--------------------------------------------------------------------------------
1 | window.OmegaDebug =
2 | getProjectVersion: ->
3 | chrome.runtime.getManifest().version
4 | getExtensionVersion: ->
5 | chrome.runtime.getManifest().version
6 | downloadLog: ->
7 | blob = new Blob [localStorage['log']], {type: "text/plain;charset=utf-8"}
8 | filename = "OmegaLog_#{Date.now()}.txt"
9 |
10 | if browser?.downloads?.download?
11 | url = URL.createObjectURL(blob)
12 | browser.downloads.download({url: url, filename: filename})
13 | else
14 | saveAs(blob, filename)
15 | resetOptions: ->
16 | localStorage.clear()
17 | # Prevent options loading from sync storage after reload.
18 | localStorage['omega.local.syncOptions'] = '"conflict"'
19 | chrome.storage.local.clear()
20 | chrome.runtime.reload()
21 | reportIssue: ->
22 | url = 'https://github.com/FelisCatus/SwitchyOmega/issues/new?title=&body='
23 | finalUrl = url
24 | try
25 | projectVersion = OmegaDebug.getProjectVersion()
26 | extensionVersion = OmegaDebug.getExtensionVersion()
27 | env =
28 | extensionVersion: extensionVersion
29 | projectVersion: extensionVersion
30 | userAgent: navigator.userAgent
31 | body = chrome.i18n.getMessage('popup_issueTemplate', [
32 | env.projectVersion, env.userAgent
33 | ])
34 | body ||= """
35 | \n\n
36 |
37 | SwitchyOmega #{env.projectVersion}
38 | #{env.userAgent}
39 | """
40 | finalUrl = url + encodeURIComponent(body)
41 | err = localStorage['logLastError']
42 | if err
43 | body += "\n```\n#{err}\n```"
44 | finalUrl = (url + encodeURIComponent(body)).substr(0, 2000)
45 |
46 | chrome.tabs.create(url: finalUrl)
47 |
--------------------------------------------------------------------------------
/omega-target-chromium-extension/src/module/switchysharp.coffee:
--------------------------------------------------------------------------------
1 | OmegaTarget = require('omega-target')
2 | OmegaPac = OmegaTarget.OmegaPac
3 | Promise = OmegaTarget.Promise
4 | ChromePort = require('./chrome_port')
5 |
6 | module.exports = class SwitchySharp
7 | @extId: 'dpplabbmogkhghncfbfdeeokoefdjegm'
8 | port: null
9 |
10 | monitor: (action) ->
11 | return if location.href.substr(0, 4) == 'moz-'
12 | if not port? and not @_monitorTimerId?
13 | @_monitorTimerId = setInterval @_connect.bind(this), 5000
14 | if action != 'reconnect'
15 | @_connect()
16 |
17 | getOptions: ->
18 | if not @_getOptions
19 | @_getOptions = new Promise (resolve) =>
20 | @_getOptionsResolver = resolve
21 | @monitor()
22 | @_getOptions
23 |
24 | _getOptions: null
25 | _getOptionsResolver: null
26 | _monitorTimerId: null
27 |
28 | _onMessage: (msg) ->
29 | if @_monitorTimerId
30 | clearInterval @_monitorTimerId
31 | @_monitorTimerId = null
32 | switch msg?.action
33 | when 'state'
34 | # State changed.
35 | OmegaTarget.Log.log(msg)
36 | if @_getOptionsResolver
37 | @port.postMessage({action: 'getOptions'})
38 | when 'options'
39 | @_getOptionsResolver?(msg.options)
40 | @_getOptionsResolver = null
41 |
42 | _onDisconnect: (msg) ->
43 | @port = null
44 | @_getOptions = null
45 | @_getOptionsResolver = null
46 | @monitor('reconnect')
47 |
48 | _connect: ->
49 | if not @port
50 | @port = new ChromePort(chrome.runtime.connect(SwitchySharp.extId))
51 | @port.onDisconnect.addListener(@_onDisconnect.bind(this))
52 | @port?.onMessage.addListener(@_onMessage.bind(this))
53 | try
54 | @port.postMessage({action: 'disable'})
55 | catch _
56 | @port = null
57 | return @port?
58 |
--------------------------------------------------------------------------------
/omega-web/src/popup/js/index.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | handleClick('js-option', showOptions);
3 | handleClick('js-temprule', showTempRuleDropdown);
4 | handleClick('js-direct', applyProfile.bind(this, 'direct'));
5 | handleClick('js-system', applyProfile.bind(this, 'system'));
6 | OmegaPopup.addTempRule = addTempRule;
7 | OmegaPopup.setDefaultProfile = setDefaultProfile;
8 | OmegaPopup.applyProfile = applyProfile;
9 | return;
10 |
11 | function handleClick(id, handler) {
12 | document.getElementById(id).addEventListener('click', handler, false);
13 | }
14 |
15 | function closePopup() {
16 | window.close();
17 | // If the popup is opened as a tab, the above won't work. Let's reload then.
18 | document.body.style.opacity = 0;
19 | setTimeout(function() { history.go(0); }, 300);
20 | }
21 |
22 | function showOptions(e) {
23 | if (typeof OmegaTargetPopup !== 'undefined') {
24 | try {
25 | OmegaTargetPopup.openOptions(null, closePopup);
26 | e.preventDefault();
27 | } catch (_) {
28 | }
29 | }
30 | }
31 |
32 | function applyProfile(profileName) {
33 | $script.ready('om-target', function() {
34 | OmegaTargetPopup.applyProfile(profileName, closePopup);
35 | });
36 | }
37 |
38 | function setDefaultProfile(profileName, defaultProfileName) {
39 | $script.ready('om-target', function() {
40 | OmegaTargetPopup.setDefaultProfile(profileName, defaultProfileName,
41 | closePopup);
42 | });
43 | }
44 |
45 | function addTempRule(domain, profileName) {
46 | $script.ready('om-target', function() {
47 | OmegaTargetPopup.addTempRule(domain, profileName, closePopup);
48 | });
49 | }
50 |
51 | function showTempRuleDropdown() {
52 | $script.ready('om-dropdowns', function() {
53 | OmegaPopup.showTempRuleDropdown();
54 | });
55 | }
56 | })();
57 |
--------------------------------------------------------------------------------
/omega-target-chromium-extension/src/module/proxy/proxy_impl.coffee:
--------------------------------------------------------------------------------
1 | OmegaTarget = require('omega-target')
2 | Promise = OmegaTarget.Promise
3 | ProxyAuth = require('./proxy_auth')
4 |
5 | class ProxyImpl
6 | constructor: (log) ->
7 | @log = log
8 | @isSupported: -> false
9 | applyProfile: (profile, meta) -> Promise.reject()
10 | watchProxyChange: (callback) -> null
11 | parseExternalProfile: (details, options) -> null
12 | _profileNotFound: (name) ->
13 | @log.error("Profile #{name} not found! Things may go very, very wrong.")
14 | return OmegaPac.Profiles.create({
15 | name: name
16 | profileType: 'VirtualProfile'
17 | defaultProfileName: 'direct'
18 | })
19 | setProxyAuth: (profile, options) ->
20 | return Promise.try(=>
21 | @_proxyAuth ?= new ProxyAuth(@log)
22 | @_proxyAuth.listen()
23 | referenced_profiles = []
24 | ref_set = OmegaPac.Profiles.allReferenceSet(profile,
25 | options, profileNotFound: @_profileNotFound.bind(this))
26 | for own _, name of ref_set
27 | profile = OmegaPac.Profiles.byName(name, options)
28 | if profile
29 | referenced_profiles.push(profile)
30 | @_proxyAuth.setProxies(referenced_profiles)
31 | )
32 | getProfilePacScript: (profile, meta, options) ->
33 | meta ?= profile
34 | ast = OmegaPac.PacGenerator.script(options, profile,
35 | profileNotFound: @_profileNotFound.bind(this))
36 | ast = OmegaPac.PacGenerator.compress(ast)
37 | script = OmegaPac.PacGenerator.ascii(ast.print_to_string())
38 | profileName = OmegaPac.PacGenerator.ascii(JSON.stringify(meta.name))
39 | profileName = profileName.replace(/\*/g, '\\u002a')
40 | profileName = profileName.replace(/\\/g, '\\u002f')
41 | prefix = "/*OmegaProfile*#{profileName}*#{meta.revision}*/"
42 | return prefix + script
43 |
44 | module.exports = ProxyImpl
45 |
--------------------------------------------------------------------------------
/omega-web/src/popup/js/proxy_not_controllable.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | function closePopup() {
3 | window.close();
4 | // If the popup is opened as a tab, the above won't work. Let's reload then.
5 | document.body.style.opacity = 0;
6 | setTimeout(function() { history.go(0); }, 300);
7 | }
8 | var closeButton = document.getElementById('js-close');
9 | closeButton.addEventListener('click', closePopup, false);
10 |
11 | var manageButton = document.getElementById('js-manage-ext');
12 | manageButton.addEventListener('click', function () {
13 | OmegaTargetPopup.openManage(closePopup);
14 | }, false);
15 |
16 | var learnMoreButton = document.getElementById('js-nc-learn-more');
17 | learnMoreButton.addEventListener('click', function () {
18 | OmegaTargetPopup.openOptions('#!/general', closePopup);
19 | }, false);
20 |
21 | closeButton.textContent = OmegaTargetPopup.getMessage('dialog_cancel');
22 | learnMoreButton.textContent = 'Learn More'
23 | //OmegaTargetPopup.getMessage('popup_proxyNotControllableLearnMore');
24 | manageButton.textContent = OmegaTargetPopup.getMessage(
25 | 'popup_proxyNotControllableManage');
26 |
27 |
28 | OmegaTargetPopup.getState([
29 | 'proxyNotControllable',
30 | ], function(err, state) {
31 | var reason = state.proxyNotControllable;
32 | var messageElement = document.getElementById('js-nc-text');
33 | var detailsElement = document.getElementById('js-nc-details');
34 | messageElement.textContent = OmegaTargetPopup.getMessage(
35 | 'popup_proxyNotControllable_' + reason);
36 | var detailsMessage = OmegaTargetPopup.getMessage(
37 | 'popup_proxyNotControllableDetails_' + reason);
38 | if (!detailsMessage) detailsMessage = OmegaTargetPopup.getMessage(
39 | 'popup_proxyNotControllableDetails');
40 |
41 | detailsElement.textContent = detailsMessage;
42 | });
43 | })();
44 |
--------------------------------------------------------------------------------
/omega-web/src/partials/profile.jade:
--------------------------------------------------------------------------------
1 | .page-header
2 | .profile-actions
3 | button.btn(ng-show='exportRuleList' ng-click='exportRuleList(profile.name)'
4 | title="{{'options_profileExportRuleListHelp' | tr}}"
5 | ng-class="exportRuleListOptions.warning ? 'btn-warning' : 'btn-default'")
6 | span.glyphicon.glyphicon-list
7 | = ' '
8 | | {{'options_profileExportRuleList' | tr}}
9 | = ' '
10 | button.btn.btn-default(ng-show='scriptable' ng-click='exportScript(profile.name)' title="{{'options_exportPacFileHelp' | tr}}")
11 | span.glyphicon.glyphicon-download
12 | = ' '
13 | | {{'options_profileExportPac' | tr}}
14 | = ' '
15 | button.btn.btn-default(ng-click='renameProfile(profile.name)')
16 | span.glyphicon.glyphicon-edit
17 | = ' '
18 | | {{'options_renameProfile' | tr}}
19 | = ' '
20 | button.btn.btn-danger(ng-click='deleteProfile(profile.name)')
21 | span.glyphicon.glyphicon-trash
22 | = ' '
23 | | {{'options_deleteProfile' | tr}}
24 | span.profile-color-editor
25 | .profile-color-editor-fake(ng-if='profile.profileType == "VirtualProfile"'
26 | ng-style="{'background-color': getProfileColor()}")
27 | x-spectrum-colorpicker(ng-model='profile.color' options='spectrumOptions'
28 | ng-if='profile.profileType != "VirtualProfile"')
29 | h2.profile-name {{'options_profileTabPrefix' | tr}}{{profile.name}}
30 | section.settings-group(ng-show='profile.syncOptions == "disabled"')
31 | p.alert.alert-info.width-limit(ng-show='!profile.syncError')
32 | span.glyphicon.glyphicon-info-sign
33 | = ' '
34 | | {{'Syncing is disabled for this profile.'}}
35 | p.alert.alert-danger.width-limit(ng-show='!!profile.syncError')
36 | span.glyphicon.glyphicon-remove
37 | = ' '
38 | | {{('options_profileSyncDisabled_' + profile.syncError.reason) | tr}}
39 | div(ng-include='profileTemplate')
40 |
--------------------------------------------------------------------------------
/omega-web/src/partials/fixed_auth_edit.jade:
--------------------------------------------------------------------------------
1 | form(ng-submit='authForm.$valid && $close(auth)' name='authForm')
2 | .modal-header
3 | button.close(type='button' ng-click='$dismiss()')
4 | span(aria-hidden='true') ×
5 | span.sr-only Close
6 | h4.modal-title {{'options_modalHeader_proxyAuth' | tr}}
7 | .modal-body(style='padding-bottom: 0;')
8 | .form-group(ng-show='!authSupported')
9 | .alert.alert-danger
10 | span.glyphicon.glyphicon-warning-sign
11 | = ' '
12 | | {{"options_proxy_authNotSupported" | tr:[protocolDisp]}}
13 | .form-group
14 | label.sr-only {{'options_proxyAuthUsername' | tr}}
15 | div(input-group-clear type='text' model='auth.username' autofocus
16 | placeholder='{{"options_proxyAuthUsername" | tr}}')
17 | .form-group(ng-class='{"has-error": !authForm.password.$valid}')
18 | label.sr-only {{'options_proxyAuthPassword' | tr}}
19 | .input-group
20 | input.form-control(type='text' name='password' ng-model='auth.password'
21 | ng-attr-type='{{showPassword ? "text" : "password"}}'
22 | placeholder='{{"options_proxyAuthPassword" | tr}}' ng-show='!!auth.username')
23 | input.form-control(type='text' value='' placeholder='{{"options_proxyAuthNone" | tr}}'
24 | disabled ng-show='!auth.username')
25 | span.input-group-btn
26 | button.btn.btn-default(type='button' ng-click='showPassword = !showPassword'
27 | title="{{(showPassword ? 'options_proxyAuthHidePassword' : 'options_proxyAuthShowPassword') | tr}}" ng-disabled='!auth.username')
28 | span.glyphicon(ng-class='{"glyphicon-eye-close": !showPassword, "glyphicon-eye-open": !!showPassword}')
29 | .modal-footer
30 | button.btn.btn-default(type='button' ng-click='$dismiss()') {{'dialog_cancel' | tr}}
31 | button.btn.btn-primary(type='submit' ng-disabled='!authForm.$valid') {{'dialog_save' | tr}}
32 |
--------------------------------------------------------------------------------
/omega-pac/src/utils.coffee:
--------------------------------------------------------------------------------
1 | Revision =
2 | fromTime: (time) ->
3 | time = if time then new Date(time) else new Date()
4 | return time.getTime().toString(16)
5 | compare: (a, b) ->
6 | return 0 if not a and not b
7 | return -1 if not a
8 | return 1 if not b
9 | return 1 if a.length > b.length
10 | return -1 if a.length < b.length
11 | return 1 if a > b
12 | return -1 if a < b
13 | return 0
14 |
15 | exports.Revision = Revision
16 |
17 | class AttachedCache
18 | constructor: (opt_prop, @tag) ->
19 | @prop = opt_prop
20 | if typeof @tag == 'undefined'
21 | @tag = opt_prop
22 | @prop = '_cache'
23 | get: (obj, otherwise) ->
24 | tag = @tag(obj)
25 | cache = @_getCache(obj)
26 | if cache? and cache.tag == tag
27 | return cache.value
28 | value = if typeof otherwise == 'function' then otherwise() else otherwise
29 | @_setCache(obj, {tag: tag, value: value})
30 | return value
31 | drop: (obj) ->
32 | if obj[@prop]?
33 | obj[@prop] = undefined
34 | _getCache: (obj) -> obj[@prop]
35 | _setCache: (obj, value) ->
36 | if not Object::hasOwnProperty.call obj, @prop
37 | Object.defineProperty obj, @prop, writable: true
38 | obj[@prop] = value
39 |
40 | exports.AttachedCache = AttachedCache
41 |
42 | tld = require('tldjs')
43 |
44 | exports.isIp = (domain) ->
45 | return true if domain.indexOf(':') > 0 # IPv6
46 | lastCharCode = domain.charCodeAt(domain.length - 1)
47 | return true if 48 <= lastCharCode <= 57 # IP address ending with number.
48 | return false
49 |
50 | exports.getBaseDomain = (domain) ->
51 | return domain if exports.isIp(domain)
52 | return tld.getDomain(domain) ? domain
53 |
54 | exports.wildcardForDomain = (domain) ->
55 | return domain if exports.isIp(domain)
56 | return '*.' + exports.getBaseDomain(domain)
57 |
58 | Url = require('url')
59 | exports.wildcardForUrl = (url) ->
60 | domain = Url.parse(url).hostname
61 | return exports.wildcardForDomain(domain)
62 |
--------------------------------------------------------------------------------
/omega-web/src/omega/controllers/pac_profile.coffee:
--------------------------------------------------------------------------------
1 | angular.module('omega').controller 'PacProfileCtrl', ($scope, $modal) ->
2 | # coffeelint: disable=max_line_length
3 |
4 | # https://github.com/angular/angular.js/blob/master/src/ng/directive/input.js#L13
5 | $scope.urlRegex = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/
6 | # With the file: scheme added to the pattern:
7 | $scope.urlWithFile = /^(ftp|http|https|file):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/
8 |
9 | # coffeelint: enable=max_line_length
10 |
11 | $scope.isFileUrl = OmegaPac.Profiles.isFileUrl
12 | $scope.pacUrlCtrl = {ctrl: null}
13 |
14 | set = OmegaPac.Profiles.referencedBySet($scope.profile, $scope.options)
15 | $scope.referenced = Object.keys(set).length > 0
16 |
17 | oldPacUrl = null
18 | oldLastUpdate = null
19 | oldPacScript = null
20 | onProfileChange = (profile, oldProfile) ->
21 | return unless profile and oldProfile
22 | if profile.pacUrl != oldProfile.pacUrl
23 | if profile.lastUpdate
24 | oldPacUrl = oldProfile.pacUrl
25 | oldLastUpdate = profile.lastUpdate
26 | oldPacScript = oldProfile.pacScript
27 | profile.lastUpdate = null
28 | else if oldPacUrl and profile.pacUrl == oldPacUrl
29 | profile.lastUpdate = oldLastUpdate
30 | profile.pacScript = oldPacScript
31 | $scope.pacUrlIsFile = $scope.isFileUrl(profile.pacUrl)
32 | $scope.$watch 'profile', onProfileChange, true
33 |
34 | $scope.editProxyAuth = (scheme) ->
35 | prop = 'all'
36 | auth = $scope.profile.auth?[prop]
37 | scope = $scope.$new('isolate')
38 | scope.auth = auth && angular.copy(auth)
39 | $modal.open(
40 | templateUrl: 'partials/fixed_auth_edit.html'
41 | scope: scope
42 | size: 'sm'
43 | ).result.then (auth) ->
44 | if not auth?.username
45 | if $scope.profile.auth
46 | $scope.profile.auth[prop] = undefined
47 | else
48 | $scope.profile.auth ?= {}
49 | $scope.profile.auth[prop] = auth
50 |
--------------------------------------------------------------------------------
/omega-web/src/omega/directives.coffee:
--------------------------------------------------------------------------------
1 | angular.module('omega').directive 'inputGroupClear', ($timeout) ->
2 | restrict: 'A'
3 | templateUrl: 'partials/input_group_clear.html'
4 | scope:
5 | 'model': '=model'
6 | 'type': '@type'
7 | 'ngPattern': '=?ngPattern'
8 | 'placeholder': '@placeholder'
9 | 'controller': '=?controller'
10 | link: (scope, element, attrs) ->
11 | scope.catchAll = new RegExp('')
12 | $timeout ->
13 | scope.controller = element.find('input').controller('ngModel')
14 |
15 | scope.oldModel = ''
16 | scope.controller = scope.input
17 | scope.modelChange = ->
18 | if scope.model
19 | scope.oldModel = ''
20 | scope.toggleClear = ->
21 | [scope.model, scope.oldModel] = [scope.oldModel, scope.model]
22 | angular.module('omega').directive 'omegaUpload', ->
23 | restrict: 'A'
24 | scope:
25 | success: '&omegaUpload'
26 | error: '&omegaError'
27 | link: (scope, element, attrs) ->
28 | input = element[0]
29 | element.on 'change', ->
30 | if input.files.length > 0 and input.files[0].name.length > 0
31 | reader = new FileReader()
32 | reader.addEventListener 'load', (e) ->
33 | scope.$apply ->
34 | scope.success({'$content': e.target.result})
35 | reader.addEventListener 'error', (e) ->
36 | scope.$apply ->
37 | scope.error({'$error': e.target.error})
38 | reader.readAsText(input.files[0])
39 | input.value = ''
40 | angular.module('omega').directive 'omegaIp2str', ->
41 | restrict: 'A'
42 | priority: 2 # Run post-link after input directive (0) and ngModel (1).
43 | require: 'ngModel'
44 | link: (scope, element, attr, ngModel) ->
45 | ngModel.$parsers.push (value) ->
46 | if value
47 | OmegaPac.Conditions.fromStr('Ip: ' + value)
48 | else
49 | ({conditionType: 'IpCondition', ip: '0.0.0.0', prefixLength: 0})
50 | ngModel.$formatters.push (value) ->
51 | if value?.ip
52 | OmegaPac.Conditions.str(value).split(' ', 2)[1]
53 | else
54 | ''
55 |
--------------------------------------------------------------------------------
/omega-target/src/log.coffee:
--------------------------------------------------------------------------------
1 | ### @module omega-target/log ###
2 | Log = require './log'
3 |
4 | replacer = (key, value) ->
5 | switch key
6 | # Hide values for a few keys with privacy concerns.
7 | when "username", "password", "host", "port"
8 | return ""
9 | else
10 | value
11 |
12 | # Log is used as singleton.
13 | # coffeelint: disable=missing_fat_arrows
14 | module.exports = Log =
15 | ###*
16 | # Pretty-print an object and return the result string.
17 | # @param {{}} obj The object to format
18 | # @returns {String} the formatted object in string
19 | ###
20 | str: (obj) ->
21 | # TODO(catus): This can be improved to print things more friendly.
22 | if typeof obj == 'object' and obj != null
23 | if obj.debugStr?
24 | if typeof obj.debugStr == 'function'
25 | obj.debugStr()
26 | else
27 | obj.debugStr
28 | else if obj instanceof Error
29 | obj.stack || obj.message
30 | else
31 | JSON.stringify(obj, replacer, 4)
32 | else if typeof obj == 'function'
33 | if obj.name
34 | ""
35 | else
36 | obj.toString()
37 | else
38 | '' + obj
39 |
40 | ###*
41 | # Print something to the log.
42 | # @param {...{}} args The objects to log
43 | ###
44 | log: console.log.bind(console)
45 |
46 | ###*
47 | # Print something to the error log.
48 | # @param {...{}} args The objects to log
49 | ###
50 | error: console.error.bind(console)
51 |
52 | ###*
53 | # Log a function call with target and arguments
54 | # @param {string} name The name of the method
55 | # @param {Array} args The arguments to the method call
56 | ###
57 | func: (name, args) ->
58 | this.log(name, '(', [].slice.call(args), ')')
59 |
60 | ###*
61 | # Log a method call with target and arguments
62 | # @param {string} name The name of the method
63 | # @param {{}} self The target of the method call
64 | # @param {Array} args The arguments to the method call
65 | ###
66 | method: (name, self, args) ->
67 | this.log(this.str(self), '<<', name, [].slice.call(args))
68 |
69 | # coffeelint: enable=missing_fat_arrows
70 |
--------------------------------------------------------------------------------
/omega-target-chromium-extension/overlay/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "manifest_version": 2,
3 | "name": "__MSG_manifest_app_name__",
4 | "version": "2.5.21",
5 | "description": "__MSG_manifest_app_description__",
6 | "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkhwZJT76btQ04EEMOFtZPLESD1TmSVjbLjs0OyesD9Ht8YllFPfJ3qmtbSQGVuvmxH1GK/jUO2QcEWb8bHuOjoRlq20fi5j5Aq90O8FKET+y5D8PxCyi3WmnquiEwaE5cNmaCsw/G2JlO+bZOtdQ/QKOvMxBAegABYimEGfSvCMVUEvpymys0gBhLoch72zPAiJUBkf0z8BtjYTueMRcRXkrSeRPLygUDQnZ1TkQWMYYBp/zqpD5ggxytAklEMQzR9Hn0lqu5s7iuUAgihbysPn/8Wh00Zj5FySpK//KcpG3JS7UWxC28oSt8z5ZR3YimnX+HX3P36V0mC1pgM4o7wIDAQAB",
7 | "icons": {
8 | "16": "img/icons/omega-action-16.png",
9 | "24": "img/icons/omega-action-24.png",
10 | "32": "img/icons/omega-action-32.png",
11 | "48": "img/icons/omega-48.png",
12 | "64": "img/icons/omega-64.png",
13 | "128": "img/icons/omega-128.png"
14 | },
15 | "default_locale": "en",
16 | "browser_action": {
17 | "browser_style": false,
18 | "default_icon": {
19 | "16": "img/icons/omega-action-16.png",
20 | "19": "img/icons/omega-action-19.png",
21 | "24": "img/icons/omega-action-24.png",
22 | "32": "img/icons/omega-action-32.png"
23 | },
24 | "default_title": "__MSG_manifest_icon_default_title__",
25 | "default_popup": "popup/index.html"
26 | },
27 | "background": {
28 | "page": "background.html"
29 | },
30 | "minimum_chrome_version": "22.0.0",
31 | "options_page": "options.html",
32 | "options_ui": {
33 | "page": "options.html",
34 | "browser_style": false,
35 | "open_in_tab": true
36 | },
37 | "permissions": [
38 | "proxy",
39 | "tabs",
40 | "alarms",
41 | "storage",
42 | "webRequest",
43 | "downloads",
44 | "webRequestBlocking",
45 | "contextMenus",
46 |
47 | "http://*/*",
48 | "https://*/*",
49 | ""
50 | ],
51 | "commands": {
52 | "_execute_browser_action": {
53 | "suggested_key": {
54 | "default": "Alt+Shift+O"
55 | }
56 | }
57 | },
58 | "applications": {
59 | "gecko": {
60 | "id": "switchyomega@feliscatus.addons.mozilla.org",
61 | "strict_min_version": "55.0a1"
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/omega-pac/test/pac_generator.coffee:
--------------------------------------------------------------------------------
1 | chai = require 'chai'
2 | should = chai.should()
3 |
4 | describe 'PacGenerator', ->
5 | PacGenerator = require '../src/pac_generator.coffee'
6 |
7 | options =
8 | '+auto':
9 | name: 'auto'
10 | profileType: 'SwitchProfile'
11 | revision: 'test'
12 | defaultProfileName: 'direct'
13 | rules: [
14 | {profileName: 'proxy', condition:
15 | conditionType: 'UrlRegexCondition'
16 | pattern: '^http://(www|www2)\\.example\\.com/'
17 | }
18 | {profileName: 'direct', condition:
19 | conditionType: 'HostLevelsCondition'
20 | minValue: 3
21 | maxValue: 8
22 | }
23 | {
24 | profileName: 'proxy'
25 | condition: {conditionType: 'KeywordCondition', pattern: 'keyword'}
26 | }
27 | {profileName: 'proxy', condition:
28 | conditionType: 'UrlWildcardCondition'
29 | pattern: 'https://ssl.example.com/*'
30 | }
31 | ]
32 | '+proxy':
33 | name: 'proxy'
34 | profileType: 'FixedProfile'
35 | revision: 'test'
36 | fallbackProxy: {scheme: 'http', host: '127.0.0.1', port: 8888}
37 | bypassList: [
38 | {conditionType: 'BypassCondition', pattern: '127.0.0.1:8080'}
39 | {conditionType: 'BypassCondition', pattern: '127.0.0.1'}
40 | {conditionType: 'BypassCondition', pattern: ''}
41 | ]
42 |
43 | it 'should generate pac scripts from options', ->
44 | ast = PacGenerator.script(options, 'auto')
45 | pac = ast.print_to_string(beautify: true, comments: true)
46 | pac.should.not.be.empty
47 | func = eval("(function () { #{pac}\n return FindProxyForURL; })()")
48 | result = func('http://www.example.com/', 'www.example.com')
49 | result.should.equal('PROXY 127.0.0.1:8888')
50 | it 'should be able to compress pac scripts', ->
51 | ast = PacGenerator.script(options, 'auto')
52 | pac = PacGenerator.compress(ast).print_to_string()
53 | pac.should.not.be.empty
54 | func = eval("(function () { #{pac}\n return FindProxyForURL; })()")
55 | result = func('http://www.example.com/', 'www.example.com')
56 | result.should.equal('PROXY 127.0.0.1:8888')
57 |
--------------------------------------------------------------------------------
/omega-web/src/popup/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SwitchyOmega Popup
6 |
7 |
8 |
9 |
10 |
54 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/omega-target-chromium-extension/src/module/external_api.coffee:
--------------------------------------------------------------------------------
1 | OmegaTarget = require('omega-target')
2 | OmegaPac = OmegaTarget.OmegaPac
3 | Promise = OmegaTarget.Promise
4 | ChromePort = require('./chrome_port')
5 |
6 | module.exports = class ExternalApi
7 | constructor: (options) ->
8 | @options = options
9 | knownExts:
10 | 'padekgcemlokbadohgkifijomclgjgif': 32
11 | disabled: false
12 | listen: ->
13 | return unless chrome.runtime.onConnectExternal
14 | chrome.runtime.onConnectExternal.addListener (rawPort) =>
15 | port = new ChromePort(rawPort)
16 | port.onMessage.addListener (msg) => @onMessage(msg, port)
17 | port.onDisconnect.addListener @reenable.bind(this)
18 |
19 | _previousProfileName: null
20 |
21 | reenable: ->
22 | return unless @disabled
23 |
24 | @options.setProxyNotControllable(null)
25 | chrome.browserAction.setPopup?({popup: 'popup/index.html'})
26 | @options.reloadQuickSwitch()
27 | @disabled = false
28 | @options.clearBadge()
29 | @options.applyProfile(@_previousProfileName)
30 |
31 | checkPerm: (port, level) ->
32 | perm = @knownExts[port.sender.id] || 0
33 | if perm < level
34 | port.postMessage({action: 'error', error: 'permission'})
35 | false
36 | else
37 | true
38 |
39 | onMessage: (msg, port) ->
40 | @options.log.log("#{port.sender.id} -> #{msg.action}", msg)
41 | switch msg.action
42 | when 'disable'
43 | return unless @checkPerm(port, 16)
44 | return if @disabled
45 | @disabled = true
46 | @_previousProfileName = @options.currentProfile()?.name || 'system'
47 | @options.applyProfile('system').then =>
48 | reason = 'disabled'
49 | if @knownExts[port.sender.id] >= 32
50 | reason = 'upgrade'
51 | @options.setProxyNotControllable reason, {text: 'X', color: '#5ab432'}
52 | chrome.browserAction.setPopup?({popup: 'popup/index.html'})
53 | port.postMessage({action: 'state', state: 'disabled'})
54 | when 'enable'
55 | @reenable()
56 | port.postMessage({action: 'state', state: 'enabled'})
57 | when 'getOptions'
58 | return unless @checkPerm(port, 8)
59 | port.postMessage({action: 'options', options: @options.getAll()})
60 | else
61 | port.postMessage(
62 | action: 'error'
63 | error: 'noSuchAction'
64 | action_name: msg.action
65 | )
66 |
--------------------------------------------------------------------------------
/omega-target-chromium-extension/src/module/inspect.coffee:
--------------------------------------------------------------------------------
1 | OmegaTarget = require('omega-target')
2 | OmegaPac = OmegaTarget.OmegaPac
3 | Promise = OmegaTarget.Promise
4 |
5 | module.exports = class Inspect
6 | _enabled: false
7 | constructor: (onInspect) ->
8 | @onInspect = onInspect
9 |
10 | enable: ->
11 | return unless chrome.contextMenus?
12 | # We don't need this API. However its presence indicates that Chrome >= 35,
13 | # which provides the menuItemId we need in contextMenu callback.
14 | # https://developer.chrome.com/extensions/contextMenus
15 | return unless chrome.i18n.getUILanguage?
16 |
17 | return if @_enabled
18 |
19 | webResource = [
20 | "http://*/*"
21 | "https://*/*"
22 | "ftp://*/*"
23 | ]
24 |
25 | ### Not so useful...
26 | chrome.contextMenus.create({
27 | id: 'inspectPage'
28 | title: chrome.i18n.getMessage('contextMenu_inspectPage')
29 | contexts: ['page']
30 | onclick: @inspect.bind(this)
31 | documentUrlPatterns: webResource
32 | })
33 | ###
34 |
35 | chrome.contextMenus.create({
36 | id: 'inspectFrame'
37 | title: chrome.i18n.getMessage('contextMenu_inspectFrame')
38 | contexts: ['frame']
39 | onclick: @inspect.bind(this)
40 | documentUrlPatterns: webResource
41 | })
42 |
43 | chrome.contextMenus.create({
44 | id: 'inspectLink'
45 | title: chrome.i18n.getMessage('contextMenu_inspectLink')
46 | contexts: ['link']
47 | onclick: @inspect.bind(this)
48 | targetUrlPatterns: webResource
49 | })
50 |
51 | chrome.contextMenus.create({
52 | id: 'inspectElement'
53 | title: chrome.i18n.getMessage('contextMenu_inspectElement')
54 | contexts: [
55 | 'image'
56 | 'video'
57 | 'audio'
58 | ]
59 | onclick: @inspect.bind(this)
60 | targetUrlPatterns: webResource
61 | })
62 |
63 | @_enabled = true
64 |
65 | disable: ->
66 | return unless @_enabled
67 | for own menuId of @propForMenuItem
68 | try chrome.contextMenus.remove(menuId)
69 | @_enabled = false
70 |
71 | propForMenuItem:
72 | 'inspectPage': 'pageUrl'
73 | 'inspectFrame': 'frameUrl'
74 | 'inspectLink': 'linkUrl'
75 | 'inspectElement': 'srcUrl'
76 |
77 | inspect: (info, tab) ->
78 | return unless info.menuItemId
79 | url = info[@propForMenuItem[info.menuItemId]]
80 | if not url and info.menuItemId == 'inspectPage'
81 | url = tab.url
82 | return unless url
83 |
84 | @onInspect(url, tab)
85 |
--------------------------------------------------------------------------------
/omega-web/src/partials/profile_fixed.jade:
--------------------------------------------------------------------------------
1 | div(ng-controller='FixedProfileCtrl')
2 | section.settings-group.settings-group-fixed-servers
3 | h3 {{'options_group_proxyServers' | tr}}
4 | .table-responsive
5 | table.fixed-servers.table.table-bordered.table-striped.width-limit-lg
6 | thead
7 | tr
8 | th {{'options_proxy_scheme' | tr}}
9 | th {{'options_proxy_protocol' | tr}}
10 | th {{'options_proxy_server' | tr}}
11 | th {{'options_proxy_port' | tr}}
12 | th
13 | tbody
14 | tr(ng-repeat='scheme in urlSchemes' ng-show='scheme == "" || showAdvanced')
15 | td {{schemeDisp[scheme] || ('options_scheme_default' | tr)}}
16 | td
17 | select.form-control(ng-model='proxyEditors[scheme].scheme'
18 | ng-options='opt.value as opt.label for opt in optionsForScheme[scheme]')
19 | td(ng-if='proxyEditors[scheme].scheme')
20 | input.form-control(type='text' ng-model='proxyEditors[scheme].host' required)
21 | td(ng-if='!proxyEditors[scheme].scheme')
22 | input.form-control(type='text' value='' placeholder='{{proxyEditors[""].host}}' disabled)
23 |
24 | td(ng-if='proxyEditors[scheme].scheme')
25 | input.form-control(type='number' min='1' ng-model='proxyEditors[scheme].port' required)
26 | td(ng-if='!proxyEditors[scheme].scheme')
27 | input.form-control(type='number' value='' placeholder='{{proxyEditors[""].port}}' disabled)
28 | td.proxy-actions
29 | button.btn.btn-xs.proxy-auth-toggle(
30 | ng-class='isProxyAuthActive(scheme) ? "btn-success" : "btn-default"'
31 | type='button' role='button' ng-click='editProxyAuth(scheme)' title='{{"options_proxy_auth" | tr}}')
32 | span.glyphicon.glyphicon-lock
33 | tbody(ng-show='!showAdvanced')
34 | tr.fixed-show-advanced
35 | td(colspan='7')
36 | button.btn.btn-link(ng-click='showAdvanced = true')
37 | | #[span.glyphicon.glyphicon-chevron-down] {{'options_proxy_expand' | tr}}
38 | section.settings-group
39 | h3 {{'options_group_bypassList' | tr}}
40 | p.help-block {{'options_bypassListHelp' | tr}}
41 | p.help-block
42 | a(href='https://developer.chrome.com/extensions/proxy#bypass_list' target='_blank')
43 | | {{'options_bypassListHelpLinkText' | tr}}
44 | textarea.monospace.form-control.width-limit(rows='10' ng-model='bypassList' ng-model-options="{updateOn:'blur'}")
45 |
--------------------------------------------------------------------------------
/omega-web/src/partials/profile_pac.jade:
--------------------------------------------------------------------------------
1 | div(ng-controller='PacProfileCtrl')
2 | p.alert.alert-danger.width-limit(ng-show='pacProfilesUnsupported')
3 | span.glyphicon.glyphicon-remove
4 | = ' '
5 | | {{'options_pac_profile_unsupported_moz' | tr}}
6 | section.settings-group
7 | h3 {{'options_group_pacUrl' | tr}}
8 | .width-limit(input-group-clear type='text' model='profile.pacUrl'
9 | ng-pattern='referenced ? urlRegex : urlWithFile' controller='pacUrlCtrl.ctrl')
10 | p.help-block {{'options_pacUrlHelp' | tr}}
11 | .has-warning(ng-show='pacUrlIsFile && !referenced')
12 | p.help-block #[span.glyphicon.glyphicon-warning-sign] {{'options_pacUrlFile' | tr}}
13 | .has-error(ng-show='isFileUrl(pacUrlCtrl.ctrl.$viewValue) && referenced')
14 | p.help-block #[span.glyphicon.glyphicon-remove-sign] {{'options_pacUrlFile' | tr}}
15 | p.help-block {{'options_pacUrlFileDisabled' | tr}}
16 | p(ng-show='profile.pacUrl && !pacUrlIsFile')
17 | button.btn(ng-click='updateProfile(profile.name)'
18 | ladda='updatingProfile[profile.name]' data-spinner-color="#000000"
19 | ng-class='profile.pacUrl && !profile.lastUpdate ? "btn-primary" : "btn-default"')
20 | | #[span.glyphicon.glyphicon-download-alt] {{'options_downloadProfileNow' | tr}}
21 | section.settings-group
22 | h3
23 | | {{'options_group_pacScript' | tr}}
24 | = ' '
25 | button.btn.btn-xs.proxy-auth-toggle(ng-class='profile.auth["all"] ? "btn-success" : "btn-default"'
26 | type='button' role='button' ng-click='editProxyAuth()' title='{{"options_proxy_auth" | tr}}')
27 | span.glyphicon.glyphicon-lock
28 | div.alert.alert-warning.width-limit(ng-show='profile.auth["all"]')
29 | p {{'options_proxy_authAllWarningPac' | tr}}
30 | p(ng-show='!!profile.pacUrl') {{'options_proxy_authAllWarningPacUrl' | tr}}
31 | p(ng-show='!profile.pacUrl') {{'options_proxy_authAllWarningPacScript' | tr}}
32 | p(ng-show='!!referenced')
33 | | #[span.glyphicon.glyphicon-warning-sign] {{'options_proxy_authReferencedWarning' | tr}}
34 | div(ng-hide='pacUrlIsFile')
35 | p.alert.alert-success.width-limit(ng-show='profile.pacUrl && profile.lastUpdate')
36 | | {{'options_pacScriptLastUpdate' | tr:[(profile.lastUpdate | date:'medium')]}}
37 | p.alert.alert-danger.width-limit(ng-show='profile.pacUrl && !profile.lastUpdate')
38 | | {{'options_pacScriptObsolete' | tr}}
39 | textarea.monospace.form-control.width-limit(ng-model='profile.pacScript' rows=20
40 | ng-disabled='pacUrlCtrl.ctrl.$invalid || !!profile.pacUrl')
41 |
--------------------------------------------------------------------------------
/omega-web/src/coffee/options.coffee:
--------------------------------------------------------------------------------
1 | this.UglifyJS_NoUnsafeEval = true
2 | $script 'lib/angular-loader/angular-loader.min.js',
3 | 'angular-loader'
4 | $script 'lib/jquery/jquery.min.js', 'jquery'
5 | $script 'js/omega_pac.min.js', 'omega-pac'
6 | $script 'lib/FileSaver/FileSaver.min.js', 'filesaver'
7 | $script 'lib/blob/Blob.js', 'blob'
8 | $script 'lib/spin.js/spin.js', ->
9 | $script 'lib/ladda/ladda.min.js', ->
10 | $script.ready ['angular-loader'], ->
11 | $script 'lib/angular-ladda/angular-ladda.min.js', 'angular-ladda'
12 |
13 | $script.ready ['angular-loader'], ->
14 | angular.module 'omega', ['ngLocale', 'ngAnimate', 'ngSanitize',
15 | 'ui.bootstrap', 'ui.router', 'ngProgress', 'ui.sortable',
16 | 'angularSpectrumColorpicker', 'ui.validate', 'angular-ladda', 'omegaTarget',
17 | 'omegaDecoration']
18 | $script.ready ['omega-pac'], ->
19 | $script 'js/omega.js', 'omega'
20 |
21 | $script([
22 | 'js/omega_target_web.js'
23 | 'js/omega_decoration.js'
24 | 'lib/angular-animate/angular-animate.min.js'
25 | 'lib/angular-bootstrap/ui-bootstrap-tpls.min.js'
26 | 'lib/ngprogress/ngProgress.min.js'
27 | 'lib/angular-ui-sortable/sortable.min.js'
28 | 'lib/angular-ui-utils/validate.min.js'
29 | 'lib/jsondiffpatch/jsondiffpatch.min.js'
30 | 'lib/angular-spectrum-colorpicker/angular-spectrum-colorpicker.min.js'
31 | ], 'omega-deps')
32 | $script.ready ['jquery'], ->
33 | $script 'lib/jquery-ui-1.10.4.custom.min.js', 'jquery-ui-base'
34 | $script 'lib/spectrum/spectrum.js', 'spectrum'
35 | $script.ready ['jquery-ui-base'], ->
36 | $script 'lib/jqueryui-touch-punch/jquery.ui.touch-punch.min.js', 'jquery-ui'
37 |
38 | $script.ready ['angular-loader', 'jquery'], ->
39 | $script 'lib/angular/angular.min.js', 'angular'
40 |
41 | $script.ready ['angular'], ->
42 | $script 'lib/angular-ui-router/angular-ui-router.min.js', 'angular-ui-router'
43 | $script 'lib/angular-sanitize/angular-sanitize.min.js', 'angular-sanitize'
44 |
45 | locales =
46 | '': 'en-us'
47 |
48 | 'en': 'en-us'
49 |
50 | 'zh': 'zh-cn'
51 | 'zh-hans': 'zh-cn'
52 | 'zh-hant': 'zh-tw'
53 | 'zh-cn': 'zh-cn'
54 | 'zh-hk': 'zh-hk'
55 | 'zh-tw': 'zh-tw'
56 |
57 | lang = navigator.language
58 | lang1 = navigator.language?.split('-')[0] || ''
59 | locale = locales[lang] || locales[lang1] || locales['']
60 | $script 'lib/angular-i18n/angular-locale_' + locale + '.js', 'angular-i18n'
61 |
62 | $script.ready ['angular', 'omega', 'omega-deps', 'angular-ui-router',
63 | 'jquery-ui', 'spectrum', 'filesaver', 'blob', 'angular-ladda',
64 | 'angular-sanitize', 'angular-i18n'], ->
65 | angular.bootstrap document, ['omega']
66 |
--------------------------------------------------------------------------------
/omega-web/src/coffee/switch_profile_guide.coffee:
--------------------------------------------------------------------------------
1 | $script 'lib/tether/tether.js', ->
2 | $script 'lib/shepherd.js/shepherd.min.js', ->
3 | return if jQuery('.switch-rule-row').length == 0
4 | jQuery('html, body').scrollTop(0)
5 | tr = chrome.i18n.getMessage.bind(chrome.i18n)
6 | tour = new Shepherd.Tour
7 | defaults:
8 | classes: 'shepherd-theme-arrows'
9 |
10 | tour.addStep 'condition-step',
11 | text: tr('options_guide_conditionStep')
12 | attachTo: '.switch-rule-row bottom'
13 | buttons: [
14 | {
15 | text: tr('options_guideSkip')
16 | action: tour.cancel
17 | classes: 'shepherd-button-secondary'
18 | }
19 | {
20 | text: tr('options_guideNext')
21 | action: tour.next
22 | }
23 | ]
24 |
25 | conditionTypeStep = tour.addStep('condition-type-step',
26 | text: tr('options_guide_conditionTypeStep')
27 | attachTo: '.condition-type-th bottom'
28 | advanceOn:
29 | selector: '.close-condition-help'
30 | event: 'click'
31 | buttons: [
32 | text: tr('options_guideNext')
33 | action: tour.next
34 | ]
35 | )
36 |
37 | conditionTypeStep.on 'show', ->
38 | jQuery('.toggle-condition-help').one 'click', ->
39 | return unless conditionTypeStep.isOpen()
40 | jQuery('.shepherd-step.shepherd-enabled').hide()
41 | jQuery('.toggle-condition-help, .close-condition-help').one 'click', ->
42 | tour.next()
43 |
44 | tour.addStep 'condition-profile-step',
45 | text: tr('options_guide_conditionProfileStep')
46 | attachTo: '.switch-rule-row-target bottom'
47 | buttons: [
48 | text: tr('options_guideNext')
49 | action: tour.next
50 | ]
51 |
52 | defaultStep = tour.addStep 'switch-default-step',
53 | text: tr('options_guide_switchDefaultStep')
54 | attachTo: '.switch-default-row top'
55 | buttons: [
56 | text: tr('options_guideNext')
57 | action: tour.next
58 | ]
59 |
60 | defaultStep.on 'show', ->
61 | row = jQuery('.switch-default-row')
62 | scrollTop = row.offset().top + row.height() - $(window).height()
63 | scrollTop = 0 if scrollTop < 0
64 | jQuery('html, body').animate({scrollTop: scrollTop})
65 |
66 | tour.addStep 'apply-switch-profile-step',
67 | text: tr('options_guide_applySwitchProfileStep')
68 | attachTo: 'body top'
69 | scrollTo: false
70 | classes: 'shepherd-theme-arrows fixed-top-right'
71 | buttons: [
72 | text: tr('options_guideDone')
73 | action: tour.next
74 | ]
75 |
76 | Shepherd.activeTour?.cancel()
77 | tour.start()
78 |
--------------------------------------------------------------------------------
/omega-web/src/omega/controllers/io.coffee:
--------------------------------------------------------------------------------
1 | angular.module('omega').controller 'IoCtrl', ($scope, $rootScope,
2 | $window, $http, omegaTarget, downloadFile) ->
3 |
4 | omegaTarget.state('web.restoreOnlineUrl').then (url) ->
5 | if url
6 | $scope.restoreOnlineUrl = url
7 |
8 | $scope.exportOptions = ->
9 | $rootScope.applyOptionsConfirm().then ->
10 | plainOptions = angular.fromJson(angular.toJson($rootScope.options))
11 | content = JSON.stringify(plainOptions)
12 | blob = new Blob [content], {type: "text/plain;charset=utf-8"}
13 | downloadFile(blob, "OmegaOptions.bak")
14 |
15 | $scope.importSuccess = ->
16 | $rootScope.showAlert(
17 | type: 'success'
18 | i18n: 'options_importSuccess'
19 | message: 'Options imported.'
20 | )
21 |
22 | $scope.restoreLocal = (content) ->
23 | $scope.restoringLocal = true
24 | $rootScope.resetOptions(content).then(( ->
25 | $scope.importSuccess()
26 | ), -> $scope.restoreLocalError()).finally ->
27 | $scope.restoringLocal = false
28 |
29 | $scope.restoreLocalError = ->
30 | $rootScope.showAlert(
31 | type: 'error'
32 | i18n: 'options_importFormatError'
33 | message: 'Invalid backup file!'
34 | )
35 | $scope.downloadError = ->
36 | $rootScope.showAlert(
37 | type: 'error'
38 | i18n: 'options_importDownloadError'
39 | message: 'Error downloading backup file!'
40 | )
41 | $scope.triggerFileInput = ->
42 | angular.element('#restore-local-file').click()
43 | return
44 | $scope.restoreOnline = ->
45 | omegaTarget.state('web.restoreOnlineUrl', $scope.restoreOnlineUrl)
46 | $scope.restoringOnline = true
47 | $http(
48 | method: 'GET'
49 | url: $scope.restoreOnlineUrl
50 | cache: false
51 | timeout: 10000
52 | responseType: "text"
53 | ).then(((result) ->
54 | $rootScope.resetOptions(result.data).then (->
55 | $scope.importSuccess()
56 | ), -> $scope.restoreLocalError()
57 | ), $scope.downloadError).finally ->
58 | $scope.restoringOnline = false
59 |
60 | $scope.enableOptionsSync = (args) ->
61 | enable = ->
62 | omegaTarget.setOptionsSync(true, args).finally ->
63 | $window.location.reload()
64 | if args?.force
65 | enable()
66 | else
67 | $rootScope.applyOptionsConfirm().then enable
68 |
69 | $scope.disableOptionsSync = ->
70 | omegaTarget.setOptionsSync(false).then ->
71 | $rootScope.applyOptionsConfirm().then ->
72 | $window.location.reload()
73 |
74 | $scope.resetOptionsSync = ->
75 | omegaTarget.resetOptionsSync().then ->
76 | $rootScope.applyOptionsConfirm().then ->
77 | $window.location.reload()
78 |
--------------------------------------------------------------------------------
/omega-web/src/popup/css/dialog.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2017 The SwitchyOmega Authors. Please see the AUTHORS file
3 | * for details.
4 | * Based on Bootstrap v3.3.2 (http://getbootstrap.com)
5 | * Copyright 2011-2015 Twitter, Inc.
6 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
7 | */
8 |
9 | /* Dialog */
10 |
11 | body, html {
12 | margin: 0;
13 | padding: 0;
14 | }
15 |
16 | p {
17 | margin: 0 0 1em 0;
18 | }
19 |
20 | .om-dialog {
21 | min-width: 400px;
22 | padding: 10px 10px;
23 | font-size: 14px;
24 | }
25 |
26 | .om-text-danger {
27 | color: #a94442;
28 | }
29 |
30 | .om-dialog-help {
31 | display: block;
32 | margin-top: 5px;
33 | margin-bottom: 10px;
34 | color: #737373;
35 | }
36 |
37 | .om-dialog-controls {
38 | margin-bottom: 0;
39 | }
40 |
41 | .om-dialog-controls .om-btn-primary {
42 | float: right;
43 | }
44 |
45 | /* Button */
46 |
47 | .om-btn {
48 | display: inline-block;
49 | padding: 6px 12px;
50 | margin-bottom: 0;
51 | font-size: 14px;
52 | font-weight: 400;
53 | line-height: 1.42857143;
54 | text-align: center;
55 | white-space: nowrap;
56 | vertical-align: middle;
57 | -ms-touch-action: manipulation;
58 | touch-action: manipulation;
59 | cursor: pointer;
60 | -webkit-user-select: none;
61 | -moz-user-select: none;
62 | -ms-user-select: none;
63 | user-select: none;
64 | background-image: none;
65 | border: 1px solid transparent;
66 | border-radius: 4px
67 | }
68 |
69 | .om-btn.active, .om-btn:active {
70 | background-image: none;
71 | outline: 0;
72 | -webkit-box-shadow: inset 0 3px 5px rgba(0,0,0,.125);
73 | box-shadow: inset 0 3px 5px rgba(0,0,0,.125);
74 | }
75 |
76 | .om-btn-default {
77 | color: #333;
78 | background-color: #fff;
79 | border-color: #ccc;
80 | }
81 |
82 | .om-btn-default:hover {
83 | background-color: #e6e6e6;
84 | border-color: #adadad;
85 | }
86 |
87 | .om-btn-link {
88 | font-weight: 400;
89 | color: #337ab7;
90 | border-radius: 0;
91 | background-color: rgba(0, 0, 0, 0);
92 | -webkit-box-shadow: none;
93 | box-shadow: none;
94 | border-color: rgba(0, 0, 0, 0);
95 | }
96 |
97 | .om-btn-link:hover {
98 | color: #23527c;
99 | text-decoration: underline;
100 | background-color: rgba(0, 0, 0, 0);
101 | }
102 |
103 | .om-btn-link:active {
104 | background-color: rgba(0, 0, 0, 0);
105 | -webkit-box-shadow: none;
106 | box-shadow: none;
107 | }
108 |
109 | .om-btn-primary {
110 | color: #fff;
111 | background-color: #337ab7;
112 | border-color: #2e6da4;
113 | }
114 |
115 | .om-btn-primary:hover {
116 | color: #fff;
117 | background-color: #286090;
118 | border-color: #204d74;
119 | }
120 |
--------------------------------------------------------------------------------
/omega-target-chromium-extension/src/module/proxy/proxy_auth.coffee:
--------------------------------------------------------------------------------
1 | OmegaTarget = require('omega-target')
2 | OmegaPac = OmegaTarget.OmegaPac
3 | Promise = OmegaTarget.Promise
4 |
5 | module.exports = class ProxyAuth
6 | constructor: (log) ->
7 | @_requests = {}
8 | @log = log
9 |
10 | listening: false
11 | listen: ->
12 | return if @listening
13 | if not chrome.webRequest
14 | @log.error('Proxy auth disabled! No webRequest permission.')
15 | return
16 | if not chrome.webRequest.onAuthRequired
17 | @log.error('Proxy auth disabled! onAuthRequired not available.')
18 | return
19 | chrome.webRequest.onAuthRequired.addListener(
20 | @authHandler.bind(this)
21 | {urls: ['']}
22 | ['blocking']
23 | )
24 | chrome.webRequest.onCompleted.addListener(
25 | @_requestDone.bind(this)
26 | {urls: ['']}
27 | )
28 | chrome.webRequest.onErrorOccurred.addListener(
29 | @_requestDone.bind(this)
30 | {urls: ['']}
31 | )
32 | @listening = true
33 |
34 | _keyForProxy: (proxy) -> "#{proxy.host.toLowerCase()}:#{proxy.port}"
35 | setProxies: (profiles) ->
36 | @_proxies = {}
37 | @_fallbacks = []
38 | for profile in profiles when profile.auth
39 | for scheme in OmegaPac.Profiles.schemes when profile[scheme.prop]
40 | auth = profile.auth?[scheme.prop]
41 | continue unless auth
42 | proxy = profile[scheme.prop]
43 | key = @_keyForProxy(proxy)
44 | list = @_proxies[key]
45 | if not list?
46 | @_proxies[key] = list = []
47 | list.push({
48 | config: proxy
49 | auth: auth
50 | name: profile.name + '.' + scheme.prop
51 | })
52 |
53 | fallback = profile.auth?['all']
54 | if fallback?
55 | @_fallbacks.push({
56 | auth: fallback
57 | name: profile.name + '.' + 'all'
58 | })
59 |
60 | _proxies: {}
61 | _fallbacks: []
62 | _requests: null
63 | authHandler: (details) ->
64 | return {} unless details.isProxy
65 | req = @_requests[details.requestId]
66 | if not req?
67 | @_requests[details.requestId] = req = {authTries: 0}
68 |
69 | key = @_keyForProxy(
70 | host: details.challenger.host
71 | port: details.challenger.port
72 | )
73 |
74 | list = @_proxies[key]
75 | listLen = if list? then list.length else 0
76 | if req.authTries < listLen
77 | proxy = list[req.authTries]
78 | else
79 | proxy = @_fallbacks[req.authTries - listLen]
80 | @log.log('ProxyAuth', key, req.authTries, proxy?.name)
81 |
82 | return {} unless proxy?
83 | req.authTries++
84 | return authCredentials: proxy.auth
85 |
86 | _requestDone: (details) ->
87 | delete @_requests[details.requestId]
88 |
--------------------------------------------------------------------------------
/omega-target-chromium-extension/src/js/omega_target_popup.js:
--------------------------------------------------------------------------------
1 | function callBackgroundNoReply(method, args, cb) {
2 | chrome.runtime.sendMessage({
3 | method: method,
4 | args: args,
5 | noReply: true,
6 | refreshActivePage: true,
7 | });
8 | if (cb) return cb();
9 | }
10 |
11 | function callBackground(method, args, cb) {
12 | chrome.runtime.sendMessage({
13 | method: method,
14 | args: args,
15 | }, function(response) {
16 | if (chrome.runtime.lastError != null)
17 | return cb && cb(chrome.runtime.lastError)
18 | if (response.error) return cb && cb(response.error)
19 | return cb && cb(null, response.result)
20 | });
21 | }
22 |
23 | var requestInfoCallback = null;
24 |
25 | OmegaTargetPopup = {
26 | getState: function (keys, cb) {
27 | if (typeof localStorage === 'undefined' || !localStorage.length) {
28 | callBackground('getState', [keys], cb);
29 | return;
30 | }
31 | var results = {};
32 | keys.forEach(function(key) {
33 | try {
34 | results[key] = JSON.parse(localStorage['omega.local.' + key]);
35 | } catch (_) {
36 | return null;
37 | }
38 | });
39 | if (cb) cb(null, results);
40 | },
41 | applyProfile: function (name, cb) {
42 | callBackgroundNoReply('applyProfile', [name], cb);
43 | },
44 | openOptions: function (hash, cb) {
45 | var options_url = chrome.extension.getURL('options.html');
46 |
47 | chrome.tabs.query({
48 | url: options_url
49 | }, function(tabs) {
50 | if (!chrome.runtime.lastError && tabs && tabs.length > 0) {
51 | var props = {
52 | active: true
53 | };
54 | if (hash) {
55 | var url = options_url + hash;
56 | props.url = url;
57 | }
58 | chrome.tabs.update(tabs[0].id, props);
59 | } else {
60 | chrome.tabs.create({
61 | url: options_url
62 | });
63 | }
64 | if (cb) return cb();
65 | });
66 | },
67 | getActivePageInfo: function(cb) {
68 | chrome.tabs.query({active: true, lastFocusedWindow: true}, function (tabs) {
69 | if (tabs.length === 0 || !tabs[0].url) return cb();
70 | var args = {tabId: tabs[0].id, url: tabs[0].url};
71 | callBackground('getPageInfo', [args], cb)
72 | });
73 | },
74 | setDefaultProfile: function(profileName, defaultProfileName, cb) {
75 | callBackgroundNoReply('setDefaultProfile',
76 | [profileName, defaultProfileName], cb);
77 | },
78 | addTempRule: function(domain, profileName, cb) {
79 | callBackgroundNoReply('addTempRule', [domain, profileName], cb);
80 | },
81 | openManage: function(domain, profileName, cb) {
82 | chrome.tabs.create({
83 | url: 'chrome://extensions/?id=' + chrome.runtime.id,
84 | }, cb);
85 | },
86 | getMessage: chrome.i18n.getMessage.bind(chrome.i18n),
87 | };
88 |
--------------------------------------------------------------------------------
/omega-web/src/partials/ui.jade:
--------------------------------------------------------------------------------
1 | .page-header
2 | h2 {{'options_tab_ui' | tr}}
3 | section.settings-group
4 | h3 {{'options_group_miscOptions' | tr}}
5 | div.checkbox
6 | label
7 | input(type='checkbox' ng-model='options["-confirmDeletion"]')
8 | span {{'options_confirmDeletion' | tr}}
9 | div.checkbox
10 | label
11 | input#refresh-on-profile-change(type='checkbox' ng-model='options["-refreshOnProfileChange"]')
12 | span {{'options_refreshOnProfileChange' | tr}}
13 | div.checkbox
14 | label
15 | input(type='checkbox' ng-model='options["-showInspectMenu"]')
16 | span {{'options_showInspectMenu' | tr}}
17 | div.checkbox
18 | label
19 | input(type='checkbox' ng-model='options["-addConditionsToBottom"]')
20 | span {{'options_addConditionsToBottom' | tr}}
21 | section.settings-group
22 | h3 {{'options_group_keyboardShortcut' | tr}}
23 | p
24 | button.btn.btn-default(type='button' role='button' ng-click='openShortcutConfig()')
25 | span.glyphicon.glyphicon-share-alt
26 | = ' '
27 | | {{'options_menuShortcutConfigure' | tr}}
28 | = ' '
29 | | {{'options_menuShortcutHelp' | tr}}
30 | p.help-block
31 | | {{'options_menuShortcutMore' | tr}}
32 | section.settings-group
33 | h3 {{'options_group_switchOptions' | tr}}
34 | div.form-group
35 | label {{'options_startupProfile' | tr}}
36 | = ' '
37 | div(omega-profile-select='options | profiles:"all"' ng-model='options["-startupProfileName"]'
38 | default-text="{{'options_startupProfile_none' | tr}}" disp-name='dispNameFilter'
39 | style='display: inline-block;' options='options')
40 | div.checkbox
41 | label
42 | input(type='checkbox' ng-model='options["-showConditionTypes"]' ng-true-value='1' ng-false-value='0')
43 | span {{'options_showConditionTypesAdvanced' | tr}}
44 | p.help-block
45 | | {{'options_showConditionTypesAdvancedHelp' | tr}}
46 | div.checkbox
47 | label
48 | input(type='checkbox' ng-model='options["-enableQuickSwitch"]')
49 | span {{'options_quickSwitch' | tr}}
50 | #quick-switch-settings.settings-group(ng-show='options["-enableQuickSwitch"]' ng-controller='QuickSwitchCtrl')
51 | h4 {{'options_cycledProfiles' | tr}}
52 | p.help-block {{'options_cycledProfilesHelp' | tr}}
53 | div.has-error(ng-show='options["-quickSwitchProfiles"].length < 2')
54 | p.help-block {{'options_cycledProfilesTooFew' | tr}}
55 | ul.cycle-profile-container.cycle-enabled(ui-sortable="sortableOptions"
56 | ng-model='options["-quickSwitchProfiles"]')
57 | li(ng-repeat='name in options["-quickSwitchProfiles"]')
58 | span(omega-profile-inline='profileByName(name)' options='options' disp-name='dispNameFilter')
59 | h4 {{'options_notCycledProfiles' | tr}}
60 | ul.cycle-profile-container(ui-sortable="sortableOptions" ng-model='notCycledProfiles')
61 | li.bg-success(ng-repeat='name in notCycledProfiles')
62 | span(omega-profile-inline='profileByName(name)' options='options' disp-name='dispNameFilter')
63 |
--------------------------------------------------------------------------------
/omega-web/src/less/popup.less:
--------------------------------------------------------------------------------
1 | @import 'common.less';
2 |
3 | /* popup */
4 |
5 | body {
6 | margin: 0;
7 | padding: 0;
8 | min-width: 180px;
9 | }
10 |
11 | body.with-condition-form {
12 | }
13 |
14 | .condition-form {
15 | min-width: 360px;
16 | }
17 |
18 | .nav {
19 | margin-bottom: 0;
20 | }
21 |
22 | li > a {
23 | text-overflow: ellipsis;
24 | overflow: hidden;
25 | max-width: 20em;
26 | }
27 |
28 | .shortcut-help {
29 | .monospace();
30 | border: solid 1px #000;
31 | border-radius: 2px;
32 | display: inline-block;
33 | color: #000;
34 | box-shadow: 1px 1px;
35 | width: 1em;
36 | height: 1em;
37 | line-height: 1em;
38 | text-align: center;
39 | margin-top: -3px;
40 | }
41 |
42 | .nav-pills.nav-stacked {
43 | > li > a {
44 | padding: 5px 25px 5px 8px;
45 | white-space: nowrap;
46 | cursor: pointer;
47 |
48 | .glyphicon {
49 | margin-right: 6px;
50 | }
51 |
52 | &.profile-with-default-edit {
53 | padding-right: 32px;
54 | position: relative;
55 | .dropdown-toggle {
56 | margin: -5px 0;
57 | color: inherit;
58 | padding: 5px 8px 3px;
59 | min-width: 20px;
60 | position: absolute;
61 | right: 0;
62 |
63 | .glyphicon {
64 | margin-right: 0;
65 | }
66 | }
67 | }
68 | }
69 |
70 | > li.active .dropdown-toggle {
71 | border-color: transparent !important;
72 | border-left: solid 1px !important;
73 | background: none !important;
74 | border-radius: 0 !important;
75 | }
76 | }
77 |
78 | .divider {
79 | height: 1px;
80 | overflow: hidden;
81 | background-color: #E5E5E5;
82 | }
83 |
84 | .temp-rule a {
85 | padding-left: 8px !important;
86 | box-shadow: none !important;
87 | }
88 |
89 | li .dropdown-menu {
90 | position: static;
91 | top: initial;
92 | top: -moz-initial;
93 | margin: 0 5px !important;
94 | float: none;
95 |
96 | a {
97 | cursor: pointer;
98 | }
99 | }
100 |
101 | .current-domain {
102 | color: #08C;
103 | }
104 |
105 | select, textarea, input {
106 | margin-bottom: 0px !important;
107 | }
108 |
109 | form {
110 | margin: 10px;
111 | }
112 |
113 | legend,
114 | .well {
115 | margin-bottom: 10px;
116 | }
117 |
118 | .well {
119 | padding: 10px;
120 | }
121 |
122 | .condition-controls, .proxy-not-controllable-controls {
123 | .btn-primary {
124 | float: right;
125 | }
126 | }
127 |
128 | .external-profile {
129 | a {
130 | padding-right: 10px !important;
131 | }
132 |
133 | form {
134 | margin: 0;
135 | padding: 0;
136 | display: inline-block;
137 | }
138 |
139 | .form-control {
140 | display: inline-block;
141 | padding: 0;
142 | height: 2em;
143 | width: ~"calc(100% - 1em - 8px)";
144 | }
145 | }
146 |
147 | .proxy-not-controllable {
148 | min-width: 400px;
149 | padding: 10px 10px;
150 |
151 | .proxy-not-controllable-controls {
152 | margin-bottom: 0;
153 | }
154 | }
155 |
156 | .request-info-details {
157 | min-width: 360px;
158 | }
159 |
--------------------------------------------------------------------------------
/omega-target-chromium-extension/src/module/chrome_port.coffee:
--------------------------------------------------------------------------------
1 | # A wrapper around type Port in Chromium Extension API.
2 | # https://developer.chrome.com/extensions/runtime#type-Port
3 | #
4 | # Please wrap any Port object in this class BEFORE adding listeners. Adding
5 | # listeners to events of raw Port objects should be avoided to minimize the risk
6 | # of memory leaks. See the comments of the TrackedEvent class for more details.
7 | module.exports = class ChromePort
8 | constructor: (@port) ->
9 | @name = @port.name
10 | @sender = @port.sender
11 |
12 | @disconnect = @port.disconnect.bind(@port)
13 | @postMessage = (args...) =>
14 | try
15 | @port.postMessage(args...)
16 | catch _
17 | return
18 |
19 | @onMessage = new TrackedEvent(@port.onMessage)
20 | @onDisconnect = new TrackedEvent(@port.onDisconnect)
21 | @onDisconnect.addListener @dispose.bind(this)
22 |
23 | dispose: ->
24 | @onMessage.dispose()
25 | @onDisconnect.dispose()
26 |
27 | # A wrapper around chrome.Event.
28 | # https://developer.chrome.com/extensions/events#type-Event
29 | #
30 | # ALL event listeners MUST be manually removed before disposing any Event or
31 | # object containing Event, such as Port. Otherwise, a memory leak will happen.
32 | # https://code.google.com/p/chromium/issues/detail?id=320723
33 | #
34 | # TrackedEvent helps to solve this problem by keeping track of all listeners
35 | # installed and removes them when the #dispose method is called.
36 | # Don't forget to call #dispose when this TrackedEvent is not needed any more.
37 | class TrackedEvent
38 | constructor: (@event) ->
39 | @callbacks = []
40 | mes = ['hasListener', 'hasListeners', 'addRules', 'getRules', 'removeRules']
41 | for methodName in mes
42 | method = @event[methodName]
43 | if method?
44 | this[methodName] = method.bind(@event)
45 |
46 | addListener: (callback) ->
47 | @event.addListener(callback)
48 | @callbacks.push(callback)
49 | return this
50 |
51 | removeListener: (callback) ->
52 | @event.removeListener(callback)
53 | i = @callbacks.indexOf(callback)
54 | @callbacks.splice(i, 1) if i >= 0
55 | return this
56 |
57 | ###*
58 | # Removes all listeners added via this TrackedEvent instance.
59 | # Note: Won't remove listeners added via other TrackedEvent or raw Event.
60 | ###
61 | removeAllListeners: ->
62 | for callback in @callbacks
63 | @event.removeListener(callback)
64 | @callbacks = []
65 | return this
66 |
67 | ###*
68 | # Removes all listeners added via this TrackedEvent instance and prevent any
69 | # further listeners from being added. It is considered safe to nullify any
70 | # references to this instance and the underlying Event without causing leaks.
71 | # This should be the last method called in the lifetime of TrackedEvent.
72 | #
73 | # Throws if the underlying raw Event object still has listeners. This can
74 | # happen when listeners have been added via other TrackedEvents or raw Event.
75 | ###
76 | dispose: ->
77 | @removeAllListeners()
78 | if @event.hasListeners?()
79 | throw new Error("Underlying Event still has listeners!")
80 | @event = null
81 | @callbacks = null
82 |
--------------------------------------------------------------------------------
/omega-target-chromium-extension/src/module/fetch_url.coffee:
--------------------------------------------------------------------------------
1 | Promise = OmegaTarget.Promise
2 | xhr = Promise.promisify(require('xhr'))
3 | Url = require('url')
4 | ContentTypeRejectedError = OmegaTarget.ContentTypeRejectedError
5 |
6 | xhrWrapper = (args...) ->
7 | xhr(args...).catch (err) ->
8 | throw err unless err.isOperational
9 | if not err.statusCode
10 | throw new OmegaTarget.NetworkError(err)
11 | if err.statusCode == 404
12 | throw new OmegaTarget.HttpNotFoundError(err)
13 | if err.statusCode >= 500 and err.statusCode < 600
14 | throw new OmegaTarget.HttpServerError(err)
15 | throw new OmegaTarget.HttpError(err)
16 |
17 | fetchUrl = (dest_url, opt_bypass_cache, opt_type_hints) ->
18 | getResBody = ([response, body]) ->
19 | return body unless opt_type_hints
20 | contentType = response.headers['content-type']?.toLowerCase()
21 | for hint in opt_type_hints
22 | handler = hintHandlers[hint] ? defaultHintHandler
23 | result = handler(response, body, {contentType, hint})
24 | return result if result?
25 | throw new ContentTypeRejectedError(
26 | 'Unrecognized Content-Type: ' + contentType)
27 | return body
28 |
29 | if opt_bypass_cache and dest_url.indexOf('?') < 0
30 | parsed = Url.parse(dest_url, true)
31 | parsed.search = undefined
32 | parsed.query['_'] = Date.now()
33 | dest_url_nocache = Url.format(parsed)
34 | # Try first with the dumb parameter to bypass cache.
35 | xhrWrapper(dest_url_nocache).then(getResBody).catch ->
36 | # If failed, try again with the original URL.
37 | xhrWrapper(dest_url).then(getResBody)
38 | else
39 | xhrWrapper(dest_url).then(getResBody)
40 |
41 | defaultHintHandler = (response, body, {contentType, hint}) ->
42 | if '!' + contentType == hint
43 | throw new ContentTypeRejectedError(
44 | 'Response Content-Type blacklisted: ' + contentType)
45 | if contentType == hint
46 | return body
47 |
48 | hintHandlers =
49 | '*': (response, body) ->
50 | # Allow all contents.
51 | return body
52 |
53 | '!text/html': (response, body, {contentType, hint}) ->
54 | if contentType == hint
55 | # Sometimes other content can also be served with the text/html
56 | # Content-Type header. So we check if the body actually looks like HTML.
57 | looksLikeHtml = false
58 | if body.indexOf('= 0 || body.indexOf('= 0
59 | looksLikeHtml = true
60 | else if body.indexOf('