Skip to content

Commit 4d0208f

Browse files
authored
Merge pull request #365 from canjs/363-web-worker
WIP: Move search into a web worker
2 parents e654e63 + 4417162 commit 4d0208f

File tree

7 files changed

+279
-115
lines changed

7 files changed

+279
-115
lines changed

make-example-search-worker.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
var fs = require("fs");
2+
var path = require("path");
3+
var stealTools = require("steal-tools");
4+
5+
var forceBuild = process.argv.indexOf("-f") !== -1;
6+
7+
var siteConfig = {
8+
debug: false,
9+
dest: path.join(__dirname, 'doc', 'workers'),
10+
minifyBuild: true
11+
};
12+
13+
var dest = siteConfig.dest + '/static/search-worker.js';
14+
15+
fs.mkdir(siteConfig.dest, function() {
16+
stealTools.export({
17+
system: {
18+
config: __dirname + '/package.json!npm',
19+
main: 'static/search-worker'
20+
},
21+
options: {
22+
bundleAssets: true,
23+
bundleSteal: true,
24+
debug: siteConfig.debug ? true : false,
25+
minify: siteConfig.minifyBuild === false ? false : true,
26+
quiet: siteConfig.debug ? false : true
27+
},
28+
outputs: {
29+
"+standalone" : {
30+
dest: dest
31+
}
32+
}
33+
}).then(function(){
34+
// Work around for https://github.com/stealjs/steal-tools/issues/775
35+
console.info('Replacing "window" with "self"');
36+
fs.readFile(dest, 'utf8', function(err, file){
37+
if(err) return console.error(err);
38+
file = file.replace(/window/gmi, 'self');
39+
fs.writeFile(dest, file, function(err){
40+
if(err) return console.error(err);
41+
console.info('Done');
42+
});
43+
});
44+
});
45+
});

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"description": "The plugins to produce the CanJS site",
55
"main": "static/canjs",
66
"scripts": {
7-
"start": "node make-example.js -f",
7+
"start": "node make-example-search-worker.js && node make-example.js -f",
88
"styles": "rm -rf node_modules/bit-docs-generate-html/site/static && npm start",
99
"test": "npm start && npm run testee",
1010
"testee": "testee test/browser.html --browsers firefox",
@@ -35,7 +35,7 @@
3535
"escape-html": "^1.0.3",
3636
"jquery": "^3.1.1",
3737
"lodash": "^4.17.4",
38-
"lunr": "^2.1.0",
38+
"lunr": "bit-docs/lunr.js#279-safari-exception",
3939
"steal-stache": "^3.0.1",
4040
"unescape-html": "^1.0.0"
4141
},

static/loading-bar.js

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,27 @@
1-
function LoadingBar(c){
2-
this.meter = $('<span>', {style: 'width:0%;'});
3-
this.elem = $('<div>', {style: 'display:none', class: 'meter animate '+c}).append(
4-
this.meter.append($('<span>'))
1+
function LoadingBar(color, parent) {
2+
this.meter = $('<span>', {style: 'transition: width 1s ease; width:0%;'});
3+
this.elem = $('<div>', {style: 'display:none', class: 'meter animate ' + color}).append(
4+
this.meter.append($('<span>'))
55
);
6-
$('body').prepend(this.elem);
7-
return this;
6+
7+
parent = parent || $('body');
8+
parent.prepend(this.elem);
9+
10+
return this;
811
}
912

10-
LoadingBar.prototype.start = function(){
11-
this.meter.css('width', '1%');
12-
this.elem.show();
13+
LoadingBar.prototype.start = function(percentage) {
14+
percentage = percentage || 0;
15+
this.meter.css('width', percentage + '%');
16+
this.elem.show();
1317
};
1418

15-
LoadingBar.prototype.end = function(){
16-
this.elem.hide();
19+
LoadingBar.prototype.end = function() {
20+
this.elem.hide();
1721
};
1822

19-
LoadingBar.prototype.update = function(p){
20-
this.meter.css('width', p+'%');
23+
LoadingBar.prototype.update = function(percentage) {
24+
this.meter.css('width', percentage + '%');
2125
};
2226

23-
module.exports = LoadingBar;
27+
module.exports = LoadingBar;

static/search-logic.js

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
var lunr = require("lunr");
2+
var searchEngine;
3+
4+
module.exports = {
5+
indexData: function(items) {
6+
searchEngine = lunr(function() {
7+
lunr.tokenizer.separator = /[\s]+/;
8+
9+
this.pipeline.remove(lunr.stemmer);
10+
this.pipeline.remove(lunr.stopWordFilter);
11+
this.pipeline.remove(lunr.trimmer);
12+
this.searchPipeline.remove(lunr.stemmer);
13+
14+
this.ref('name');
15+
this.field('title');
16+
this.field('description');
17+
this.field('name');
18+
this.field('url');
19+
20+
items.forEach(function(item) {
21+
if (!item.title) {
22+
item.title = item.name;
23+
}
24+
this.add(item);
25+
}.bind(this));
26+
});
27+
return searchEngine;
28+
},
29+
30+
loadIndex: function(index) {
31+
return lunr.Index.load(index);
32+
},
33+
34+
search: function(value) {
35+
var searchTerm = value.toLowerCase();
36+
37+
//run the search
38+
return searchEngine.query(function(q) {
39+
40+
if (searchTerm.indexOf('can-') > -1) {// If the search term includes “can-”
41+
42+
// look for an exact match and apply a large positive boost
43+
q.term(searchTerm, { boost: 375 });
44+
45+
} else {
46+
// add “can-”, look for an exact match in the title field, and apply a positive boost
47+
q.term('can-' + searchTerm, { boost: 12 });
48+
49+
// look for terms that match the beginning or end of this query
50+
// look in the title field specifically to boost matches in it
51+
q.term(searchTerm, { fields: ['title'], wildcard: lunr.Query.wildcard.LEADING | lunr.Query.wildcard.TRAILING });
52+
q.term(searchTerm, { wildcard: lunr.Query.wildcard.LEADING | lunr.Query.wildcard.TRAILING });
53+
}
54+
55+
// look for matches in any of the fields and apply a medium positive boost
56+
var split = searchTerm.split(lunr.tokenizer.separator);
57+
split.forEach(function(term) {
58+
q.term(term, { boost: 10, fields: q.allFields });
59+
q.term(term, { fields: q.allFields, wildcard: lunr.Query.wildcard.TRAILING });
60+
});
61+
});
62+
}
63+
};

static/search-worker.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
var logic = require('static/search-logic');
2+
var searchEngine;
3+
4+
self.addEventListener('message', function(message) {
5+
var data = message.data;
6+
7+
switch (data.name) {
8+
case 'index data':
9+
self.postMessage({
10+
name: 'search engine ready',
11+
searchEngine: logic.indexData(data.items)
12+
});
13+
break;
14+
15+
case 'load index':
16+
self.postMessage({
17+
name: 'search engine ready',
18+
searchEngine: logic(data.index)
19+
});
20+
break;
21+
22+
case 'search':
23+
self.postMessage({
24+
name: 'search results',
25+
results: logic.search(data.query),
26+
query: data.query
27+
});
28+
break;
29+
30+
default:
31+
console.info('Worker received message:', message);
32+
}
33+
}, false);

0 commit comments

Comments
 (0)