action_policy 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +233 -171
- data/LICENSE.txt +1 -1
- data/README.md +7 -11
- 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 +15 -33
- data/lib/action_policy/ext/{symbol_classify.rb → symbol_camelize.rb} +6 -6
- data/lib/action_policy/i18n.rb +1 -1
- data/lib/action_policy/lookup_chain.rb +41 -21
- data/lib/action_policy/policy/aliases.rb +7 -12
- data/lib/action_policy/policy/authorization.rb +14 -17
- data/lib/action_policy/policy/cache.rb +34 -18
- 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 +30 -20
- data/lib/action_policy/policy/scoping.rb +5 -6
- data/lib/action_policy/rails/controller.rb +6 -1
- data/lib/action_policy/rails/ext/active_record.rb +7 -0
- 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 +3 -3
- data/lib/action_policy/rspec/have_authorized_scope.rb +5 -7
- data/lib/action_policy/testing.rb +1 -1
- 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} +1 -1
- 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 +30 -119
- data/.gitattributes +0 -2
- data/.github/FUNDING.yml +0 -1
- data/.github/ISSUE_TEMPLATE.md +0 -18
- data/.github/PULL_REQUEST_TEMPLATE.md +0 -29
- 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 -71
- 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 -77
- 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 -273
- 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 -17
- 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 -333
- data/docs/writing_policies.md +0 -107
- data/gemfiles/jruby.gemfile +0 -8
- data/gemfiles/rails42.gemfile +0 -8
- 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
|
-
|