mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-23 02:53:29 +00:00
Merge pull request #40 from GitbookIO/feature/searchclient
Feature/searchclient
This commit is contained in:
+6
-2
@@ -28,14 +28,15 @@ module.exports = function (grunt) {
|
||||
baseUrl: "theme/javascript/",
|
||||
out: "theme/assets/app.js",
|
||||
preserveLicenseComments: false,
|
||||
optimize: "uglify",
|
||||
optimize: "none", //"uglify",
|
||||
include: ["requireLib"],
|
||||
paths: {
|
||||
"jQuery": 'vendors/jquery',
|
||||
"lodash": 'vendors/lodash',
|
||||
"requireLib": 'vendors/require',
|
||||
"Mousetrap": 'vendors/mousetrap',
|
||||
"mixpanel": 'vendors/mixpanel'
|
||||
"mixpanel": 'vendors/mixpanel',
|
||||
"lunr": path.join(__dirname, "node_modules/lunr/lunr")
|
||||
},
|
||||
shim: {
|
||||
'jQuery': {
|
||||
@@ -49,6 +50,9 @@ module.exports = function (grunt) {
|
||||
},
|
||||
'mixpanel': {
|
||||
exports: 'mixpanel'
|
||||
},
|
||||
'lunr': {
|
||||
exports: 'lunr'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ var Generator = function() {
|
||||
// Attach methods to instance
|
||||
_.bindAll(this);
|
||||
|
||||
this.revision = Date.now();
|
||||
this.indexer = indexer();
|
||||
|
||||
// Load base template
|
||||
@@ -41,6 +42,8 @@ Generator.prototype._writeTemplate = function(tpl, options, output) {
|
||||
return Q()
|
||||
.then(function(sections) {
|
||||
return tpl(_.extend({
|
||||
revision: that.revision,
|
||||
|
||||
title: that.options.title,
|
||||
description: that.options.description,
|
||||
|
||||
|
||||
+21415
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -8,10 +8,10 @@ require([
|
||||
"core/keyboard",
|
||||
"core/exercise",
|
||||
"core/progress",
|
||||
"core/sidebar"
|
||||
], function($, storage, analytic, sharing, _state, keyboard, exercise, progress, sidebar){
|
||||
"core/sidebar",
|
||||
"core/search"
|
||||
], function($, storage, analytic, sharing, state, keyboard, exercise, progress, sidebar, search){
|
||||
$(document).ready(function() {
|
||||
var state = _state();
|
||||
var $book = state.$book;
|
||||
|
||||
// Initialize storage
|
||||
@@ -20,6 +20,9 @@ require([
|
||||
// Init sidebar
|
||||
sidebar.init();
|
||||
|
||||
// Load search
|
||||
search.init();
|
||||
|
||||
// Init keyboard
|
||||
keyboard.init();
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ define([
|
||||
|
||||
// Prepare all exercise
|
||||
var init = function() {
|
||||
state().$book.find("section.exercise").each(function() {
|
||||
state.$book.find("section.exercise").each(function() {
|
||||
prepareExercise($(this));
|
||||
});
|
||||
};
|
||||
|
||||
@@ -2,8 +2,9 @@ define([
|
||||
"jQuery",
|
||||
"Mousetrap",
|
||||
"core/navigation",
|
||||
"core/sidebar"
|
||||
], function($, Mousetrap, navigation, sidebar){
|
||||
"core/sidebar",
|
||||
"core/search"
|
||||
], function($, Mousetrap, navigation, sidebar, search){
|
||||
// Bind keyboard shortcuts
|
||||
var init = function() {
|
||||
// Next
|
||||
@@ -23,9 +24,16 @@ define([
|
||||
sidebar.toggle();
|
||||
return false;
|
||||
});
|
||||
|
||||
// Toggle Search
|
||||
Mousetrap.bind(['f'], function(e) {
|
||||
search.toggle();
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
init: init
|
||||
init: init,
|
||||
search: search
|
||||
};
|
||||
});
|
||||
@@ -6,7 +6,7 @@ define([
|
||||
], function(_, $, storage, state) {
|
||||
// Get current level
|
||||
var getCurrentLevel = function() {
|
||||
return state().level;
|
||||
return state.level;
|
||||
};
|
||||
|
||||
// Return all levels
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
define([
|
||||
"jQuery",
|
||||
"lodash",
|
||||
"lunr",
|
||||
"utils/storage",
|
||||
"core/state",
|
||||
"core/sidebar"
|
||||
], function($, _, lunr, storage, state, sidebar) {
|
||||
var index = null;
|
||||
var $searchBar = state.$book.find(".book-search");
|
||||
var $searchInput = $searchBar.find("input");
|
||||
|
||||
// Use a specific idnex
|
||||
var useIndex = function(data) {
|
||||
index = lunr.Index.load(data);
|
||||
};
|
||||
|
||||
// Load complete index
|
||||
var loadIndex = function() {
|
||||
var cacheKey = state.revision+":"+"searchIndex";
|
||||
var cache = storage.get(cacheKey);
|
||||
|
||||
if (cache) return useIndex(cache);
|
||||
|
||||
$.getJSON(state.basePath+"/search_index.json")
|
||||
.then(function(index) {
|
||||
storage.set(cacheKey, index);
|
||||
return index;
|
||||
})
|
||||
.then(useIndex);
|
||||
};
|
||||
|
||||
// Search for a term
|
||||
var search = function(q) {
|
||||
if (!index) return;
|
||||
var results = _.chain(index.search(q))
|
||||
.map(function(result) {
|
||||
var parts = result.ref.split("#")
|
||||
return {
|
||||
path: parts[0],
|
||||
hash: parts[1]
|
||||
}
|
||||
})
|
||||
.value();
|
||||
|
||||
return results;
|
||||
};
|
||||
|
||||
// Toggle search bar
|
||||
var toggleSearch = function(_state) {
|
||||
if (state != null && isSearchOpen() == _state) return;
|
||||
|
||||
state.$book.toggleClass("with-search", _state);
|
||||
|
||||
// If search bar is open: focus input
|
||||
if (isSearchOpen()) {
|
||||
sidebar.toggle(true);
|
||||
$searchInput.focus();
|
||||
} else {
|
||||
$searchInput.blur();
|
||||
$searchInput.val("");
|
||||
sidebar.filter(null);
|
||||
}
|
||||
};
|
||||
|
||||
// Return true if search bar is open
|
||||
var isSearchOpen = function() {
|
||||
return state.$book.hasClass("with-search");
|
||||
};
|
||||
|
||||
|
||||
var init = function() {
|
||||
loadIndex();
|
||||
|
||||
// Toggle search
|
||||
state.$book.find(".book-header .toggle-search").click(function(e) {
|
||||
e.preventDefault();
|
||||
toggleSearch();
|
||||
});
|
||||
|
||||
$searchInput.keyup(function(e) {
|
||||
var key = (e.keyCode ? e.keyCode : e.which);
|
||||
var q = $(this).val();
|
||||
|
||||
if (key == 27) {
|
||||
e.preventDefault();
|
||||
toggleSearch(false);
|
||||
return;
|
||||
}
|
||||
if (q.length == 0) {
|
||||
sidebar.filter(null);
|
||||
} else {
|
||||
var results = search(q);
|
||||
sidebar.filter(
|
||||
_.pluck(results, "path")
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
init: init,
|
||||
search: search,
|
||||
toggle: toggleSearch
|
||||
};
|
||||
});
|
||||
@@ -1,32 +1,31 @@
|
||||
define([
|
||||
"lodash",
|
||||
"utils/storage",
|
||||
"utils/platform",
|
||||
"core/state"
|
||||
], function(storage, platform, state) {
|
||||
], function(_, storage, platform, state) {
|
||||
var $summary = state.$book.find(".book-summary");
|
||||
|
||||
// Toggle sidebar with or withour animation
|
||||
var toggleSidebar = function(_state, animation) {
|
||||
if (-state != null && isOpen() == _state) return;
|
||||
if (state != null && isOpen() == _state) return;
|
||||
if (animation == null) animation = true;
|
||||
|
||||
var $book = state().$book;
|
||||
$book.toggleClass("without-animation", !animation);
|
||||
$book.toggleClass("with-summary", _state);
|
||||
state.$book.toggleClass("without-animation", !animation);
|
||||
state.$book.toggleClass("with-summary", _state);
|
||||
|
||||
storage.set("sidebar", isOpen());
|
||||
};
|
||||
|
||||
// Return true if sidebar is open
|
||||
var isOpen = function() {
|
||||
return state().$book.hasClass("with-summary");
|
||||
return state.$book.hasClass("with-summary");
|
||||
};
|
||||
|
||||
// Prepare sidebar: state and toggle button
|
||||
var init = function() {
|
||||
var $book = state().$book;
|
||||
|
||||
// Toggle summary
|
||||
$book.find(".book-header .toggle-summary").click(function(e) {
|
||||
state.$book.find(".book-header .toggle-summary").click(function(e) {
|
||||
e.preventDefault();
|
||||
toggleSidebar();
|
||||
});
|
||||
@@ -37,8 +36,21 @@ define([
|
||||
}
|
||||
};
|
||||
|
||||
// Filter summary with a list of path
|
||||
var filterSummary = function(paths) {
|
||||
$summary.find("li").each(function() {
|
||||
var path = $(this).data("path");
|
||||
var st = paths == null || _.contains(paths, path);
|
||||
|
||||
$(this).toggle(st);
|
||||
if (st) $(this).parents("li").show();
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
$el: $summary,
|
||||
init: init,
|
||||
toggle: toggleSidebar
|
||||
toggle: toggleSidebar,
|
||||
filter: filterSummary
|
||||
}
|
||||
});
|
||||
@@ -1,14 +1,14 @@
|
||||
define([
|
||||
"jQuery"
|
||||
], function() {
|
||||
return function() {
|
||||
var $book = $(".book");
|
||||
var $book = $(".book");
|
||||
|
||||
return {
|
||||
'$book': $book,
|
||||
return {
|
||||
'$book': $book,
|
||||
|
||||
'githubId': $book.data("github"),
|
||||
'level': $book.data("level")
|
||||
};
|
||||
'githubId': $book.data("github"),
|
||||
'level': $book.data("level"),
|
||||
'basePath': $book.data("basepath"),
|
||||
'revision': $book.data("revision")
|
||||
};
|
||||
});
|
||||
@@ -1,4 +1,6 @@
|
||||
.book {
|
||||
@searchHeight: 41px;
|
||||
|
||||
.book-summary {
|
||||
@width: 250px;
|
||||
|
||||
@@ -8,19 +10,49 @@
|
||||
bottom: 0px;
|
||||
z-index: 1;
|
||||
|
||||
overflow-y: auto;
|
||||
|
||||
width: @width;
|
||||
background: #2e3133;
|
||||
color: #e2edf2;
|
||||
|
||||
.transition(all 0.5s ease);
|
||||
|
||||
.book-search {
|
||||
padding: 6px;
|
||||
padding-top: 0px;
|
||||
|
||||
background: #242628;
|
||||
border-bottom: 1px solid #333;
|
||||
|
||||
position: absolute;
|
||||
top: -@searchHeight;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
|
||||
.transition(top 0.5s ease);
|
||||
|
||||
|
||||
input, input:focus {
|
||||
width: 100%;
|
||||
border: none;
|
||||
.box-shadow(none);
|
||||
}
|
||||
}
|
||||
|
||||
ul.summary {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
|
||||
overflow-y: auto;
|
||||
|
||||
list-style: none;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
|
||||
.transition(top 0.5s ease);
|
||||
|
||||
li {
|
||||
list-style: none;
|
||||
|
||||
@@ -96,4 +128,16 @@
|
||||
.transition(none) !important;
|
||||
}
|
||||
}
|
||||
|
||||
&.with-search {
|
||||
.book-summary {
|
||||
.book-search {
|
||||
top: 0px;
|
||||
}
|
||||
|
||||
ul.summary {
|
||||
top: @searchHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<!-- Actions Left -->
|
||||
<a href="{{ githubHost }}{{ githubId }}" target="_blank" class="btn pull-left"><i class="fa fa-github-alt"></i></a>
|
||||
<a href="#" class="btn pull-left toggle-summary"><i class="fa fa-align-justify"></i></a>
|
||||
<a href="#" class="btn pull-left toggle-search"><i class="fa fa-search"></i></a>
|
||||
|
||||
<!-- Actions Right -->
|
||||
<a href="#" target="_blank" class="btn pull-right" data-sharing="google-plus"><i class="fa fa-google-plus"></i></a>
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
<div class="book-summary">
|
||||
<div class="book-search">
|
||||
<input type="text" placeholder="Search" class="form-control" />
|
||||
</div>
|
||||
<ul class="summary">
|
||||
<li>
|
||||
<a href="{{ githubHost }}{{ githubAuthor }}" target="blank">About the author</a>
|
||||
@@ -10,11 +13,11 @@
|
||||
<a href="{{ githubHost }}{{ githubId }}/edit/master/{{ _input }}" target="blank">Edit and Contribute</a>
|
||||
</li>
|
||||
<li class="divider"></li>
|
||||
<li data-level="0">
|
||||
<li data-level="0" data-path="index.html">
|
||||
<a href="{{ basePath }}/"><i class="fa fa-check"></i> Introduction</a>
|
||||
</li>
|
||||
{% for item in summary.chapters %}
|
||||
<li {% if item._path == _input %}class="active"{% endif %} data-level="{{ item.level }}">
|
||||
<li {% if item._path == _input %}class="active"{% endif %} data-level="{{ item.level }}" {% if item.path %}data-path="{{ item.path|mdLink }}"{% endif %}>
|
||||
{% if item.path %}
|
||||
<a href="{{ basePath }}/{{ item.path|mdLink }}">
|
||||
<i class="fa fa-check"></i> <b>{{ item.level }})</b> {{ item.title }}
|
||||
@@ -25,7 +28,7 @@
|
||||
{% if item.articles.length > 0 %}
|
||||
<ul class="articles">
|
||||
{% for article in item.articles %}
|
||||
<li {% if article._path == _input %}class="active"{% endif %} data-level="{{ article.level }}">
|
||||
<li {% if article._path == _input %}class="active"{% endif %} data-level="{{ article.level }}" {% if article.path %}data-path="{{ article.path|mdLink }}"{% endif %}>
|
||||
{% if article.path %}
|
||||
<a href="{{ basePath }}/{{ article.path|mdLink }}">
|
||||
<i class="fa fa-check"></i> <b>{{ article.level }})</b> {{ article.title }}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
{% block title %}{{ progress.current.title }}{% parent %}{% endblock %}
|
||||
{% block content %}
|
||||
<div class="book" data-github="{{ githubId }}" data-level="{{ progress.current.level }}">
|
||||
<div class="book" data-github="{{ githubId }}" data-level="{{ progress.current.level }}" data-basepath="{{ basePath }}" data-revision="{{ revision }}">
|
||||
{% include "includes/book/header.html" %}
|
||||
{% include "includes/book/summary.html" %}
|
||||
<div class="book-body" tabindex="-1">
|
||||
|
||||
Reference in New Issue
Block a user