Hide advanced condition types by default.

Add text descriptions for condition types. Fix #30.
This commit is contained in:
FelisCatus 2014-10-08 18:43:57 +08:00
parent ccaa3d57cd
commit 50fa7640a6
12 changed files with 284 additions and 55 deletions

View File

@ -29,35 +29,62 @@
"message": "[System Proxy]"
},
"condition_hostWildcard" : {
"condition_HostWildcardCondition" : {
"message": "Host wildcard"
},
"condition_hostRegex" : {
"condition_help_HostWildcardCondition" : {
"message": "Matches hosts (domain names) by wildcard.<br><b>The asterisk <code>*</code></b> matches zero or more characters.<br><b>The question mark <code>?</code></b> matches exactly one character.<br><br>Note that rules beginning with <code>*.</code> are specially treated only in Host wildcard conditions.<br>Example: <code>*.example.com</code> will match www.example.com <b>AND example.com as well.</b><br>To match subdomains <b>only</b>, use <b>two</b> asterisks like <code>**.example.com</code>"
},
"condition_HostRegexCondition" : {
"message": "Host regex"
},
"condition_hostLevels" : {
"condition_help_HostRegexCondition" : {
"message": "Like Host wildcard condition, but matches hosts (domain names) by <a href='https://www.google.com/search?q=regular%20expression'>regular expression</a>.<br>Regular expressions can be hard to construct (and read).<br>It is recommended to use wildcards for most cases and only use regex for conditions that cannot be achieved by any other condition type."
},
"condition_HostLevelsCondition" : {
"message": "Host levels"
},
"condition_urlWildcard" : {
"condition_help_HostLevelsCondition" : {
"message": "Matches the request if and only if the host level in within the given range.<br>Host level is defined as the <b>number of dot-separated segments</b> of the host (domain name).<br>Example: <code>www.example.com</code> is with a host level of 3, while <code>internal</code> is of host level 1."
},
"condition_UrlWildcardCondition" : {
"message": "URL wildcard"
},
"condition_urlRegex" : {
"condition_help_UrlWildcardCondition" : {
"message": "Matches URLs of the request by wildcard.<br>See the Host wildcard section above for a quick wildcard reference.<br>Note that URL wildcards are not specially treated (no subdomain magic as in Host wildcard).<br>So <code>*://*.example.com/*</code> matches http://www.example.com/ but <b>does not</b> match http://example.com/."
},
"condition_UrlRegexCondition" : {
"message": "URL regex"
},
"condition_keyword" : {
"condition_help_UrlRegexCondition" : {
"message": "Matches URL by extremely powerful <a href='https://www.google.com/search?q=regular%20expression'>regular expression</a>.<br>However, regular expressions can be hard to construct (and read).<br>It is recommended to use wildcards for most cases and only use regex for conditions that cannot be achieved by any other condition type."
},
"condition_KeywordCondition" : {
"message": "Keyword"
},
"condition_always" : {
"message": "Always"
"condition_help_KeywordCondition" : {
"message": "A keyword condition matches if the URL protocol is HTTP, and the pattern is an exact sub-string of the URL.<br>It behaves like the URL wildcard pattern <code>http://*<b>pattern</b>*</code>, where <b>pattern</b> is the keyword pattern.<br>Keyword conditions are useful if you want to bypass a firewall blocking some keywords in the URL, by requesting such URLs through a proxy."
},
"condition_always_details" : {
"message": "(Always matches)"
"condition_FalseCondition" : {
"message": "(Disabled)"
},
"condition_never" : {
"message": "Never"
"condition_details_FalseCondition" : {
"message": "(Condition ignored when matching)"
},
"condition_never_details" : {
"message": "(Never matches)"
"condition_help_FalseCondition" : {
"message": "You can disable a condition by setting its type to <code>(Disabled)</code>. A Disabled condition act as if it does not exist.<br>This feature can be used to disable conditions temporarily.<br>Disabled conditions still hold the previous information (like patterns) and can be re-enabled by setting the condition type back to the previous type."
},
"condition_group_default" : {
"message": ""
},
"condition_group_host" : {
"message": "Host"
},
"condition_group_url" : {
"message": "Url"
},
"condition_group_special" : {
"message": "Special"
},
"ruleListFormat_Switchy": {
@ -138,6 +165,12 @@
"options_startupProfile_none": {
"message": "(Current profile)"
},
"options_showConditionTypesAdvanced": {
"message": "Show advanced condition types"
},
"options_showConditionTypesAdvancedHelp": {
"message": "Unlocks new types of advanced but complicated switch conditions. For most senarios, the basic condition types should be enough, so this options is not recommended."
},
"options_quickSwitch": {
"message": "Quick Switch"
},
@ -333,6 +366,9 @@
"options_conditionType": {
"message": "Condition Type"
},
"options_showConditionTypeHelp": {
"message": "Show help"
},
"options_conditionDetails": {
"message": "Condition Details"
},
@ -360,6 +396,9 @@
"options_hostLevelsBetween": {
"message": "\u2264 host levels \u2264"
},
"options_group_conditionHelp": {
"message": "About Condition Types"
},
"options_group_attachProfile": {
"message": "Import online rule lists"
},

View File

@ -29,35 +29,62 @@
"message": "[系统代理]"
},
"condition_hostWildcard" : {
"message": "主机通配符"
"condition_HostWildcardCondition" : {
"message": "域名通配符"
},
"condition_hostRegex" : {
"message": "主机正则表达式"
"condition_help_HostWildcardCondition" : {
"message": "根据域名(主机名)匹配请求。<br><b>星号 <code>*</code></b> 匹配零个或者多个字符。<br><b>问号 <code>?</code></b> 匹配任意一个字符。<br><br>请注意以 <code>*.</code> 开头的规则有特别处理,会同时匹配子域名和自身。<br>例如: <code>*.example.com</code> 能匹配 www.example.com <b>而且也能匹配 example.com 。</b><br>如果<b>只需要匹配子域名</b>,请使用<b>两个</b>星号开头,如 <code>**.example.com</code>。"
},
"condition_hostLevels" : {
"message": "主机层数"
"condition_HostRegexCondition" : {
"message": "域名正则"
},
"condition_urlWildcard" : {
"condition_help_HostRegexCondition" : {
"message": "类似域名通配符,但使用<a href='https://www.google.com/search?q=%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F'>正则表达式</a>.<br>正则表达式很难编写,且可读性差。<br>因此,一般情况建议使用通配符。当其他任何条件都不能满足要求时,才使用正则表达式。"
},
"condition_HostLevelsCondition" : {
"message": "域名层数"
},
"condition_help_HostLevelsCondition" : {
"message": "如果域名层数在设定的范围内则匹配,否则不匹配。<br>域名层数是指 <b>域名共有几段(以点分隔)</b>.<br>例如: <code>www.example.com</code> 的域名层数为 3而 <code>internal</code> 的域名层数为 1."
},
"condition_UrlWildcardCondition" : {
"message": "网址通配符"
},
"condition_urlRegex" : {
"message": "网址正则表达式"
"condition_help_UrlWildcardCondition" : {
"message": "根据通配符规则匹配网址。<br>关于通配符表达式,请参考上方的域名通配符一节的说明。<br>请注意网址通配符没有任何特殊处理,不会特殊处理子域名等。<br>所以 <code>*://*.example.com/*</code> 能匹配 http://www.example.com/ 但是 <b>不匹配</b> http://example.com/."
},
"condition_keyword" : {
"condition_UrlRegexCondition" : {
"message": "网址正则"
},
"condition_help_UrlRegexCondition" : {
"message": "使用功能强大的<a href='https://www.google.com/search?q=%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F'>正则表达式</a>来匹配网址。<br>但正则表达式很难编写,且可读性差。<br>因此,一般情况建议使用通配符。当其他任何条件都不能满足要求时,才使用正则表达式。"
},
"condition_KeywordCondition" : {
"message": "关键字"
},
"condition_always" : {
"message": "总是"
"condition_help_KeywordCondition" : {
"message": "关键字条件的具体匹配规则是网址协议为HTTP且网址中包含该关键字。<br>类似于 <code>http://*<b>关键字</b>*</code>, 其中 <b>关键字</b> 是设定好的关键字。<br>如果某防火墙根据网址中是否包含关键字来屏蔽网址,那么可以使用关键字条件来通过代理访问这样的请求,以达到绕过防火墙的目的。"
},
"condition_always_details" : {
"message": "(匹配所有请求)"
"condition_FalseCondition" : {
"message": "(禁用)"
},
"condition_never" : {
"message": "从不"
"condition_details_FalseCondition" : {
"message": "(匹配请求时无视此条规则)"
},
"condition_never_details" : {
"message": "(不匹配任何请求)"
"condition_help_FalseCondition" : {
"message": "设置规则类型为<code>(禁用)</code>可以临时禁用某个条件。禁用的条件在匹配时视为不存在。<br>条件被禁用后,仍然保存有之前的数据(例如通配符或正则),因此当需要时,可以把条件类型改回之前的类型,以方便地重新启用条件。"
},
"condition_group_default" : {
"message": ""
},
"condition_group_host" : {
"message": "域名"
},
"condition_group_url" : {
"message": "网址"
},
"condition_group_special" : {
"message": "特殊"
},
"ruleListFormat_Switchy": {
@ -138,6 +165,12 @@
"options_startupProfile_none": {
"message": "(当前情景模式)"
},
"options_showConditionTypesAdvanced": {
"message": "显示高级切换条件"
},
"options_showConditionTypesAdvancedHelp": {
"message": "启用后,可以使用一些新种类的切换条件,功能强大但难以掌握。对于大多数情况来说,只用基本条件类型就可以满足需求,因此不推荐启用此功能。"
},
"options_quickSwitch": {
"message": "快速切换"
},
@ -333,6 +366,9 @@
"options_conditionType": {
"message": "条件类型"
},
"options_showConditionTypeHelp": {
"message": "显示帮助"
},
"options_conditionDetails": {
"message": "条件设置"
},
@ -360,6 +396,9 @@
"options_hostLevelsBetween": {
"message": "\u2264 主机层数 \u2264"
},
"options_group_conditionHelp": {
"message": "条件类型说明"
},
"options_group_attachProfile": {
"message": "导入在线规则列表"
},

View File

@ -37,7 +37,8 @@
"FileSaver": "*",
"angular-ui-utils": "bower-validate",
"angular-ladda": "~0.1.6",
"bootstrap-select": "~1.6.2"
"bootstrap-select": "~1.6.2",
"angular-sanitize": "~1.2.26"
},
"exportsOverride": {
"script.js": {
@ -67,6 +68,9 @@
"angular-loader": {
"": "*.min.js"
},
"angular-sanitize": {
"": "*.min.js"
},
"angular-spectrum-colorpicker": {
"": "dist/*.min.js"
},

View File

@ -11,9 +11,10 @@ $script 'lib/spin.js/spin.js', ->
$script 'lib/angular-ladda/angular-ladda.min.js', 'angular-ladda'
$script.ready ['angular-loader'], ->
angular.module 'omega', ['ngLocale', 'ngAnimate', 'ui.bootstrap', 'ui.router',
'ngProgress', 'ui.sortable', 'angularSpectrumColorpicker', 'ui.validate',
'angular-ladda', 'omegaTarget', 'omegaDecoration']
angular.module 'omega', ['ngLocale', 'ngAnimate', 'ngSanitize',
'ui.bootstrap', 'ui.router', 'ngProgress', 'ui.sortable',
'angularSpectrumColorpicker', 'ui.validate', 'angular-ladda', 'omegaTarget',
'omegaDecoration']
$script.ready ['omega-pac'], ->
$script 'js/omega.js', 'omega'
$script([
@ -39,7 +40,9 @@ $script.ready ['angular-loader', 'jquery'], ->
$script.ready ['angular'], ->
$script 'lib/angular-ui-router/angular-ui-router.js', 'angular-ui-router'
$script 'lib/angular-sanitize/angular-sanitize.min.js', 'angular-sanitize'
$script.ready ['angular', 'omega', 'omega-deps', 'angular-ui-router',
'jquery-ui', 'spectrum', 'filesaver', 'blob', 'angular-ladda'], ->
'jquery-ui', 'spectrum', 'filesaver', 'blob', 'angular-ladda',
'angular-sanitize'], ->
angular.bootstrap document, ['omega']

View File

@ -37,6 +37,10 @@ a[role="button"] {
min-width: 0 !important;
}
.clear-padding {
padding: 0 !important;
}
.align-initial {
text-align: left;
text-align: initial;
@ -280,6 +284,30 @@ main {
}
}
.condition-help {
dl {
margin-left: 5px;
padding-left: 10px;
border-left: solid 1px #ccc;
}
dt {
font-size: 1.2em;
}
dd {
padding-left: 20px;
}
}
.close-condition-help {
float: none !important;
margin-left: 15px !important;
opacity: 1 !important;
&:hover, &:active {
color: #444 !important;
}
}
.switch-attached {
> tr > td {
background-color: #F9F9F9;

View File

@ -20,7 +20,9 @@ angular.module('omega').constant 'getParentName', (name) ->
undefined
angular.module('omega').config ($stateProvider, $urlRouterProvider,
$httpProvider) ->
$httpProvider, $animateProvider) ->
$animateProvider.classNameFilter(/angular-animate/)
$urlRouterProvider.otherwise '/ui'
$stateProvider

View File

@ -1,15 +1,93 @@
angular.module('omega').controller 'SwitchProfileCtrl', ($scope, $modal,
profileIcons, getAttachedName) ->
$scope.conditionI18n =
'HostWildcardCondition': 'condition_hostWildcard'
'HostRegexCondition': 'condition_hostRegex'
'HostLevelsCondition': 'condition_hostLevels'
'UrlWildcardCondition': 'condition_urlWildcard'
'UrlRegexCondition': 'condition_urlRegex'
'KeywordCondition': 'condition_keyword'
'AlwaysCondition': 'condition_always'
'NeverCondition': 'condition_never'
$scope.basicConditionTypes = [
{
group: 'default'
types: [
'HostWildcardCondition'
'UrlWildcardCondition'
'UrlRegexCondition'
'FalseCondition'
]
}
]
$scope.advancedConditionTypes = [
{
group: 'host'
types: [
'HostWildcardCondition'
'HostRegexCondition'
'HostLevelsCondition'
]
}
{
group: 'url'
types: [
'UrlWildcardCondition'
'UrlRegexCondition'
'KeywordCondition'
]
}
{
group: 'special'
types: [
'FalseCondition'
]
}
]
expandGroups = (groups) ->
result = []
for group in groups
for type in group.types
result.push({type: type, group: 'condition_group_' + group.group})
result
basicConditionTypesExpanded = expandGroups($scope.basicConditionTypes)
advancedConditionTypesExpanded = expandGroups($scope.advancedConditionTypes)
basicConditionTypeSet = {}
for type in basicConditionTypesExpanded
basicConditionTypeSet[type.type] = type.type
$scope.conditionTypes = basicConditionTypesExpanded
$scope.showConditionTypes = 0
$scope.hasConditionTypes = 0
updateHasConditionTypes = ->
return unless $scope.hasConditionTypes == 0
return unless $scope.profile?.rules?
for rule in $scope.profile.rules
# Convert TrueCondition to a HostWildcardCondition with pattern '*'.
if rule.condition.conditionType == 'TrueCondition'
rule.condition = {
conditionType: 'HostWildcardCondition'
pattern: '*'
}
if not basicConditionTypeSet[rule.condition.conditionType]
$scope.hasConditionTypes = 1
$scope.showConditionTypes = 1
break
$scope.$watch 'options["-showConditionTypes"]', (show) ->
show ||= 0
if show > 0
$scope.showConditionTypes = show
else
updateHasConditionTypes()
$scope.showConditionTypes = $scope.hasConditionTypes
if $scope.showConditionTypes == 0
$scope.conditionTypes = basicConditionTypesExpanded
else
$scope.conditionTypes = advancedConditionTypesExpanded
if not $scope.options["-showConditionTypes"]?
$scope.options["-showConditionTypes"] = $scope.showConditionTypes
unwatchRules?()
if $scope.hasConditionTypes == 0
unwatchRules = $scope.$watch 'profile.rules', updateHasConditionTypes, true
$scope.addRule = ->
rule =
@ -37,7 +115,6 @@ angular.module('omega').controller 'SwitchProfileCtrl', ($scope, $modal,
if $scope.options['-confirmDeletion']
scope = $scope.$new('isolate')
scope.rule = $scope.profile.rules[index]
scope.conditionI18n = $scope.conditionI18n
scope.ruleProfile = $scope.profileByName(scope.rule.profileName)
scope.profileIcons = $scope.profileIcons
$modal.open(

View File

@ -37,3 +37,12 @@ angular.module('omega').directive 'omegaUpload', ->
scope.error({'$error': e.target.error})
reader.readAsText(input.files[0])
input.value = ''
angular.module('omega').directive 'omegaInt2str', ->
restrict: 'A'
priority: 2 # Run post-link after input directive (0) and ngModel (1).
require: 'ngModel'
link: (scope, element, attr, ngModel) ->
ngModel.$parsers.push (value) ->
parseInt(value)
ngModel.$formatters.push (value) ->
'' + value

View File

@ -53,7 +53,7 @@ html(lang='en' ng-controller='MasterCtrl' ng-csp)
span.glyphicon.glyphicon-remove-circle
= ' '
| {{'options_discard' | tr}}
main.col-lg-10.col-sm-9.col-lg-offset-2.col-sm-offset-3(ui-view)
main.col-lg-10.col-sm-9.col-lg-offset-2.col-sm-offset-3.angular-animate(ui-view)
//-
Note: Alert type classes cannot be changed in angular-bootstrap.

View File

@ -1,4 +1,19 @@
div(ng-controller='SwitchProfileCtrl')
section.settings-group(ng-show='showConditionHelp' ng-init='expandedSection = {id: 0}')
h3
| {{'options_group_conditionHelp' | tr}}
button.close.close-condition-help(type='button' ng-click='showConditionHelp = false')
span(aria-hidden='true') ×
span.sr-only {{'dialog_close' | tr}}
div.condition-help(ng-repeat='group in (showConditionTypes == 0 ? basicConditionTypes : advancedConditionTypes)')
h4(ng-show="!!('condition_group_' + group.group | tr)")
a(ng-click='expandedSection.id = $index' role='button')
span.glyphicon(ng-class="{'glyphicon-chevron-down': expandedSection.id == $index, 'glyphicon-chevron-right': expandedSection.id != $index}")
= ' '
| {{'condition_group_' + group.group | tr}}
dl(ng-show='expandedSection.id == $index')
dt(ng-repeat-start='type in group.types') {{'condition_' + type | tr}}
dd(ng-repeat-end ng-bind-html='"condition_help_" + type | tr')
section.settings-group
h3 {{'options_group_switchRules' | tr}}
.table-responsive
@ -6,7 +21,12 @@ div(ng-controller='SwitchProfileCtrl')
thead
tr
th(style='white-space: nowrap') {{'options_sort' | tr}}
th {{'options_conditionType' | tr}}
th
| {{'options_conditionType' | tr}}
= ' '
button.btn.btn-link.btn-sm.clear-padding(title='{{"options_showConditionTypeHelp" | tr}}'
ng-click='showConditionHelp = !showConditionHelp' ng-init='showConditionHelp = false')
span.glyphicon.glyphicon-question-sign
th {{'options_conditionDetails' | tr}}
th {{'options_resultProfile' | tr}}
th {{'options_conditionActions' | tr}}
@ -16,10 +36,12 @@ div(ng-controller='SwitchProfileCtrl')
span.glyphicon.glyphicon-sort
td
select.form-control(ng-model='rule.condition.conditionType'
ng-options='cond as (i18n | tr) for (cond, i18n) in conditionI18n')
ng-options='type.type as ("condition_" + type.type | tr) group by (type.group | tr) for type in conditionTypes')
td(ng-switch='rule.condition.conditionType')
span(ng-switch-when='AlwaysCondition') {{'condition_always_details' | tr}}
span(ng-switch-when='NeverCondition') {{'condition_never_details' | tr}}
span(ng-switch-when='FalseCondition')
span(ng-show='!!rule.condition.pattern')
input.form-control(ng-model='rule.condition.pattern' disabled title="{{'condition_details_FalseCondition' | tr}}")
span(ng-show='!rule.condition.pattern') {{'condition_details_FalseCondition' | tr}}
span.host-levels-details(ng-switch-when='HostLevelsCondition')
input.form-control(type='number' min='1' max='99' ng-model='rule.condition.minValue' required)
= ' '
@ -30,7 +52,7 @@ div(ng-controller='SwitchProfileCtrl')
ui-validate='{pattern: "validateCondition(rule.condition, $value)"}')
td
div.form-control(omega-profile-select='options | profiles:profile' ng-model='rule.profileName'
disp-name='$profile.name | dispName')
disp-name='$profile.name | dispName' ng-class='{disabled: rule.condition.conditionType == "NeverCondition"}')
td
button.btn.btn-danger.btn-sm(title="{{'options_deleteRule' | tr}}" ng-click='removeRule($index)')
span.glyphicon.glyphicon-trash

View File

@ -6,7 +6,7 @@
.modal-body
p {{'options_deleteRuleConfirm' | tr}}
div.well
span.label.label-info {{conditionI18n[rule.condition.conditionType]}}
span.label.label-info {{'condition_' + rule.condition.conditionType | tr}}
= ' '
| {{rule.condition.pattern}}
span.pull-right

View File

@ -18,6 +18,12 @@ section.settings-group
div(omega-profile-select='options | profiles:"all"' ng-model='options["-startupProfileName"]'
default-text="{{'options_startupProfile_none' | tr}}" disp-name='$profile.name | dispName'
style='display: inline-block;')
div.checkbox
label
input(type='checkbox' ng-model='options["-showConditionTypes"]' ng-true-value='1' ng-false-value='0' omega-int2str)
span {{'options_showConditionTypesAdvanced' | tr}}
p.help-block
| {{'options_showConditionTypesAdvancedHelp' | tr}}
div.checkbox
label
input(type='checkbox' ng-model='options["-enableQuickSwitch"]')