action_policy 0.4.4 → 0.5.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +203 -174
- data/README.md +5 -4
- data/lib/.rbnext/2.7/action_policy/behaviours/policy_for.rb +62 -0
- data/lib/.rbnext/2.7/action_policy/i18n.rb +56 -0
- data/lib/.rbnext/2.7/action_policy/policy/cache.rb +101 -0
- data/lib/.rbnext/2.7/action_policy/policy/pre_check.rb +162 -0
- data/lib/.rbnext/2.7/action_policy/rspec/be_authorized_to.rb +89 -0
- data/lib/.rbnext/2.7/action_policy/rspec/have_authorized_scope.rb +124 -0
- data/lib/.rbnext/2.7/action_policy/utils/pretty_print.rb +159 -0
- data/lib/.rbnext/3.0/action_policy/behaviour.rb +115 -0
- data/lib/.rbnext/3.0/action_policy/behaviours/policy_for.rb +62 -0
- data/lib/.rbnext/3.0/action_policy/behaviours/scoping.rb +35 -0
- data/lib/.rbnext/3.0/action_policy/behaviours/thread_memoized.rb +59 -0
- data/lib/.rbnext/3.0/action_policy/ext/policy_cache_key.rb +72 -0
- data/lib/.rbnext/3.0/action_policy/policy/aliases.rb +69 -0
- data/lib/.rbnext/3.0/action_policy/policy/authorization.rb +87 -0
- data/lib/.rbnext/3.0/action_policy/policy/cache.rb +101 -0
- data/lib/.rbnext/3.0/action_policy/policy/core.rb +161 -0
- data/lib/.rbnext/3.0/action_policy/policy/defaults.rb +31 -0
- data/lib/.rbnext/3.0/action_policy/policy/execution_result.rb +37 -0
- data/lib/.rbnext/3.0/action_policy/policy/pre_check.rb +162 -0
- data/lib/.rbnext/3.0/action_policy/policy/reasons.rb +212 -0
- data/lib/.rbnext/3.0/action_policy/policy/scoping.rb +160 -0
- data/lib/.rbnext/3.0/action_policy/rspec/be_authorized_to.rb +89 -0
- data/lib/.rbnext/3.0/action_policy/rspec/have_authorized_scope.rb +124 -0
- data/lib/.rbnext/3.0/action_policy/utils/pretty_print.rb +159 -0
- data/lib/.rbnext/3.0/action_policy/utils/suggest_message.rb +19 -0
- data/lib/action_policy.rb +7 -1
- data/lib/action_policy/behaviour.rb +22 -16
- data/lib/action_policy/behaviours/policy_for.rb +10 -3
- data/lib/action_policy/behaviours/scoping.rb +2 -1
- data/lib/action_policy/behaviours/thread_memoized.rb +1 -3
- data/lib/action_policy/ext/module_namespace.rb +1 -6
- data/lib/action_policy/ext/policy_cache_key.rb +10 -30
- data/lib/action_policy/i18n.rb +1 -1
- data/lib/action_policy/lookup_chain.rb +29 -15
- data/lib/action_policy/policy/aliases.rb +7 -12
- data/lib/action_policy/policy/authorization.rb +8 -7
- data/lib/action_policy/policy/cache.rb +11 -17
- data/lib/action_policy/policy/core.rb +25 -12
- data/lib/action_policy/policy/defaults.rb +3 -9
- data/lib/action_policy/policy/execution_result.rb +3 -9
- data/lib/action_policy/policy/pre_check.rb +19 -58
- data/lib/action_policy/policy/reasons.rb +31 -19
- data/lib/action_policy/policy/scoping.rb +5 -6
- data/lib/action_policy/rails/controller.rb +6 -1
- data/lib/action_policy/rails/policy/instrumentation.rb +1 -1
- data/lib/action_policy/rspec/be_authorized_to.rb +5 -9
- data/lib/action_policy/rspec/dsl.rb +1 -1
- data/lib/action_policy/rspec/have_authorized_scope.rb +5 -7
- data/lib/action_policy/utils/pretty_print.rb +21 -24
- data/lib/action_policy/utils/suggest_message.rb +1 -3
- data/lib/action_policy/version.rb +1 -1
- data/lib/generators/action_policy/install/templates/{application_policy.rb → application_policy.rb.tt} +0 -0
- data/lib/generators/action_policy/policy/policy_generator.rb +4 -1
- data/lib/generators/action_policy/policy/templates/{policy.rb → policy.rb.tt} +0 -0
- data/lib/generators/rspec/templates/{policy_spec.rb → policy_spec.rb.tt} +0 -0
- data/lib/generators/test_unit/templates/{policy_test.rb → policy_test.rb.tt} +0 -0
- metadata +54 -119
- data/.gitattributes +0 -2
- data/.github/ISSUE_TEMPLATE.md +0 -21
- data/.github/PULL_REQUEST_TEMPLATE.md +0 -29
- data/.github/bug_report_template.rb +0 -175
- data/.gitignore +0 -15
- data/.rubocop.yml +0 -54
- data/.tidelift.yml +0 -6
- data/.travis.yml +0 -31
- data/Gemfile +0 -22
- data/Rakefile +0 -27
- data/action_policy.gemspec +0 -44
- data/benchmarks/namespaced_lookup_cache.rb +0 -74
- data/benchmarks/pre_checks.rb +0 -73
- data/bin/console +0 -14
- data/bin/setup +0 -8
- data/docs/.nojekyll +0 -0
- data/docs/CNAME +0 -1
- data/docs/README.md +0 -79
- data/docs/_sidebar.md +0 -27
- data/docs/aliases.md +0 -122
- data/docs/assets/docsify-search.js +0 -364
- data/docs/assets/docsify.min.js +0 -3
- data/docs/assets/fonts/FiraCode-Medium.woff +0 -0
- data/docs/assets/fonts/FiraCode-Regular.woff +0 -0
- data/docs/assets/images/banner.png +0 -0
- data/docs/assets/images/cache.png +0 -0
- data/docs/assets/images/cache.svg +0 -70
- data/docs/assets/images/layer.png +0 -0
- data/docs/assets/images/layer.svg +0 -35
- data/docs/assets/prism-ruby.min.js +0 -1
- data/docs/assets/styles.css +0 -347
- data/docs/assets/vue.min.css +0 -1
- data/docs/authorization_context.md +0 -92
- data/docs/behaviour.md +0 -113
- data/docs/caching.md +0 -291
- data/docs/controller_action_aliases.md +0 -109
- data/docs/custom_lookup_chain.md +0 -48
- data/docs/custom_policy.md +0 -53
- data/docs/debugging.md +0 -55
- data/docs/decorators.md +0 -27
- data/docs/favicon.ico +0 -0
- data/docs/graphql.md +0 -302
- data/docs/i18n.md +0 -44
- data/docs/index.html +0 -43
- data/docs/instrumentation.md +0 -84
- data/docs/lookup_chain.md +0 -22
- data/docs/namespaces.md +0 -77
- data/docs/non_rails.md +0 -28
- data/docs/pre_checks.md +0 -57
- data/docs/pundit_migration.md +0 -80
- data/docs/quick_start.md +0 -118
- data/docs/rails.md +0 -120
- data/docs/reasons.md +0 -120
- data/docs/scoping.md +0 -255
- data/docs/testing.md +0 -390
- data/docs/writing_policies.md +0 -107
- data/gemfiles/jruby.gemfile +0 -8
- data/gemfiles/rails42.gemfile +0 -9
- data/gemfiles/rails6.gemfile +0 -8
- data/gemfiles/railsmaster.gemfile +0 -6
- data/lib/action_policy/ext/string_match.rb +0 -14
- data/lib/action_policy/ext/yield_self_then.rb +0 -25
data/docs/_sidebar.md
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
* Getting Started
|
2
|
-
* [Quick Start](quick_start.md)
|
3
|
-
* [Writing Policies](writing_policies.md)
|
4
|
-
* [Rails Integration](rails.md)
|
5
|
-
* [Non-Rails Usage](non_rails.md)
|
6
|
-
* [GraphQL Integration](graphql.md)
|
7
|
-
* [Testing](testing.md)
|
8
|
-
* Features
|
9
|
-
* [Authorization Behaviour](behaviour.md)
|
10
|
-
* [Policy Lookup](lookup_chain.md)
|
11
|
-
* [Authorization Context](authorization_context.md)
|
12
|
-
* [Aliases](aliases.md)
|
13
|
-
* [Pre-Checks](pre_checks.md)
|
14
|
-
* [Scoping](scoping.md)
|
15
|
-
* [Caching](caching.md)
|
16
|
-
* [Namespaces](namespaces.md)
|
17
|
-
* [Failure Reasons](reasons.md)
|
18
|
-
* [Instrumentation](instrumentation.md)
|
19
|
-
* [I18n Support](i18n.md)
|
20
|
-
* [Debugging](debugging.md)
|
21
|
-
* Tips & Tricks
|
22
|
-
* [From Pundit to Action Policy](./pundit_migration.md)
|
23
|
-
* [Dealing with Decorators](./decorators.md)
|
24
|
-
* [Controller Action Aliases](controller_action_aliases.md)
|
25
|
-
* Customize
|
26
|
-
* [Base Policy](custom_policy.md)
|
27
|
-
* [Lookup Chain](custom_lookup_chain.md)
|
data/docs/aliases.md
DELETED
@@ -1,122 +0,0 @@
|
|
1
|
-
# Rule Aliases
|
2
|
-
|
3
|
-
Action Policy allows you to add rule aliases. It is useful when you rely on _implicit_ rules in controllers. For example:
|
4
|
-
|
5
|
-
```ruby
|
6
|
-
class PostsController < ApplicationController
|
7
|
-
before_action :load_post, only: [:edit, :update, :destroy]
|
8
|
-
|
9
|
-
private
|
10
|
-
|
11
|
-
def load_post
|
12
|
-
@post = Post.find(params[:id])
|
13
|
-
# depending on action, an `edit?`, `update?` or `destroy?`
|
14
|
-
# rule would be applied
|
15
|
-
authorize! @post
|
16
|
-
end
|
17
|
-
end
|
18
|
-
```
|
19
|
-
|
20
|
-
In your policy, you can create aliases to avoid duplication:
|
21
|
-
|
22
|
-
```ruby
|
23
|
-
class PostPolicy < ApplicationPolicy
|
24
|
-
alias_rule :edit?, :destroy?, to: :update?
|
25
|
-
end
|
26
|
-
```
|
27
|
-
|
28
|
-
**NOTE**: `alias_rule` is available only if you inherit from `ActionPolicy::Base` or include `ActionPolicy::Policy::Aliases` into your `ApplicationPolicy`.
|
29
|
-
|
30
|
-
**Why not just use Ruby's `alias`?**
|
31
|
-
|
32
|
-
An alias created with `alias_rule` is resolved at _authorization time_ (during an `authorize!` or `allowed_to?` call), and it does not add an alias method to the class.
|
33
|
-
|
34
|
-
That allows us to write tests easier, as we should only test the rule, not the alias–and to leverage [caching](caching.md) better.
|
35
|
-
|
36
|
-
By default, `ActionPolicy::Base` adds one alias: `alias_rule :new?, to: :create?`.
|
37
|
-
|
38
|
-
## Default rule
|
39
|
-
|
40
|
-
You can add a _default_ rule–the rule that would be applied if the rule specified during authorization is missing (like a "wildcard" alias):
|
41
|
-
|
42
|
-
```ruby
|
43
|
-
class PostPolicy < ApplicationPolicy
|
44
|
-
# For an ApplicationPolicy, makes :manage? match anything that is
|
45
|
-
# not :index?, :create? or :new?
|
46
|
-
default_rule :manage?
|
47
|
-
|
48
|
-
# If you want manage? to catch really everything, place this alias
|
49
|
-
#alias_rule :index?, :create?, :new?, to: :manage?
|
50
|
-
def manage?
|
51
|
-
# ...
|
52
|
-
end
|
53
|
-
end
|
54
|
-
```
|
55
|
-
|
56
|
-
Now when you call `authorize! post` with any rule not defined in the policy class, the `manage?` rule is applied. Note that `index?` `create?` and `new?` are already defined in the [superclass by default](custom_policy.md) (returning `false`) - if you want the same behaviour for *all* actions, define aliases like in the example above (commented out).
|
57
|
-
|
58
|
-
By default, `ActionPolicy::Base` sets `manage?` as a default rule.
|
59
|
-
|
60
|
-
## Aliases and Private Methods
|
61
|
-
|
62
|
-
Rules in `action_policy` can only be public methods. Trying to use a private method as a rule will raise an error. Thus, aliases can also only point to public methods.
|
63
|
-
|
64
|
-
## Rule resolution with subclasses
|
65
|
-
|
66
|
-
Here's the order in which aliases and concrete rule methods are resolved in regards to subclasses:
|
67
|
-
|
68
|
-
1. If there is a concrete rule method on the subclass, this is called, else
|
69
|
-
2. If there is a matching alias then this is called, else
|
70
|
-
* When aliases are defined on the subclass they will overwrite matching aliases on the superclass.
|
71
|
-
3. If there is a concrete rule method on the superclass, then this is called, else
|
72
|
-
4. If there is a default rule defined, then this is called, else
|
73
|
-
5. `ActionPolicy::UnknownRule` is raised.
|
74
|
-
|
75
|
-
Here's an example with the expected results:
|
76
|
-
|
77
|
-
```ruby
|
78
|
-
class SuperPolicy < ApplicationPolicy
|
79
|
-
default_rule :manage?
|
80
|
-
|
81
|
-
alias_rule :update?, :destroy?, :create?, to: :edit?
|
82
|
-
|
83
|
-
def manage?
|
84
|
-
end
|
85
|
-
|
86
|
-
def edit?
|
87
|
-
end
|
88
|
-
|
89
|
-
def index?
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
class SubPolicy < AbstractPolicy
|
94
|
-
default_rule nil
|
95
|
-
|
96
|
-
alias_rule :index?, :update?, to: :manage?
|
97
|
-
|
98
|
-
def create?
|
99
|
-
end
|
100
|
-
end
|
101
|
-
```
|
102
|
-
|
103
|
-
Authorizing against the SuperPolicy:
|
104
|
-
|
105
|
-
* `update?` will resolve to `edit?`
|
106
|
-
* `destroy?` will resolve to `edit?`
|
107
|
-
* `create?` will resolve to `edit?`
|
108
|
-
* `manage?` will resolve to `manage?`
|
109
|
-
* `edit?` will resolve to `edit?`
|
110
|
-
* `index?` will resolve to `index?`
|
111
|
-
* `something?` will resolve to `manage?`
|
112
|
-
|
113
|
-
Authorizing against the SubPolicy:
|
114
|
-
|
115
|
-
* `index?` will resolve to `manage?`
|
116
|
-
* `update?` will resolve to `manage?`
|
117
|
-
* `create?` will resolve to `create?`
|
118
|
-
* `destroy?` will resolve to `edit?`
|
119
|
-
* `manage?` will resolve to `manage?`
|
120
|
-
* `edit?` will resolve to `edit?`
|
121
|
-
* `index?` will resolve to `manage?`
|
122
|
-
* `something?` will raise `ActionPolicy::UnknownRule`
|
@@ -1,364 +0,0 @@
|
|
1
|
-
(function () {
|
2
|
-
var INDEXS = {};
|
3
|
-
|
4
|
-
var LOCAL_STORAGE = {
|
5
|
-
EXPIRE_KEY: 'docsify.search.expires',
|
6
|
-
INDEX_KEY: 'docsify.search.index'
|
7
|
-
};
|
8
|
-
|
9
|
-
function resolveExpireKey(namespace) {
|
10
|
-
return namespace ? ((LOCAL_STORAGE.EXPIRE_KEY) + "/" + namespace) : LOCAL_STORAGE.EXPIRE_KEY
|
11
|
-
}
|
12
|
-
function resolveIndexKey(namespace) {
|
13
|
-
return namespace ? ((LOCAL_STORAGE.INDEX_KEY) + "/" + namespace) : LOCAL_STORAGE.INDEX_KEY
|
14
|
-
}
|
15
|
-
|
16
|
-
function escapeHtml(string) {
|
17
|
-
var entityMap = {
|
18
|
-
'&': '&',
|
19
|
-
'<': '<',
|
20
|
-
'>': '>',
|
21
|
-
'"': '"',
|
22
|
-
'\'': ''',
|
23
|
-
'/': '/'
|
24
|
-
};
|
25
|
-
|
26
|
-
return String(string).replace(/[&<>"'/]/g, function (s) { return entityMap[s]; })
|
27
|
-
}
|
28
|
-
|
29
|
-
function getAllPaths(router) {
|
30
|
-
var paths = [];
|
31
|
-
|
32
|
-
Docsify.dom.findAll('.sidebar-nav a:not(.section-link):not([data-nosearch])').forEach(function (node) {
|
33
|
-
var href = node.href;
|
34
|
-
var originHref = node.getAttribute('href');
|
35
|
-
var path = router.parse(href).path;
|
36
|
-
|
37
|
-
if (
|
38
|
-
path &&
|
39
|
-
paths.indexOf(path) === -1 &&
|
40
|
-
!Docsify.util.isAbsolutePath(originHref)
|
41
|
-
) {
|
42
|
-
paths.push(path);
|
43
|
-
}
|
44
|
-
});
|
45
|
-
|
46
|
-
return paths
|
47
|
-
}
|
48
|
-
|
49
|
-
function saveData(maxAge, expireKey, indexKey) {
|
50
|
-
localStorage.setItem(expireKey, Date.now() + maxAge);
|
51
|
-
localStorage.setItem(indexKey, JSON.stringify(INDEXS));
|
52
|
-
}
|
53
|
-
|
54
|
-
function genIndex(path, content, router, depth) {
|
55
|
-
if ( content === void 0 ) content = '';
|
56
|
-
|
57
|
-
var tokens = window.marked.lexer(content);
|
58
|
-
var slugify = window.Docsify.slugify;
|
59
|
-
var index = {};
|
60
|
-
var slug;
|
61
|
-
|
62
|
-
tokens.forEach(function (token) {
|
63
|
-
if (token.type === 'heading' && token.depth <= depth) {
|
64
|
-
slug = router.toURL(path, {id: slugify(token.text)});
|
65
|
-
index[slug] = {slug: slug, title: token.text, body: ''};
|
66
|
-
} else {
|
67
|
-
if (!slug) {
|
68
|
-
return
|
69
|
-
}
|
70
|
-
if (!index[slug]) {
|
71
|
-
index[slug] = {slug: slug, title: '', body: ''};
|
72
|
-
} else if (index[slug].body) {
|
73
|
-
index[slug].body += '\n' + (token.text || '');
|
74
|
-
} else {
|
75
|
-
index[slug].body = token.text;
|
76
|
-
}
|
77
|
-
}
|
78
|
-
});
|
79
|
-
slugify.clear();
|
80
|
-
return index
|
81
|
-
}
|
82
|
-
|
83
|
-
/**
|
84
|
-
* @param {String} query
|
85
|
-
* @returns {Array}
|
86
|
-
*/
|
87
|
-
function search(query) {
|
88
|
-
var matchingResults = [];
|
89
|
-
var data = [];
|
90
|
-
Object.keys(INDEXS).forEach(function (key) {
|
91
|
-
data = data.concat(Object.keys(INDEXS[key]).map(function (page) { return INDEXS[key][page]; }));
|
92
|
-
});
|
93
|
-
|
94
|
-
query = query.trim();
|
95
|
-
var keywords = query.split(/[\s\-,\\/]+/);
|
96
|
-
if (keywords.length !== 1) {
|
97
|
-
keywords = [].concat(query, keywords);
|
98
|
-
}
|
99
|
-
|
100
|
-
var loop = function ( i ) {
|
101
|
-
var post = data[i];
|
102
|
-
var matchesScore = 0;
|
103
|
-
var resultStr = '';
|
104
|
-
var postTitle = post.title && post.title.trim();
|
105
|
-
var postContent = post.body && post.body.trim();
|
106
|
-
var postUrl = post.slug || '';
|
107
|
-
|
108
|
-
if (postTitle) {
|
109
|
-
keywords.forEach( function (keyword) {
|
110
|
-
// From https://github.com/sindresorhus/escape-string-regexp
|
111
|
-
var regEx = new RegExp(
|
112
|
-
keyword.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&'),
|
113
|
-
'gi'
|
114
|
-
);
|
115
|
-
var indexTitle = -1;
|
116
|
-
var indexContent = -1;
|
117
|
-
|
118
|
-
indexTitle = postTitle ? postTitle.search(regEx) : -1;
|
119
|
-
indexContent = postContent ? postContent.search(regEx) : -1;
|
120
|
-
|
121
|
-
if (indexTitle >= 0 || indexContent >= 0) {
|
122
|
-
matchesScore += indexTitle >= 0 ? 3 : indexContent >= 0 ? 2 : 0;
|
123
|
-
if (indexContent < 0) {
|
124
|
-
indexContent = 0;
|
125
|
-
}
|
126
|
-
|
127
|
-
var start = 0;
|
128
|
-
var end = 0;
|
129
|
-
|
130
|
-
start = indexContent < 11 ? 0 : indexContent - 10;
|
131
|
-
end = start === 0 ? 70 : indexContent + keyword.length + 60;
|
132
|
-
|
133
|
-
if (end > postContent.length) {
|
134
|
-
end = postContent.length;
|
135
|
-
}
|
136
|
-
|
137
|
-
var matchContent =
|
138
|
-
'...' +
|
139
|
-
escapeHtml(postContent)
|
140
|
-
.substring(start, end)
|
141
|
-
.replace(regEx, ("<em class=\"search-keyword\">" + keyword + "</em>")) +
|
142
|
-
'...';
|
143
|
-
|
144
|
-
resultStr += matchContent;
|
145
|
-
}
|
146
|
-
});
|
147
|
-
|
148
|
-
if (matchesScore > 0) {
|
149
|
-
var matchingPost = {
|
150
|
-
title: escapeHtml(postTitle),
|
151
|
-
content: postContent ? resultStr : '',
|
152
|
-
url: postUrl,
|
153
|
-
score: matchesScore
|
154
|
-
};
|
155
|
-
|
156
|
-
matchingResults.push(matchingPost);
|
157
|
-
}
|
158
|
-
}
|
159
|
-
};
|
160
|
-
|
161
|
-
for (var i = 0; i < data.length; i++) loop( i );
|
162
|
-
|
163
|
-
return matchingResults.sort(function (r1, r2) { return r2.score - r1.score; });
|
164
|
-
}
|
165
|
-
|
166
|
-
function init$1(config, vm) {
|
167
|
-
var isAuto = config.paths === 'auto';
|
168
|
-
|
169
|
-
var expireKey = resolveExpireKey(config.namespace);
|
170
|
-
var indexKey = resolveIndexKey(config.namespace);
|
171
|
-
|
172
|
-
var isExpired = localStorage.getItem(expireKey) < Date.now();
|
173
|
-
|
174
|
-
INDEXS = JSON.parse(localStorage.getItem(indexKey));
|
175
|
-
|
176
|
-
if (isExpired) {
|
177
|
-
INDEXS = {};
|
178
|
-
} else if (!isAuto) {
|
179
|
-
return
|
180
|
-
}
|
181
|
-
|
182
|
-
var paths = isAuto ? getAllPaths(vm.router) : config.paths;
|
183
|
-
var len = paths.length;
|
184
|
-
var count = 0;
|
185
|
-
|
186
|
-
paths.forEach(function (path) {
|
187
|
-
if (INDEXS[path]) {
|
188
|
-
return count++
|
189
|
-
}
|
190
|
-
|
191
|
-
Docsify
|
192
|
-
.get(vm.router.getFile(path), false, vm.config.requestHeaders)
|
193
|
-
.then(function (result) {
|
194
|
-
INDEXS[path] = genIndex(path, result, vm.router, config.depth);
|
195
|
-
len === ++count && saveData(config.maxAge, expireKey, indexKey);
|
196
|
-
});
|
197
|
-
});
|
198
|
-
}
|
199
|
-
|
200
|
-
var NO_DATA_TEXT = '';
|
201
|
-
var options;
|
202
|
-
|
203
|
-
function style() {
|
204
|
-
var code = "\n.sidebar {\n padding-top: 0;\n}\n\n.search {\n margin-bottom: 20px;\n padding: 6px;\n border-bottom: 1px solid #eee;\n}\n\n.search .input-wrap {\n display: flex;\n align-items: center;\n}\n\n.search .results-panel {\n display: none;\n}\n\n.search .results-panel.show {\n display: block;\n}\n\n.search input {\n outline: none;\n border: none;\n width: 100%;\n padding: 0 7px;\n line-height: 36px;\n font-size: 14px;\n}\n\n.search input::-webkit-search-decoration,\n.search input::-webkit-search-cancel-button,\n.search input {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n}\n.search .clear-button {\n width: 36px;\n text-align: right;\n display: none;\n}\n\n.search .clear-button.show {\n display: block;\n}\n\n.search .clear-button svg {\n transform: scale(.5);\n}\n\n.search h2 {\n font-size: 17px;\n margin: 10px 0;\n}\n\n.search a {\n text-decoration: none;\n color: inherit;\n}\n\n.search .matching-post {\n border-bottom: 1px solid #eee;\n}\n\n.search .matching-post:last-child {\n border-bottom: 0;\n}\n\n.search p {\n font-size: 14px;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n}\n\n.search p.empty {\n text-align: center;\n}\n\n.app-name.hide, .sidebar-nav.hide {\n display: none;\n}";
|
205
|
-
|
206
|
-
Docsify.dom.style(code);
|
207
|
-
}
|
208
|
-
|
209
|
-
function tpl(defaultValue) {
|
210
|
-
if ( defaultValue === void 0 ) defaultValue = '';
|
211
|
-
|
212
|
-
var html =
|
213
|
-
"<div class=\"input-wrap\">\n <input type=\"search\" value=\"" + defaultValue + "\" />\n <div class=\"clear-button\">\n <svg width=\"26\" height=\"24\">\n <circle cx=\"12\" cy=\"12\" r=\"11\" fill=\"#ccc\" />\n <path stroke=\"white\" stroke-width=\"2\" d=\"M8.25,8.25,15.75,15.75\" />\n <path stroke=\"white\" stroke-width=\"2\"d=\"M8.25,15.75,15.75,8.25\" />\n </svg>\n </div>\n </div>\n <div class=\"results-panel\"></div>\n </div>";
|
214
|
-
var el = Docsify.dom.create('div', html);
|
215
|
-
var aside = Docsify.dom.find('aside');
|
216
|
-
|
217
|
-
Docsify.dom.toggleClass(el, 'search');
|
218
|
-
Docsify.dom.before(aside, el);
|
219
|
-
}
|
220
|
-
|
221
|
-
function doSearch(value) {
|
222
|
-
var $search = Docsify.dom.find('div.search');
|
223
|
-
var $panel = Docsify.dom.find($search, '.results-panel');
|
224
|
-
var $clearBtn = Docsify.dom.find($search, '.clear-button');
|
225
|
-
var $sidebarNav = Docsify.dom.find('.sidebar-nav');
|
226
|
-
var $appName = Docsify.dom.find('.app-name');
|
227
|
-
|
228
|
-
if (!value) {
|
229
|
-
$panel.classList.remove('show');
|
230
|
-
$clearBtn.classList.remove('show');
|
231
|
-
$panel.innerHTML = '';
|
232
|
-
|
233
|
-
if (options.hideOtherSidebarContent) {
|
234
|
-
$sidebarNav.classList.remove('hide');
|
235
|
-
$appName.classList.remove('hide');
|
236
|
-
}
|
237
|
-
return
|
238
|
-
}
|
239
|
-
var matchs = search(value);
|
240
|
-
|
241
|
-
var html = '';
|
242
|
-
matchs.forEach(function (post) {
|
243
|
-
html += "<div class=\"matching-post\">\n<a href=\"" + (post.url) + "\">\n<h2>" + (post.title) + "</h2>\n<p>" + (post.content) + "</p>\n</a>\n</div>";
|
244
|
-
});
|
245
|
-
|
246
|
-
$panel.classList.add('show');
|
247
|
-
$clearBtn.classList.add('show');
|
248
|
-
$panel.innerHTML = html || ("<p class=\"empty\">" + NO_DATA_TEXT + "</p>");
|
249
|
-
if (options.hideOtherSidebarContent) {
|
250
|
-
$sidebarNav.classList.add('hide');
|
251
|
-
$appName.classList.add('hide');
|
252
|
-
}
|
253
|
-
}
|
254
|
-
|
255
|
-
function bindEvents() {
|
256
|
-
var $search = Docsify.dom.find('div.search');
|
257
|
-
var $input = Docsify.dom.find($search, 'input');
|
258
|
-
var $inputWrap = Docsify.dom.find($search, '.input-wrap');
|
259
|
-
|
260
|
-
var timeId;
|
261
|
-
// Prevent to Fold sidebar
|
262
|
-
Docsify.dom.on(
|
263
|
-
$search,
|
264
|
-
'click',
|
265
|
-
function (e) { return e.target.tagName !== 'A' && e.stopPropagation(); }
|
266
|
-
);
|
267
|
-
Docsify.dom.on($input, 'input', function (e) {
|
268
|
-
clearTimeout(timeId);
|
269
|
-
timeId = setTimeout(function (_) { return doSearch(e.target.value.trim()); }, 100);
|
270
|
-
});
|
271
|
-
Docsify.dom.on($inputWrap, 'click', function (e) {
|
272
|
-
// Click input outside
|
273
|
-
if (e.target.tagName !== 'INPUT') {
|
274
|
-
$input.value = '';
|
275
|
-
doSearch();
|
276
|
-
}
|
277
|
-
});
|
278
|
-
}
|
279
|
-
|
280
|
-
function updatePlaceholder(text, path) {
|
281
|
-
var $input = Docsify.dom.getNode('.search input[type="search"]');
|
282
|
-
|
283
|
-
if (!$input) {
|
284
|
-
return
|
285
|
-
}
|
286
|
-
if (typeof text === 'string') {
|
287
|
-
$input.placeholder = text;
|
288
|
-
} else {
|
289
|
-
var match = Object.keys(text).filter(function (key) { return path.indexOf(key) > -1; })[0];
|
290
|
-
$input.placeholder = text[match];
|
291
|
-
}
|
292
|
-
}
|
293
|
-
|
294
|
-
function updateNoData(text, path) {
|
295
|
-
if (typeof text === 'string') {
|
296
|
-
NO_DATA_TEXT = text;
|
297
|
-
} else {
|
298
|
-
var match = Object.keys(text).filter(function (key) { return path.indexOf(key) > -1; })[0];
|
299
|
-
NO_DATA_TEXT = text[match];
|
300
|
-
}
|
301
|
-
}
|
302
|
-
|
303
|
-
function updateOptions(opts) {
|
304
|
-
options = opts;
|
305
|
-
}
|
306
|
-
|
307
|
-
function init(opts, vm) {
|
308
|
-
var keywords = vm.router.parse().query.s;
|
309
|
-
|
310
|
-
updateOptions(opts);
|
311
|
-
style();
|
312
|
-
tpl(keywords);
|
313
|
-
bindEvents();
|
314
|
-
keywords && setTimeout(function (_) { return doSearch(keywords); }, 500);
|
315
|
-
}
|
316
|
-
|
317
|
-
function update(opts, vm) {
|
318
|
-
updateOptions(opts);
|
319
|
-
updatePlaceholder(opts.placeholder, vm.route.path);
|
320
|
-
updateNoData(opts.noData, vm.route.path);
|
321
|
-
}
|
322
|
-
|
323
|
-
var CONFIG = {
|
324
|
-
placeholder: 'Type to search',
|
325
|
-
noData: 'No Results!',
|
326
|
-
paths: 'auto',
|
327
|
-
depth: 2,
|
328
|
-
maxAge: 86400000, // 1 day
|
329
|
-
hideOtherSidebarContent: false,
|
330
|
-
namespace: undefined
|
331
|
-
};
|
332
|
-
|
333
|
-
var install = function (hook, vm) {
|
334
|
-
var util = Docsify.util;
|
335
|
-
var opts = vm.config.search || CONFIG;
|
336
|
-
|
337
|
-
if (Array.isArray(opts)) {
|
338
|
-
CONFIG.paths = opts;
|
339
|
-
} else if (typeof opts === 'object') {
|
340
|
-
CONFIG.paths = Array.isArray(opts.paths) ? opts.paths : 'auto';
|
341
|
-
CONFIG.maxAge = util.isPrimitive(opts.maxAge) ? opts.maxAge : CONFIG.maxAge;
|
342
|
-
CONFIG.placeholder = opts.placeholder || CONFIG.placeholder;
|
343
|
-
CONFIG.noData = opts.noData || CONFIG.noData;
|
344
|
-
CONFIG.depth = opts.depth || CONFIG.depth;
|
345
|
-
CONFIG.hideOtherSidebarContent = opts.hideOtherSidebarContent || CONFIG.hideOtherSidebarContent;
|
346
|
-
CONFIG.namespace = opts.namespace || CONFIG.namespace;
|
347
|
-
}
|
348
|
-
|
349
|
-
var isAuto = CONFIG.paths === 'auto';
|
350
|
-
|
351
|
-
hook.mounted(function (_) {
|
352
|
-
init(CONFIG, vm);
|
353
|
-
!isAuto && init$1(CONFIG, vm);
|
354
|
-
});
|
355
|
-
hook.doneEach(function (_) {
|
356
|
-
update(CONFIG, vm);
|
357
|
-
isAuto && init$1(CONFIG, vm);
|
358
|
-
});
|
359
|
-
};
|
360
|
-
|
361
|
-
$docsify.plugins = [].concat(install, $docsify.plugins);
|
362
|
-
|
363
|
-
}());
|
364
|
-
|