praxis-docs-search 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3b87c5a598e8934d2adc02815102130441fcca42
4
+ data.tar.gz: 883f4fef35d32169517eb3e5a1c8643681a3ed07
5
+ SHA512:
6
+ metadata.gz: 5d7db10a0cf2fbc703864f59d91434c5d04f1677c1469dffe3555a3de302b65dedad7e316c7a300f72170bf4c57956484f2d9afed1802ac4c91927248801d834
7
+ data.tar.gz: a3c34a641de45578a9e8f1d327105bd84ea8b9ffe62f3dfd46d747b1b7860750c8373d1b528b41c8c4f0127e5a65533da8a9b026d35c47ba37e10ee9dc26d9ff
File without changes
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
Binary file
@@ -0,0 +1,5 @@
1
+ {
2
+ "dependencies": {
3
+ "lunr.js": "^0.6.0"
4
+ }
5
+ }
@@ -0,0 +1,23 @@
1
+ .praxis-docs-search {
2
+ input {
3
+ width: 280px;
4
+ }
5
+ .popover-content {
6
+ padding: 0;
7
+ .list-group-item {
8
+ width: 270px;
9
+ margin-right: 20px;
10
+ border-left: none;
11
+ border-right: none;
12
+ &:first-child {
13
+ border-top: none;
14
+ }
15
+ .result-type {
16
+ color: $text-muted;
17
+ position: absolute;
18
+ right: 12px;
19
+ bottom: 11px;
20
+ }
21
+ }
22
+ }
23
+ }
@@ -0,0 +1,218 @@
1
+ app.value('pathIndex', {});
2
+ app.provider('praxisDocsSearch', function() {
3
+ function localSearchFactory(Documentation, $timeout, $q, $state, pathIndex) {
4
+ 'ngInject';
5
+ console.log('Using Local Search Index');
6
+
7
+ // Create the lunr index
8
+ var index = lunr(function() {
9
+ this.ref('path');
10
+ this.field('titleWords', {boost: 50});
11
+ this.field('members', {boost: 40});
12
+ this.field('keywords', {boost: 20});
13
+ this.field('parent', {boost: 30});
14
+ });
15
+
16
+ // Delay building the index by loading the data asynchronously
17
+ var indexReadyPromise = Documentation.versions().then(function(versions) {
18
+ // Delay building the index for 500ms to allow the page to render
19
+ return $timeout(function() {
20
+ $q.all(versions.map(function (version) {
21
+ return Documentation.items(version).then(function(items) {
22
+ function extractMembers(thing) {
23
+ return _.map(_.get(thing, 'type.attributes', {}), function(v, k) {
24
+ var res = [k];
25
+ if (v.description) {
26
+ res.push(v.description);
27
+ }
28
+ if (_.get(v, 'type.attributes')) {
29
+ res = res.concat(extractMembers(v));
30
+ }
31
+ return res.join(' ');
32
+ }).join(' ');
33
+ }
34
+ function extractActionMembers(action) {
35
+ var headers = _.keys(_.get(action, 'headers.type.attributes', {})).join(' '),
36
+ params = extractMembers(_.get(action, 'params', {})),
37
+ payload = extractMembers(_.get(action, 'payload', {})),
38
+ responses = _.map(_.get(action, 'responses'), function(v, k) { return k + ' ' + v.description; }).join(' '),
39
+ urls = _.map(_.get(action, 'urls'), 'path').join(' ');
40
+ return [headers, params, payload, responses, urls].join(' ');
41
+ }
42
+ function extractTypeMembers(type) {
43
+ return extractMembers({type: type});
44
+ }
45
+ _.each(items.resources, function(resource, id) {
46
+ var href = $state.href('root.controller', {version: version, controller: id});
47
+ index.add({
48
+ path: href,
49
+ titleWords: resource.display_name,
50
+ members: _.map(resource.actions, 'name').join(' ') + ' ' + _.get(resource.media_type, 'name') + ' ' + _.get(resource, 'traits', []).join(' '),
51
+ keywords: resource.description + ' ' + version,
52
+ parent: resource.parent ? items.resources[resource.parent].display_name : ''
53
+ });
54
+ pathIndex[href] = {
55
+ type: 'resource',
56
+ name: resource.display_name,
57
+ id: id,
58
+ version: version
59
+ };
60
+
61
+ _.each(resource.actions, function(action) {
62
+ var href = $state.href('root.action', {version: version, controller: id, action: action.name});
63
+ index.add({
64
+ path: $state.href('root.action', {version: version, controller: id, action: action.name}),
65
+ titleWords: action.name,
66
+ members: extractActionMembers(action),
67
+ keywords: action.description + ' ' + version,
68
+ parent: resource.display_name
69
+ });
70
+
71
+ pathIndex[href] = {
72
+ type: 'action',
73
+ name: resource.display_name + ' » ' + action.name,
74
+ id: action.name,
75
+ version: version,
76
+ resource: id
77
+ };
78
+ });
79
+ });
80
+ _.each(items.schemas, function(type) {
81
+ var href = $state.href('root.type', {version: version, type: type.id});
82
+ index.add({
83
+ path: href,
84
+ titleWords: type.display_name,
85
+ members: extractTypeMembers(type),
86
+ keywords: type.description + ' ' + version
87
+ });
88
+ pathIndex[href] = {
89
+ type: 'schema',
90
+ name: type.display_name,
91
+ id: type.id,
92
+ version: version
93
+ };
94
+ });
95
+
96
+ _.each(items.traits, function(trait, id) {
97
+ var href = $state.href('root.trait', {version: version, trait: id});
98
+ index.add({
99
+ path: href,
100
+ titleWords: id,
101
+ members: extractTypeMembers(trait),
102
+ keywords: trait.description + ' ' + version
103
+ });
104
+ pathIndex[href] = {
105
+ type: 'trait',
106
+ name: id,
107
+ id: id,
108
+ version: version
109
+ };
110
+ });
111
+ });
112
+ }));
113
+ }, 500);
114
+ });
115
+
116
+ // The actual service is a function that takes a query string and
117
+ // returns a promise to the search results
118
+ // (In this case we just resolve the promise immediately as it is not
119
+ // inherently an async process)
120
+ return function(q) {
121
+ return indexReadyPromise.then(function() {
122
+ return index.search(q);
123
+ });
124
+ };
125
+ }
126
+
127
+ return {
128
+ $get:localSearchFactory //window.Worker ? webWorkerSearchFactory : localSearchFactory
129
+ };
130
+ });
131
+
132
+ app.controller('DocsSearchCtrl', function($scope, $location, praxisDocsSearch, pathIndex, $timeout) {
133
+
134
+ function clearResults() {
135
+ $scope.results = [];
136
+ $scope.showResults = false;
137
+ $scope.colClassName = null;
138
+ $scope.hasResults = false;
139
+ }
140
+
141
+ clearResults();
142
+ $scope.focus = false;
143
+
144
+ $scope.search = function(q) {
145
+ var MIN_SEARCH_LENGTH = 2;
146
+ if(q.length >= MIN_SEARCH_LENGTH) {
147
+ praxisDocsSearch(q).then(function(hits) {
148
+ $scope.hasResults = hits.length > 0;
149
+ $scope.results = _.map(_.take(hits, 10), function(hit) {
150
+ var result = pathIndex[hit.ref];
151
+ result.path = hit.ref;
152
+ return result;
153
+ });
154
+ });
155
+ } else {
156
+ clearResults();
157
+ }
158
+ if(!$scope.$$phase) $scope.$apply();
159
+ };
160
+
161
+ $scope.submit = function() {
162
+ var result;
163
+ for(var i in $scope.results) {
164
+ result = $scope.results[i];
165
+ if(result) {
166
+ break;
167
+ }
168
+ }
169
+ if(result) {
170
+ $location.path(result.path);
171
+ $scope.hideResults();
172
+ }
173
+ };
174
+
175
+ $scope.hideResults = function() {
176
+ clearResults();
177
+ $scope.q = '';
178
+ };
179
+
180
+ $scope.goToResult = function(result) {
181
+ var str = $scope.q;
182
+ $scope.hideResults();
183
+ $timeout(function() {
184
+ if (window.find) { // Firefox, Google Chrome, Safari
185
+ // if some content is selected, the start position of the search
186
+ // will be the end position of the selection
187
+ window.find(str, false, false, true);
188
+ } else {
189
+ if (document.selection && document.selection.createRange) { // Internet Explorer, Opera before version 10.5
190
+ var textRange = document.selection.createRange ();
191
+ if (textRange.findText) { // Internet Explorer
192
+ // if some content is selected, the start position of the search
193
+ // will be the position after the start position of the selection
194
+ if (textRange.text.length > 0) {
195
+ textRange.collapse(true);
196
+ textRange.mov("character", 1);
197
+ }
198
+ if (textRange.findText(str)) {
199
+ textRange.select();
200
+ }
201
+ }
202
+ }
203
+ }
204
+ }, 200);
205
+ };
206
+
207
+ $scope.$watch('focus && hasResults', function(val) {
208
+ $scope.showResults = val;
209
+ });
210
+ });
211
+
212
+ app.directive('searchForm', function() {
213
+ return {
214
+ restrict: 'E',
215
+ controller: 'DocsSearchCtrl',
216
+ templateUrl: 'views/search_form.html'
217
+ };
218
+ });
@@ -0,0 +1,10 @@
1
+ <div class="header">
2
+ <div class="navbar navbar-default navbar-fixed-top" role="navigation">
3
+ <div class="container">
4
+ <div class="navbar-header">
5
+ <a class="navbar-brand" href="#/">{{:: title}}</a>
6
+ </div>
7
+ <search-form class="navbar-form navbar-right"></search-form>
8
+ </div>
9
+ </div>
10
+ </div>
@@ -0,0 +1,8 @@
1
+ <form ng-controller="DocsSearchCtrl" class="praxis-docs-search" role="search" ng-class="{focus:focus}" ng-submit="submit()">
2
+ <div class="form-group">
3
+ <input type="search" class="form-control" placeholder="Search" ng-focus="focus=true"
4
+ ng-blur="focus=false"
5
+ ng-change="search(q)"
6
+ ng-model="q" autocomplete="off" popover-template="'views/search_results.html'" popover-is-open="showResults" popover-placement="bottom" popover-trigger="none" />
7
+ </div>
8
+ </form>
@@ -0,0 +1,5 @@
1
+ <div class="list-group">
2
+ <a ng-repeat="result in results" href="{{result.path}}" class="list-group-item" ng-click="goToResult(result)">
3
+ {{result.name}} <small class="result-type">{{result.type}}</small>
4
+ </a>
5
+ </div>
@@ -0,0 +1,12 @@
1
+ module Praxis
2
+ class DocsSearch < ::Praxis::Plugin
3
+ include Singleton
4
+ def setup!
5
+ register_doc_browser_plugin File.join(File.dirname(__FILE__), 'api_browser')
6
+ end
7
+
8
+ def config_key
9
+ :docs_search
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,3 @@
1
+ module PraxisDocsSearch
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,26 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+
4
+ require 'praxis-docs-search/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "praxis-docs-search"
8
+ spec.version = PraxisDocsSearch::VERSION
9
+ spec.authors = ["Jakub Hampl"]
10
+ spec.summary = %q{Doc Browser Search plugin for Praxis.}
11
+ spec.email = ["jakub.hampl@rightscale.com"]
12
+
13
+ spec.homepage = "https://github.com/rightscale/praxis-docs-search"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = ">=2.1"
16
+
17
+ spec.require_paths = ["lib"]
18
+ spec.files = `git ls-files -z`.split("\x0")
19
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
+
21
+ spec.add_runtime_dependency 'praxis', [">= 0.18"]
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.6"
24
+ spec.add_development_dependency "rake", "~> 0"
25
+
26
+ end
@@ -0,0 +1,36 @@
1
+ # Praxis Docs Search
2
+
3
+ This plugin for Praxis adds the capability to search the doc browser.
4
+
5
+ ![Demo](demo.gif)
6
+
7
+ ## Usage
8
+
9
+ Add this to your gemfile:
10
+
11
+ ```ruby
12
+ gem 'praxis-docs-search'
13
+ ```
14
+
15
+ In your `config.ru` add it to the Praxis Bootloader:
16
+
17
+ ```ruby
18
+ application.bootloader.use Praxis::DocsSearch
19
+ ```
20
+
21
+ Finally if you would like to use the default styling, add this to your `docs/styles.scss`:
22
+
23
+ ```scss
24
+ @import "praxis-docs-search.scss";
25
+ ```
26
+
27
+ If you are using your own navbar, you can include the search form anywhere by using
28
+ the `<search-form></search-form>` element in a custom template.
29
+
30
+ ## Authors
31
+
32
+ Jakub Hampl @gampleman
33
+
34
+ MIT LICENSE
35
+
36
+ (c) RightScale, Inc. 2015
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: praxis-docs-search
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jakub Hampl
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-01-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: praxis
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0.18'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0.18'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.6'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.6'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description:
56
+ email:
57
+ - jakub.hampl@rightscale.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - Gemfile
64
+ - demo.gif
65
+ - lib/api_browser/bower.json
66
+ - lib/api_browser/praxis-docs-search.scss
67
+ - lib/api_browser/search.js
68
+ - lib/api_browser/views/navbar.html
69
+ - lib/api_browser/views/search_form.html
70
+ - lib/api_browser/views/search_results.html
71
+ - lib/praxis-docs-search.rb
72
+ - lib/praxis-docs-search/version.rb
73
+ - praxis-docs-search.gemspec
74
+ - readme.md
75
+ homepage: https://github.com/rightscale/praxis-docs-search
76
+ licenses:
77
+ - MIT
78
+ metadata: {}
79
+ post_install_message:
80
+ rdoc_options: []
81
+ require_paths:
82
+ - lib
83
+ required_ruby_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '2.1'
88
+ required_rubygems_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ requirements: []
94
+ rubyforge_project:
95
+ rubygems_version: 2.2.2
96
+ signing_key:
97
+ specification_version: 4
98
+ summary: Doc Browser Search plugin for Praxis.
99
+ test_files: []
100
+ has_rdoc: