diff --git a/sphinx/builder.py b/sphinx/builder.py index e98594075..c478873fd 100644 --- a/sphinx/builder.py +++ b/sphinx/builder.py @@ -34,6 +34,7 @@ from sphinx.latexwriter import LaTeXWriter from sphinx.environment import BuildEnvironment, NoUri from sphinx.highlighting import PygmentsBridge from sphinx.util.console import bold, purple, darkgreen +from sphinx.search import js_index # side effect: registers roles and directives from sphinx import roles @@ -338,10 +339,10 @@ class StandaloneHTMLBuilder(Builder): name = 'html' copysource = True out_suffix = '.html' - indexer_format = json + indexer_format = js_index supported_image_types = ['image/svg+xml', 'image/png', 'image/gif', 'image/jpeg'] - searchindex_filename = 'searchindex.json' + searchindex_filename = 'searchindex.js' add_header_links = True add_definition_links = True diff --git a/sphinx/search.py b/sphinx/search.py index a9a6ee597..342a253ac 100644 --- a/sphinx/search.py +++ b/sphinx/search.py @@ -10,6 +10,7 @@ """ import re import cPickle as pickle +from cStringIO import StringIO from docutils.nodes import Text, NodeVisitor @@ -20,6 +21,37 @@ from sphinx.util import json word_re = re.compile(r'\w+(?u)') +class _JavaScriptIndex(object): + """ + The search index as javascript file that calls a function + on the documentation search object to register the index. + This serializing system does not support chaining because + simplejson (which it depends on) doesn't support it either. + """ + + PREFIX = 'Search.setIndex(' + SUFFIX = ')' + + def dumps(self, data): + return self.PREFIX + json.dumps(data) + self.SUFFIX + + def loads(self, s): + data = s[len(self.PREFIX):-len(self.SUFFIX)] + if not data or not s.startswith(self.PREFIX) or not \ + s.endswith(self.SUFFIX): + raise ValueError('invalid data') + return json.loads(data) + + def dump(self, data, f): + f.write(self.dumps(data)) + + def load(self, f): + return self.loads(f.read()) + + +js_index = _JavaScriptIndex() + + class Stemmer(PorterStemmer): """ All those porter stemmer implementations look hideous. diff --git a/sphinx/static/doctools.js b/sphinx/static/doctools.js index ae0c3f461..be4bdc889 100644 --- a/sphinx/static/doctools.js +++ b/sphinx/static/doctools.js @@ -94,11 +94,9 @@ jQuery.fn.highlightText = function(text, className) { var Documentation = { init : function() { - /* this.addContextElements(); -- now done statically */ this.fixFirefoxAnchorBug(); this.highlightSearchWords(); this.initModIndex(); - this.initComments(); }, /** @@ -108,6 +106,8 @@ var Documentation = { PLURAL_EXPR : function(n) { return n == 1 ? 0 : 1; }, LOCALE : 'unknown', + // gettext and ngettext don't access this so that the functions + // can savely bound to a different name (_ = Documentation.gettext) gettext : function(string) { var translated = Documentation.TRANSLATIONS[string]; if (typeof translated == 'undefined') @@ -133,14 +133,12 @@ var Documentation = { * add context elements like header anchor links */ addContextElements : function() { - for (var i = 1; i <= 6; i++) { - $('h' + i + '[@id]').each(function() { - $('\u00B6'). - attr('href', '#' + this.id). - attr('title', _('Permalink to this headline')). - appendTo(this); - }); - } + $('div[@id] > :header:first').each(function() { + $('\u00B6'). + attr('href', '#' + this.id). + attr('title', _('Permalink to this headline')). + appendTo(this); + }); $('dt[@id]').each(function() { $('\u00B6'). attr('href', '#' + this.id). @@ -196,37 +194,6 @@ var Documentation = { } }, - /** - * init the inline comments - */ - initComments : function() { - $('.inlinecomments div.actions').each(function() { - this.innerHTML += ' | '; - $(this).append($('hide comments').click(function() { - $(this).parent().parent().toggle(); - return false; - })); - }); - $('.inlinecomments .comments').hide(); - $('.inlinecomments a.bubble').each(function() { - $(this).click($(this).is('.emptybubble') ? function() { - var params = $.getQueryParameters(this.href); - Documentation.newComment(params.target[0]); - return false; - } : function() { - $('.comments', $(this).parent().parent()[0]).toggle(); - return false; - }); - }); - $('#comments div.actions a.newcomment').click(function() { - Documentation.newComment(); - return false; - }); - if (document.location.hash.match(/^#comment-/)) - $('.inlinecomments .comments ' + document.location.hash) - .parent().toggle(); - }, - /** * helper function to hide the search marks again */ @@ -235,22 +202,6 @@ var Documentation = { $('span.highlight').removeClass('highlight'); }, - /** - * show the comment window for a certain id or the whole page. - */ - newComment : function(id) { - Documentation.CommentWindow.openFor(id || ''); - }, - - /** - * write a new comment from within a comment view box - */ - newCommentFromBox : function(link) { - var params = $.getQueryParameters(link.href); - $(link).parent().parent().fadeOut('slow'); - this.newComment(params.target); - }, - /** * make the url absolute */ @@ -270,108 +221,7 @@ var Documentation = { }); var url = parts.join('/'); return path.substring(url.lastIndexOf('/') + 1, path.length - 1); - }, - - /** - * class that represents the comment window - */ - CommentWindow : (function() { - var openWindows = {}; - - var Window = function(sectionID) { - this.url = Documentation.makeURL('@comments/' + Documentation.getCurrentURL() - + '/?target=' + $.urlencode(sectionID) + '&mode=ajax'); - this.sectionID = sectionID; - - this.root = $('
'); - this.root.appendTo($('body')); - this.title = $('

New Comment

').appendTo(this.root); - this.body = $('
please wait...
').appendTo(this.root); - this.resizeHandle = $('
').appendTo(this.root); - - this.root.Draggable({ - handle: this.title[0] - }); - - this.root.css({ - left: window.innerWidth / 2 - $(this.root).width() / 2, - top: window.scrollY + (window.innerHeight / 2 - 150) - }); - this.root.fadeIn('slow'); - this.updateView(); - }; - - Window.prototype.updateView = function(data) { - var self = this; - function update(data) { - if (data.posted) { - document.location.hash = '#comment-' + data.commentID; - document.location.reload(); - } - else { - self.body.html(data.body); - $('div.actions', self.body).append($('') - .attr('type', 'button') - .attr('value', 'Close') - .click(function() { self.close(); }) - ); - $('div.actions input[@name="preview"]') - .attr('type', 'button') - .click(function() { self.submitForm($('form', self.body)[0], true); }); - $('form', self.body).bind("submit", function() { - self.submitForm(this); - return false; - }); - - if (data.error) { - self.root.Highlight(1000, '#aadee1'); - $('div.error', self.root).slideDown(500); - } - } - } - - if (typeof data == 'undefined') - $.getJSON(this.url, function(json) { update(json); }); - else - $.ajax({ - url: this.url, - type: 'POST', - dataType: 'json', - data: data, - success: function(json) { update(json); } - }); - } - - Window.prototype.getFormValue = function(name) { - return $('*[@name="' + name + '"]', this.body)[0].value; - } - - Window.prototype.submitForm = function(form, previewMode) { - this.updateView({ - author: form.author.value, - author_mail: form.author_mail.value, - title: form.title.value, - comment_body: form.comment_body.value, - preview: previewMode ? 'yes' : '' - }); - } - - Window.prototype.close = function() { - var self = this; - delete openWindows[this.sectionID]; - this.root.fadeOut('slow', function() { - self.root.remove(); - }); - } - - Window.openFor = function(sectionID) { - if (sectionID in openWindows) - return openWindows[sectionID]; - return new Window(sectionID); - } - - return Window; - })() + } }; // quick alias for translations diff --git a/sphinx/static/searchtools.js b/sphinx/static/searchtools.js index 5e60d2bfd..e3781ba2b 100644 --- a/sphinx/static/searchtools.js +++ b/sphinx/static/searchtools.js @@ -224,6 +224,10 @@ var PorterStemmer = function() { */ var Search = { + _index : null, + _queued_query : null, + _pulse_status : -1, + init : function() { var params = $.getQueryParameters(); if (params.q) { @@ -234,33 +238,68 @@ var Search = { }, /** - * perform a search for something + * Sets the index */ - performSearch : function(query) { - // create the required interface elements - var out = $('#search-results'); - var title = $('

' + _('Searching') + '

').appendTo(out); - var dots = $('').appendTo(title); - var status = $('

').appendTo(out); - var output = $('