diff options
Diffstat (limited to 'nikola')
256 files changed, 3838 insertions, 6433 deletions
diff --git a/nikola/__init__.py b/nikola/__init__.py index 4ab6e34..03d83da 100644 --- a/nikola/__init__.py +++ b/nikola/__init__.py @@ -29,7 +29,7 @@ from __future__ import absolute_import import os -__version__ = "7.6.4" +__version__ = "7.7.3" DEBUG = bool(os.getenv('NIKOLA_DEBUG')) from .nikola import Nikola # NOQA diff --git a/nikola/__main__.py b/nikola/__main__.py index 2aa63f4..a4bd989 100644 --- a/nikola/__main__.py +++ b/nikola/__main__.py @@ -44,6 +44,7 @@ from doit.doit_cmd import DoitMain from doit.cmd_help import Help as DoitHelp from doit.cmd_run import Run as DoitRun from doit.cmd_clean import Clean as DoitClean +from doit.cmd_completion import TabCompletion from doit.cmd_auto import Auto as DoitAuto from logbook import NullHandler from blinker import signal @@ -175,7 +176,6 @@ def main(args=None): class Help(DoitHelp): - """Show Nikola usage.""" @staticmethod @@ -199,7 +199,6 @@ class Help(DoitHelp): class Build(DoitRun): - """Expose "run" command as "build" for backwards compatibility.""" def __init__(self, *args, **kw): @@ -238,7 +237,6 @@ class Build(DoitRun): class Clean(DoitClean): - """Clean site, including the cache directory.""" def clean_tasks(self, tasks, dryrun): @@ -255,7 +253,6 @@ DoitAuto.name = 'doit_auto' class NikolaTaskLoader(TaskLoader): - """Nikola-specific task loader.""" def __init__(self, nikola, quiet=False): @@ -288,7 +285,6 @@ class NikolaTaskLoader(TaskLoader): class DoitNikola(DoitMain): - """Nikola-specific implementation of DoitMain.""" # overwite help command @@ -359,7 +355,7 @@ class DoitNikola(DoitMain): LOGGER.info('Did you mean "{}" or "{}"?'.format('", "'.join(best_sugg[:-1]), best_sugg[-1])) return 3 - if sub_cmds[args[0]] is not Help and not isinstance(sub_cmds[args[0]], Command): # Is a doit command + if not sub_cmds[args[0]] in (Help, TabCompletion) and not isinstance(sub_cmds[args[0]], Command): if not self.nikola.configured: LOGGER.error("This command needs to run inside an " "existing Nikola site.") @@ -399,5 +395,6 @@ def levenshtein(s1, s2): return previous_row[-1] + if __name__ == "__main__": sys.exit(main(sys.argv[1:])) diff --git a/nikola/conf.py.in b/nikola/conf.py.in index b7a88f7..7ef6927 100644 --- a/nikola/conf.py.in +++ b/nikola/conf.py.in @@ -68,8 +68,8 @@ TRANSLATIONS_PATTERN = ${TRANSLATIONS_PATTERN} # For submenus: # ( # ( -# ('http://apple.com/', 'Apple'), -# ('http://orange.com/', 'Orange'), +# ('https://apple.com/', 'Apple'), +# ('https://orange.com/', 'Orange'), # ), # 'Fruits' # ) @@ -89,6 +89,39 @@ NAVIGATION_LINKS = ${NAVIGATION_LINKS} # Name of the theme to use. THEME = ${THEME} +# Primary color of your theme. This will be used to customize your theme and +# auto-generate related colors in POSTS_SECTION_COLORS. Must be a HEX value. +THEME_COLOR = '#5670d4' + +# POSTS and PAGES contains (wildcard, destination, template) tuples. +# +# The wildcard is used to generate a list of reSt source files +# (whatever/thing.txt). +# +# That fragment could have an associated metadata file (whatever/thing.meta), +# and optionally translated files (example for Spanish, with code "es"): +# whatever/thing.es.txt and whatever/thing.es.meta +# +# This assumes you use the default TRANSLATIONS_PATTERN. +# +# From those files, a set of HTML fragment files will be generated: +# cache/whatever/thing.html (and maybe cache/whatever/thing.html.es) +# +# These files are combined with the template to produce rendered +# pages, which will be placed at +# output / TRANSLATIONS[lang] / destination / pagename.html +# +# where "pagename" is the "slug" specified in the metadata file. +# +# The difference between POSTS and PAGES is that POSTS are added +# to feeds and are considered part of a blog, while PAGES are +# just independent HTML pages. +# + +POSTS = ${POSTS} +PAGES = ${PAGES} + + ############################################## # Below this point, everything is optional ############################################## @@ -96,7 +129,7 @@ THEME = ${THEME} # Post's dates are considered in UTC by default, if you want to use # another time zone, please set TIMEZONE to match. Check the available # list from Wikipedia: -# http://en.wikipedia.org/wiki/List_of_tz_database_time_zones +# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones # (e.g. 'Europe/Zurich') # Also, if you want to use a different time zone in some of your posts, # you can use the ISO 8601/RFC 3339 format (ex. 2012-03-30T23:00:00+02:00) @@ -107,11 +140,11 @@ TIMEZONE = ${TIMEZONE} # Note that this does not affect DATE_FORMAT. # FORCE_ISO8601 = False -# Date format used to display post dates. +# Date format used to display post dates. (translatable) # (str used by datetime.datetime.strftime) # DATE_FORMAT = '%Y-%m-%d %H:%M' -# Date format used to display post dates, if local dates are used. +# Date format used to display post dates, if local dates are used. (translatable) # (str used by moment.js) # JS_DATE_FORMAT = 'YYYY-MM-DD HH:mm' @@ -135,34 +168,6 @@ TIMEZONE = ${TIMEZONE} # LOCALE_DEFAULT = locale to use for languages not mentioned in LOCALES; if # not set the default Nikola mapping is used. -# POSTS and PAGES contains (wildcard, destination, template) tuples. -# -# The wildcard is used to generate a list of reSt source files -# (whatever/thing.txt). -# -# That fragment could have an associated metadata file (whatever/thing.meta), -# and optionally translated files (example for Spanish, with code "es"): -# whatever/thing.es.txt and whatever/thing.es.meta -# -# This assumes you use the default TRANSLATIONS_PATTERN. -# -# From those files, a set of HTML fragment files will be generated: -# cache/whatever/thing.html (and maybe cache/whatever/thing.html.es) -# -# These files are combined with the template to produce rendered -# pages, which will be placed at -# output / TRANSLATIONS[lang] / destination / pagename.html -# -# where "pagename" is the "slug" specified in the metadata file. -# -# The difference between POSTS and PAGES is that POSTS are added -# to feeds and are considered part of a blog, while PAGES are -# just independent HTML pages. -# - -POSTS = ${POSTS} -PAGES = ${PAGES} - # One or more folders containing files to be copied as-is into the output. # The format is a dictionary of {source: relative destination}. # Default is: @@ -208,6 +213,57 @@ COMPILERS = ${COMPILERS} # Warning: this option will change its default value to False in v8! WRITE_TAG_CLOUD = True +# Generate pages for each section. The site must have at least two sections +# for this option to take effect. It wouldn't build for just one section. +POSTS_SECTIONS = True + +# Setting this to False generates a list page instead of an index. Indexes +# are the default and will apply GENERATE_ATOM if set. +# POSTS_SECTIONS_ARE_INDEXES = True + +# Each post and section page will have an associated color that can be used +# to style them with a recognizable color detail across your site. A color +# is assigned to each section based on shifting the hue of your THEME_COLOR +# at least 7.5 % while leaving the lightness and saturation untouched in the +# HUSL colorspace. You can overwrite colors by assigning them colors in HEX. +# POSTS_SECTION_COLORS = { +# DEFAULT_LANG: { +# 'posts': '#49b11bf', +# 'reviews': '#ffe200', +# }, +# } + +# Associate a description with a section. For use in meta description on +# section index pages or elsewhere in themes. +# POSTS_SECTION_DESCRIPTIONS = { +# DEFAULT_LANG: { +# 'how-to': 'Learn how-to things properly with these amazing tutorials.', +# }, +# } + +# Sections are determined by their output directory as set in POSTS by default, +# but can alternatively be determined from file metadata instead. +# POSTS_SECTION_FROM_META = False + +# Names are determined from the output directory name automatically or the +# metadata label. Unless overwritten below, names will use title cased and +# hyphens replaced by spaces. +# POSTS_SECTION_NAME = { +# DEFAULT_LANG: { +# 'posts': 'Blog Posts', +# 'uncategorized': 'Odds and Ends', +# }, +# } + +# Titles for per-section index pages. Can be either one string where "{name}" +# is substituted or the POSTS_SECTION_NAME, or a dict of sections. Note +# that the INDEX_PAGES option is also applied to section page titles. +# POSTS_SECTION_TITLE = { +# DEFAULT_LANG: { +# 'how-to': 'How-to and Tutorials', +# }, +# } + # Paths for different autogenerated bits. These are combined with the # translation paths. @@ -215,8 +271,14 @@ WRITE_TAG_CLOUD = True # output / TRANSLATION[lang] / TAG_PATH / index.html (list of tags) # output / TRANSLATION[lang] / TAG_PATH / tag.html (list of posts for a tag) # output / TRANSLATION[lang] / TAG_PATH / tag.xml (RSS feed for a tag) + # (translatable) # TAG_PATH = "categories" +# See TAG_PATH's "list of tags" for the default setting value. Can be overwritten +# here any path relative to the output directory. + # (translatable) +# TAGS_INDEX_PATH = "tags.html" + # If TAG_PAGES_ARE_INDEXES is set to True, each tag's page will contain # the posts themselves. If set to False, it will be just a list of links. # TAG_PAGES_ARE_INDEXES = False @@ -231,6 +293,13 @@ WRITE_TAG_CLOUD = True # }, # } +# Set special titles for tag pages. The default is "Posts about TAG". +# TAG_PAGES_TITLES = { +# DEFAULT_LANG: { +# "blogging": "Meta-posts about blogging", +# "open source": "Posts about open source software" +# }, +# } # If you do not want to display a tag publicly, you can mark it as hidden. # The tag will not be displayed on the tag list page, the tag cloud and posts. @@ -247,6 +316,7 @@ HIDDEN_TAGS = ['mathjax'] # output / TRANSLATION[lang] / CATEGORY_PATH / index.html (list of categories) # output / TRANSLATION[lang] / CATEGORY_PATH / CATEGORY_PREFIX category.html (list of posts for a category) # output / TRANSLATION[lang] / CATEGORY_PATH / CATEGORY_PREFIX category.xml (RSS feed for a category) +# (translatable) # CATEGORY_PATH = "categories" # CATEGORY_PREFIX = "cat_" @@ -274,11 +344,49 @@ CATEGORY_OUTPUT_FLAT_HIERARCHY = ${CATEGORY_OUTPUT_FLAT_HIERARCHY} # }, # } +# Set special titles for category pages. The default is "Posts about CATEGORY". +# CATEGORY_PAGES_TITLES = { +# DEFAULT_LANG: { +# "blogging": "Meta-posts about blogging", +# "open source": "Posts about open source software" +# }, +# } + # If you do not want to display a category publicly, you can mark it as hidden. # The category will not be displayed on the category list page. # Category pages will still be generated. HIDDEN_CATEGORIES = [] +# If ENABLE_AUTHOR_PAGES is set to True and there is more than one +# author, author pages are generated. +# ENABLE_AUTHOR_PAGES = True + +# Final locations are: +# output / TRANSLATION[lang] / AUTHOR_PATH / index.html (list of tags) +# output / TRANSLATION[lang] / AUTHOR_PATH / author.html (list of posts for a tag) +# output / TRANSLATION[lang] / AUTHOR_PATH / author.xml (RSS feed for a tag) +# AUTHOR_PATH = "authors" + +# If AUTHOR_PAGES_ARE_INDEXES is set to True, each author's page will contain +# the posts themselves. If set to False, it will be just a list of links. +# AUTHOR_PAGES_ARE_INDEXES = False + +# Set descriptions for author pages to make them more interesting. The +# default is no description. The value is used in the meta description +# and displayed underneath the author list or index page’s title. +# AUTHOR_PAGES_DESCRIPTIONS = { +# DEFAULT_LANG: { +# "Juanjo Conti": "Python coder and writer.", +# "Roberto Alsina": "Nikola father." +# }, +# } + + +# If you do not want to display an author publicly, you can mark it as hidden. +# The author will not be displayed on the author list page and posts. +# Tag pages will still be generated. +HIDDEN_AUTHORS = ['Guest'] + # Final location for the main blog page and sibling paginated pages is # output / TRANSLATION[lang] / INDEX_PATH / index-*.html # INDEX_PATH = "" @@ -311,17 +419,25 @@ HIDDEN_CATEGORIES = [] # absolute: a complete URL (that includes the SITE_URL) # URL_TYPE = 'rel_path' +# If USE_BASE_TAG is True, then all HTML files will include +# something like <base href=http://foo.var.com/baz/bat> to help +# the browser resolve relative links. +# In some rare cases, this will be a problem, and you can +# disable it by setting USE_BASE_TAG to False. +# USE_BASE_TAG = True + # Final location for the blog main RSS feed is: # output / TRANSLATION[lang] / RSS_PATH / rss.xml # RSS_PATH = "" -# Number of posts in RSS feeds -# FEED_LENGTH = 10 - -# Slug the Tag URL easier for users to type, special characters are +# Slug the Tag URL. Easier for users to type, special characters are # often removed or replaced as well. # SLUG_TAG_PATH = True +# Slug the Author URL. Easier for users to type, special characters are +# often removed or replaced as well. +# SLUG_AUTHOR_PATH = True + # A list of redirection tuples, [("foo/from.html", "/bar/to.html")]. # # A HTML file will be created in output/foo/from.html that redirects @@ -563,10 +679,10 @@ IMAGE_FOLDERS = {'images': 'images'} # 'Read more...' for the index page, if INDEX_TEASERS is True (translatable) INDEX_READ_MORE_LINK = ${INDEX_READ_MORE_LINK} -# 'Read more...' for the RSS_FEED, if RSS_TEASERS is True (translatable) -RSS_READ_MORE_LINK = ${RSS_READ_MORE_LINK} +# 'Read more...' for the feeds, if FEED_TEASERS is True (translatable) +FEED_READ_MORE_LINK = ${FEED_READ_MORE_LINK} -# Append a URL query to the RSS_READ_MORE_LINK in Atom and RSS feeds. Advanced +# Append a URL query to the FEED_READ_MORE_LINK in Atom and RSS feeds. Advanced # option used for traffic source tracking. # Minimum example for use with Piwik: "pk_campaign=feed" # The following tags exist and are replaced for you: @@ -574,18 +690,18 @@ RSS_READ_MORE_LINK = ${RSS_READ_MORE_LINK} # {feedFormat} The name of the syndication format. # Example using replacement for use with Google Analytics: # "utm_source={feedRelUri}&utm_medium=nikola_feed&utm_campaign={feedFormat}_feed" -RSS_LINKS_APPEND_QUERY = False +FEED_LINKS_APPEND_QUERY = False # A HTML fragment describing the license, for the sidebar. # (translatable) LICENSE = "" # I recommend using the Creative Commons' wizard: -# http://creativecommons.org/choose/ +# https://creativecommons.org/choose/ # LICENSE = """ -# <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"> +# <a rel="license" href="https://creativecommons.org/licenses/by-nc-sa/4.0/"> # <img alt="Creative Commons License BY-NC-SA" # style="border-width:0; margin-bottom:12px;" -# src="http://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png"></a>""" +# src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png"></a>""" # A small copyright notice for the page footer (in HTML). # (translatable) @@ -715,6 +831,13 @@ PRETTY_URLS = ${PRETTY_URLS} # </script> # """ +# Want to use KaTeX instead of MathJax? While KaTeX is less featureful, +# it's faster and the output looks better. +# If you set USE_KATEX to True, you also need to add an extra CSS file +# like this: +# EXTRA_HEAD_DATA = """<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/KaTeX/0.3.0/katex.min.css">""" +# USE_KATEX = False + # Do you want to customize the nbconversion of your IPython notebook? # IPYNB_CONFIG = {} # With the following example configuration you can use a custom jinja template @@ -771,24 +894,34 @@ MARKDOWN_EXTENSIONS = ['fenced_code', 'codehilite', 'extra'] # them. Generate Atom for tags by setting TAG_PAGES_ARE_INDEXES to True. # Atom feeds are built based on INDEX_DISPLAY_POST_COUNT and not FEED_LENGTH # Switch between plain-text summaries and full HTML content using the -# RSS_TEASER option. RSS_LINKS_APPEND_QUERY is also respected. Atom feeds +# FEED_TEASER option. FEED_LINKS_APPEND_QUERY is also respected. Atom feeds # are generated even for old indexes and have pagination link relations # between each other. Old Atom feeds with no changes are marked as archived. # GENERATE_ATOM = False +# Only inlclude teasers in Atom and RSS feeds. Disabling include the full +# content. Defaults to True. +# FEED_TEASERS = True + +# Strip HTML from Atom annd RSS feed summaries and content. Defaults to False. +# FEED_PLAIN = False + +# Number of posts in Atom and RSS feeds. +# FEED_LENGTH = 10 + +# Include preview image as a <figure><img></figure> at the top of the entry. +# Requires FEED_PLAIN = False. If the preview image is found in the content, +# it will not be included again. Image will be included as-is, aim to optmize +# the image source for Feedly, Apple News, Flipboard, and other popular clients. +# FEED_PREVIEWIMAGE = True + # RSS_LINK is a HTML fragment to link the RSS or Atom feeds. If set to None, # the base.tmpl will use the feed Nikola generates. However, you may want to # change it for a FeedBurner feed or something else. # RSS_LINK = None -# Show teasers (instead of full posts) in feeds? Defaults to True. -# RSS_TEASERS = True - -# Strip HTML in the RSS feed? Default to False -# RSS_PLAIN = False - # A search form to search this site, for the sidebar. You can use a Google -# custom search (http://www.google.com/cse/) +# custom search (https://www.google.com/cse/) # Or a DuckDuckGo search: https://duckduckgo.com/search_box.html # Default is no search form. # (translatable) @@ -815,7 +948,7 @@ MARKDOWN_EXTENSIONS = ['fenced_code', 'codehilite', 'extra'] # If you prefer a Google search form, here's an example that should just work: # SEARCH_FORM = """ # <!-- Google custom search --> -# <form method="get" action="http://www.google.com/search" class="navbar-form navbar-right" role="search"> +# <form method="get" action="https://www.google.com/search" class="navbar-form navbar-right" role="search"> # <div class="form-group"> # <input type="text" name="q" class="form-control" placeholder="Search"> # </div> @@ -914,6 +1047,8 @@ UNSLUGIFY_TITLES = True # LINK_CHECK_WHITELIST = [] # If set to True, enable optional hyphenation in your posts (requires pyphen) +# Enabling hyphenation has been shown to break math support in some cases, +# use with caution. # HYPHENATE = False # The <hN> tags in HTML generated by certain compilers (reST/Markdown) diff --git a/nikola/data/samplesite/images/frontispiece.jpg b/nikola/data/samplesite/images/frontispiece.jpg Binary files differnew file mode 100644 index 0000000..0baf371 --- /dev/null +++ b/nikola/data/samplesite/images/frontispiece.jpg diff --git a/nikola/data/samplesite/images/illus_001.jpg b/nikola/data/samplesite/images/illus_001.jpg Binary files differnew file mode 100644 index 0000000..c8acdcf --- /dev/null +++ b/nikola/data/samplesite/images/illus_001.jpg diff --git a/nikola/data/samplesite/posts/1.rst b/nikola/data/samplesite/posts/1.rst index 88263a7..386251b 100644 --- a/nikola/data/samplesite/posts/1.rst +++ b/nikola/data/samplesite/posts/1.rst @@ -7,8 +7,8 @@ .. description: .. category: nikola -.. figure:: http://farm1.staticflickr.com/138/352972944_4f9d568680.jpg - :target: http://farm1.staticflickr.com/138/352972944_4f9d568680_z.jpg?zz=1 +.. figure:: https://farm1.staticflickr.com/138/352972944_4f9d568680.jpg + :target: https://farm1.staticflickr.com/138/352972944_4f9d568680_z.jpg?zz=1 :class: thumbnail :alt: Nikola Tesla Corner by nicwest, on Flickr @@ -22,6 +22,6 @@ Next steps: * `See a demo photo gallery <link://gallery/demo>`__ * :doc:`See a demo listing <listings-demo>` * :doc:`See a demo slideshow <slides-demo>` -* :doc:`See a demo of the Bootstrap theme <bootstrap-demo>` +* :doc:`See a demo of a longer text <dr-nikolas-vendetta>` Send feedback to info@getnikola.com! diff --git a/nikola/data/samplesite/stories/1.rst b/nikola/data/samplesite/stories/1.rst index abe1d0b..2e70345 100644 --- a/nikola/data/samplesite/stories/1.rst +++ b/nikola/data/samplesite/stories/1.rst @@ -8,4 +8,4 @@ Hope you enjoy this software! * Home page at https://getnikola.com/ -* Author's blog (and reason why Nikola exists): http://ralsina.me/ +* Author's blog (and reason why Nikola exists): https://ralsina.me/ diff --git a/nikola/data/samplesite/stories/a-study-in-scarlet.txt b/nikola/data/samplesite/stories/a-study-in-scarlet.txt deleted file mode 100644 index 2dfee52..0000000 --- a/nikola/data/samplesite/stories/a-study-in-scarlet.txt +++ /dev/null @@ -1,5139 +0,0 @@ -.. link: -.. description: -.. tags: -.. date: 2013-08-27 18:20:55 UTC-03:00 -.. title: A STUDY IN SCARLET. -.. slug: a-study-in-scarlet - -=================== -A STUDY IN SCARLET. -=================== - -By A. Conan Doyle [1]_ -====================== - - -:Title: A Study In Scarlet -:Author: Arthur Conan Doyle -:Posting Date: July 12, 2008 [EBook #244] -:Release Date: April, 1995 [Last updated: February 17, 2013] -:Language: English -:Produced by: Roger Squires - -.. class:: alert alert-info pull-right - -.. contents:: - ------------------- - -.. class:: pull-right - -.. note:: The Project Gutenberg EBook of A Study In Scarlet, by Arthur Conan Doyle - - - This eBook is for the use of anyone anywhere at no cost and with - almost no restrictions whatsoever. You may copy it, give it away or - re-use it under the terms of the Project Gutenberg License included - with this eBook or online at www.gutenberg.org - - - Original Transcriber's Note: This etext is prepared directly - from an 1887 edition, and care has been taken to duplicate the - original exactly, including typographical and punctuation - vagaries. - - Additions to the text include adding the underscore character to - indicate italics, and textual end-notes in square braces. - - Project Gutenberg Editor's Note: In reproofing and moving old PG - files such as this to the present PG directory system it is the - policy to reformat the text to conform to present PG Standards. - In this case however, in consideration of the note above of the - original transcriber describing his care to try to duplicate the - original 1887 edition as to typography and punctuation vagaries, - no changes have been made in this ascii text file. However, in - the Latin-1 file and this html file, present standards are - followed and the several French and Spanish words have been - given their proper accents. - - Part II, The Country of the Saints, deals much with the Mormon Church. - - -PART I. -------- - -(Being a reprint from the reminiscences of JOHN H. WATSON, M.D., late of the Army Medical Department.) [2]_ - - -CHAPTER I. MR. SHERLOCK HOLMES. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -IN the year 1878 I took my degree of Doctor of Medicine of the -University of London, and proceeded to Netley to go through the course -prescribed for surgeons in the army. Having completed my studies there, -I was duly attached to the Fifth Northumberland Fusiliers as Assistant -Surgeon. The regiment was stationed in India at the time, and before -I could join it, the second Afghan war had broken out. On landing at -Bombay, I learned that my corps had advanced through the passes, and -was already deep in the enemy's country. I followed, however, with many -other officers who were in the same situation as myself, and succeeded -in reaching Candahar in safety, where I found my regiment, and at once -entered upon my new duties. - -The campaign brought honours and promotion to many, but for me it had -nothing but misfortune and disaster. I was removed from my brigade and -attached to the Berkshires, with whom I served at the fatal battle of -Maiwand. There I was struck on the shoulder by a Jezail bullet, which -shattered the bone and grazed the subclavian artery. I should have -fallen into the hands of the murderous Ghazis had it not been for the -devotion and courage shown by Murray, my orderly, who threw me across a -pack-horse, and succeeded in bringing me safely to the British lines. - -Worn with pain, and weak from the prolonged hardships which I had -undergone, I was removed, with a great train of wounded sufferers, to -the base hospital at Peshawar. Here I rallied, and had already improved -so far as to be able to walk about the wards, and even to bask a little -upon the verandah, when I was struck down by enteric fever, that curse -of our Indian possessions. For months my life was despaired of, and -when at last I came to myself and became convalescent, I was so weak and -emaciated that a medical board determined that not a day should be lost -in sending me back to England. I was dispatched, accordingly, in the -troopship "Orontes," and landed a month later on Portsmouth jetty, with -my health irretrievably ruined, but with permission from a paternal -government to spend the next nine months in attempting to improve it. - -I had neither kith nor kin in England, and was therefore as free as -air--or as free as an income of eleven shillings and sixpence a day will -permit a man to be. Under such circumstances, I naturally gravitated to -London, that great cesspool into which all the loungers and idlers of -the Empire are irresistibly drained. There I stayed for some time at -a private hotel in the Strand, leading a comfortless, meaningless -existence, and spending such money as I had, considerably more freely -than I ought. So alarming did the state of my finances become, that -I soon realized that I must either leave the metropolis and rusticate -somewhere in the country, or that I must make a complete alteration in -my style of living. Choosing the latter alternative, I began by making -up my mind to leave the hotel, and to take up my quarters in some less -pretentious and less expensive domicile. - -On the very day that I had come to this conclusion, I was standing at -the Criterion Bar, when some one tapped me on the shoulder, and turning -round I recognized young Stamford, who had been a dresser under me at -Barts. The sight of a friendly face in the great wilderness of London is -a pleasant thing indeed to a lonely man. In old days Stamford had never -been a particular crony of mine, but now I hailed him with enthusiasm, -and he, in his turn, appeared to be delighted to see me. In the -exuberance of my joy, I asked him to lunch with me at the Holborn, and -we started off together in a hansom. - -"Whatever have you been doing with yourself, Watson?" he asked in -undisguised wonder, as we rattled through the crowded London streets. -"You are as thin as a lath and as brown as a nut." - -I gave him a short sketch of my adventures, and had hardly concluded it -by the time that we reached our destination. - -"Poor devil!" he said, commiseratingly, after he had listened to my -misfortunes. "What are you up to now?" - -"Looking for lodgings." [3]_ I answered. "Trying to solve the problem -as to whether it is possible to get comfortable rooms at a reasonable -price." - -"That's a strange thing," remarked my companion; "you are the second man -to-day that has used that expression to me." - -"And who was the first?" I asked. - -"A fellow who is working at the chemical laboratory up at the hospital. -He was bemoaning himself this morning because he could not get someone -to go halves with him in some nice rooms which he had found, and which -were too much for his purse." - -"By Jove!" I cried, "if he really wants someone to share the rooms and -the expense, I am the very man for him. I should prefer having a partner -to being alone." - -Young Stamford looked rather strangely at me over his wine-glass. "You -don't know Sherlock Holmes yet," he said; "perhaps you would not care -for him as a constant companion." - -"Why, what is there against him?" - -"Oh, I didn't say there was anything against him. He is a little queer -in his ideas--an enthusiast in some branches of science. As far as I -know he is a decent fellow enough." - -"A medical student, I suppose?" said I. - -"No--I have no idea what he intends to go in for. I believe he is well -up in anatomy, and he is a first-class chemist; but, as far as I know, -he has never taken out any systematic medical classes. His studies are -very desultory and eccentric, but he has amassed a lot of out-of-the way -knowledge which would astonish his professors." - -"Did you never ask him what he was going in for?" I asked. - -"No; he is not a man that it is easy to draw out, though he can be -communicative enough when the fancy seizes him." - -"I should like to meet him," I said. "If I am to lodge with anyone, I -should prefer a man of studious and quiet habits. I am not strong -enough yet to stand much noise or excitement. I had enough of both in -Afghanistan to last me for the remainder of my natural existence. How -could I meet this friend of yours?" - -"He is sure to be at the laboratory," returned my companion. "He either -avoids the place for weeks, or else he works there from morning to -night. If you like, we shall drive round together after luncheon." - -"Certainly," I answered, and the conversation drifted away into other -channels. - -As we made our way to the hospital after leaving the Holborn, Stamford -gave me a few more particulars about the gentleman whom I proposed to -take as a fellow-lodger. - -"You mustn't blame me if you don't get on with him," he said; "I know -nothing more of him than I have learned from meeting him occasionally in -the laboratory. You proposed this arrangement, so you must not hold me -responsible." - -"If we don't get on it will be easy to part company," I answered. "It -seems to me, Stamford," I added, looking hard at my companion, "that you -have some reason for washing your hands of the matter. Is this fellow's -temper so formidable, or what is it? Don't be mealy-mouthed about it." - -"It is not easy to express the inexpressible," he answered with a laugh. -"Holmes is a little too scientific for my tastes--it approaches to -cold-bloodedness. I could imagine his giving a friend a little pinch of -the latest vegetable alkaloid, not out of malevolence, you understand, -but simply out of a spirit of inquiry in order to have an accurate idea -of the effects. To do him justice, I think that he would take it himself -with the same readiness. He appears to have a passion for definite and -exact knowledge." - -"Very right too." - -"Yes, but it may be pushed to excess. When it comes to beating the -subjects in the dissecting-rooms with a stick, it is certainly taking -rather a bizarre shape." - -"Beating the subjects!" - -"Yes, to verify how far bruises may be produced after death. I saw him -at it with my own eyes." - -"And yet you say he is not a medical student?" - -"No. Heaven knows what the objects of his studies are. But here we -are, and you must form your own impressions about him." As he spoke, we -turned down a narrow lane and passed through a small side-door, which -opened into a wing of the great hospital. It was familiar ground to me, -and I needed no guiding as we ascended the bleak stone staircase and -made our way down the long corridor with its vista of whitewashed -wall and dun-coloured doors. Near the further end a low arched passage -branched away from it and led to the chemical laboratory. - -This was a lofty chamber, lined and littered with countless bottles. -Broad, low tables were scattered about, which bristled with retorts, -test-tubes, and little Bunsen lamps, with their blue flickering flames. -There was only one student in the room, who was bending over a distant -table absorbed in his work. At the sound of our steps he glanced round -and sprang to his feet with a cry of pleasure. "I've found it! I've -found it," he shouted to my companion, running towards us with a -test-tube in his hand. "I have found a re-agent which is precipitated -by hoemoglobin, [4]_ and by nothing else." Had he discovered a gold mine, -greater delight could not have shone upon his features. - -"Dr. Watson, Mr. Sherlock Holmes," said Stamford, introducing us. - -"How are you?" he said cordially, gripping my hand with a strength -for which I should hardly have given him credit. "You have been in -Afghanistan, I perceive." - -"How on earth did you know that?" I asked in astonishment. - -"Never mind," said he, chuckling to himself. "The question now is about -hoemoglobin. No doubt you see the significance of this discovery of -mine?" - -"It is interesting, chemically, no doubt," I answered, "but -practically----" - -"Why, man, it is the most practical medico-legal discovery for years. -Don't you see that it gives us an infallible test for blood stains. Come -over here now!" He seized me by the coat-sleeve in his eagerness, and -drew me over to the table at which he had been working. "Let us have -some fresh blood," he said, digging a long bodkin into his finger, and -drawing off the resulting drop of blood in a chemical pipette. "Now, I -add this small quantity of blood to a litre of water. You perceive that -the resulting mixture has the appearance of pure water. The proportion -of blood cannot be more than one in a million. I have no doubt, however, -that we shall be able to obtain the characteristic reaction." As he -spoke, he threw into the vessel a few white crystals, and then added -some drops of a transparent fluid. In an instant the contents assumed a -dull mahogany colour, and a brownish dust was precipitated to the bottom -of the glass jar. - -"Ha! ha!" he cried, clapping his hands, and looking as delighted as a -child with a new toy. "What do you think of that?" - -"It seems to be a very delicate test," I remarked. - -"Beautiful! beautiful! The old Guiacum test was very clumsy and -uncertain. So is the microscopic examination for blood corpuscles. The -latter is valueless if the stains are a few hours old. Now, this appears -to act as well whether the blood is old or new. Had this test been -invented, there are hundreds of men now walking the earth who would long -ago have paid the penalty of their crimes." - -"Indeed!" I murmured. - -"Criminal cases are continually hinging upon that one point. A man is -suspected of a crime months perhaps after it has been committed. His -linen or clothes are examined, and brownish stains discovered upon them. -Are they blood stains, or mud stains, or rust stains, or fruit stains, -or what are they? That is a question which has puzzled many an expert, -and why? Because there was no reliable test. Now we have the Sherlock -Holmes' test, and there will no longer be any difficulty." - -His eyes fairly glittered as he spoke, and he put his hand over his -heart and bowed as if to some applauding crowd conjured up by his -imagination. - -"You are to be congratulated," I remarked, considerably surprised at his -enthusiasm. - -"There was the case of Von Bischoff at Frankfort last year. He would -certainly have been hung had this test been in existence. Then there was -Mason of Bradford, and the notorious Muller, and Lefevre of Montpellier, -and Samson of New Orleans. I could name a score of cases in which it -would have been decisive." - -"You seem to be a walking calendar of crime," said Stamford with a -laugh. "You might start a paper on those lines. Call it the 'Police News -of the Past.'" - -"Very interesting reading it might be made, too," remarked Sherlock -Holmes, sticking a small piece of plaster over the prick on his finger. -"I have to be careful," he continued, turning to me with a smile, "for I -dabble with poisons a good deal." He held out his hand as he spoke, and -I noticed that it was all mottled over with similar pieces of plaster, -and discoloured with strong acids. - -"We came here on business," said Stamford, sitting down on a high -three-legged stool, and pushing another one in my direction with -his foot. "My friend here wants to take diggings, and as you were -complaining that you could get no one to go halves with you, I thought -that I had better bring you together." - -Sherlock Holmes seemed delighted at the idea of sharing his rooms with -me. "I have my eye on a suite in Baker Street," he said, "which would -suit us down to the ground. You don't mind the smell of strong tobacco, -I hope?" - -"I always smoke 'ship's' myself," I answered. - -"That's good enough. I generally have chemicals about, and occasionally -do experiments. Would that annoy you?" - -"By no means." - -"Let me see--what are my other shortcomings. I get in the dumps at -times, and don't open my mouth for days on end. You must not think I am -sulky when I do that. Just let me alone, and I'll soon be right. What -have you to confess now? It's just as well for two fellows to know the -worst of one another before they begin to live together." - -I laughed at this cross-examination. "I keep a bull pup," I said, "and -I object to rows because my nerves are shaken, and I get up at all sorts -of ungodly hours, and I am extremely lazy. I have another set of vices -when I'm well, but those are the principal ones at present." - -"Do you include violin-playing in your category of rows?" he asked, -anxiously. - -"It depends on the player," I answered. "A well-played violin is a treat -for the gods--a badly-played one----" - -"Oh, that's all right," he cried, with a merry laugh. "I think we may -consider the thing as settled--that is, if the rooms are agreeable to -you." - -"When shall we see them?" - -"Call for me here at noon to-morrow, and we'll go together and settle -everything," he answered. - -"All right--noon exactly," said I, shaking his hand. - -We left him working among his chemicals, and we walked together towards -my hotel. - -"By the way," I asked suddenly, stopping and turning upon Stamford, "how -the deuce did he know that I had come from Afghanistan?" - -My companion smiled an enigmatical smile. "That's just his little -peculiarity," he said. "A good many people have wanted to know how he -finds things out." - -"Oh! a mystery is it?" I cried, rubbing my hands. "This is very piquant. -I am much obliged to you for bringing us together. 'The proper study of -mankind is man,' you know." - -"You must study him, then," Stamford said, as he bade me good-bye. -"You'll find him a knotty problem, though. I'll wager he learns more -about you than you about him. Good-bye." - -"Good-bye," I answered, and strolled on to my hotel, considerably -interested in my new acquaintance. - - - - -CHAPTER II. THE SCIENCE OF DEDUCTION. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -WE met next day as he had arranged, and inspected the rooms at No. 221B, -[5]_ Baker Street, of which he had spoken at our meeting. They -consisted of a couple of comfortable bed-rooms and a single large -airy sitting-room, cheerfully furnished, and illuminated by two broad -windows. So desirable in every way were the apartments, and so moderate -did the terms seem when divided between us, that the bargain was -concluded upon the spot, and we at once entered into possession. -That very evening I moved my things round from the hotel, and on the -following morning Sherlock Holmes followed me with several boxes and -portmanteaus. For a day or two we were busily employed in unpacking and -laying out our property to the best advantage. That done, we -gradually began to settle down and to accommodate ourselves to our new -surroundings. - -Holmes was certainly not a difficult man to live with. He was quiet -in his ways, and his habits were regular. It was rare for him to be -up after ten at night, and he had invariably breakfasted and gone out -before I rose in the morning. Sometimes he spent his day at the chemical -laboratory, sometimes in the dissecting-rooms, and occasionally in long -walks, which appeared to take him into the lowest portions of the City. -Nothing could exceed his energy when the working fit was upon him; but -now and again a reaction would seize him, and for days on end he would -lie upon the sofa in the sitting-room, hardly uttering a word or moving -a muscle from morning to night. On these occasions I have noticed such -a dreamy, vacant expression in his eyes, that I might have suspected him -of being addicted to the use of some narcotic, had not the temperance -and cleanliness of his whole life forbidden such a notion. - -As the weeks went by, my interest in him and my curiosity as to his -aims in life, gradually deepened and increased. His very person and -appearance were such as to strike the attention of the most casual -observer. In height he was rather over six feet, and so excessively -lean that he seemed to be considerably taller. His eyes were sharp and -piercing, save during those intervals of torpor to which I have alluded; -and his thin, hawk-like nose gave his whole expression an air of -alertness and decision. His chin, too, had the prominence and squareness -which mark the man of determination. His hands were invariably -blotted with ink and stained with chemicals, yet he was possessed of -extraordinary delicacy of touch, as I frequently had occasion to observe -when I watched him manipulating his fragile philosophical instruments. - -The reader may set me down as a hopeless busybody, when I confess how -much this man stimulated my curiosity, and how often I endeavoured -to break through the reticence which he showed on all that concerned -himself. Before pronouncing judgment, however, be it remembered, how -objectless was my life, and how little there was to engage my attention. -My health forbade me from venturing out unless the weather was -exceptionally genial, and I had no friends who would call upon me and -break the monotony of my daily existence. Under these circumstances, I -eagerly hailed the little mystery which hung around my companion, and -spent much of my time in endeavouring to unravel it. - -He was not studying medicine. He had himself, in reply to a question, -confirmed Stamford's opinion upon that point. Neither did he appear to -have pursued any course of reading which might fit him for a degree in -science or any other recognized portal which would give him an entrance -into the learned world. Yet his zeal for certain studies was remarkable, -and within eccentric limits his knowledge was so extraordinarily ample -and minute that his observations have fairly astounded me. Surely no man -would work so hard or attain such precise information unless he had some -definite end in view. Desultory readers are seldom remarkable for the -exactness of their learning. No man burdens his mind with small matters -unless he has some very good reason for doing so. - -His ignorance was as remarkable as his knowledge. Of contemporary -literature, philosophy and politics he appeared to know next to nothing. -Upon my quoting Thomas Carlyle, he inquired in the naivest way who he -might be and what he had done. My surprise reached a climax, however, -when I found incidentally that he was ignorant of the Copernican Theory -and of the composition of the Solar System. That any civilized human -being in this nineteenth century should not be aware that the earth -travelled round the sun appeared to be to me such an extraordinary fact -that I could hardly realize it. - -"You appear to be astonished," he said, smiling at my expression of -surprise. "Now that I do know it I shall do my best to forget it." - -"To forget it!" - -"You see," he explained, "I consider that a man's brain originally is -like a little empty attic, and you have to stock it with such furniture -as you choose. A fool takes in all the lumber of every sort that he -comes across, so that the knowledge which might be useful to him gets -crowded out, or at best is jumbled up with a lot of other things so that -he has a difficulty in laying his hands upon it. Now the skilful workman -is very careful indeed as to what he takes into his brain-attic. He will -have nothing but the tools which may help him in doing his work, but of -these he has a large assortment, and all in the most perfect order. It -is a mistake to think that that little room has elastic walls and can -distend to any extent. Depend upon it there comes a time when for every -addition of knowledge you forget something that you knew before. It is -of the highest importance, therefore, not to have useless facts elbowing -out the useful ones." - -"But the Solar System!" I protested. - -"What the deuce is it to me?" he interrupted impatiently; "you say -that we go round the sun. If we went round the moon it would not make a -pennyworth of difference to me or to my work." - -I was on the point of asking him what that work might be, but something -in his manner showed me that the question would be an unwelcome one. I -pondered over our short conversation, however, and endeavoured to draw -my deductions from it. He said that he would acquire no knowledge which -did not bear upon his object. Therefore all the knowledge which he -possessed was such as would be useful to him. I enumerated in my own -mind all the various points upon which he had shown me that he was -exceptionally well-informed. I even took a pencil and jotted them down. -I could not help smiling at the document when I had completed it. It ran -in this way-- - - -SHERLOCK HOLMES--his limits. - -1. Knowledge of Literature.--Nil. -2. Philosophy.--Nil. -3. Astronomy.--Nil. -4. Politics.--Feeble. -5. Botany.--Variable. - Well up in belladonna, - opium, and poisons generally. - Knows nothing of practical gardening. -6. Geology.--Practical, but limited. - Tells at a glance different soils - from each other. After walks has - shown me splashes upon his trousers, - and told me by their colour and - consistence in what part of London - he had received them. -7. Chemistry.--Profound. -8. Anatomy.--Accurate, but unsystematic. -9. Sensational Literature.--Immense. He appears to know every detail of every horror perpetrated in the century. -10. Plays the violin well. -11. Is an expert singlestick player, boxer, and swordsman. -12. Has a good practical knowledge of British law. - - -When I had got so far in my list I threw it into the fire in despair. -"If I can only find what the fellow is driving at by reconciling all -these accomplishments, and discovering a calling which needs them all," -I said to myself, "I may as well give up the attempt at once." - -I see that I have alluded above to his powers upon the violin. These -were very remarkable, but as eccentric as all his other accomplishments. -That he could play pieces, and difficult pieces, I knew well, because -at my request he has played me some of Mendelssohn's Lieder, and other -favourites. When left to himself, however, he would seldom produce any -music or attempt any recognized air. Leaning back in his arm-chair of -an evening, he would close his eyes and scrape carelessly at the fiddle -which was thrown across his knee. Sometimes the chords were sonorous and -melancholy. Occasionally they were fantastic and cheerful. Clearly they -reflected the thoughts which possessed him, but whether the music aided -those thoughts, or whether the playing was simply the result of a whim -or fancy was more than I could determine. I might have rebelled against -these exasperating solos had it not been that he usually terminated them -by playing in quick succession a whole series of my favourite airs as a -slight compensation for the trial upon my patience. - -During the first week or so we had no callers, and I had begun to think -that my companion was as friendless a man as I was myself. Presently, -however, I found that he had many acquaintances, and those in the most -different classes of society. There was one little sallow rat-faced, -dark-eyed fellow who was introduced to me as Mr. Lestrade, and who came -three or four times in a single week. One morning a young girl called, -fashionably dressed, and stayed for half an hour or more. The same -afternoon brought a grey-headed, seedy visitor, looking like a Jew -pedlar, who appeared to me to be much excited, and who was closely -followed by a slip-shod elderly woman. On another occasion an old -white-haired gentleman had an interview with my companion; and on -another a railway porter in his velveteen uniform. When any of these -nondescript individuals put in an appearance, Sherlock Holmes used to -beg for the use of the sitting-room, and I would retire to my bed-room. -He always apologized to me for putting me to this inconvenience. "I have -to use this room as a place of business," he said, "and these people -are my clients." Again I had an opportunity of asking him a point blank -question, and again my delicacy prevented me from forcing another man to -confide in me. I imagined at the time that he had some strong reason for -not alluding to it, but he soon dispelled the idea by coming round to -the subject of his own accord. - -It was upon the 4th of March, as I have good reason to remember, that I -rose somewhat earlier than usual, and found that Sherlock Holmes had not -yet finished his breakfast. The landlady had become so accustomed to my -late habits that my place had not been laid nor my coffee prepared. With -the unreasonable petulance of mankind I rang the bell and gave a curt -intimation that I was ready. Then I picked up a magazine from the table -and attempted to while away the time with it, while my companion munched -silently at his toast. One of the articles had a pencil mark at the -heading, and I naturally began to run my eye through it. - -Its somewhat ambitious title was "The Book of Life," and it attempted to -show how much an observant man might learn by an accurate and systematic -examination of all that came in his way. It struck me as being a -remarkable mixture of shrewdness and of absurdity. The reasoning was -close and intense, but the deductions appeared to me to be far-fetched -and exaggerated. The writer claimed by a momentary expression, a twitch -of a muscle or a glance of an eye, to fathom a man's inmost thoughts. -Deceit, according to him, was an impossibility in the case of one -trained to observation and analysis. His conclusions were as infallible -as so many propositions of Euclid. So startling would his results appear -to the uninitiated that until they learned the processes by which he had -arrived at them they might well consider him as a necromancer. - -"From a drop of water," said the writer, "a logician could infer the -possibility of an Atlantic or a Niagara without having seen or heard of -one or the other. So all life is a great chain, the nature of which is -known whenever we are shown a single link of it. Like all other arts, -the Science of Deduction and Analysis is one which can only be acquired -by long and patient study nor is life long enough to allow any mortal -to attain the highest possible perfection in it. Before turning to -those moral and mental aspects of the matter which present the greatest -difficulties, let the enquirer begin by mastering more elementary -problems. Let him, on meeting a fellow-mortal, learn at a glance to -distinguish the history of the man, and the trade or profession to -which he belongs. Puerile as such an exercise may seem, it sharpens the -faculties of observation, and teaches one where to look and what to look -for. By a man's finger nails, by his coat-sleeve, by his boot, by his -trouser knees, by the callosities of his forefinger and thumb, by his -expression, by his shirt cuffs--by each of these things a man's calling -is plainly revealed. That all united should fail to enlighten the -competent enquirer in any case is almost inconceivable." - -"What ineffable twaddle!" I cried, slapping the magazine down on the -table, "I never read such rubbish in my life." - -"What is it?" asked Sherlock Holmes. - -"Why, this article," I said, pointing at it with my egg spoon as I sat -down to my breakfast. "I see that you have read it since you have marked -it. I don't deny that it is smartly written. It irritates me though. It -is evidently the theory of some arm-chair lounger who evolves all these -neat little paradoxes in the seclusion of his own study. It is not -practical. I should like to see him clapped down in a third class -carriage on the Underground, and asked to give the trades of all his -fellow-travellers. I would lay a thousand to one against him." - -"You would lose your money," Sherlock Holmes remarked calmly. "As for -the article I wrote it myself." - -"You!" - -"Yes, I have a turn both for observation and for deduction. The -theories which I have expressed there, and which appear to you to be so -chimerical are really extremely practical--so practical that I depend -upon them for my bread and cheese." - -"And how?" I asked involuntarily. - -"Well, I have a trade of my own. I suppose I am the only one in the -world. I'm a consulting detective, if you can understand what that is. -Here in London we have lots of Government detectives and lots of private -ones. When these fellows are at fault they come to me, and I manage to -put them on the right scent. They lay all the evidence before me, and I -am generally able, by the help of my knowledge of the history of -crime, to set them straight. There is a strong family resemblance about -misdeeds, and if you have all the details of a thousand at your finger -ends, it is odd if you can't unravel the thousand and first. Lestrade -is a well-known detective. He got himself into a fog recently over a -forgery case, and that was what brought him here." - -"And these other people?" - -"They are mostly sent on by private inquiry agencies. They are -all people who are in trouble about something, and want a little -enlightening. I listen to their story, they listen to my comments, and -then I pocket my fee." - -"But do you mean to say," I said, "that without leaving your room you -can unravel some knot which other men can make nothing of, although they -have seen every detail for themselves?" - -"Quite so. I have a kind of intuition that way. Now and again a case -turns up which is a little more complex. Then I have to bustle about and -see things with my own eyes. You see I have a lot of special knowledge -which I apply to the problem, and which facilitates matters wonderfully. -Those rules of deduction laid down in that article which aroused your -scorn, are invaluable to me in practical work. Observation with me is -second nature. You appeared to be surprised when I told you, on our -first meeting, that you had come from Afghanistan." - -"You were told, no doubt." - -"Nothing of the sort. I _knew_ you came from Afghanistan. From long -habit the train of thoughts ran so swiftly through my mind, that I -arrived at the conclusion without being conscious of intermediate steps. -There were such steps, however. The train of reasoning ran, 'Here is a -gentleman of a medical type, but with the air of a military man. Clearly -an army doctor, then. He has just come from the tropics, for his face is -dark, and that is not the natural tint of his skin, for his wrists are -fair. He has undergone hardship and sickness, as his haggard face says -clearly. His left arm has been injured. He holds it in a stiff and -unnatural manner. Where in the tropics could an English army doctor have -seen much hardship and got his arm wounded? Clearly in Afghanistan.' The -whole train of thought did not occupy a second. I then remarked that you -came from Afghanistan, and you were astonished." - -"It is simple enough as you explain it," I said, smiling. "You remind -me of Edgar Allen Poe's Dupin. I had no idea that such individuals did -exist outside of stories." - -Sherlock Holmes rose and lit his pipe. "No doubt you think that you are -complimenting me in comparing me to Dupin," he observed. "Now, in my -opinion, Dupin was a very inferior fellow. That trick of his of breaking -in on his friends' thoughts with an apropos remark after a quarter of -an hour's silence is really very showy and superficial. He had some -analytical genius, no doubt; but he was by no means such a phenomenon as -Poe appeared to imagine." - -"Have you read Gaboriau's works?" I asked. "Does Lecoq come up to your -idea of a detective?" - -Sherlock Holmes sniffed sardonically. "Lecoq was a miserable bungler," -he said, in an angry voice; "he had only one thing to recommend him, and -that was his energy. That book made me positively ill. The question was -how to identify an unknown prisoner. I could have done it in twenty-four -hours. Lecoq took six months or so. It might be made a text-book for -detectives to teach them what to avoid." - -I felt rather indignant at having two characters whom I had admired -treated in this cavalier style. I walked over to the window, and stood -looking out into the busy street. "This fellow may be very clever," I -said to myself, "but he is certainly very conceited." - -"There are no crimes and no criminals in these days," he said, -querulously. "What is the use of having brains in our profession. I know -well that I have it in me to make my name famous. No man lives or has -ever lived who has brought the same amount of study and of natural -talent to the detection of crime which I have done. And what is the -result? There is no crime to detect, or, at most, some bungling villainy -with a motive so transparent that even a Scotland Yard official can see -through it." - -I was still annoyed at his bumptious style of conversation. I thought it -best to change the topic. - -"I wonder what that fellow is looking for?" I asked, pointing to a -stalwart, plainly-dressed individual who was walking slowly down the -other side of the street, looking anxiously at the numbers. He had -a large blue envelope in his hand, and was evidently the bearer of a -message. - -"You mean the retired sergeant of Marines," said Sherlock Holmes. - -"Brag and bounce!" thought I to myself. "He knows that I cannot verify -his guess." - -The thought had hardly passed through my mind when the man whom we were -watching caught sight of the number on our door, and ran rapidly across -the roadway. We heard a loud knock, a deep voice below, and heavy steps -ascending the stair. - -"For Mr. Sherlock Holmes," he said, stepping into the room and handing -my friend the letter. - -Here was an opportunity of taking the conceit out of him. He little -thought of this when he made that random shot. "May I ask, my lad," I -said, in the blandest voice, "what your trade may be?" - -"Commissionaire, sir," he said, gruffly. "Uniform away for repairs." - -"And you were?" I asked, with a slightly malicious glance at my -companion. - -"A sergeant, sir, Royal Marine Light Infantry, sir. No answer? Right, -sir." - -He clicked his heels together, raised his hand in a salute, and was -gone. - - - - -CHAPTER III. THE LAURISTON GARDEN MYSTERY [6]_ -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -I CONFESS that I was considerably startled by this fresh proof of the -practical nature of my companion's theories. My respect for his powers -of analysis increased wondrously. There still remained some lurking -suspicion in my mind, however, that the whole thing was a pre-arranged -episode, intended to dazzle me, though what earthly object he could have -in taking me in was past my comprehension. When I looked at him he -had finished reading the note, and his eyes had assumed the vacant, -lack-lustre expression which showed mental abstraction. - -"How in the world did you deduce that?" I asked. - -"Deduce what?" said he, petulantly. - -"Why, that he was a retired sergeant of Marines." - -"I have no time for trifles," he answered, brusquely; then with a smile, -"Excuse my rudeness. You broke the thread of my thoughts; but perhaps -it is as well. So you actually were not able to see that that man was a -sergeant of Marines?" - -"No, indeed." - -"It was easier to know it than to explain why I knew it. If you -were asked to prove that two and two made four, you might find some -difficulty, and yet you are quite sure of the fact. Even across the -street I could see a great blue anchor tattooed on the back of the -fellow's hand. That smacked of the sea. He had a military carriage, -however, and regulation side whiskers. There we have the marine. He was -a man with some amount of self-importance and a certain air of command. -You must have observed the way in which he held his head and swung -his cane. A steady, respectable, middle-aged man, too, on the face of -him--all facts which led me to believe that he had been a sergeant." - -"Wonderful!" I ejaculated. - -"Commonplace," said Holmes, though I thought from his expression that he -was pleased at my evident surprise and admiration. "I said just now that -there were no criminals. It appears that I am wrong--look at this!" He -threw me over the note which the commissionaire had brought. [7]_ - -"Why," I cried, as I cast my eye over it, "this is terrible!" - -"It does seem to be a little out of the common," he remarked, calmly. -"Would you mind reading it to me aloud?" - -This is the letter which I read to him---- - - -"MY DEAR MR. SHERLOCK HOLMES,-- - -"There has been a bad business during the night at 3, Lauriston Gardens, -off the Brixton Road. Our man on the beat saw a light there about two in -the morning, and as the house was an empty one, suspected that something -was amiss. He found the door open, and in the front room, which is bare -of furniture, discovered the body of a gentleman, well dressed, and -having cards in his pocket bearing the name of 'Enoch J. Drebber, -Cleveland, Ohio, U.S.A.' There had been no robbery, nor is there any -evidence as to how the man met his death. There are marks of blood in -the room, but there is no wound upon his person. We are at a loss as to -how he came into the empty house; indeed, the whole affair is a puzzler. -If you can come round to the house any time before twelve, you will find -me there. I have left everything *in statu quo* until I hear from you. -If you are unable to come I shall give you fuller details, and would -esteem it a great kindness if you would favour me with your opinion. -Yours faithfully, - -"TOBIAS GREGSON." - - -"Gregson is the smartest of the Scotland Yarders," my friend remarked; -"he and Lestrade are the pick of a bad lot. They are both quick and -energetic, but conventional--shockingly so. They have their knives -into one another, too. They are as jealous as a pair of professional -beauties. There will be some fun over this case if they are both put -upon the scent." - -I was amazed at the calm way in which he rippled on. "Surely there is -not a moment to be lost," I cried, "shall I go and order you a cab?" - -"I'm not sure about whether I shall go. I am the most incurably lazy -devil that ever stood in shoe leather--that is, when the fit is on me, -for I can be spry enough at times." - -"Why, it is just such a chance as you have been longing for." - -"My dear fellow, what does it matter to me. Supposing I unravel the -whole matter, you may be sure that Gregson, Lestrade, and Co. will -pocket all the credit. That comes of being an unofficial personage." - -"But he begs you to help him." - -"Yes. He knows that I am his superior, and acknowledges it to me; but -he would cut his tongue out before he would own it to any third person. -However, we may as well go and have a look. I shall work it out on my -own hook. I may have a laugh at them if I have nothing else. Come on!" - -He hustled on his overcoat, and bustled about in a way that showed that -an energetic fit had superseded the apathetic one. - -"Get your hat," he said. - -"You wish me to come?" - -"Yes, if you have nothing better to do." A minute later we were both in -a hansom, driving furiously for the Brixton Road. - -It was a foggy, cloudy morning, and a dun-coloured veil hung over the -house-tops, looking like the reflection of the mud-coloured streets -beneath. My companion was in the best of spirits, and prattled away -about Cremona fiddles, and the difference between a Stradivarius and -an Amati. As for myself, I was silent, for the dull weather and the -melancholy business upon which we were engaged, depressed my spirits. - -"You don't seem to give much thought to the matter in hand," I said at -last, interrupting Holmes' musical disquisition. - -"No data yet," he answered. "It is a capital mistake to theorize before -you have all the evidence. It biases the judgment." - -"You will have your data soon," I remarked, pointing with my finger; -"this is the Brixton Road, and that is the house, if I am not very much -mistaken." - -"So it is. Stop, driver, stop!" We were still a hundred yards or so from -it, but he insisted upon our alighting, and we finished our journey upon -foot. - -Number 3, Lauriston Gardens wore an ill-omened and minatory look. It was -one of four which stood back some little way from the street, two being -occupied and two empty. The latter looked out with three tiers of vacant -melancholy windows, which were blank and dreary, save that here and -there a "To Let" card had developed like a cataract upon the bleared -panes. A small garden sprinkled over with a scattered eruption of sickly -plants separated each of these houses from the street, and was traversed -by a narrow pathway, yellowish in colour, and consisting apparently of a -mixture of clay and of gravel. The whole place was very sloppy from the -rain which had fallen through the night. The garden was bounded by a -three-foot brick wall with a fringe of wood rails upon the top, and -against this wall was leaning a stalwart police constable, surrounded by -a small knot of loafers, who craned their necks and strained their eyes -in the vain hope of catching some glimpse of the proceedings within. - -I had imagined that Sherlock Holmes would at once have hurried into the -house and plunged into a study of the mystery. Nothing appeared to be -further from his intention. With an air of nonchalance which, under the -circumstances, seemed to me to border upon affectation, he lounged up -and down the pavement, and gazed vacantly at the ground, the sky, the -opposite houses and the line of railings. Having finished his scrutiny, -he proceeded slowly down the path, or rather down the fringe of grass -which flanked the path, keeping his eyes riveted upon the ground. Twice -he stopped, and once I saw him smile, and heard him utter an exclamation -of satisfaction. There were many marks of footsteps upon the wet clayey -soil, but since the police had been coming and going over it, I was -unable to see how my companion could hope to learn anything from it. -Still I had had such extraordinary evidence of the quickness of his -perceptive faculties, that I had no doubt that he could see a great deal -which was hidden from me. - -At the door of the house we were met by a tall, white-faced, -flaxen-haired man, with a notebook in his hand, who rushed forward and -wrung my companion's hand with effusion. "It is indeed kind of you to -come," he said, "I have had everything left untouched." - -"Except that!" my friend answered, pointing at the pathway. "If a herd -of buffaloes had passed along there could not be a greater mess. No -doubt, however, you had drawn your own conclusions, Gregson, before you -permitted this." - -"I have had so much to do inside the house," the detective said -evasively. "My colleague, Mr. Lestrade, is here. I had relied upon him -to look after this." - -Holmes glanced at me and raised his eyebrows sardonically. "With two -such men as yourself and Lestrade upon the ground, there will not be -much for a third party to find out," he said. - -Gregson rubbed his hands in a self-satisfied way. "I think we have done -all that can be done," he answered; "it's a queer case though, and I -knew your taste for such things." - -"You did not come here in a cab?" asked Sherlock Holmes. - -"No, sir." - -"Nor Lestrade?" - -"No, sir." - -"Then let us go and look at the room." With which inconsequent remark he -strode on into the house, followed by Gregson, whose features expressed -his astonishment. - -A short passage, bare planked and dusty, led to the kitchen and offices. -Two doors opened out of it to the left and to the right. One of these -had obviously been closed for many weeks. The other belonged to the -dining-room, which was the apartment in which the mysterious affair had -occurred. Holmes walked in, and I followed him with that subdued feeling -at my heart which the presence of death inspires. - -It was a large square room, looking all the larger from the absence -of all furniture. A vulgar flaring paper adorned the walls, but it was -blotched in places with mildew, and here and there great strips had -become detached and hung down, exposing the yellow plaster beneath. -Opposite the door was a showy fireplace, surmounted by a mantelpiece of -imitation white marble. On one corner of this was stuck the stump of a -red wax candle. The solitary window was so dirty that the light was -hazy and uncertain, giving a dull grey tinge to everything, which was -intensified by the thick layer of dust which coated the whole apartment. - -All these details I observed afterwards. At present my attention was -centred upon the single grim motionless figure which lay stretched upon -the boards, with vacant sightless eyes staring up at the discoloured -ceiling. It was that of a man about forty-three or forty-four years of -age, middle-sized, broad shouldered, with crisp curling black hair, and -a short stubbly beard. He was dressed in a heavy broadcloth frock coat -and waistcoat, with light-coloured trousers, and immaculate collar -and cuffs. A top hat, well brushed and trim, was placed upon the floor -beside him. His hands were clenched and his arms thrown abroad, while -his lower limbs were interlocked as though his death struggle had been a -grievous one. On his rigid face there stood an expression of horror, -and as it seemed to me, of hatred, such as I have never seen upon human -features. This malignant and terrible contortion, combined with the low -forehead, blunt nose, and prognathous jaw gave the dead man a singularly -simious and ape-like appearance, which was increased by his writhing, -unnatural posture. I have seen death in many forms, but never has -it appeared to me in a more fearsome aspect than in that dark grimy -apartment, which looked out upon one of the main arteries of suburban -London. - -Lestrade, lean and ferret-like as ever, was standing by the doorway, and -greeted my companion and myself. - -"This case will make a stir, sir," he remarked. "It beats anything I -have seen, and I am no chicken." - -"There is no clue?" said Gregson. - -"None at all," chimed in Lestrade. - -Sherlock Holmes approached the body, and, kneeling down, examined it -intently. "You are sure that there is no wound?" he asked, pointing to -numerous gouts and splashes of blood which lay all round. - -"Positive!" cried both detectives. - -"Then, of course, this blood belongs to a second individual--[8]_ -presumably the murderer, if murder has been committed. It reminds me of -the circumstances attendant on the death of Van Jansen, in Utrecht, in -the year '34. Do you remember the case, Gregson?" - -"No, sir." - -"Read it up--you really should. There is nothing new under the sun. It -has all been done before." - -As he spoke, his nimble fingers were flying here, there, and everywhere, -feeling, pressing, unbuttoning, examining, while his eyes wore the same -far-away expression which I have already remarked upon. So swiftly was -the examination made, that one would hardly have guessed the minuteness -with which it was conducted. Finally, he sniffed the dead man's lips, -and then glanced at the soles of his patent leather boots. - -"He has not been moved at all?" he asked. - -"No more than was necessary for the purposes of our examination." - -"You can take him to the mortuary now," he said. "There is nothing more -to be learned." - -Gregson had a stretcher and four men at hand. At his call they entered -the room, and the stranger was lifted and carried out. As they raised -him, a ring tinkled down and rolled across the floor. Lestrade grabbed -it up and stared at it with mystified eyes. - -"There's been a woman here," he cried. "It's a woman's wedding-ring." - -He held it out, as he spoke, upon the palm of his hand. We all gathered -round him and gazed at it. There could be no doubt that that circlet of -plain gold had once adorned the finger of a bride. - -"This complicates matters," said Gregson. "Heaven knows, they were -complicated enough before." - -"You're sure it doesn't simplify them?" observed Holmes. "There's -nothing to be learned by staring at it. What did you find in his -pockets?" - -"We have it all here," said Gregson, pointing to a litter of objects -upon one of the bottom steps of the stairs. "A gold watch, No. 97163, by -Barraud, of London. Gold Albert chain, very heavy and solid. Gold ring, -with masonic device. Gold pin--bull-dog's head, with rubies as eyes. -Russian leather card-case, with cards of Enoch J. Drebber of Cleveland, -corresponding with the E. J. D. upon the linen. No purse, but loose -money to the extent of seven pounds thirteen. Pocket edition of -Boccaccio's 'Decameron,' with name of Joseph Stangerson upon the -fly-leaf. Two letters--one addressed to E. J. Drebber and one to Joseph -Stangerson." - -"At what address?" - -"American Exchange, Strand--to be left till called for. They are both -from the Guion Steamship Company, and refer to the sailing of their -boats from Liverpool. It is clear that this unfortunate man was about to -return to New York." - -"Have you made any inquiries as to this man, Stangerson?" - -"I did it at once, sir," said Gregson. "I have had advertisements -sent to all the newspapers, and one of my men has gone to the American -Exchange, but he has not returned yet." - -"Have you sent to Cleveland?" - -"We telegraphed this morning." - -"How did you word your inquiries?" - -"We simply detailed the circumstances, and said that we should be glad -of any information which could help us." - -"You did not ask for particulars on any point which appeared to you to -be crucial?" - -"I asked about Stangerson." - -"Nothing else? Is there no circumstance on which this whole case appears -to hinge? Will you not telegraph again?" - -"I have said all I have to say," said Gregson, in an offended voice. - -Sherlock Holmes chuckled to himself, and appeared to be about to make -some remark, when Lestrade, who had been in the front room while we -were holding this conversation in the hall, reappeared upon the scene, -rubbing his hands in a pompous and self-satisfied manner. - -"Mr. Gregson," he said, "I have just made a discovery of the highest -importance, and one which would have been overlooked had I not made a -careful examination of the walls." - -The little man's eyes sparkled as he spoke, and he was evidently in -a state of suppressed exultation at having scored a point against his -colleague. - -"Come here," he said, bustling back into the room, the atmosphere of -which felt clearer since the removal of its ghastly inmate. "Now, stand -there!" - -He struck a match on his boot and held it up against the wall. - -"Look at that!" he said, triumphantly. - -I have remarked that the paper had fallen away in parts. In this -particular corner of the room a large piece had peeled off, leaving a -yellow square of coarse plastering. Across this bare space there was -scrawled in blood-red letters a single word-- - - RACHE. - - -"What do you think of that?" cried the detective, with the air of a -showman exhibiting his show. "This was overlooked because it was in the -darkest corner of the room, and no one thought of looking there. The -murderer has written it with his or her own blood. See this smear where -it has trickled down the wall! That disposes of the idea of suicide -anyhow. Why was that corner chosen to write it on? I will tell you. See -that candle on the mantelpiece. It was lit at the time, and if it was -lit this corner would be the brightest instead of the darkest portion of -the wall." - -"And what does it mean now that you _have_ found it?" asked Gregson in a -depreciatory voice. - -"Mean? Why, it means that the writer was going to put the female name -Rachel, but was disturbed before he or she had time to finish. You mark -my words, when this case comes to be cleared up you will find that a -woman named Rachel has something to do with it. It's all very well for -you to laugh, Mr. Sherlock Holmes. You may be very smart and clever, but -the old hound is the best, when all is said and done." - -"I really beg your pardon!" said my companion, who had ruffled the -little man's temper by bursting into an explosion of laughter. "You -certainly have the credit of being the first of us to find this out, -and, as you say, it bears every mark of having been written by the other -participant in last night's mystery. I have not had time to examine this -room yet, but with your permission I shall do so now." - -As he spoke, he whipped a tape measure and a large round magnifying -glass from his pocket. With these two implements he trotted noiselessly -about the room, sometimes stopping, occasionally kneeling, and once -lying flat upon his face. So engrossed was he with his occupation that -he appeared to have forgotten our presence, for he chattered away to -himself under his breath the whole time, keeping up a running fire -of exclamations, groans, whistles, and little cries suggestive of -encouragement and of hope. As I watched him I was irresistibly reminded -of a pure-blooded well-trained foxhound as it dashes backwards and -forwards through the covert, whining in its eagerness, until it comes -across the lost scent. For twenty minutes or more he continued his -researches, measuring with the most exact care the distance between -marks which were entirely invisible to me, and occasionally applying his -tape to the walls in an equally incomprehensible manner. In one place -he gathered up very carefully a little pile of grey dust from the floor, -and packed it away in an envelope. Finally, he examined with his glass -the word upon the wall, going over every letter of it with the most -minute exactness. This done, he appeared to be satisfied, for he -replaced his tape and his glass in his pocket. - -"They say that genius is an infinite capacity for taking pains," he -remarked with a smile. "It's a very bad definition, but it does apply to -detective work." - -Gregson and Lestrade had watched the manoeuvres [9]_ of their amateur -companion with considerable curiosity and some contempt. They evidently -failed to appreciate the fact, which I had begun to realize, that -Sherlock Holmes' smallest actions were all directed towards some -definite and practical end. - -"What do you think of it, sir?" they both asked. - -"It would be robbing you of the credit of the case if I was to presume -to help you," remarked my friend. "You are doing so well now that it -would be a pity for anyone to interfere." There was a world of -sarcasm in his voice as he spoke. "If you will let me know how your -investigations go," he continued, "I shall be happy to give you any help -I can. In the meantime I should like to speak to the constable who found -the body. Can you give me his name and address?" - -Lestrade glanced at his note-book. "John Rance," he said. "He is off -duty now. You will find him at 46, Audley Court, Kennington Park Gate." - -Holmes took a note of the address. - -"Come along, Doctor," he said; "we shall go and look him up. I'll tell -you one thing which may help you in the case," he continued, turning to -the two detectives. "There has been murder done, and the murderer was a -man. He was more than six feet high, was in the prime of life, had -small feet for his height, wore coarse, square-toed boots and smoked a -Trichinopoly cigar. He came here with his victim in a four-wheeled cab, -which was drawn by a horse with three old shoes and one new one on his -off fore leg. In all probability the murderer had a florid face, and the -finger-nails of his right hand were remarkably long. These are only a -few indications, but they may assist you." - -Lestrade and Gregson glanced at each other with an incredulous smile. - -"If this man was murdered, how was it done?" asked the former. - -"Poison," said Sherlock Holmes curtly, and strode off. "One other thing, -Lestrade," he added, turning round at the door: "'Rache,' is the German -for 'revenge;' so don't lose your time looking for Miss Rachel." - -With which Parthian shot he walked away, leaving the two rivals -open-mouthed behind him. - - - - -CHAPTER IV. WHAT JOHN RANCE HAD TO TELL. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -IT was one o'clock when we left No. 3, Lauriston Gardens. Sherlock -Holmes led me to the nearest telegraph office, whence he dispatched a -long telegram. He then hailed a cab, and ordered the driver to take us -to the address given us by Lestrade. - -"There is nothing like first hand evidence," he remarked; "as a matter -of fact, my mind is entirely made up upon the case, but still we may as -well learn all that is to be learned." - -"You amaze me, Holmes," said I. "Surely you are not as sure as you -pretend to be of all those particulars which you gave." - -"There's no room for a mistake," he answered. "The very first thing -which I observed on arriving there was that a cab had made two ruts with -its wheels close to the curb. Now, up to last night, we have had no rain -for a week, so that those wheels which left such a deep impression must -have been there during the night. There were the marks of the horse's -hoofs, too, the outline of one of which was far more clearly cut than -that of the other three, showing that that was a new shoe. Since the cab -was there after the rain began, and was not there at any time during the -morning--I have Gregson's word for that--it follows that it must have -been there during the night, and, therefore, that it brought those two -individuals to the house." - -"That seems simple enough," said I; "but how about the other man's -height?" - -"Why, the height of a man, in nine cases out of ten, can be told from -the length of his stride. It is a simple calculation enough, though -there is no use my boring you with figures. I had this fellow's stride -both on the clay outside and on the dust within. Then I had a way of -checking my calculation. When a man writes on a wall, his instinct leads -him to write about the level of his own eyes. Now that writing was just -over six feet from the ground. It was child's play." - -"And his age?" I asked. - -"Well, if a man can stride four and a-half feet without the smallest -effort, he can't be quite in the sere and yellow. That was the breadth -of a puddle on the garden walk which he had evidently walked across. -Patent-leather boots had gone round, and Square-toes had hopped over. -There is no mystery about it at all. I am simply applying to ordinary -life a few of those precepts of observation and deduction which I -advocated in that article. Is there anything else that puzzles you?" - -"The finger nails and the Trichinopoly," I suggested. - -"The writing on the wall was done with a man's forefinger dipped in -blood. My glass allowed me to observe that the plaster was slightly -scratched in doing it, which would not have been the case if the man's -nail had been trimmed. I gathered up some scattered ash from the floor. -It was dark in colour and flakey--such an ash as is only made by a -Trichinopoly. I have made a special study of cigar ashes--in fact, I -have written a monograph upon the subject. I flatter myself that I can -distinguish at a glance the ash of any known brand, either of cigar -or of tobacco. It is just in such details that the skilled detective -differs from the Gregson and Lestrade type." - -"And the florid face?" I asked. - -"Ah, that was a more daring shot, though I have no doubt that I was -right. You must not ask me that at the present state of the affair." - -I passed my hand over my brow. "My head is in a whirl," I remarked; "the -more one thinks of it the more mysterious it grows. How came these two -men--if there were two men--into an empty house? What has become of the -cabman who drove them? How could one man compel another to take poison? -Where did the blood come from? What was the object of the murderer, -since robbery had no part in it? How came the woman's ring there? Above -all, why should the second man write up the German word RACHE before -decamping? I confess that I cannot see any possible way of reconciling -all these facts." - -My companion smiled approvingly. - -"You sum up the difficulties of the situation succinctly and well," he -said. "There is much that is still obscure, though I have quite made up -my mind on the main facts. As to poor Lestrade's discovery it was simply -a blind intended to put the police upon a wrong track, by suggesting -Socialism and secret societies. It was not done by a German. The A, if -you noticed, was printed somewhat after the German fashion. Now, a real -German invariably prints in the Latin character, so that we may safely -say that this was not written by one, but by a clumsy imitator who -overdid his part. It was simply a ruse to divert inquiry into a wrong -channel. I'm not going to tell you much more of the case, Doctor. You -know a conjuror gets no credit when once he has explained his trick, -and if I show you too much of my method of working, you will come to the -conclusion that I am a very ordinary individual after all." - -"I shall never do that," I answered; "you have brought detection as near -an exact science as it ever will be brought in this world." - -My companion flushed up with pleasure at my words, and the earnest way -in which I uttered them. I had already observed that he was as sensitive -to flattery on the score of his art as any girl could be of her beauty. - -"I'll tell you one other thing," he said. "Patent leathers [10]_ and -Square-toes came in the same cab, and they walked down the pathway -together as friendly as possible--arm-in-arm, in all probability. -When they got inside they walked up and down the room--or rather, -Patent-leathers stood still while Square-toes walked up and down. I -could read all that in the dust; and I could read that as he walked he -grew more and more excited. That is shown by the increased length of his -strides. He was talking all the while, and working himself up, no doubt, -into a fury. Then the tragedy occurred. I've told you all I know myself -now, for the rest is mere surmise and conjecture. We have a good working -basis, however, on which to start. We must hurry up, for I want to go to -Halle's concert to hear Norman Neruda this afternoon." - -This conversation had occurred while our cab had been threading its way -through a long succession of dingy streets and dreary by-ways. In the -dingiest and dreariest of them our driver suddenly came to a stand. -"That's Audley Court in there," he said, pointing to a narrow slit in -the line of dead-coloured brick. "You'll find me here when you come -back." - -Audley Court was not an attractive locality. The narrow passage led us -into a quadrangle paved with flags and lined by sordid dwellings. We -picked our way among groups of dirty children, and through lines of -discoloured linen, until we came to Number 46, the door of which -was decorated with a small slip of brass on which the name Rance was -engraved. On enquiry we found that the constable was in bed, and we were -shown into a little front parlour to await his coming. - -He appeared presently, looking a little irritable at being disturbed in -his slumbers. "I made my report at the office," he said. - -Holmes took a half-sovereign from his pocket and played with it -pensively. "We thought that we should like to hear it all from your own -lips," he said. - -"I shall be most happy to tell you anything I can," the constable -answered with his eyes upon the little golden disk. - -"Just let us hear it all in your own way as it occurred." - -Rance sat down on the horsehair sofa, and knitted his brows as though -determined not to omit anything in his narrative. - -"I'll tell it ye from the beginning," he said. "My time is from ten at -night to six in the morning. At eleven there was a fight at the 'White -Hart'; but bar that all was quiet enough on the beat. At one o'clock it -began to rain, and I met Harry Murcher--him who has the Holland Grove -beat--and we stood together at the corner of Henrietta Street a-talkin'. -Presently--maybe about two or a little after--I thought I would take -a look round and see that all was right down the Brixton Road. It was -precious dirty and lonely. Not a soul did I meet all the way down, -though a cab or two went past me. I was a strollin' down, thinkin' -between ourselves how uncommon handy a four of gin hot would be, when -suddenly the glint of a light caught my eye in the window of that same -house. Now, I knew that them two houses in Lauriston Gardens was empty -on account of him that owns them who won't have the drains seen to, -though the very last tenant what lived in one of them died o' typhoid -fever. I was knocked all in a heap therefore at seeing a light in -the window, and I suspected as something was wrong. When I got to the -door----" - -"You stopped, and then walked back to the garden gate," my companion -interrupted. "What did you do that for?" - -Rance gave a violent jump, and stared at Sherlock Holmes with the utmost -amazement upon his features. - -"Why, that's true, sir," he said; "though how you come to know it, -Heaven only knows. Ye see, when I got up to the door it was so still and -so lonesome, that I thought I'd be none the worse for some one with me. -I ain't afeared of anything on this side o' the grave; but I thought -that maybe it was him that died o' the typhoid inspecting the drains -what killed him. The thought gave me a kind o' turn, and I walked back -to the gate to see if I could see Murcher's lantern, but there wasn't no -sign of him nor of anyone else." - -"There was no one in the street?" - -"Not a livin' soul, sir, nor as much as a dog. Then I pulled myself -together and went back and pushed the door open. All was quiet inside, -so I went into the room where the light was a-burnin'. There was a -candle flickerin' on the mantelpiece--a red wax one--and by its light I -saw----" - -"Yes, I know all that you saw. You walked round the room several times, -and you knelt down by the body, and then you walked through and tried -the kitchen door, and then----" - -John Rance sprang to his feet with a frightened face and suspicion in -his eyes. "Where was you hid to see all that?" he cried. "It seems to me -that you knows a deal more than you should." - -Holmes laughed and threw his card across the table to the constable. -"Don't get arresting me for the murder," he said. "I am one of the -hounds and not the wolf; Mr. Gregson or Mr. Lestrade will answer for -that. Go on, though. What did you do next?" - -Rance resumed his seat, without however losing his mystified expression. -"I went back to the gate and sounded my whistle. That brought Murcher -and two more to the spot." - -"Was the street empty then?" - -"Well, it was, as far as anybody that could be of any good goes." - -"What do you mean?" - -The constable's features broadened into a grin. "I've seen many a drunk -chap in my time," he said, "but never anyone so cryin' drunk as -that cove. He was at the gate when I came out, a-leanin' up agin the -railings, and a-singin' at the pitch o' his lungs about Columbine's -New-fangled Banner, or some such stuff. He couldn't stand, far less -help." - -"What sort of a man was he?" asked Sherlock Holmes. - -John Rance appeared to be somewhat irritated at this digression. "He was -an uncommon drunk sort o' man," he said. "He'd ha' found hisself in the -station if we hadn't been so took up." - -"His face--his dress--didn't you notice them?" Holmes broke in -impatiently. - -"I should think I did notice them, seeing that I had to prop him up--me -and Murcher between us. He was a long chap, with a red face, the lower -part muffled round----" - -"That will do," cried Holmes. "What became of him?" - -"We'd enough to do without lookin' after him," the policeman said, in an -aggrieved voice. "I'll wager he found his way home all right." - -"How was he dressed?" - -"A brown overcoat." - -"Had he a whip in his hand?" - -"A whip--no." - -"He must have left it behind," muttered my companion. "You didn't happen -to see or hear a cab after that?" - -"No." - -"There's a half-sovereign for you," my companion said, standing up and -taking his hat. "I am afraid, Rance, that you will never rise in the -force. That head of yours should be for use as well as ornament. You -might have gained your sergeant's stripes last night. The man whom you -held in your hands is the man who holds the clue of this mystery, and -whom we are seeking. There is no use of arguing about it now; I tell you -that it is so. Come along, Doctor." - -We started off for the cab together, leaving our informant incredulous, -but obviously uncomfortable. - -"The blundering fool," Holmes said, bitterly, as we drove back to our -lodgings. "Just to think of his having such an incomparable bit of good -luck, and not taking advantage of it." - -"I am rather in the dark still. It is true that the description of this -man tallies with your idea of the second party in this mystery. But why -should he come back to the house after leaving it? That is not the way -of criminals." - -"The ring, man, the ring: that was what he came back for. If we have no -other way of catching him, we can always bait our line with the ring. I -shall have him, Doctor--I'll lay you two to one that I have him. I must -thank you for it all. I might not have gone but for you, and so have -missed the finest study I ever came across: a study in scarlet, eh? -Why shouldn't we use a little art jargon. There's the scarlet thread of -murder running through the colourless skein of life, and our duty is -to unravel it, and isolate it, and expose every inch of it. And now -for lunch, and then for Norman Neruda. Her attack and her bowing -are splendid. What's that little thing of Chopin's she plays so -magnificently: Tra-la-la-lira-lira-lay." - -Leaning back in the cab, this amateur bloodhound carolled away like a -lark while I meditated upon the many-sidedness of the human mind. - - - - -CHAPTER V. OUR ADVERTISEMENT BRINGS A VISITOR. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -OUR morning's exertions had been too much for my weak health, and I was -tired out in the afternoon. After Holmes' departure for the concert, I -lay down upon the sofa and endeavoured to get a couple of hours' sleep. -It was a useless attempt. My mind had been too much excited by all that -had occurred, and the strangest fancies and surmises crowded into -it. Every time that I closed my eyes I saw before me the distorted -baboon-like countenance of the murdered man. So sinister was the -impression which that face had produced upon me that I found it -difficult to feel anything but gratitude for him who had removed its -owner from the world. If ever human features bespoke vice of the most -malignant type, they were certainly those of Enoch J. Drebber, of -Cleveland. Still I recognized that justice must be done, and that the -depravity of the victim was no condonment [11]_ in the eyes of the law. - -The more I thought of it the more extraordinary did my companion's -hypothesis, that the man had been poisoned, appear. I remembered how he -had sniffed his lips, and had no doubt that he had detected something -which had given rise to the idea. Then, again, if not poison, what -had caused the man's death, since there was neither wound nor marks of -strangulation? But, on the other hand, whose blood was that which lay so -thickly upon the floor? There were no signs of a struggle, nor had the -victim any weapon with which he might have wounded an antagonist. As -long as all these questions were unsolved, I felt that sleep would be -no easy matter, either for Holmes or myself. His quiet self-confident -manner convinced me that he had already formed a theory which explained -all the facts, though what it was I could not for an instant conjecture. - -He was very late in returning--so late, that I knew that the concert -could not have detained him all the time. Dinner was on the table before -he appeared. - -"It was magnificent," he said, as he took his seat. "Do you remember -what Darwin says about music? He claims that the power of producing and -appreciating it existed among the human race long before the power of -speech was arrived at. Perhaps that is why we are so subtly influenced -by it. There are vague memories in our souls of those misty centuries -when the world was in its childhood." - -"That's rather a broad idea," I remarked. - -"One's ideas must be as broad as Nature if they are to interpret -Nature," he answered. "What's the matter? You're not looking quite -yourself. This Brixton Road affair has upset you." - -"To tell the truth, it has," I said. "I ought to be more case-hardened -after my Afghan experiences. I saw my own comrades hacked to pieces at -Maiwand without losing my nerve." - -"I can understand. There is a mystery about this which stimulates the -imagination; where there is no imagination there is no horror. Have you -seen the evening paper?" - -"No." - -"It gives a fairly good account of the affair. It does not mention the -fact that when the man was raised up, a woman's wedding ring fell upon -the floor. It is just as well it does not." - -"Why?" - -"Look at this advertisement," he answered. "I had one sent to every -paper this morning immediately after the affair." - -He threw the paper across to me and I glanced at the place indicated. It -was the first announcement in the "Found" column. "In Brixton Road, -this morning," it ran, "a plain gold wedding ring, found in the roadway -between the 'White Hart' Tavern and Holland Grove. Apply Dr. Watson, -221B, Baker Street, between eight and nine this evening." - -"Excuse my using your name," he said. "If I used my own some of these -dunderheads would recognize it, and want to meddle in the affair." - -"That is all right," I answered. "But supposing anyone applies, I have -no ring." - -"Oh yes, you have," said he, handing me one. "This will do very well. It -is almost a facsimile." - -"And who do you expect will answer this advertisement." - -"Why, the man in the brown coat--our florid friend with the square toes. -If he does not come himself he will send an accomplice." - -"Would he not consider it as too dangerous?" - -"Not at all. If my view of the case is correct, and I have every reason -to believe that it is, this man would rather risk anything than lose the -ring. According to my notion he dropped it while stooping over Drebber's -body, and did not miss it at the time. After leaving the house he -discovered his loss and hurried back, but found the police already in -possession, owing to his own folly in leaving the candle burning. He had -to pretend to be drunk in order to allay the suspicions which might have -been aroused by his appearance at the gate. Now put yourself in that -man's place. On thinking the matter over, it must have occurred to him -that it was possible that he had lost the ring in the road after leaving -the house. What would he do, then? He would eagerly look out for the -evening papers in the hope of seeing it among the articles found. His -eye, of course, would light upon this. He would be overjoyed. Why should -he fear a trap? There would be no reason in his eyes why the finding -of the ring should be connected with the murder. He would come. He will -come. You shall see him within an hour?" - -"And then?" I asked. - -"Oh, you can leave me to deal with him then. Have you any arms?" - -"I have my old service revolver and a few cartridges." - -"You had better clean it and load it. He will be a desperate man, -and though I shall take him unawares, it is as well to be ready for -anything." - -I went to my bedroom and followed his advice. When I returned with -the pistol the table had been cleared, and Holmes was engaged in his -favourite occupation of scraping upon his violin. - -"The plot thickens," he said, as I entered; "I have just had an answer -to my American telegram. My view of the case is the correct one." - -"And that is?" I asked eagerly. - -"My fiddle would be the better for new strings," he remarked. "Put your -pistol in your pocket. When the fellow comes speak to him in an ordinary -way. Leave the rest to me. Don't frighten him by looking at him too -hard." - -"It is eight o'clock now," I said, glancing at my watch. - -"Yes. He will probably be here in a few minutes. Open the door slightly. -That will do. Now put the key on the inside. Thank you! This is a -queer old book I picked up at a stall yesterday--'De Jure inter -Gentes'--published in Latin at Liege in the Lowlands, in 1642. Charles' -head was still firm on his shoulders when this little brown-backed -volume was struck off." - -"Who is the printer?" - -"Philippe de Croy, whoever he may have been. On the fly-leaf, in very -faded ink, is written 'Ex libris Guliolmi Whyte.' I wonder who William -Whyte was. Some pragmatical seventeenth century lawyer, I suppose. His -writing has a legal twist about it. Here comes our man, I think." - -As he spoke there was a sharp ring at the bell. Sherlock Holmes rose -softly and moved his chair in the direction of the door. We heard the -servant pass along the hall, and the sharp click of the latch as she -opened it. - -"Does Dr. Watson live here?" asked a clear but rather harsh voice. We -could not hear the servant's reply, but the door closed, and some one -began to ascend the stairs. The footfall was an uncertain and shuffling -one. A look of surprise passed over the face of my companion as he -listened to it. It came slowly along the passage, and there was a feeble -tap at the door. - -"Come in," I cried. - -At my summons, instead of the man of violence whom we expected, a very -old and wrinkled woman hobbled into the apartment. She appeared to be -dazzled by the sudden blaze of light, and after dropping a curtsey, she -stood blinking at us with her bleared eyes and fumbling in her pocket -with nervous, shaky fingers. I glanced at my companion, and his face -had assumed such a disconsolate expression that it was all I could do to -keep my countenance. - -The old crone drew out an evening paper, and pointed at our -advertisement. "It's this as has brought me, good gentlemen," she said, -dropping another curtsey; "a gold wedding ring in the Brixton Road. It -belongs to my girl Sally, as was married only this time twelvemonth, -which her husband is steward aboard a Union boat, and what he'd say if -he come 'ome and found her without her ring is more than I can think, he -being short enough at the best o' times, but more especially when he -has the drink. If it please you, she went to the circus last night along -with----" - -"Is that her ring?" I asked. - -"The Lord be thanked!" cried the old woman; "Sally will be a glad woman -this night. That's the ring." - -"And what may your address be?" I inquired, taking up a pencil. - -"13, Duncan Street, Houndsditch. A weary way from here." - -"The Brixton Road does not lie between any circus and Houndsditch," said -Sherlock Holmes sharply. - -The old woman faced round and looked keenly at him from her little -red-rimmed eyes. "The gentleman asked me for _my_ address," she said. -"Sally lives in lodgings at 3, Mayfield Place, Peckham." - -"And your name is----?" - -"My name is Sawyer--her's is Dennis, which Tom Dennis married her--and -a smart, clean lad, too, as long as he's at sea, and no steward in the -company more thought of; but when on shore, what with the women and what -with liquor shops----" - -"Here is your ring, Mrs. Sawyer," I interrupted, in obedience to a sign -from my companion; "it clearly belongs to your daughter, and I am glad -to be able to restore it to the rightful owner." - -With many mumbled blessings and protestations of gratitude the old crone -packed it away in her pocket, and shuffled off down the stairs. Sherlock -Holmes sprang to his feet the moment that she was gone and rushed into -his room. He returned in a few seconds enveloped in an ulster and -a cravat. "I'll follow her," he said, hurriedly; "she must be an -accomplice, and will lead me to him. Wait up for me." The hall door had -hardly slammed behind our visitor before Holmes had descended the stair. -Looking through the window I could see her walking feebly along the -other side, while her pursuer dogged her some little distance behind. -"Either his whole theory is incorrect," I thought to myself, "or else he -will be led now to the heart of the mystery." There was no need for him -to ask me to wait up for him, for I felt that sleep was impossible until -I heard the result of his adventure. - -It was close upon nine when he set out. I had no idea how long he might -be, but I sat stolidly puffing at my pipe and skipping over the pages -of Henri Murger's "Vie de Bohème." Ten o'clock passed, and I heard the -footsteps of the maid as they pattered off to bed. Eleven, and the -more stately tread of the landlady passed my door, bound for the same -destination. It was close upon twelve before I heard the sharp sound of -his latch-key. The instant he entered I saw by his face that he had not -been successful. Amusement and chagrin seemed to be struggling for the -mastery, until the former suddenly carried the day, and he burst into a -hearty laugh. - -"I wouldn't have the Scotland Yarders know it for the world," he cried, -dropping into his chair; "I have chaffed them so much that they would -never have let me hear the end of it. I can afford to laugh, because I -know that I will be even with them in the long run." - -"What is it then?" I asked. - -"Oh, I don't mind telling a story against myself. That creature had -gone a little way when she began to limp and show every sign of being -foot-sore. Presently she came to a halt, and hailed a four-wheeler which -was passing. I managed to be close to her so as to hear the address, but -I need not have been so anxious, for she sang it out loud enough to -be heard at the other side of the street, 'Drive to 13, Duncan Street, -Houndsditch,' she cried. This begins to look genuine, I thought, and -having seen her safely inside, I perched myself behind. That's an art -which every detective should be an expert at. Well, away we rattled, and -never drew rein until we reached the street in question. I hopped off -before we came to the door, and strolled down the street in an easy, -lounging way. I saw the cab pull up. The driver jumped down, and I saw -him open the door and stand expectantly. Nothing came out though. When -I reached him he was groping about frantically in the empty cab, and -giving vent to the finest assorted collection of oaths that ever I -listened to. There was no sign or trace of his passenger, and I fear it -will be some time before he gets his fare. On inquiring at Number 13 -we found that the house belonged to a respectable paperhanger, named -Keswick, and that no one of the name either of Sawyer or Dennis had ever -been heard of there." - -"You don't mean to say," I cried, in amazement, "that that tottering, -feeble old woman was able to get out of the cab while it was in motion, -without either you or the driver seeing her?" - -"Old woman be damned!" said Sherlock Holmes, sharply. "We were the old -women to be so taken in. It must have been a young man, and an -active one, too, besides being an incomparable actor. The get-up was -inimitable. He saw that he was followed, no doubt, and used this means -of giving me the slip. It shows that the man we are after is not as -lonely as I imagined he was, but has friends who are ready to risk -something for him. Now, Doctor, you are looking done-up. Take my advice -and turn in." - -I was certainly feeling very weary, so I obeyed his injunction. I -left Holmes seated in front of the smouldering fire, and long into the -watches of the night I heard the low, melancholy wailings of his violin, -and knew that he was still pondering over the strange problem which he -had set himself to unravel. - - - - -CHAPTER VI. TOBIAS GREGSON SHOWS WHAT HE CAN DO. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -THE papers next day were full of the "Brixton Mystery," as they termed -it. Each had a long account of the affair, and some had leaders upon it -in addition. There was some information in them which was new to me. I -still retain in my scrap-book numerous clippings and extracts bearing -upon the case. Here is a condensation of a few of them:-- - -The *Daily Telegraph* remarked that in the history of crime there had -seldom been a tragedy which presented stranger features. The German -name of the victim, the absence of all other motive, and the sinister -inscription on the wall, all pointed to its perpetration by political -refugees and revolutionists. The Socialists had many branches in -America, and the deceased had, no doubt, infringed their unwritten laws, -and been tracked down by them. After alluding airily to the Vehmgericht, -aqua tofana, Carbonari, the Marchioness de Brinvilliers, the Darwinian -theory, the principles of Malthus, and the Ratcliff Highway murders, the -article concluded by admonishing the Government and advocating a closer -watch over foreigners in England. - -The _Standard_ commented upon the fact that lawless outrages of the sort -usually occurred under a Liberal Administration. They arose from the -unsettling of the minds of the masses, and the consequent weakening -of all authority. The deceased was an American gentleman who had -been residing for some weeks in the Metropolis. He had stayed at the -boarding-house of Madame Charpentier, in Torquay Terrace, Camberwell. -He was accompanied in his travels by his private secretary, Mr. Joseph -Stangerson. The two bade adieu to their landlady upon Tuesday, the -4th inst., and departed to Euston Station with the avowed intention of -catching the Liverpool express. They were afterwards seen together upon -the platform. Nothing more is known of them until Mr. Drebber's body -was, as recorded, discovered in an empty house in the Brixton Road, -many miles from Euston. How he came there, or how he met his fate, are -questions which are still involved in mystery. Nothing is known of the -whereabouts of Stangerson. We are glad to learn that Mr. Lestrade and -Mr. Gregson, of Scotland Yard, are both engaged upon the case, and it -is confidently anticipated that these well-known officers will speedily -throw light upon the matter. - -The *Daily News* observed that there was no doubt as to the crime being -a political one. The despotism and hatred of Liberalism which animated -the Continental Governments had had the effect of driving to our shores -a number of men who might have made excellent citizens were they not -soured by the recollection of all that they had undergone. Among these -men there was a stringent code of honour, any infringement of which was -punished by death. Every effort should be made to find the secretary, -Stangerson, and to ascertain some particulars of the habits of the -deceased. A great step had been gained by the discovery of the address -of the house at which he had boarded--a result which was entirely due to -the acuteness and energy of Mr. Gregson of Scotland Yard. - -Sherlock Holmes and I read these notices over together at breakfast, and -they appeared to afford him considerable amusement. - -"I told you that, whatever happened, Lestrade and Gregson would be sure -to score." - -"That depends on how it turns out." - -"Oh, bless you, it doesn't matter in the least. If the man is caught, it -will be *on account* of their exertions; if he escapes, it will be *in -spite* of their exertions. It's heads I win and tails you lose. Whatever -they do, they will have followers. 'Un sot trouve toujours un plus sot -qui l'admire.'" - -"What on earth is this?" I cried, for at this moment there came the -pattering of many steps in the hall and on the stairs, accompanied by -audible expressions of disgust upon the part of our landlady. - -"It's the Baker Street division of the detective police force," said my -companion, gravely; and as he spoke there rushed into the room half a -dozen of the dirtiest and most ragged street Arabs that ever I clapped -eyes on. - -"'Tention!" cried Holmes, in a sharp tone, and the six dirty little -scoundrels stood in a line like so many disreputable statuettes. "In -future you shall send up Wiggins alone to report, and the rest of you -must wait in the street. Have you found it, Wiggins?" - -"No, sir, we hain't," said one of the youths. - -"I hardly expected you would. You must keep on until you do. Here are -your wages." [13]_ He handed each of them a shilling. - -"Now, off you go, and come back with a better report next time." - -He waved his hand, and they scampered away downstairs like so many rats, -and we heard their shrill voices next moment in the street. - -"There's more work to be got out of one of those little beggars than -out of a dozen of the force," Holmes remarked. "The mere sight of an -official-looking person seals men's lips. These youngsters, however, go -everywhere and hear everything. They are as sharp as needles, too; all -they want is organisation." - -"Is it on this Brixton case that you are employing them?" I asked. - -"Yes; there is a point which I wish to ascertain. It is merely a matter -of time. Hullo! we are going to hear some news now with a vengeance! -Here is Gregson coming down the road with beatitude written upon every -feature of his face. Bound for us, I know. Yes, he is stopping. There he -is!" - -There was a violent peal at the bell, and in a few seconds the -fair-haired detective came up the stairs, three steps at a time, and -burst into our sitting-room. - -"My dear fellow," he cried, wringing Holmes' unresponsive hand, -"congratulate me! I have made the whole thing as clear as day." - -A shade of anxiety seemed to me to cross my companion's expressive face. - -"Do you mean that you are on the right track?" he asked. - -"The right track! Why, sir, we have the man under lock and key." - -"And his name is?" - -"Arthur Charpentier, sub-lieutenant in Her Majesty's navy," cried -Gregson, pompously, rubbing his fat hands and inflating his chest. - -Sherlock Holmes gave a sigh of relief, and relaxed into a smile. - -"Take a seat, and try one of these cigars," he said. "We are anxious to -know how you managed it. Will you have some whiskey and water?" - -"I don't mind if I do," the detective answered. "The tremendous -exertions which I have gone through during the last day or two have worn -me out. Not so much bodily exertion, you understand, as the strain upon -the mind. You will appreciate that, Mr. Sherlock Holmes, for we are both -brain-workers." - -"You do me too much honour," said Holmes, gravely. "Let us hear how you -arrived at this most gratifying result." - -The detective seated himself in the arm-chair, and puffed complacently -at his cigar. Then suddenly he slapped his thigh in a paroxysm of -amusement. - -"The fun of it is," he cried, "that that fool Lestrade, who thinks -himself so smart, has gone off upon the wrong track altogether. He is -after the secretary Stangerson, who had no more to do with the crime -than the babe unborn. I have no doubt that he has caught him by this -time." - -The idea tickled Gregson so much that he laughed until he choked. - -"And how did you get your clue?" - -"Ah, I'll tell you all about it. Of course, Doctor Watson, this is -strictly between ourselves. The first difficulty which we had to contend -with was the finding of this American's antecedents. Some people would -have waited until their advertisements were answered, or until parties -came forward and volunteered information. That is not Tobias Gregson's -way of going to work. You remember the hat beside the dead man?" - -"Yes," said Holmes; "by John Underwood and Sons, 129, Camberwell Road." - -Gregson looked quite crest-fallen. - -"I had no idea that you noticed that," he said. "Have you been there?" - -"No." - -"Ha!" cried Gregson, in a relieved voice; "you should never neglect a -chance, however small it may seem." - -"To a great mind, nothing is little," remarked Holmes, sententiously. - -"Well, I went to Underwood, and asked him if he had sold a hat of that -size and description. He looked over his books, and came on it at once. -He had sent the hat to a Mr. Drebber, residing at Charpentier's Boarding -Establishment, Torquay Terrace. Thus I got at his address." - -"Smart--very smart!" murmured Sherlock Holmes. - -"I next called upon Madame Charpentier," continued the detective. -"I found her very pale and distressed. Her daughter was in the room, -too--an uncommonly fine girl she is, too; she was looking red about -the eyes and her lips trembled as I spoke to her. That didn't escape -my notice. I began to smell a rat. You know the feeling, Mr. Sherlock -Holmes, when you come upon the right scent--a kind of thrill in your -nerves. 'Have you heard of the mysterious death of your late boarder Mr. -Enoch J. Drebber, of Cleveland?' I asked. - -"The mother nodded. She didn't seem able to get out a word. The daughter -burst into tears. I felt more than ever that these people knew something -of the matter. - -"'At what o'clock did Mr. Drebber leave your house for the train?' I -asked. - -"'At eight o'clock,' she said, gulping in her throat to keep down her -agitation. 'His secretary, Mr. Stangerson, said that there were two -trains--one at 9.15 and one at 11. He was to catch the first. [14]_ - -"'And was that the last which you saw of him?' - -"A terrible change came over the woman's face as I asked the question. -Her features turned perfectly livid. It was some seconds before she -could get out the single word 'Yes'--and when it did come it was in a -husky unnatural tone. - -"There was silence for a moment, and then the daughter spoke in a calm -clear voice. - -"'No good can ever come of falsehood, mother,' she said. 'Let us be -frank with this gentleman. We _did_ see Mr. Drebber again.' - -"'God forgive you!' cried Madame Charpentier, throwing up her hands and -sinking back in her chair. 'You have murdered your brother.' - -"'Arthur would rather that we spoke the truth,' the girl answered -firmly. - -"'You had best tell me all about it now,' I said. 'Half-confidences are -worse than none. Besides, you do not know how much we know of it.' - -"'On your head be it, Alice!' cried her mother; and then, turning to me, -'I will tell you all, sir. Do not imagine that my agitation on behalf -of my son arises from any fear lest he should have had a hand in this -terrible affair. He is utterly innocent of it. My dread is, however, -that in your eyes and in the eyes of others he may appear to be -compromised. That however is surely impossible. His high character, his -profession, his antecedents would all forbid it.' - -"'Your best way is to make a clean breast of the facts,' I answered. -'Depend upon it, if your son is innocent he will be none the worse.' - -"'Perhaps, Alice, you had better leave us together,' she said, and her -daughter withdrew. 'Now, sir,' she continued, 'I had no intention of -telling you all this, but since my poor daughter has disclosed it I -have no alternative. Having once decided to speak, I will tell you all -without omitting any particular.' - -"'It is your wisest course,' said I. - -"'Mr. Drebber has been with us nearly three weeks. He and his secretary, -Mr. Stangerson, had been travelling on the Continent. I noticed a -"Copenhagen" label upon each of their trunks, showing that that had been -their last stopping place. Stangerson was a quiet reserved man, but his -employer, I am sorry to say, was far otherwise. He was coarse in his -habits and brutish in his ways. The very night of his arrival he became -very much the worse for drink, and, indeed, after twelve o'clock in the -day he could hardly ever be said to be sober. His manners towards the -maid-servants were disgustingly free and familiar. Worst of all, he -speedily assumed the same attitude towards my daughter, Alice, and spoke -to her more than once in a way which, fortunately, she is too innocent -to understand. On one occasion he actually seized her in his arms and -embraced her--an outrage which caused his own secretary to reproach him -for his unmanly conduct.' - -"'But why did you stand all this,' I asked. 'I suppose that you can get -rid of your boarders when you wish.' - -"Mrs. Charpentier blushed at my pertinent question. 'Would to God that -I had given him notice on the very day that he came,' she said. 'But -it was a sore temptation. They were paying a pound a day each--fourteen -pounds a week, and this is the slack season. I am a widow, and my boy in -the Navy has cost me much. I grudged to lose the money. I acted for the -best. This last was too much, however, and I gave him notice to leave on -account of it. That was the reason of his going.' - -"'Well?' - -"'My heart grew light when I saw him drive away. My son is on leave -just now, but I did not tell him anything of all this, for his temper -is violent, and he is passionately fond of his sister. When I closed the -door behind them a load seemed to be lifted from my mind. Alas, in -less than an hour there was a ring at the bell, and I learned that Mr. -Drebber had returned. He was much excited, and evidently the worse for -drink. He forced his way into the room, where I was sitting with my -daughter, and made some incoherent remark about having missed his train. -He then turned to Alice, and before my very face, proposed to her that -she should fly with him. "You are of age," he said, "and there is no law -to stop you. I have money enough and to spare. Never mind the old girl -here, but come along with me now straight away. You shall live like a -princess." Poor Alice was so frightened that she shrunk away from him, -but he caught her by the wrist and endeavoured to draw her towards the -door. I screamed, and at that moment my son Arthur came into the room. -What happened then I do not know. I heard oaths and the confused sounds -of a scuffle. I was too terrified to raise my head. When I did look up -I saw Arthur standing in the doorway laughing, with a stick in his hand. -"I don't think that fine fellow will trouble us again," he said. "I will -just go after him and see what he does with himself." With those words -he took his hat and started off down the street. The next morning we -heard of Mr. Drebber's mysterious death.' - -"This statement came from Mrs. Charpentier's lips with many gasps and -pauses. At times she spoke so low that I could hardly catch the words. I -made shorthand notes of all that she said, however, so that there should -be no possibility of a mistake." - -"It's quite exciting," said Sherlock Holmes, with a yawn. "What happened -next?" - -"When Mrs. Charpentier paused," the detective continued, "I saw that the -whole case hung upon one point. Fixing her with my eye in a way which -I always found effective with women, I asked her at what hour her son -returned. - -"'I do not know,' she answered. - -"'Not know?' - -"'No; he has a latch-key, and he let himself in.' - -"'After you went to bed?' - -"'Yes.' - -"'When did you go to bed?' - -"'About eleven.' - -"'So your son was gone at least two hours?' - -"'Yes.' - -"'Possibly four or five?' - -"'Yes.' - -"'What was he doing during that time?' - -"'I do not know,' she answered, turning white to her very lips. - -"Of course after that there was nothing more to be done. I found -out where Lieutenant Charpentier was, took two officers with me, and -arrested him. When I touched him on the shoulder and warned him to come -quietly with us, he answered us as bold as brass, 'I suppose you -are arresting me for being concerned in the death of that scoundrel -Drebber,' he said. We had said nothing to him about it, so that his -alluding to it had a most suspicious aspect." - -"Very," said Holmes. - -"He still carried the heavy stick which the mother described him as -having with him when he followed Drebber. It was a stout oak cudgel." - -"What is your theory, then?" - -"Well, my theory is that he followed Drebber as far as the Brixton Road. -When there, a fresh altercation arose between them, in the course of -which Drebber received a blow from the stick, in the pit of the stomach, -perhaps, which killed him without leaving any mark. The night was so -wet that no one was about, so Charpentier dragged the body of his victim -into the empty house. As to the candle, and the blood, and the writing -on the wall, and the ring, they may all be so many tricks to throw the -police on to the wrong scent." - -"Well done!" said Holmes in an encouraging voice. "Really, Gregson, you -are getting along. We shall make something of you yet." - -"I flatter myself that I have managed it rather neatly," the detective -answered proudly. "The young man volunteered a statement, in which he -said that after following Drebber some time, the latter perceived him, -and took a cab in order to get away from him. On his way home he met an -old shipmate, and took a long walk with him. On being asked where this -old shipmate lived, he was unable to give any satisfactory reply. I -think the whole case fits together uncommonly well. What amuses me is to -think of Lestrade, who had started off upon the wrong scent. I am afraid -he won't make much of [15]_ Why, by Jove, here's the very man himself!" - -It was indeed Lestrade, who had ascended the stairs while we were -talking, and who now entered the room. The assurance and jauntiness -which generally marked his demeanour and dress were, however, wanting. -His face was disturbed and troubled, while his clothes were disarranged -and untidy. He had evidently come with the intention of consulting -with Sherlock Holmes, for on perceiving his colleague he appeared to be -embarrassed and put out. He stood in the centre of the room, fumbling -nervously with his hat and uncertain what to do. "This is a most -extraordinary case," he said at last--"a most incomprehensible affair." - -"Ah, you find it so, Mr. Lestrade!" cried Gregson, triumphantly. "I -thought you would come to that conclusion. Have you managed to find the -Secretary, Mr. Joseph Stangerson?" - -"The Secretary, Mr. Joseph Stangerson," said Lestrade gravely, "was -murdered at Halliday's Private Hotel about six o'clock this morning." - - - - -CHAPTER VII. LIGHT IN THE DARKNESS. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -THE intelligence with which Lestrade greeted us was so momentous and so -unexpected, that we were all three fairly dumfoundered. Gregson sprang -out of his chair and upset the remainder of his whiskey and water. I -stared in silence at Sherlock Holmes, whose lips were compressed and his -brows drawn down over his eyes. - -"Stangerson too!" he muttered. "The plot thickens." - -"It was quite thick enough before," grumbled Lestrade, taking a chair. -"I seem to have dropped into a sort of council of war." - -"Are you--are you sure of this piece of intelligence?" stammered -Gregson. - -"I have just come from his room," said Lestrade. "I was the first to -discover what had occurred." - -"We have been hearing Gregson's view of the matter," Holmes observed. -"Would you mind letting us know what you have seen and done?" - -"I have no objection," Lestrade answered, seating himself. "I freely -confess that I was of the opinion that Stangerson was concerned in -the death of Drebber. This fresh development has shown me that I was -completely mistaken. Full of the one idea, I set myself to find out -what had become of the Secretary. They had been seen together at Euston -Station about half-past eight on the evening of the third. At two in the -morning Drebber had been found in the Brixton Road. The question which -confronted me was to find out how Stangerson had been employed between -8.30 and the time of the crime, and what had become of him afterwards. -I telegraphed to Liverpool, giving a description of the man, and warning -them to keep a watch upon the American boats. I then set to work calling -upon all the hotels and lodging-houses in the vicinity of Euston. You -see, I argued that if Drebber and his companion had become separated, -the natural course for the latter would be to put up somewhere in the -vicinity for the night, and then to hang about the station again next -morning." - -"They would be likely to agree on some meeting-place beforehand," -remarked Holmes. - -"So it proved. I spent the whole of yesterday evening in making -enquiries entirely without avail. This morning I began very early, and -at eight o'clock I reached Halliday's Private Hotel, in Little George -Street. On my enquiry as to whether a Mr. Stangerson was living there, -they at once answered me in the affirmative. - -"'No doubt you are the gentleman whom he was expecting,' they said. 'He -has been waiting for a gentleman for two days.' - -"'Where is he now?' I asked. - -"'He is upstairs in bed. He wished to be called at nine.' - -"'I will go up and see him at once,' I said. - -"It seemed to me that my sudden appearance might shake his nerves and -lead him to say something unguarded. The Boots volunteered to show me -the room: it was on the second floor, and there was a small corridor -leading up to it. The Boots pointed out the door to me, and was about to -go downstairs again when I saw something that made me feel sickish, in -spite of my twenty years' experience. From under the door there curled -a little red ribbon of blood, which had meandered across the passage and -formed a little pool along the skirting at the other side. I gave a cry, -which brought the Boots back. He nearly fainted when he saw it. The door -was locked on the inside, but we put our shoulders to it, and knocked it -in. The window of the room was open, and beside the window, all huddled -up, lay the body of a man in his nightdress. He was quite dead, and had -been for some time, for his limbs were rigid and cold. When we turned -him over, the Boots recognized him at once as being the same gentleman -who had engaged the room under the name of Joseph Stangerson. The cause -of death was a deep stab in the left side, which must have penetrated -the heart. And now comes the strangest part of the affair. What do you -suppose was above the murdered man?" - -I felt a creeping of the flesh, and a presentiment of coming horror, -even before Sherlock Holmes answered. - -"The word RACHE, written in letters of blood," he said. - -"That was it," said Lestrade, in an awe-struck voice; and we were all -silent for a while. - -There was something so methodical and so incomprehensible about the -deeds of this unknown assassin, that it imparted a fresh ghastliness to -his crimes. My nerves, which were steady enough on the field of battle -tingled as I thought of it. - -"The man was seen," continued Lestrade. "A milk boy, passing on his way -to the dairy, happened to walk down the lane which leads from the mews -at the back of the hotel. He noticed that a ladder, which usually lay -there, was raised against one of the windows of the second floor, which -was wide open. After passing, he looked back and saw a man descend the -ladder. He came down so quietly and openly that the boy imagined him to -be some carpenter or joiner at work in the hotel. He took no particular -notice of him, beyond thinking in his own mind that it was early for him -to be at work. He has an impression that the man was tall, had a reddish -face, and was dressed in a long, brownish coat. He must have stayed in -the room some little time after the murder, for we found blood-stained -water in the basin, where he had washed his hands, and marks on the -sheets where he had deliberately wiped his knife." - -I glanced at Holmes on hearing the description of the murderer, which -tallied so exactly with his own. There was, however, no trace of -exultation or satisfaction upon his face. - -"Did you find nothing in the room which could furnish a clue to the -murderer?" he asked. - -"Nothing. Stangerson had Drebber's purse in his pocket, but it seems -that this was usual, as he did all the paying. There was eighty odd -pounds in it, but nothing had been taken. Whatever the motives of these -extraordinary crimes, robbery is certainly not one of them. There were -no papers or memoranda in the murdered man's pocket, except a single -telegram, dated from Cleveland about a month ago, and containing -the words, 'J. H. is in Europe.' There was no name appended to this -message." - -"And there was nothing else?" Holmes asked. - -"Nothing of any importance. The man's novel, with which he had read -himself to sleep was lying upon the bed, and his pipe was on a chair -beside him. There was a glass of water on the table, and on the -window-sill a small chip ointment box containing a couple of pills." - -Sherlock Holmes sprang from his chair with an exclamation of delight. - -"The last link," he cried, exultantly. "My case is complete." - -The two detectives stared at him in amazement. - -"I have now in my hands," my companion said, confidently, "all the -threads which have formed such a tangle. There are, of course, details -to be filled in, but I am as certain of all the main facts, from the -time that Drebber parted from Stangerson at the station, up to the -discovery of the body of the latter, as if I had seen them with my own -eyes. I will give you a proof of my knowledge. Could you lay your hand -upon those pills?" - -"I have them," said Lestrade, producing a small white box; "I took them -and the purse and the telegram, intending to have them put in a place of -safety at the Police Station. It was the merest chance my taking these -pills, for I am bound to say that I do not attach any importance to -them." - -"Give them here," said Holmes. "Now, Doctor," turning to me, "are those -ordinary pills?" - -They certainly were not. They were of a pearly grey colour, small, -round, and almost transparent against the light. "From their lightness -and transparency, I should imagine that they are soluble in water," I -remarked. - -"Precisely so," answered Holmes. "Now would you mind going down and -fetching that poor little devil of a terrier which has been bad so long, -and which the landlady wanted you to put out of its pain yesterday." - -I went downstairs and carried the dog upstair in my arms. It's laboured -breathing and glazing eye showed that it was not far from its end. -Indeed, its snow-white muzzle proclaimed that it had already exceeded -the usual term of canine existence. I placed it upon a cushion on the -rug. - -"I will now cut one of these pills in two," said Holmes, and drawing his -penknife he suited the action to the word. "One half we return into the -box for future purposes. The other half I will place in this wine glass, -in which is a teaspoonful of water. You perceive that our friend, the -Doctor, is right, and that it readily dissolves." - -"This may be very interesting," said Lestrade, in the injured tone of -one who suspects that he is being laughed at, "I cannot see, however, -what it has to do with the death of Mr. Joseph Stangerson." - -"Patience, my friend, patience! You will find in time that it has -everything to do with it. I shall now add a little milk to make the -mixture palatable, and on presenting it to the dog we find that he laps -it up readily enough." - -As he spoke he turned the contents of the wine glass into a saucer and -placed it in front of the terrier, who speedily licked it dry. Sherlock -Holmes' earnest demeanour had so far convinced us that we all sat in -silence, watching the animal intently, and expecting some startling -effect. None such appeared, however. The dog continued to lie stretched -upon tho [16]_ cushion, breathing in a laboured way, but apparently -neither the better nor the worse for its draught. - -Holmes had taken out his watch, and as minute followed minute without -result, an expression of the utmost chagrin and disappointment appeared -upon his features. He gnawed his lip, drummed his fingers upon the -table, and showed every other symptom of acute impatience. So great -was his emotion, that I felt sincerely sorry for him, while the two -detectives smiled derisively, by no means displeased at this check which -he had met. - -"It can't be a coincidence," he cried, at last springing from his chair -and pacing wildly up and down the room; "it is impossible that it should -be a mere coincidence. The very pills which I suspected in the case of -Drebber are actually found after the death of Stangerson. And yet they -are inert. What can it mean? Surely my whole chain of reasoning cannot -have been false. It is impossible! And yet this wretched dog is none the -worse. Ah, I have it! I have it!" With a perfect shriek of delight he -rushed to the box, cut the other pill in two, dissolved it, added milk, -and presented it to the terrier. The unfortunate creature's tongue -seemed hardly to have been moistened in it before it gave a convulsive -shiver in every limb, and lay as rigid and lifeless as if it had been -struck by lightning. - -Sherlock Holmes drew a long breath, and wiped the perspiration from his -forehead. "I should have more faith," he said; "I ought to know by -this time that when a fact appears to be opposed to a long train of -deductions, it invariably proves to be capable of bearing some other -interpretation. Of the two pills in that box one was of the most deadly -poison, and the other was entirely harmless. I ought to have known that -before ever I saw the box at all." - -This last statement appeared to me to be so startling, that I could -hardly believe that he was in his sober senses. There was the dead dog, -however, to prove that his conjecture had been correct. It seemed to me -that the mists in my own mind were gradually clearing away, and I began -to have a dim, vague perception of the truth. - -"All this seems strange to you," continued Holmes, "because you failed -at the beginning of the inquiry to grasp the importance of the single -real clue which was presented to you. I had the good fortune to seize -upon that, and everything which has occurred since then has served to -confirm my original supposition, and, indeed, was the logical sequence -of it. Hence things which have perplexed you and made the case more -obscure, have served to enlighten me and to strengthen my conclusions. -It is a mistake to confound strangeness with mystery. The most -commonplace crime is often the most mysterious because it presents no -new or special features from which deductions may be drawn. This murder -would have been infinitely more difficult to unravel had the body of -the victim been simply found lying in the roadway without any of -those _outré_ and sensational accompaniments which have rendered -it remarkable. These strange details, far from making the case more -difficult, have really had the effect of making it less so." - -Mr. Gregson, who had listened to this address with considerable -impatience, could contain himself no longer. "Look here, Mr. Sherlock -Holmes," he said, "we are all ready to acknowledge that you are a smart -man, and that you have your own methods of working. We want something -more than mere theory and preaching now, though. It is a case of taking -the man. I have made my case out, and it seems I was wrong. Young -Charpentier could not have been engaged in this second affair. Lestrade -went after his man, Stangerson, and it appears that he was wrong too. -You have thrown out hints here, and hints there, and seem to know more -than we do, but the time has come when we feel that we have a right to -ask you straight how much you do know of the business. Can you name the -man who did it?" - -"I cannot help feeling that Gregson is right, sir," remarked Lestrade. -"We have both tried, and we have both failed. You have remarked more -than once since I have been in the room that you had all the evidence -which you require. Surely you will not withhold it any longer." - -"Any delay in arresting the assassin," I observed, "might give him time -to perpetrate some fresh atrocity." - -Thus pressed by us all, Holmes showed signs of irresolution. He -continued to walk up and down the room with his head sunk on his chest -and his brows drawn down, as was his habit when lost in thought. - -"There will be no more murders," he said at last, stopping abruptly and -facing us. "You can put that consideration out of the question. You have -asked me if I know the name of the assassin. I do. The mere knowing of -his name is a small thing, however, compared with the power of laying -our hands upon him. This I expect very shortly to do. I have good hopes -of managing it through my own arrangements; but it is a thing which -needs delicate handling, for we have a shrewd and desperate man to deal -with, who is supported, as I have had occasion to prove, by another who -is as clever as himself. As long as this man has no idea that anyone -can have a clue there is some chance of securing him; but if he had the -slightest suspicion, he would change his name, and vanish in an instant -among the four million inhabitants of this great city. Without meaning -to hurt either of your feelings, I am bound to say that I consider these -men to be more than a match for the official force, and that is why I -have not asked your assistance. If I fail I shall, of course, incur all -the blame due to this omission; but that I am prepared for. At present -I am ready to promise that the instant that I can communicate with you -without endangering my own combinations, I shall do so." - -Gregson and Lestrade seemed to be far from satisfied by this assurance, -or by the depreciating allusion to the detective police. The former had -flushed up to the roots of his flaxen hair, while the other's beady eyes -glistened with curiosity and resentment. Neither of them had time to -speak, however, before there was a tap at the door, and the spokesman -of the street Arabs, young Wiggins, introduced his insignificant and -unsavoury person. - -"Please, sir," he said, touching his forelock, "I have the cab -downstairs." - -"Good boy," said Holmes, blandly. "Why don't you introduce this pattern -at Scotland Yard?" he continued, taking a pair of steel handcuffs from -a drawer. "See how beautifully the spring works. They fasten in an -instant." - -"The old pattern is good enough," remarked Lestrade, "if we can only -find the man to put them on." - -"Very good, very good," said Holmes, smiling. "The cabman may as well -help me with my boxes. Just ask him to step up, Wiggins." - -I was surprised to find my companion speaking as though he were about -to set out on a journey, since he had not said anything to me about it. -There was a small portmanteau in the room, and this he pulled out and -began to strap. He was busily engaged at it when the cabman entered the -room. - -"Just give me a help with this buckle, cabman," he said, kneeling over -his task, and never turning his head. - -The fellow came forward with a somewhat sullen, defiant air, and put -down his hands to assist. At that instant there was a sharp click, the -jangling of metal, and Sherlock Holmes sprang to his feet again. - -"Gentlemen," he cried, with flashing eyes, "let me introduce you to Mr. -Jefferson Hope, the murderer of Enoch Drebber and of Joseph Stangerson." - -The whole thing occurred in a moment--so quickly that I had no time -to realize it. I have a vivid recollection of that instant, of Holmes' -triumphant expression and the ring of his voice, of the cabman's -dazed, savage face, as he glared at the glittering handcuffs, which had -appeared as if by magic upon his wrists. For a second or two we might -have been a group of statues. Then, with an inarticulate roar of fury, -the prisoner wrenched himself free from Holmes's grasp, and hurled -himself through the window. Woodwork and glass gave way before him; but -before he got quite through, Gregson, Lestrade, and Holmes sprang upon -him like so many staghounds. He was dragged back into the room, and then -commenced a terrific conflict. So powerful and so fierce was he, that -the four of us were shaken off again and again. He appeared to have the -convulsive strength of a man in an epileptic fit. His face and hands -were terribly mangled by his passage through the glass, but loss of -blood had no effect in diminishing his resistance. It was not until -Lestrade succeeded in getting his hand inside his neckcloth and -half-strangling him that we made him realize that his struggles were of -no avail; and even then we felt no security until we had pinioned his -feet as well as his hands. That done, we rose to our feet breathless and -panting. - -"We have his cab," said Sherlock Holmes. "It will serve to take him to -Scotland Yard. And now, gentlemen," he continued, with a pleasant smile, -"we have reached the end of our little mystery. You are very welcome to -put any questions that you like to me now, and there is no danger that I -will refuse to answer them." - - - - - -PART II. *The Country of the Saints.* -------------------------------------- - - - -CHAPTER I. ON THE GREAT ALKALI PLAIN. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -IN the central portion of the great North American Continent there lies -an arid and repulsive desert, which for many a long year served as a -barrier against the advance of civilisation. From the Sierra Nevada to -Nebraska, and from the Yellowstone River in the north to the Colorado -upon the south, is a region of desolation and silence. Nor is Nature -always in one mood throughout this grim district. It comprises -snow-capped and lofty mountains, and dark and gloomy valleys. There are -swift-flowing rivers which dash through jagged cañons; and there are -enormous plains, which in winter are white with snow, and in summer are -grey with the saline alkali dust. They all preserve, however, the common -characteristics of barrenness, inhospitality, and misery. - -There are no inhabitants of this land of despair. A band of Pawnees -or of Blackfeet may occasionally traverse it in order to reach other -hunting-grounds, but the hardiest of the braves are glad to lose sight -of those awesome plains, and to find themselves once more upon their -prairies. The coyote skulks among the scrub, the buzzard flaps heavily -through the air, and the clumsy grizzly bear lumbers through the dark -ravines, and picks up such sustenance as it can amongst the rocks. These -are the sole dwellers in the wilderness. - -In the whole world there can be no more dreary view than that from -the northern slope of the Sierra Blanco. As far as the eye can reach -stretches the great flat plain-land, all dusted over with patches of -alkali, and intersected by clumps of the dwarfish chaparral bushes. On -the extreme verge of the horizon lie a long chain of mountain peaks, -with their rugged summits flecked with snow. In this great stretch of -country there is no sign of life, nor of anything appertaining to life. -There is no bird in the steel-blue heaven, no movement upon the dull, -grey earth--above all, there is absolute silence. Listen as one may, -there is no shadow of a sound in all that mighty wilderness; nothing but -silence--complete and heart-subduing silence. - -It has been said there is nothing appertaining to life upon the broad -plain. That is hardly true. Looking down from the Sierra Blanco, one -sees a pathway traced out across the desert, which winds away and is -lost in the extreme distance. It is rutted with wheels and trodden down -by the feet of many adventurers. Here and there there are scattered -white objects which glisten in the sun, and stand out against the dull -deposit of alkali. Approach, and examine them! They are bones: some -large and coarse, others smaller and more delicate. The former have -belonged to oxen, and the latter to men. For fifteen hundred miles one -may trace this ghastly caravan route by these scattered remains of those -who had fallen by the wayside. - -Looking down on this very scene, there stood upon the fourth of May, -eighteen hundred and forty-seven, a solitary traveller. His appearance -was such that he might have been the very genius or demon of the region. -An observer would have found it difficult to say whether he was nearer -to forty or to sixty. His face was lean and haggard, and the brown -parchment-like skin was drawn tightly over the projecting bones; his -long, brown hair and beard were all flecked and dashed with white; his -eyes were sunken in his head, and burned with an unnatural lustre; while -the hand which grasped his rifle was hardly more fleshy than that of a -skeleton. As he stood, he leaned upon his weapon for support, and yet -his tall figure and the massive framework of his bones suggested a wiry -and vigorous constitution. His gaunt face, however, and his clothes, -which hung so baggily over his shrivelled limbs, proclaimed what it -was that gave him that senile and decrepit appearance. The man was -dying--dying from hunger and from thirst. - -He had toiled painfully down the ravine, and on to this little -elevation, in the vain hope of seeing some signs of water. Now the great -salt plain stretched before his eyes, and the distant belt of savage -mountains, without a sign anywhere of plant or tree, which might -indicate the presence of moisture. In all that broad landscape there -was no gleam of hope. North, and east, and west he looked with wild -questioning eyes, and then he realised that his wanderings had come to -an end, and that there, on that barren crag, he was about to die. "Why -not here, as well as in a feather bed, twenty years hence," he muttered, -as he seated himself in the shelter of a boulder. - -Before sitting down, he had deposited upon the ground his useless rifle, -and also a large bundle tied up in a grey shawl, which he had carried -slung over his right shoulder. It appeared to be somewhat too heavy for -his strength, for in lowering it, it came down on the ground with some -little violence. Instantly there broke from the grey parcel a little -moaning cry, and from it there protruded a small, scared face, with very -bright brown eyes, and two little speckled, dimpled fists. - -"You've hurt me!" said a childish voice reproachfully. - -"Have I though," the man answered penitently, "I didn't go for to do -it." As he spoke he unwrapped the grey shawl and extricated a pretty -little girl of about five years of age, whose dainty shoes and smart -pink frock with its little linen apron all bespoke a mother's care. The -child was pale and wan, but her healthy arms and legs showed that she -had suffered less than her companion. - -"How is it now?" he answered anxiously, for she was still rubbing the -towsy golden curls which covered the back of her head. - -"Kiss it and make it well," she said, with perfect gravity, shoving -[19]_ the injured part up to him. "That's what mother used to do. Where's -mother?" - -"Mother's gone. I guess you'll see her before long." - -"Gone, eh!" said the little girl. "Funny, she didn't say good-bye; she -'most always did if she was just goin' over to Auntie's for tea, and now -she's been away three days. Say, it's awful dry, ain't it? Ain't there -no water, nor nothing to eat?" - -"No, there ain't nothing, dearie. You'll just need to be patient awhile, -and then you'll be all right. Put your head up agin me like that, and -then you'll feel bullier. It ain't easy to talk when your lips is like -leather, but I guess I'd best let you know how the cards lie. What's -that you've got?" - -"Pretty things! fine things!" cried the little girl enthusiastically, -holding up two glittering fragments of mica. "When we goes back to home -I'll give them to brother Bob." - -"You'll see prettier things than them soon," said the man confidently. -"You just wait a bit. I was going to tell you though--you remember when -we left the river?" - -"Oh, yes." - -"Well, we reckoned we'd strike another river soon, d'ye see. But there -was somethin' wrong; compasses, or map, or somethin', and it didn't -turn up. Water ran out. Just except a little drop for the likes of you -and--and----" - -"And you couldn't wash yourself," interrupted his companion gravely, -staring up at his grimy visage. - -"No, nor drink. And Mr. Bender, he was the fust to go, and then Indian -Pete, and then Mrs. McGregor, and then Johnny Hones, and then, dearie, -your mother." - -"Then mother's a deader too," cried the little girl dropping her face in -her pinafore and sobbing bitterly. - -"Yes, they all went except you and me. Then I thought there was some -chance of water in this direction, so I heaved you over my shoulder and -we tramped it together. It don't seem as though we've improved matters. -There's an almighty small chance for us now!" - -"Do you mean that we are going to die too?" asked the child, checking -her sobs, and raising her tear-stained face. - -"I guess that's about the size of it." - -"Why didn't you say so before?" she said, laughing gleefully. "You gave -me such a fright. Why, of course, now as long as we die we'll be with -mother again." - -"Yes, you will, dearie." - -"And you too. I'll tell her how awful good you've been. I'll bet she -meets us at the door of Heaven with a big pitcher of water, and a lot -of buckwheat cakes, hot, and toasted on both sides, like Bob and me was -fond of. How long will it be first?" - -"I don't know--not very long." The man's eyes were fixed upon the -northern horizon. In the blue vault of the heaven there had appeared -three little specks which increased in size every moment, so rapidly did -they approach. They speedily resolved themselves into three large brown -birds, which circled over the heads of the two wanderers, and then -settled upon some rocks which overlooked them. They were buzzards, the -vultures of the west, whose coming is the forerunner of death. - -"Cocks and hens," cried the little girl gleefully, pointing at their -ill-omened forms, and clapping her hands to make them rise. "Say, did -God make this country?" - -"In course He did," said her companion, rather startled by this -unexpected question. - -"He made the country down in Illinois, and He made the Missouri," the -little girl continued. "I guess somebody else made the country in these -parts. It's not nearly so well done. They forgot the water and the -trees." - -"What would ye think of offering up prayer?" the man asked diffidently. - -"It ain't night yet," she answered. - -"It don't matter. It ain't quite regular, but He won't mind that, you -bet. You say over them ones that you used to say every night in the -waggon when we was on the Plains." - -"Why don't you say some yourself?" the child asked, with wondering eyes. - -"I disremember them," he answered. "I hain't said none since I was half -the height o' that gun. I guess it's never too late. You say them out, -and I'll stand by and come in on the choruses." - -"Then you'll need to kneel down, and me too," she said, laying the shawl -out for that purpose. "You've got to put your hands up like this. It -makes you feel kind o' good." - -It was a strange sight had there been anything but the buzzards to see -it. Side by side on the narrow shawl knelt the two wanderers, the little -prattling child and the reckless, hardened adventurer. Her chubby face, -and his haggard, angular visage were both turned up to the cloudless -heaven in heartfelt entreaty to that dread being with whom they were -face to face, while the two voices--the one thin and clear, the other -deep and harsh--united in the entreaty for mercy and forgiveness. The -prayer finished, they resumed their seat in the shadow of the boulder -until the child fell asleep, nestling upon the broad breast of her -protector. He watched over her slumber for some time, but Nature proved -to be too strong for him. For three days and three nights he had allowed -himself neither rest nor repose. Slowly the eyelids drooped over the -tired eyes, and the head sunk lower and lower upon the breast, until the -man's grizzled beard was mixed with the gold tresses of his companion, -and both slept the same deep and dreamless slumber. - -Had the wanderer remained awake for another half hour a strange sight -would have met his eyes. Far away on the extreme verge of the alkali -plain there rose up a little spray of dust, very slight at first, and -hardly to be distinguished from the mists of the distance, but gradually -growing higher and broader until it formed a solid, well-defined cloud. -This cloud continued to increase in size until it became evident that it -could only be raised by a great multitude of moving creatures. In more -fertile spots the observer would have come to the conclusion that one -of those great herds of bisons which graze upon the prairie land was -approaching him. This was obviously impossible in these arid wilds. As -the whirl of dust drew nearer to the solitary bluff upon which the two -castaways were reposing, the canvas-covered tilts of waggons and the -figures of armed horsemen began to show up through the haze, and the -apparition revealed itself as being a great caravan upon its journey for -the West. But what a caravan! When the head of it had reached the base -of the mountains, the rear was not yet visible on the horizon. Right -across the enormous plain stretched the straggling array, waggons -and carts, men on horseback, and men on foot. Innumerable women who -staggered along under burdens, and children who toddled beside the -waggons or peeped out from under the white coverings. This was evidently -no ordinary party of immigrants, but rather some nomad people who had -been compelled from stress of circumstances to seek themselves a new -country. There rose through the clear air a confused clattering and -rumbling from this great mass of humanity, with the creaking of wheels -and the neighing of horses. Loud as it was, it was not sufficient to -rouse the two tired wayfarers above them. - -At the head of the column there rode a score or more of grave ironfaced -men, clad in sombre homespun garments and armed with rifles. On reaching -the base of the bluff they halted, and held a short council among -themselves. - -"The wells are to the right, my brothers," said one, a hard-lipped, -clean-shaven man with grizzly hair. - -"To the right of the Sierra Blanco--so we shall reach the Rio Grande," -said another. - -"Fear not for water," cried a third. "He who could draw it from the -rocks will not now abandon His own chosen people." - -"Amen! Amen!" responded the whole party. - -They were about to resume their journey when one of the youngest and -keenest-eyed uttered an exclamation and pointed up at the rugged crag -above them. From its summit there fluttered a little wisp of pink, -showing up hard and bright against the grey rocks behind. At the sight -there was a general reining up of horses and unslinging of guns, while -fresh horsemen came galloping up to reinforce the vanguard. The word -'Redskins' was on every lip. - -"There can't be any number of Injuns here," said the elderly man who -appeared to be in command. "We have passed the Pawnees, and there are no -other tribes until we cross the great mountains." - -"Shall I go forward and see, Brother Stangerson," asked one of the band. - -"And I," "and I," cried a dozen voices. - -"Leave your horses below and we will await you here," the Elder -answered. In a moment the young fellows had dismounted, fastened their -horses, and were ascending the precipitous slope which led up to the -object which had excited their curiosity. They advanced rapidly and -noiselessly, with the confidence and dexterity of practised scouts. -The watchers from the plain below could see them flit from rock to rock -until their figures stood out against the skyline. The young man who had -first given the alarm was leading them. Suddenly his followers saw him -throw up his hands, as though overcome with astonishment, and on joining -him they were affected in the same way by the sight which met their -eyes. - -On the little plateau which crowned the barren hill there stood a -single giant boulder, and against this boulder there lay a tall man, -long-bearded and hard-featured, but of an excessive thinness. His placid -face and regular breathing showed that he was fast asleep. Beside him -lay a little child, with her round white arms encircling his brown -sinewy neck, and her golden haired head resting upon the breast of his -velveteen tunic. Her rosy lips were parted, showing the regular line of -snow-white teeth within, and a playful smile played over her infantile -features. Her plump little white legs terminating in white socks and -neat shoes with shining buckles, offered a strange contrast to the long -shrivelled members of her companion. On the ledge of rock above this -strange couple there stood three solemn buzzards, who, at the sight of -the new comers uttered raucous screams of disappointment and flapped -sullenly away. - -The cries of the foul birds awoke the two sleepers who stared about [20]_ -them in bewilderment. The man staggered to his feet and looked down upon -the plain which had been so desolate when sleep had overtaken him, and -which was now traversed by this enormous body of men and of beasts. His -face assumed an expression of incredulity as he gazed, and he passed his -boney hand over his eyes. "This is what they call delirium, I guess," -he muttered. The child stood beside him, holding on to the skirt of -his coat, and said nothing but looked all round her with the wondering -questioning gaze of childhood. - -The rescuing party were speedily able to convince the two castaways that -their appearance was no delusion. One of them seized the little girl, -and hoisted her upon his shoulder, while two others supported her gaunt -companion, and assisted him towards the waggons. - -"My name is John Ferrier," the wanderer explained; "me and that little -un are all that's left o' twenty-one people. The rest is all dead o' -thirst and hunger away down in the south." - -"Is she your child?" asked someone. - -"I guess she is now," the other cried, defiantly; "she's mine 'cause I -saved her. No man will take her from me. She's Lucy Ferrier from this -day on. Who are you, though?" he continued, glancing with curiosity at -his stalwart, sunburned rescuers; "there seems to be a powerful lot of -ye." - -"Nigh upon ten thousand," said one of the young men; "we are the -persecuted children of God--the chosen of the Angel Merona." - -"I never heard tell on him," said the wanderer. "He appears to have -chosen a fair crowd of ye." - -"Do not jest at that which is sacred," said the other sternly. "We are -of those who believe in those sacred writings, drawn in Egyptian letters -on plates of beaten gold, which were handed unto the holy Joseph Smith -at Palmyra. We have come from Nauvoo, in the State of Illinois, where we -had founded our temple. We have come to seek a refuge from the violent -man and from the godless, even though it be the heart of the desert." - -The name of Nauvoo evidently recalled recollections to John Ferrier. "I -see," he said, "you are the Mormons." - -"We are the Mormons," answered his companions with one voice. - -"And where are you going?" - -"We do not know. The hand of God is leading us under the person of our -Prophet. You must come before him. He shall say what is to be done with -you." - -They had reached the base of the hill by this time, and were surrounded -by crowds of the pilgrims--pale-faced meek-looking women, strong -laughing children, and anxious earnest-eyed men. Many were the cries -of astonishment and of commiseration which arose from them when they -perceived the youth of one of the strangers and the destitution of the -other. Their escort did not halt, however, but pushed on, followed by -a great crowd of Mormons, until they reached a waggon, which was -conspicuous for its great size and for the gaudiness and smartness of -its appearance. Six horses were yoked to it, whereas the others were -furnished with two, or, at most, four a-piece. Beside the driver there -sat a man who could not have been more than thirty years of age, but -whose massive head and resolute expression marked him as a leader. He -was reading a brown-backed volume, but as the crowd approached he laid -it aside, and listened attentively to an account of the episode. Then he -turned to the two castaways. - -"If we take you with us," he said, in solemn words, "it can only be as -believers in our own creed. We shall have no wolves in our fold. Better -far that your bones should bleach in this wilderness than that you -should prove to be that little speck of decay which in time corrupts the -whole fruit. Will you come with us on these terms?" - -"Guess I'll come with you on any terms," said Ferrier, with such -emphasis that the grave Elders could not restrain a smile. The leader -alone retained his stern, impressive expression. - -"Take him, Brother Stangerson," he said, "give him food and drink, -and the child likewise. Let it be your task also to teach him our holy -creed. We have delayed long enough. Forward! On, on to Zion!" - -"On, on to Zion!" cried the crowd of Mormons, and the words rippled down -the long caravan, passing from mouth to mouth until they died away in a -dull murmur in the far distance. With a cracking of whips and a creaking -of wheels the great waggons got into motion, and soon the whole caravan -was winding along once more. The Elder to whose care the two waifs -had been committed, led them to his waggon, where a meal was already -awaiting them. - -"You shall remain here," he said. "In a few days you will have recovered -from your fatigues. In the meantime, remember that now and for ever you -are of our religion. Brigham Young has said it, and he has spoken with -the voice of Joseph Smith, which is the voice of God." - - - - -CHAPTER II. THE FLOWER OF UTAH. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -THIS is not the place to commemorate the trials and privations endured -by the immigrant Mormons before they came to their final haven. From the -shores of the Mississippi to the western slopes of the Rocky Mountains -they had struggled on with a constancy almost unparalleled in history. -The savage man, and the savage beast, hunger, thirst, fatigue, and -disease--every impediment which Nature could place in the way, had all -been overcome with Anglo-Saxon tenacity. Yet the long journey and the -accumulated terrors had shaken the hearts of the stoutest among them. -There was not one who did not sink upon his knees in heartfelt prayer -when they saw the broad valley of Utah bathed in the sunlight beneath -them, and learned from the lips of their leader that this was the -promised land, and that these virgin acres were to be theirs for -evermore. - -Young speedily proved himself to be a skilful administrator as well as a -resolute chief. Maps were drawn and charts prepared, in which the future -city was sketched out. All around farms were apportioned and allotted in -proportion to the standing of each individual. The tradesman was put -to his trade and the artisan to his calling. In the town streets and -squares sprang up, as if by magic. In the country there was draining -and hedging, planting and clearing, until the next summer saw the whole -country golden with the wheat crop. Everything prospered in the strange -settlement. Above all, the great temple which they had erected in the -centre of the city grew ever taller and larger. From the first blush of -dawn until the closing of the twilight, the clatter of the hammer -and the rasp of the saw was never absent from the monument which the -immigrants erected to Him who had led them safe through many dangers. - -The two castaways, John Ferrier and the little girl who had shared his -fortunes and had been adopted as his daughter, accompanied the Mormons -to the end of their great pilgrimage. Little Lucy Ferrier was borne -along pleasantly enough in Elder Stangerson's waggon, a retreat which -she shared with the Mormon's three wives and with his son, a headstrong -forward boy of twelve. Having rallied, with the elasticity of childhood, -from the shock caused by her mother's death, she soon became a pet -with the women, and reconciled herself to this new life in her moving -canvas-covered home. In the meantime Ferrier having recovered from his -privations, distinguished himself as a useful guide and an indefatigable -hunter. So rapidly did he gain the esteem of his new companions, that -when they reached the end of their wanderings, it was unanimously agreed -that he should be provided with as large and as fertile a tract of land -as any of the settlers, with the exception of Young himself, and of -Stangerson, Kemball, Johnston, and Drebber, who were the four principal -Elders. - -On the farm thus acquired John Ferrier built himself a substantial -log-house, which received so many additions in succeeding years that it -grew into a roomy villa. He was a man of a practical turn of mind, -keen in his dealings and skilful with his hands. His iron constitution -enabled him to work morning and evening at improving and tilling his -lands. Hence it came about that his farm and all that belonged to -him prospered exceedingly. In three years he was better off than his -neighbours, in six he was well-to-do, in nine he was rich, and in twelve -there were not half a dozen men in the whole of Salt Lake City who could -compare with him. From the great inland sea to the distant Wahsatch -Mountains there was no name better known than that of John Ferrier. - -There was one way and only one in which he offended the susceptibilities -of his co-religionists. No argument or persuasion could ever induce him -to set up a female establishment after the manner of his companions. He -never gave reasons for this persistent refusal, but contented himself by -resolutely and inflexibly adhering to his determination. There were some -who accused him of lukewarmness in his adopted religion, and others who -put it down to greed of wealth and reluctance to incur expense. Others, -again, spoke of some early love affair, and of a fair-haired girl who -had pined away on the shores of the Atlantic. Whatever the reason, -Ferrier remained strictly celibate. In every other respect he conformed -to the religion of the young settlement, and gained the name of being an -orthodox and straight-walking man. - -Lucy Ferrier grew up within the log-house, and assisted her adopted -father in all his undertakings. The keen air of the mountains and the -balsamic odour of the pine trees took the place of nurse and mother to -the young girl. As year succeeded to year she grew taller and stronger, -her cheek more rudy, and her step more elastic. Many a wayfarer upon -the high road which ran by Ferrier's farm felt long-forgotten thoughts -revive in their mind as they watched her lithe girlish figure tripping -through the wheatfields, or met her mounted upon her father's mustang, -and managing it with all the ease and grace of a true child of the West. -So the bud blossomed into a flower, and the year which saw her father -the richest of the farmers left her as fair a specimen of American -girlhood as could be found in the whole Pacific slope. - -It was not the father, however, who first discovered that the child had -developed into the woman. It seldom is in such cases. That mysterious -change is too subtle and too gradual to be measured by dates. Least of -all does the maiden herself know it until the tone of a voice or the -touch of a hand sets her heart thrilling within her, and she learns, -with a mixture of pride and of fear, that a new and a larger nature has -awoken within her. There are few who cannot recall that day and remember -the one little incident which heralded the dawn of a new life. In the -case of Lucy Ferrier the occasion was serious enough in itself, apart -from its future influence on her destiny and that of many besides. - -It was a warm June morning, and the Latter Day Saints were as busy as -the bees whose hive they have chosen for their emblem. In the fields and -in the streets rose the same hum of human industry. Down the dusty high -roads defiled long streams of heavily-laden mules, all heading to the -west, for the gold fever had broken out in California, and the Overland -Route lay through the City of the Elect. There, too, were droves of -sheep and bullocks coming in from the outlying pasture lands, and trains -of tired immigrants, men and horses equally weary of their interminable -journey. Through all this motley assemblage, threading her way with the -skill of an accomplished rider, there galloped Lucy Ferrier, her fair -face flushed with the exercise and her long chestnut hair floating out -behind her. She had a commission from her father in the City, and was -dashing in as she had done many a time before, with all the fearlessness -of youth, thinking only of her task and how it was to be performed. The -travel-stained adventurers gazed after her in astonishment, and even -the unemotional Indians, journeying in with their pelties, relaxed their -accustomed stoicism as they marvelled at the beauty of the pale-faced -maiden. - -She had reached the outskirts of the city when she found the road -blocked by a great drove of cattle, driven by a half-dozen wild-looking -herdsmen from the plains. In her impatience she endeavoured to pass this -obstacle by pushing her horse into what appeared to be a gap. Scarcely -had she got fairly into it, however, before the beasts closed in behind -her, and she found herself completely imbedded in the moving stream of -fierce-eyed, long-horned bullocks. Accustomed as she was to deal with -cattle, she was not alarmed at her situation, but took advantage of -every opportunity to urge her horse on in the hopes of pushing her way -through the cavalcade. Unfortunately the horns of one of the creatures, -either by accident or design, came in violent contact with the flank of -the mustang, and excited it to madness. In an instant it reared up upon -its hind legs with a snort of rage, and pranced and tossed in a way that -would have unseated any but a most skilful rider. The situation was full -of peril. Every plunge of the excited horse brought it against the horns -again, and goaded it to fresh madness. It was all that the girl could -do to keep herself in the saddle, yet a slip would mean a terrible death -under the hoofs of the unwieldy and terrified animals. Unaccustomed to -sudden emergencies, her head began to swim, and her grip upon the bridle -to relax. Choked by the rising cloud of dust and by the steam from the -struggling creatures, she might have abandoned her efforts in despair, -but for a kindly voice at her elbow which assured her of assistance. At -the same moment a sinewy brown hand caught the frightened horse by -the curb, and forcing a way through the drove, soon brought her to the -outskirts. - -"You're not hurt, I hope, miss," said her preserver, respectfully. - -She looked up at his dark, fierce face, and laughed saucily. "I'm awful -frightened," she said, naively; "whoever would have thought that Poncho -would have been so scared by a lot of cows?" - -"Thank God you kept your seat," the other said earnestly. He was a tall, -savage-looking young fellow, mounted on a powerful roan horse, and -clad in the rough dress of a hunter, with a long rifle slung over his -shoulders. "I guess you are the daughter of John Ferrier," he remarked, -"I saw you ride down from his house. When you see him, ask him if he -remembers the Jefferson Hopes of St. Louis. If he's the same Ferrier, my -father and he were pretty thick." - -"Hadn't you better come and ask yourself?" she asked, demurely. - -The young fellow seemed pleased at the suggestion, and his dark eyes -sparkled with pleasure. "I'll do so," he said, "we've been in the -mountains for two months, and are not over and above in visiting -condition. He must take us as he finds us." - -"He has a good deal to thank you for, and so have I," she answered, -"he's awful fond of me. If those cows had jumped on me he'd have never -got over it." - -"Neither would I," said her companion. - -"You! Well, I don't see that it would make much matter to you, anyhow. -You ain't even a friend of ours." - -The young hunter's dark face grew so gloomy over this remark that Lucy -Ferrier laughed aloud. - -"There, I didn't mean that," she said; "of course, you are a friend now. -You must come and see us. Now I must push along, or father won't trust -me with his business any more. Good-bye!" - -"Good-bye," he answered, raising his broad sombrero, and bending over -her little hand. She wheeled her mustang round, gave it a cut with her -riding-whip, and darted away down the broad road in a rolling cloud of -dust. - -Young Jefferson Hope rode on with his companions, gloomy and taciturn. -He and they had been among the Nevada Mountains prospecting for silver, -and were returning to Salt Lake City in the hope of raising capital -enough to work some lodes which they had discovered. He had been as keen -as any of them upon the business until this sudden incident had drawn -his thoughts into another channel. The sight of the fair young girl, -as frank and wholesome as the Sierra breezes, had stirred his volcanic, -untamed heart to its very depths. When she had vanished from his sight, -he realized that a crisis had come in his life, and that neither silver -speculations nor any other questions could ever be of such importance to -him as this new and all-absorbing one. The love which had sprung up in -his heart was not the sudden, changeable fancy of a boy, but rather the -wild, fierce passion of a man of strong will and imperious temper. He -had been accustomed to succeed in all that he undertook. He swore in -his heart that he would not fail in this if human effort and human -perseverance could render him successful. - -He called on John Ferrier that night, and many times again, until -his face was a familiar one at the farm-house. John, cooped up in the -valley, and absorbed in his work, had had little chance of learning -the news of the outside world during the last twelve years. All this -Jefferson Hope was able to tell him, and in a style which interested -Lucy as well as her father. He had been a pioneer in California, and -could narrate many a strange tale of fortunes made and fortunes lost -in those wild, halcyon days. He had been a scout too, and a trapper, a -silver explorer, and a ranchman. Wherever stirring adventures were to be -had, Jefferson Hope had been there in search of them. He soon became a -favourite with the old farmer, who spoke eloquently of his virtues. On -such occasions, Lucy was silent, but her blushing cheek and her bright, -happy eyes, showed only too clearly that her young heart was no longer -her own. Her honest father may not have observed these symptoms, -but they were assuredly not thrown away upon the man who had won her -affections. - -It was a summer evening when he came galloping down the road and pulled -up at the gate. She was at the doorway, and came down to meet him. He -threw the bridle over the fence and strode up the pathway. - -"I am off, Lucy," he said, taking her two hands in his, and gazing -tenderly down into her face; "I won't ask you to come with me now, but -will you be ready to come when I am here again?" - -"And when will that be?" she asked, blushing and laughing. - -"A couple of months at the outside. I will come and claim you then, my -darling. There's no one who can stand between us." - -"And how about father?" she asked. - -"He has given his consent, provided we get these mines working all -right. I have no fear on that head." - -"Oh, well; of course, if you and father have arranged it all, there's -no more to be said," she whispered, with her cheek against his broad -breast. - -"Thank God!" he said, hoarsely, stooping and kissing her. "It is -settled, then. The longer I stay, the harder it will be to go. They are -waiting for me at the cañon. Good-bye, my own darling--good-bye. In two -months you shall see me." - -He tore himself from her as he spoke, and, flinging himself upon his -horse, galloped furiously away, never even looking round, as though -afraid that his resolution might fail him if he took one glance at -what he was leaving. She stood at the gate, gazing after him until -he vanished from her sight. Then she walked back into the house, the -happiest girl in all Utah. - - - - -CHAPTER III. JOHN FERRIER TALKS WITH THE PROPHET. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -THREE weeks had passed since Jefferson Hope and his comrades had -departed from Salt Lake City. John Ferrier's heart was sore within him -when he thought of the young man's return, and of the impending loss of -his adopted child. Yet her bright and happy face reconciled him to -the arrangement more than any argument could have done. He had always -determined, deep down in his resolute heart, that nothing would ever -induce him to allow his daughter to wed a Mormon. Such a marriage he -regarded as no marriage at all, but as a shame and a disgrace. Whatever -he might think of the Mormon doctrines, upon that one point he was -inflexible. He had to seal his mouth on the subject, however, for to -express an unorthodox opinion was a dangerous matter in those days in -the Land of the Saints. - -Yes, a dangerous matter--so dangerous that even the most saintly dared -only whisper their religious opinions with bated breath, lest something -which fell from their lips might be misconstrued, and bring down a -swift retribution upon them. The victims of persecution had now turned -persecutors on their own account, and persecutors of the most -terrible description. Not the Inquisition of Seville, nor the German -Vehm-gericht, nor the Secret Societies of Italy, were ever able to put -a more formidable machinery in motion than that which cast a cloud over -the State of Utah. - -Its invisibility, and the mystery which was attached to it, made -this organization doubly terrible. It appeared to be omniscient and -omnipotent, and yet was neither seen nor heard. The man who held out -against the Church vanished away, and none knew whither he had gone or -what had befallen him. His wife and his children awaited him at home, -but no father ever returned to tell them how he had fared at the -hands of his secret judges. A rash word or a hasty act was followed -by annihilation, and yet none knew what the nature might be of this -terrible power which was suspended over them. No wonder that men -went about in fear and trembling, and that even in the heart of the -wilderness they dared not whisper the doubts which oppressed them. - -At first this vague and terrible power was exercised only upon the -recalcitrants who, having embraced the Mormon faith, wished afterwards -to pervert or to abandon it. Soon, however, it took a wider range. The -supply of adult women was running short, and polygamy without a female -population on which to draw was a barren doctrine indeed. Strange -rumours began to be bandied about--rumours of murdered immigrants and -rifled camps in regions where Indians had never been seen. Fresh women -appeared in the harems of the Elders--women who pined and wept, and -bore upon their faces the traces of an unextinguishable horror. Belated -wanderers upon the mountains spoke of gangs of armed men, masked, -stealthy, and noiseless, who flitted by them in the darkness. These -tales and rumours took substance and shape, and were corroborated and -re-corroborated, until they resolved themselves into a definite name. -To this day, in the lonely ranches of the West, the name of the Danite -Band, or the Avenging Angels, is a sinister and an ill-omened one. - -Fuller knowledge of the organization which produced such terrible -results served to increase rather than to lessen the horror which it -inspired in the minds of men. None knew who belonged to this ruthless -society. The names of the participators in the deeds of blood and -violence done under the name of religion were kept profoundly secret. -The very friend to whom you communicated your misgivings as to the -Prophet and his mission, might be one of those who would come forth at -night with fire and sword to exact a terrible reparation. Hence every -man feared his neighbour, and none spoke of the things which were -nearest his heart. - -One fine morning, John Ferrier was about to set out to his wheatfields, -when he heard the click of the latch, and, looking through the window, -saw a stout, sandy-haired, middle-aged man coming up the pathway. His -heart leapt to his mouth, for this was none other than the great Brigham -Young himself. Full of trepidation--for he knew that such a visit boded -him little good--Ferrier ran to the door to greet the Mormon chief. The -latter, however, received his salutations coldly, and followed him with -a stern face into the sitting-room. - -"Brother Ferrier," he said, taking a seat, and eyeing the farmer keenly -from under his light-coloured eyelashes, "the true believers have been -good friends to you. We picked you up when you were starving in the -desert, we shared our food with you, led you safe to the Chosen Valley, -gave you a goodly share of land, and allowed you to wax rich under our -protection. Is not this so?" - -"It is so," answered John Ferrier. - -"In return for all this we asked but one condition: that was, that you -should embrace the true faith, and conform in every way to its usages. -This you promised to do, and this, if common report says truly, you have -neglected." - -"And how have I neglected it?" asked Ferrier, throwing out his hands in -expostulation. "Have I not given to the common fund? Have I not attended -at the Temple? Have I not----?" - -"Where are your wives?" asked Young, looking round him. "Call them in, -that I may greet them." - -"It is true that I have not married," Ferrier answered. "But women -were few, and there were many who had better claims than I. I was not a -lonely man: I had my daughter to attend to my wants." - -"It is of that daughter that I would speak to you," said the leader -of the Mormons. "She has grown to be the flower of Utah, and has found -favour in the eyes of many who are high in the land." - -John Ferrier groaned internally. - -"There are stories of her which I would fain disbelieve--stories that -she is sealed to some Gentile. This must be the gossip of idle tongues. -What is the thirteenth rule in the code of the sainted Joseph Smith? -'Let every maiden of the true faith marry one of the elect; for if -she wed a Gentile, she commits a grievous sin.' This being so, it is -impossible that you, who profess the holy creed, should suffer your -daughter to violate it." - -John Ferrier made no answer, but he played nervously with his -riding-whip. - -"Upon this one point your whole faith shall be tested--so it has been -decided in the Sacred Council of Four. The girl is young, and we would -not have her wed grey hairs, neither would we deprive her of all -choice. We Elders have many heifers, [29]_ but our children must also -be provided. Stangerson has a son, and Drebber has a son, and either of -them would gladly welcome your daughter to their house. Let her choose -between them. They are young and rich, and of the true faith. What say -you to that?" - -Ferrier remained silent for some little time with his brows knitted. - -"You will give us time," he said at last. "My daughter is very -young--she is scarce of an age to marry." - -"She shall have a month to choose," said Young, rising from his seat. -"At the end of that time she shall give her answer." - -He was passing through the door, when he turned, with flushed face and -flashing eyes. "It were better for you, John Ferrier," he thundered, -"that you and she were now lying blanched skeletons upon the Sierra -Blanco, than that you should put your weak wills against the orders of -the Holy Four!" - -With a threatening gesture of his hand, he turned from the door, and -Ferrier heard his heavy step scrunching along the shingly path. - -He was still sitting with his elbows upon his knees, considering how he -should broach the matter to his daughter when a soft hand was laid upon -his, and looking up, he saw her standing beside him. One glance at her -pale, frightened face showed him that she had heard what had passed. - -"I could not help it," she said, in answer to his look. "His voice rang -through the house. Oh, father, father, what shall we do?" - -"Don't you scare yourself," he answered, drawing her to him, and passing -his broad, rough hand caressingly over her chestnut hair. "We'll fix it -up somehow or another. You don't find your fancy kind o' lessening for -this chap, do you?" - -A sob and a squeeze of his hand was her only answer. - -"No; of course not. I shouldn't care to hear you say you did. He's a -likely lad, and he's a Christian, which is more than these folk here, in -spite o' all their praying and preaching. There's a party starting for -Nevada to-morrow, and I'll manage to send him a message letting him know -the hole we are in. If I know anything o' that young man, he'll be back -here with a speed that would whip electro-telegraphs." - -Lucy laughed through her tears at her father's description. - -"When he comes, he will advise us for the best. But it is for you that -I am frightened, dear. One hears--one hears such dreadful stories about -those who oppose the Prophet: something terrible always happens to -them." - -"But we haven't opposed him yet," her father answered. "It will be time -to look out for squalls when we do. We have a clear month before us; at -the end of that, I guess we had best shin out of Utah." - -"Leave Utah!" - -"That's about the size of it." - -"But the farm?" - -"We will raise as much as we can in money, and let the rest go. To tell -the truth, Lucy, it isn't the first time I have thought of doing it. I -don't care about knuckling under to any man, as these folk do to their -darned prophet. I'm a free-born American, and it's all new to me. Guess -I'm too old to learn. If he comes browsing about this farm, he might -chance to run up against a charge of buckshot travelling in the opposite -direction." - -"But they won't let us leave," his daughter objected. - -"Wait till Jefferson comes, and we'll soon manage that. In the meantime, -don't you fret yourself, my dearie, and don't get your eyes swelled up, -else he'll be walking into me when he sees you. There's nothing to be -afeared about, and there's no danger at all." - -John Ferrier uttered these consoling remarks in a very confident tone, -but she could not help observing that he paid unusual care to the -fastening of the doors that night, and that he carefully cleaned and -loaded the rusty old shotgun which hung upon the wall of his bedroom. - - - - -CHAPTER IV. A FLIGHT FOR LIFE. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -ON the morning which followed his interview with the Mormon Prophet, -John Ferrier went in to Salt Lake City, and having found his -acquaintance, who was bound for the Nevada Mountains, he entrusted him -with his message to Jefferson Hope. In it he told the young man of the -imminent danger which threatened them, and how necessary it was that he -should return. Having done thus he felt easier in his mind, and returned -home with a lighter heart. - -As he approached his farm, he was surprised to see a horse hitched to -each of the posts of the gate. Still more surprised was he on entering -to find two young men in possession of his sitting-room. One, with a -long pale face, was leaning back in the rocking-chair, with his feet -cocked up upon the stove. The other, a bull-necked youth with coarse -bloated features, was standing in front of the window with his hands in -his pocket, whistling a popular hymn. Both of them nodded to Ferrier as -he entered, and the one in the rocking-chair commenced the conversation. - -"Maybe you don't know us," he said. "This here is the son of Elder -Drebber, and I'm Joseph Stangerson, who travelled with you in the desert -when the Lord stretched out His hand and gathered you into the true -fold." - -"As He will all the nations in His own good time," said the other in a -nasal voice; "He grindeth slowly but exceeding small." - -John Ferrier bowed coldly. He had guessed who his visitors were. - -"We have come," continued Stangerson, "at the advice of our fathers to -solicit the hand of your daughter for whichever of us may seem good to -you and to her. As I have but four wives and Brother Drebber here has -seven, it appears to me that my claim is the stronger one." - -"Nay, nay, Brother Stangerson," cried the other; "the question is not -how many wives we have, but how many we can keep. My father has now -given over his mills to me, and I am the richer man." - -"But my prospects are better," said the other, warmly. "When the -Lord removes my father, I shall have his tanning yard and his leather -factory. Then I am your elder, and am higher in the Church." - -"It will be for the maiden to decide," rejoined young Drebber, smirking -at his own reflection in the glass. "We will leave it all to her -decision." - -During this dialogue, John Ferrier had stood fuming in the doorway, -hardly able to keep his riding-whip from the backs of his two visitors. - -"Look here," he said at last, striding up to them, "when my daughter -summons you, you can come, but until then I don't want to see your faces -again." - -The two young Mormons stared at him in amazement. In their eyes this -competition between them for the maiden's hand was the highest of -honours both to her and her father. - -"There are two ways out of the room," cried Ferrier; "there is the door, -and there is the window. Which do you care to use?" - -His brown face looked so savage, and his gaunt hands so threatening, -that his visitors sprang to their feet and beat a hurried retreat. The -old farmer followed them to the door. - -"Let me know when you have settled which it is to be," he said, -sardonically. - -"You shall smart for this!" Stangerson cried, white with rage. "You have -defied the Prophet and the Council of Four. You shall rue it to the end -of your days." - -"The hand of the Lord shall be heavy upon you," cried young Drebber; "He -will arise and smite you!" - -"Then I'll start the smiting," exclaimed Ferrier furiously, and would -have rushed upstairs for his gun had not Lucy seized him by the arm and -restrained him. Before he could escape from her, the clatter of horses' -hoofs told him that they were beyond his reach. - -"The young canting rascals!" he exclaimed, wiping the perspiration from -his forehead; "I would sooner see you in your grave, my girl, than the -wife of either of them." - -"And so should I, father," she answered, with spirit; "but Jefferson -will soon be here." - -"Yes. It will not be long before he comes. The sooner the better, for we -do not know what their next move may be." - -It was, indeed, high time that someone capable of giving advice and -help should come to the aid of the sturdy old farmer and his adopted -daughter. In the whole history of the settlement there had never been -such a case of rank disobedience to the authority of the Elders. If -minor errors were punished so sternly, what would be the fate of this -arch rebel. Ferrier knew that his wealth and position would be of no -avail to him. Others as well known and as rich as himself had been -spirited away before now, and their goods given over to the Church. He -was a brave man, but he trembled at the vague, shadowy terrors which -hung over him. Any known danger he could face with a firm lip, but -this suspense was unnerving. He concealed his fears from his daughter, -however, and affected to make light of the whole matter, though she, -with the keen eye of love, saw plainly that he was ill at ease. - -He expected that he would receive some message or remonstrance from -Young as to his conduct, and he was not mistaken, though it came in an -unlooked-for manner. Upon rising next morning he found, to his surprise, -a small square of paper pinned on to the coverlet of his bed just over -his chest. On it was printed, in bold straggling letters:-- - -"Twenty-nine days are given you for amendment, and then----" - -The dash was more fear-inspiring than any threat could have been. How -this warning came into his room puzzled John Ferrier sorely, for his -servants slept in an outhouse, and the doors and windows had all been -secured. He crumpled the paper up and said nothing to his daughter, but -the incident struck a chill into his heart. The twenty-nine days were -evidently the balance of the month which Young had promised. What -strength or courage could avail against an enemy armed with such -mysterious powers? The hand which fastened that pin might have struck -him to the heart, and he could never have known who had slain him. - -Still more shaken was he next morning. They had sat down to their -breakfast when Lucy with a cry of surprise pointed upwards. In the -centre of the ceiling was scrawled, with a burned stick apparently, -the number 28. To his daughter it was unintelligible, and he did not -enlighten her. That night he sat up with his gun and kept watch and -ward. He saw and he heard nothing, and yet in the morning a great 27 had -been painted upon the outside of his door. - -Thus day followed day; and as sure as morning came he found that his -unseen enemies had kept their register, and had marked up in some -conspicuous position how many days were still left to him out of the -month of grace. Sometimes the fatal numbers appeared upon the walls, -sometimes upon the floors, occasionally they were on small placards -stuck upon the garden gate or the railings. With all his vigilance John -Ferrier could not discover whence these daily warnings proceeded. A -horror which was almost superstitious came upon him at the sight of -them. He became haggard and restless, and his eyes had the troubled look -of some hunted creature. He had but one hope in life now, and that was -for the arrival of the young hunter from Nevada. - -Twenty had changed to fifteen and fifteen to ten, but there was no news -of the absentee. One by one the numbers dwindled down, and still there -came no sign of him. Whenever a horseman clattered down the road, or a -driver shouted at his team, the old farmer hurried to the gate thinking -that help had arrived at last. At last, when he saw five give way to -four and that again to three, he lost heart, and abandoned all hope of -escape. Single-handed, and with his limited knowledge of the mountains -which surrounded the settlement, he knew that he was powerless. The -more-frequented roads were strictly watched and guarded, and none could -pass along them without an order from the Council. Turn which way he -would, there appeared to be no avoiding the blow which hung over him. -Yet the old man never wavered in his resolution to part with life itself -before he consented to what he regarded as his daughter's dishonour. - -He was sitting alone one evening pondering deeply over his troubles, and -searching vainly for some way out of them. That morning had shown the -figure 2 upon the wall of his house, and the next day would be the last -of the allotted time. What was to happen then? All manner of vague and -terrible fancies filled his imagination. And his daughter--what was to -become of her after he was gone? Was there no escape from the invisible -network which was drawn all round them. He sank his head upon the table -and sobbed at the thought of his own impotence. - -What was that? In the silence he heard a gentle scratching sound--low, -but very distinct in the quiet of the night. It came from the door of -the house. Ferrier crept into the hall and listened intently. There -was a pause for a few moments, and then the low insidious sound was -repeated. Someone was evidently tapping very gently upon one of the -panels of the door. Was it some midnight assassin who had come to carry -out the murderous orders of the secret tribunal? Or was it some agent -who was marking up that the last day of grace had arrived. John Ferrier -felt that instant death would be better than the suspense which shook -his nerves and chilled his heart. Springing forward he drew the bolt and -threw the door open. - -Outside all was calm and quiet. The night was fine, and the stars were -twinkling brightly overhead. The little front garden lay before the -farmer's eyes bounded by the fence and gate, but neither there nor on -the road was any human being to be seen. With a sigh of relief, Ferrier -looked to right and to left, until happening to glance straight down at -his own feet he saw to his astonishment a man lying flat upon his face -upon the ground, with arms and legs all asprawl. - -So unnerved was he at the sight that he leaned up against the wall with -his hand to his throat to stifle his inclination to call out. His first -thought was that the prostrate figure was that of some wounded or dying -man, but as he watched it he saw it writhe along the ground and into the -hall with the rapidity and noiselessness of a serpent. Once within the -house the man sprang to his feet, closed the door, and revealed to the -astonished farmer the fierce face and resolute expression of Jefferson -Hope. - -"Good God!" gasped John Ferrier. "How you scared me! Whatever made you -come in like that." - -"Give me food," the other said, hoarsely. "I have had no time for bite -or sup for eight-and-forty hours." He flung himself upon the [21]_ cold -meat and bread which were still lying upon the table from his host's -supper, and devoured it voraciously. "Does Lucy bear up well?" he asked, -when he had satisfied his hunger. - -"Yes. She does not know the danger," her father answered. - -"That is well. The house is watched on every side. That is why I crawled -my way up to it. They may be darned sharp, but they're not quite sharp -enough to catch a Washoe hunter." - -John Ferrier felt a different man now that he realized that he had -a devoted ally. He seized the young man's leathery hand and wrung it -cordially. "You're a man to be proud of," he said. "There are not many -who would come to share our danger and our troubles." - -"You've hit it there, pard," the young hunter answered. "I have a -respect for you, but if you were alone in this business I'd think twice -before I put my head into such a hornet's nest. It's Lucy that brings me -here, and before harm comes on her I guess there will be one less o' the -Hope family in Utah." - -"What are we to do?" - -"To-morrow is your last day, and unless you act to-night you are lost. -I have a mule and two horses waiting in the Eagle Ravine. How much money -have you?" - -"Two thousand dollars in gold, and five in notes." - -"That will do. I have as much more to add to it. We must push for Carson -City through the mountains. You had best wake Lucy. It is as well that -the servants do not sleep in the house." - -While Ferrier was absent, preparing his daughter for the approaching -journey, Jefferson Hope packed all the eatables that he could find into -a small parcel, and filled a stoneware jar with water, for he knew by -experience that the mountain wells were few and far between. He had -hardly completed his arrangements before the farmer returned with his -daughter all dressed and ready for a start. The greeting between the -lovers was warm, but brief, for minutes were precious, and there was -much to be done. - -"We must make our start at once," said Jefferson Hope, speaking in a low -but resolute voice, like one who realizes the greatness of the peril, -but has steeled his heart to meet it. "The front and back entrances are -watched, but with caution we may get away through the side window and -across the fields. Once on the road we are only two miles from the -Ravine where the horses are waiting. By daybreak we should be half-way -through the mountains." - -"What if we are stopped," asked Ferrier. - -Hope slapped the revolver butt which protruded from the front of his -tunic. "If they are too many for us we shall take two or three of them -with us," he said with a sinister smile. - -The lights inside the house had all been extinguished, and from the -darkened window Ferrier peered over the fields which had been his own, -and which he was now about to abandon for ever. He had long nerved -himself to the sacrifice, however, and the thought of the honour and -happiness of his daughter outweighed any regret at his ruined fortunes. -All looked so peaceful and happy, the rustling trees and the broad -silent stretch of grain-land, that it was difficult to realize that -the spirit of murder lurked through it all. Yet the white face and set -expression of the young hunter showed that in his approach to the house -he had seen enough to satisfy him upon that head. - -Ferrier carried the bag of gold and notes, Jefferson Hope had the scanty -provisions and water, while Lucy had a small bundle containing a few -of her more valued possessions. Opening the window very slowly and -carefully, they waited until a dark cloud had somewhat obscured the -night, and then one by one passed through into the little garden. With -bated breath and crouching figures they stumbled across it, and gained -the shelter of the hedge, which they skirted until they came to the gap -which opened into the cornfields. They had just reached this point when -the young man seized his two companions and dragged them down into the -shadow, where they lay silent and trembling. - -It was as well that his prairie training had given Jefferson Hope the -ears of a lynx. He and his friends had hardly crouched down before the -melancholy hooting of a mountain owl was heard within a few yards -of them, which was immediately answered by another hoot at a small -distance. At the same moment a vague shadowy figure emerged from the -gap for which they had been making, and uttered the plaintive signal cry -again, on which a second man appeared out of the obscurity. - -"To-morrow at midnight," said the first who appeared to be in authority. -"When the Whip-poor-Will calls three times." - -"It is well," returned the other. "Shall I tell Brother Drebber?" - -"Pass it on to him, and from him to the others. Nine to seven!" - -"Seven to five!" repeated the other, and the two figures flitted away -in different directions. Their concluding words had evidently been some -form of sign and countersign. The instant that their footsteps had died -away in the distance, Jefferson Hope sprang to his feet, and helping his -companions through the gap, led the way across the fields at the top -of his speed, supporting and half-carrying the girl when her strength -appeared to fail her. - -"Hurry on! hurry on!" he gasped from time to time. "We are through the -line of sentinels. Everything depends on speed. Hurry on!" - -Once on the high road they made rapid progress. Only once did they -meet anyone, and then they managed to slip into a field, and so avoid -recognition. Before reaching the town the hunter branched away into a -rugged and narrow footpath which led to the mountains. Two dark jagged -peaks loomed above them through the darkness, and the defile which led -between them was the Eagle Cañon in which the horses were awaiting them. -With unerring instinct Jefferson Hope picked his way among the great -boulders and along the bed of a dried-up watercourse, until he came to -the retired corner, screened with rocks, where the faithful animals had -been picketed. The girl was placed upon the mule, and old Ferrier upon -one of the horses, with his money-bag, while Jefferson Hope led the -other along the precipitous and dangerous path. - -It was a bewildering route for anyone who was not accustomed to face -Nature in her wildest moods. On the one side a great crag towered up a -thousand feet or more, black, stern, and menacing, with long basaltic -columns upon its rugged surface like the ribs of some petrified monster. -On the other hand a wild chaos of boulders and debris made all advance -impossible. Between the two ran the irregular track, so narrow in places -that they had to travel in Indian file, and so rough that only practised -riders could have traversed it at all. Yet in spite of all dangers and -difficulties, the hearts of the fugitives were light within them, -for every step increased the distance between them and the terrible -despotism from which they were flying. - -They soon had a proof, however, that they were still within the -jurisdiction of the Saints. They had reached the very wildest and most -desolate portion of the pass when the girl gave a startled cry, and -pointed upwards. On a rock which overlooked the track, showing out dark -and plain against the sky, there stood a solitary sentinel. He saw them -as soon as they perceived him, and his military challenge of "Who goes -there?" rang through the silent ravine. - -"Travellers for Nevada," said Jefferson Hope, with his hand upon the -rifle which hung by his saddle. - -They could see the lonely watcher fingering his gun, and peering down at -them as if dissatisfied at their reply. - -"By whose permission?" he asked. - -"The Holy Four," answered Ferrier. His Mormon experiences had taught him -that that was the highest authority to which he could refer. - -"Nine from seven," cried the sentinel. - -"Seven from five," returned Jefferson Hope promptly, remembering the -countersign which he had heard in the garden. - -"Pass, and the Lord go with you," said the voice from above. Beyond his -post the path broadened out, and the horses were able to break into a -trot. Looking back, they could see the solitary watcher leaning upon -his gun, and knew that they had passed the outlying post of the chosen -people, and that freedom lay before them. - - - - -CHAPTER V. THE AVENGING ANGELS. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -ALL night their course lay through intricate defiles and over irregular -and rock-strewn paths. More than once they lost their way, but Hope's -intimate knowledge of the mountains enabled them to regain the track -once more. When morning broke, a scene of marvellous though savage -beauty lay before them. In every direction the great snow-capped peaks -hemmed them in, peeping over each other's shoulders to the far horizon. -So steep were the rocky banks on either side of them, that the larch -and the pine seemed to be suspended over their heads, and to need only a -gust of wind to come hurtling down upon them. Nor was the fear entirely -an illusion, for the barren valley was thickly strewn with trees and -boulders which had fallen in a similar manner. Even as they passed, -a great rock came thundering down with a hoarse rattle which woke -the echoes in the silent gorges, and startled the weary horses into a -gallop. - -As the sun rose slowly above the eastern horizon, the caps of the great -mountains lit up one after the other, like lamps at a festival, until -they were all ruddy and glowing. The magnificent spectacle cheered the -hearts of the three fugitives and gave them fresh energy. At a wild -torrent which swept out of a ravine they called a halt and watered their -horses, while they partook of a hasty breakfast. Lucy and her father -would fain have rested longer, but Jefferson Hope was inexorable. "They -will be upon our track by this time," he said. "Everything depends upon -our speed. Once safe in Carson we may rest for the remainder of our -lives." - -During the whole of that day they struggled on through the defiles, and -by evening they calculated that they were more than thirty miles from -their enemies. At night-time they chose the base of a beetling crag, -where the rocks offered some protection from the chill wind, and there -huddled together for warmth, they enjoyed a few hours' sleep. Before -daybreak, however, they were up and on their way once more. They had -seen no signs of any pursuers, and Jefferson Hope began to think that -they were fairly out of the reach of the terrible organization whose -enmity they had incurred. He little knew how far that iron grasp could -reach, or how soon it was to close upon them and crush them. - -About the middle of the second day of their flight their scanty store -of provisions began to run out. This gave the hunter little uneasiness, -however, for there was game to be had among the mountains, and he had -frequently before had to depend upon his rifle for the needs of life. -Choosing a sheltered nook, he piled together a few dried branches and -made a blazing fire, at which his companions might warm themselves, for -they were now nearly five thousand feet above the sea level, and the air -was bitter and keen. Having tethered the horses, and bade Lucy adieu, -he threw his gun over his shoulder, and set out in search of whatever -chance might throw in his way. Looking back he saw the old man and the -young girl crouching over the blazing fire, while the three animals -stood motionless in the back-ground. Then the intervening rocks hid them -from his view. - -He walked for a couple of miles through one ravine after another without -success, though from the marks upon the bark of the trees, and other -indications, he judged that there were numerous bears in the vicinity. -At last, after two or three hours' fruitless search, he was thinking of -turning back in despair, when casting his eyes upwards he saw a sight -which sent a thrill of pleasure through his heart. On the edge of a -jutting pinnacle, three or four hundred feet above him, there stood a -creature somewhat resembling a sheep in appearance, but armed with a -pair of gigantic horns. The big-horn--for so it is called--was acting, -probably, as a guardian over a flock which were invisible to the hunter; -but fortunately it was heading in the opposite direction, and had not -perceived him. Lying on his face, he rested his rifle upon a rock, and -took a long and steady aim before drawing the trigger. The animal sprang -into the air, tottered for a moment upon the edge of the precipice, and -then came crashing down into the valley beneath. - -The creature was too unwieldy to lift, so the hunter contented himself -with cutting away one haunch and part of the flank. With this trophy -over his shoulder, he hastened to retrace his steps, for the evening was -already drawing in. He had hardly started, however, before he realized -the difficulty which faced him. In his eagerness he had wandered far -past the ravines which were known to him, and it was no easy matter -to pick out the path which he had taken. The valley in which he found -himself divided and sub-divided into many gorges, which were so like -each other that it was impossible to distinguish one from the other. -He followed one for a mile or more until he came to a mountain torrent -which he was sure that he had never seen before. Convinced that he had -taken the wrong turn, he tried another, but with the same result. Night -was coming on rapidly, and it was almost dark before he at last found -himself in a defile which was familiar to him. Even then it was no easy -matter to keep to the right track, for the moon had not yet risen, and -the high cliffs on either side made the obscurity more profound. Weighed -down with his burden, and weary from his exertions, he stumbled along, -keeping up his heart by the reflection that every step brought him -nearer to Lucy, and that he carried with him enough to ensure them food -for the remainder of their journey. - -He had now come to the mouth of the very defile in which he had left -them. Even in the darkness he could recognize the outline of the cliffs -which bounded it. They must, he reflected, be awaiting him anxiously, -for he had been absent nearly five hours. In the gladness of his heart -he put his hands to his mouth and made the glen re-echo to a loud halloo -as a signal that he was coming. He paused and listened for an answer. -None came save his own cry, which clattered up the dreary silent -ravines, and was borne back to his ears in countless repetitions. Again -he shouted, even louder than before, and again no whisper came back from -the friends whom he had left such a short time ago. A vague, nameless -dread came over him, and he hurried onwards frantically, dropping the -precious food in his agitation. - -When he turned the corner, he came full in sight of the spot where the -fire had been lit. There was still a glowing pile of wood ashes there, -but it had evidently not been tended since his departure. The same -dead silence still reigned all round. With his fears all changed to -convictions, he hurried on. There was no living creature near the -remains of the fire: animals, man, maiden, all were gone. It was only -too clear that some sudden and terrible disaster had occurred during -his absence--a disaster which had embraced them all, and yet had left no -traces behind it. - -Bewildered and stunned by this blow, Jefferson Hope felt his head spin -round, and had to lean upon his rifle to save himself from falling. He -was essentially a man of action, however, and speedily recovered from -his temporary impotence. Seizing a half-consumed piece of wood from the -smouldering fire, he blew it into a flame, and proceeded with its help -to examine the little camp. The ground was all stamped down by the feet -of horses, showing that a large party of mounted men had overtaken -the fugitives, and the direction of their tracks proved that they had -afterwards turned back to Salt Lake City. Had they carried back both of -his companions with them? Jefferson Hope had almost persuaded himself -that they must have done so, when his eye fell upon an object which made -every nerve of his body tingle within him. A little way on one side of -the camp was a low-lying heap of reddish soil, which had assuredly -not been there before. There was no mistaking it for anything but a -newly-dug grave. As the young hunter approached it, he perceived that a -stick had been planted on it, with a sheet of paper stuck in the cleft -fork of it. The inscription upon the paper was brief, but to the point: - - JOHN FERRIER, - FORMERLY OF SALT LAKE CITY, [22]_ - Died August 4th, 1860. - -The sturdy old man, whom he had left so short a time before, was gone, -then, and this was all his epitaph. Jefferson Hope looked wildly round -to see if there was a second grave, but there was no sign of one. Lucy -had been carried back by their terrible pursuers to fulfil her original -destiny, by becoming one of the harem of the Elder's son. As the young -fellow realized the certainty of her fate, and his own powerlessness to -prevent it, he wished that he, too, was lying with the old farmer in his -last silent resting-place. - -Again, however, his active spirit shook off the lethargy which springs -from despair. If there was nothing else left to him, he could at least -devote his life to revenge. With indomitable patience and perseverance, -Jefferson Hope possessed also a power of sustained vindictiveness, which -he may have learned from the Indians amongst whom he had lived. As he -stood by the desolate fire, he felt that the only one thing which could -assuage his grief would be thorough and complete retribution, brought -by his own hand upon his enemies. His strong will and untiring energy -should, he determined, be devoted to that one end. With a grim, white -face, he retraced his steps to where he had dropped the food, and having -stirred up the smouldering fire, he cooked enough to last him for a -few days. This he made up into a bundle, and, tired as he was, he -set himself to walk back through the mountains upon the track of the -avenging angels. - -For five days he toiled footsore and weary through the defiles which he -had already traversed on horseback. At night he flung himself down among -the rocks, and snatched a few hours of sleep; but before daybreak he was -always well on his way. On the sixth day, he reached the Eagle Cañon, -from which they had commenced their ill-fated flight. Thence he could -look down upon the home of the saints. Worn and exhausted, he leaned -upon his rifle and shook his gaunt hand fiercely at the silent -widespread city beneath him. As he looked at it, he observed that -there were flags in some of the principal streets, and other signs of -festivity. He was still speculating as to what this might mean when he -heard the clatter of horse's hoofs, and saw a mounted man riding towards -him. As he approached, he recognized him as a Mormon named Cowper, to -whom he had rendered services at different times. He therefore accosted -him when he got up to him, with the object of finding out what Lucy -Ferrier's fate had been. - -"I am Jefferson Hope," he said. "You remember me." - -The Mormon looked at him with undisguised astonishment--indeed, it was -difficult to recognize in this tattered, unkempt wanderer, with ghastly -white face and fierce, wild eyes, the spruce young hunter of former -days. Having, however, at last, satisfied himself as to his identity, -the man's surprise changed to consternation. - -"You are mad to come here," he cried. "It is as much as my own life is -worth to be seen talking with you. There is a warrant against you from -the Holy Four for assisting the Ferriers away." - -"I don't fear them, or their warrant," Hope said, earnestly. "You must -know something of this matter, Cowper. I conjure you by everything you -hold dear to answer a few questions. We have always been friends. For -God's sake, don't refuse to answer me." - -"What is it?" the Mormon asked uneasily. "Be quick. The very rocks have -ears and the trees eyes." - -"What has become of Lucy Ferrier?" - -"She was married yesterday to young Drebber. Hold up, man, hold up, you -have no life left in you." - -"Don't mind me," said Hope faintly. He was white to the very lips, and -had sunk down on the stone against which he had been leaning. "Married, -you say?" - -"Married yesterday--that's what those flags are for on the Endowment -House. There was some words between young Drebber and young Stangerson -as to which was to have her. They'd both been in the party that followed -them, and Stangerson had shot her father, which seemed to give him the -best claim; but when they argued it out in council, Drebber's party was -the stronger, so the Prophet gave her over to him. No one won't have -her very long though, for I saw death in her face yesterday. She is more -like a ghost than a woman. Are you off, then?" - -"Yes, I am off," said Jefferson Hope, who had risen from his seat. His -face might have been chiselled out of marble, so hard and set was its -expression, while its eyes glowed with a baleful light. - -"Where are you going?" - -"Never mind," he answered; and, slinging his weapon over his shoulder, -strode off down the gorge and so away into the heart of the mountains to -the haunts of the wild beasts. Amongst them all there was none so fierce -and so dangerous as himself. - -The prediction of the Mormon was only too well fulfilled. Whether it was -the terrible death of her father or the effects of the hateful marriage -into which she had been forced, poor Lucy never held up her head again, -but pined away and died within a month. Her sottish husband, who had -married her principally for the sake of John Ferrier's property, did not -affect any great grief at his bereavement; but his other wives mourned -over her, and sat up with her the night before the burial, as is the -Mormon custom. They were grouped round the bier in the early hours of -the morning, when, to their inexpressible fear and astonishment, -the door was flung open, and a savage-looking, weather-beaten man in -tattered garments strode into the room. Without a glance or a word to -the cowering women, he walked up to the white silent figure which had -once contained the pure soul of Lucy Ferrier. Stooping over her, he -pressed his lips reverently to her cold forehead, and then, snatching -up her hand, he took the wedding-ring from her finger. "She shall not be -buried in that," he cried with a fierce snarl, and before an alarm could -be raised sprang down the stairs and was gone. So strange and so brief -was the episode, that the watchers might have found it hard to believe -it themselves or persuade other people of it, had it not been for the -undeniable fact that the circlet of gold which marked her as having been -a bride had disappeared. - -For some months Jefferson Hope lingered among the mountains, leading -a strange wild life, and nursing in his heart the fierce desire for -vengeance which possessed him. Tales were told in the City of the weird -figure which was seen prowling about the suburbs, and which haunted -the lonely mountain gorges. Once a bullet whistled through Stangerson's -window and flattened itself upon the wall within a foot of him. On -another occasion, as Drebber passed under a cliff a great boulder -crashed down on him, and he only escaped a terrible death by throwing -himself upon his face. The two young Mormons were not long in -discovering the reason of these attempts upon their lives, and led -repeated expeditions into the mountains in the hope of capturing or -killing their enemy, but always without success. Then they adopted the -precaution of never going out alone or after nightfall, and of having -their houses guarded. After a time they were able to relax these -measures, for nothing was either heard or seen of their opponent, and -they hoped that time had cooled his vindictiveness. - -Far from doing so, it had, if anything, augmented it. The hunter's mind -was of a hard, unyielding nature, and the predominant idea of revenge -had taken such complete possession of it that there was no room for -any other emotion. He was, however, above all things practical. He soon -realized that even his iron constitution could not stand the incessant -strain which he was putting upon it. Exposure and want of wholesome food -were wearing him out. If he died like a dog among the mountains, what -was to become of his revenge then? And yet such a death was sure to -overtake him if he persisted. He felt that that was to play his enemy's -game, so he reluctantly returned to the old Nevada mines, there to -recruit his health and to amass money enough to allow him to pursue his -object without privation. - -His intention had been to be absent a year at the most, but a -combination of unforeseen circumstances prevented his leaving the mines -for nearly five. At the end of that time, however, his memory of -his wrongs and his craving for revenge were quite as keen as on that -memorable night when he had stood by John Ferrier's grave. Disguised, -and under an assumed name, he returned to Salt Lake City, careless -what became of his own life, as long as he obtained what he knew to -be justice. There he found evil tidings awaiting him. There had been a -schism among the Chosen People a few months before, some of the younger -members of the Church having rebelled against the authority of the -Elders, and the result had been the secession of a certain number of the -malcontents, who had left Utah and become Gentiles. Among these had been -Drebber and Stangerson; and no one knew whither they had gone. Rumour -reported that Drebber had managed to convert a large part of his -property into money, and that he had departed a wealthy man, while his -companion, Stangerson, was comparatively poor. There was no clue at all, -however, as to their whereabouts. - -Many a man, however vindictive, would have abandoned all thought of -revenge in the face of such a difficulty, but Jefferson Hope never -faltered for a moment. With the small competence he possessed, eked out -by such employment as he could pick up, he travelled from town to town -through the United States in quest of his enemies. Year passed into -year, his black hair turned grizzled, but still he wandered on, a human -bloodhound, with his mind wholly set upon the one object upon which he -had devoted his life. At last his perseverance was rewarded. It was -but a glance of a face in a window, but that one glance told him that -Cleveland in Ohio possessed the men whom he was in pursuit of. He -returned to his miserable lodgings with his plan of vengeance all -arranged. It chanced, however, that Drebber, looking from his window, -had recognized the vagrant in the street, and had read murder in -his eyes. He hurried before a justice of the peace, accompanied by -Stangerson, who had become his private secretary, and represented to him -that they were in danger of their lives from the jealousy and hatred of -an old rival. That evening Jefferson Hope was taken into custody, and -not being able to find sureties, was detained for some weeks. When at -last he was liberated, it was only to find that Drebber's house was -deserted, and that he and his secretary had departed for Europe. - -Again the avenger had been foiled, and again his concentrated hatred -urged him to continue the pursuit. Funds were wanting, however, and -for some time he had to return to work, saving every dollar for his -approaching journey. At last, having collected enough to keep life in -him, he departed for Europe, and tracked his enemies from city to -city, working his way in any menial capacity, but never overtaking the -fugitives. When he reached St. Petersburg they had departed for Paris; -and when he followed them there he learned that they had just set off -for Copenhagen. At the Danish capital he was again a few days late, for -they had journeyed on to London, where he at last succeeded in running -them to earth. As to what occurred there, we cannot do better than quote -the old hunter's own account, as duly recorded in Dr. Watson's Journal, -to which we are already under such obligations. - - - - -CHAPTER VI. A CONTINUATION OF THE REMINISCENCES OF JOHN WATSON, M.D. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -OUR prisoner's furious resistance did not apparently indicate any -ferocity in his disposition towards ourselves, for on finding himself -powerless, he smiled in an affable manner, and expressed his hopes that -he had not hurt any of us in the scuffle. "I guess you're going to take -me to the police-station," he remarked to Sherlock Holmes. "My cab's at -the door. If you'll loose my legs I'll walk down to it. I'm not so light -to lift as I used to be." - -Gregson and Lestrade exchanged glances as if they thought this -proposition rather a bold one; but Holmes at once took the prisoner at -his word, and loosened the towel which we had bound round his ancles. -[23]_ He rose and stretched his legs, as though to assure himself that -they were free once more. I remember that I thought to myself, as I eyed -him, that I had seldom seen a more powerfully built man; and his dark -sunburned face bore an expression of determination and energy which was -as formidable as his personal strength. - -"If there's a vacant place for a chief of the police, I reckon you -are the man for it," he said, gazing with undisguised admiration at my -fellow-lodger. "The way you kept on my trail was a caution." - -"You had better come with me," said Holmes to the two detectives. - -"I can drive you," said Lestrade. - -"Good! and Gregson can come inside with me. You too, Doctor, you have -taken an interest in the case and may as well stick to us." - -I assented gladly, and we all descended together. Our prisoner made no -attempt at escape, but stepped calmly into the cab which had been his, -and we followed him. Lestrade mounted the box, whipped up the horse, and -brought us in a very short time to our destination. We were ushered into -a small chamber where a police Inspector noted down our prisoner's name -and the names of the men with whose murder he had been charged. The -official was a white-faced unemotional man, who went through his -duties in a dull mechanical way. "The prisoner will be put before the -magistrates in the course of the week," he said; "in the mean time, Mr. -Jefferson Hope, have you anything that you wish to say? I must warn you -that your words will be taken down, and may be used against you." - -"I've got a good deal to say," our prisoner said slowly. "I want to tell -you gentlemen all about it." - -"Hadn't you better reserve that for your trial?" asked the Inspector. - -"I may never be tried," he answered. "You needn't look startled. It -isn't suicide I am thinking of. Are you a Doctor?" He turned his fierce -dark eyes upon me as he asked this last question. - -"Yes; I am," I answered. - -"Then put your hand here," he said, with a smile, motioning with his -manacled wrists towards his chest. - -I did so; and became at once conscious of an extraordinary throbbing and -commotion which was going on inside. The walls of his chest seemed to -thrill and quiver as a frail building would do inside when some powerful -engine was at work. In the silence of the room I could hear a dull -humming and buzzing noise which proceeded from the same source. - -"Why," I cried, "you have an aortic aneurism!" - -"That's what they call it," he said, placidly. "I went to a Doctor last -week about it, and he told me that it is bound to burst before many days -passed. It has been getting worse for years. I got it from over-exposure -and under-feeding among the Salt Lake Mountains. I've done my work now, -and I don't care how soon I go, but I should like to leave some account -of the business behind me. I don't want to be remembered as a common -cut-throat." - -The Inspector and the two detectives had a hurried discussion as to the -advisability of allowing him to tell his story. - -"Do you consider, Doctor, that there is immediate danger?" the former -asked, [24]_ - -"Most certainly there is," I answered. - -"In that case it is clearly our duty, in the interests of justice, to -take his statement," said the Inspector. "You are at liberty, sir, to -give your account, which I again warn you will be taken down." - -"I'll sit down, with your leave," the prisoner said, suiting the action -to the word. "This aneurism of mine makes me easily tired, and the -tussle we had half an hour ago has not mended matters. I'm on the brink -of the grave, and I am not likely to lie to you. Every word I say is the -absolute truth, and how you use it is a matter of no consequence to me." - -With these words, Jefferson Hope leaned back in his chair and began -the following remarkable statement. He spoke in a calm and methodical -manner, as though the events which he narrated were commonplace enough. -I can vouch for the accuracy of the subjoined account, for I have had -access to Lestrade's note-book, in which the prisoner's words were taken -down exactly as they were uttered. - -"It don't much matter to you why I hated these men," he said; "it's -enough that they were guilty of the death of two human beings--a father -and a daughter--and that they had, therefore, forfeited their own -lives. After the lapse of time that has passed since their crime, it was -impossible for me to secure a conviction against them in any court. I -knew of their guilt though, and I determined that I should be judge, -jury, and executioner all rolled into one. You'd have done the same, if -you have any manhood in you, if you had been in my place. - -"That girl that I spoke of was to have married me twenty years ago. She -was forced into marrying that same Drebber, and broke her heart over -it. I took the marriage ring from her dead finger, and I vowed that his -dying eyes should rest upon that very ring, and that his last thoughts -should be of the crime for which he was punished. I have carried -it about with me, and have followed him and his accomplice over two -continents until I caught them. They thought to tire me out, but they -could not do it. If I die to-morrow, as is likely enough, I die knowing -that my work in this world is done, and well done. They have perished, -and by my hand. There is nothing left for me to hope for, or to desire. - -"They were rich and I was poor, so that it was no easy matter for me to -follow them. When I got to London my pocket was about empty, and I found -that I must turn my hand to something for my living. Driving and riding -are as natural to me as walking, so I applied at a cabowner's office, -and soon got employment. I was to bring a certain sum a week to the -owner, and whatever was over that I might keep for myself. There was -seldom much over, but I managed to scrape along somehow. The hardest job -was to learn my way about, for I reckon that of all the mazes that ever -were contrived, this city is the most confusing. I had a map beside me -though, and when once I had spotted the principal hotels and stations, I -got on pretty well. - -"It was some time before I found out where my two gentlemen were living; -but I inquired and inquired until at last I dropped across them. They -were at a boarding-house at Camberwell, over on the other side of the -river. When once I found them out I knew that I had them at my mercy. I -had grown my beard, and there was no chance of their recognizing me. -I would dog them and follow them until I saw my opportunity. I was -determined that they should not escape me again. - -"They were very near doing it for all that. Go where they would about -London, I was always at their heels. Sometimes I followed them on my -cab, and sometimes on foot, but the former was the best, for then they -could not get away from me. It was only early in the morning or late -at night that I could earn anything, so that I began to get behind hand -with my employer. I did not mind that, however, as long as I could lay -my hand upon the men I wanted. - -"They were very cunning, though. They must have thought that there was -some chance of their being followed, for they would never go out alone, -and never after nightfall. During two weeks I drove behind them every -day, and never once saw them separate. Drebber himself was drunk half -the time, but Stangerson was not to be caught napping. I watched them -late and early, but never saw the ghost of a chance; but I was not -discouraged, for something told me that the hour had almost come. My -only fear was that this thing in my chest might burst a little too soon -and leave my work undone. - -"At last, one evening I was driving up and down Torquay Terrace, as the -street was called in which they boarded, when I saw a cab drive up to -their door. Presently some luggage was brought out, and after a time -Drebber and Stangerson followed it, and drove off. I whipped up my horse -and kept within sight of them, feeling very ill at ease, for I feared -that they were going to shift their quarters. At Euston Station they -got out, and I left a boy to hold my horse, and followed them on to the -platform. I heard them ask for the Liverpool train, and the guard answer -that one had just gone and there would not be another for some hours. -Stangerson seemed to be put out at that, but Drebber was rather pleased -than otherwise. I got so close to them in the bustle that I could hear -every word that passed between them. Drebber said that he had a little -business of his own to do, and that if the other would wait for him he -would soon rejoin him. His companion remonstrated with him, and reminded -him that they had resolved to stick together. Drebber answered that the -matter was a delicate one, and that he must go alone. I could not catch -what Stangerson said to that, but the other burst out swearing, and -reminded him that he was nothing more than his paid servant, and that he -must not presume to dictate to him. On that the Secretary gave it up -as a bad job, and simply bargained with him that if he missed the last -train he should rejoin him at Halliday's Private Hotel; to which Drebber -answered that he would be back on the platform before eleven, and made -his way out of the station. - -"The moment for which I had waited so long had at last come. I had my -enemies within my power. Together they could protect each other, -but singly they were at my mercy. I did not act, however, with undue -precipitation. My plans were already formed. There is no satisfaction in -vengeance unless the offender has time to realize who it is that strikes -him, and why retribution has come upon him. I had my plans arranged by -which I should have the opportunity of making the man who had wronged me -understand that his old sin had found him out. It chanced that some days -before a gentleman who had been engaged in looking over some houses in -the Brixton Road had dropped the key of one of them in my carriage. It -was claimed that same evening, and returned; but in the interval I had -taken a moulding of it, and had a duplicate constructed. By means of -this I had access to at least one spot in this great city where I could -rely upon being free from interruption. How to get Drebber to that house -was the difficult problem which I had now to solve. - -"He walked down the road and went into one or two liquor shops, staying -for nearly half-an-hour in the last of them. When he came out he -staggered in his walk, and was evidently pretty well on. There was a -hansom just in front of me, and he hailed it. I followed it so close -that the nose of my horse was within a yard of his driver the whole way. -We rattled across Waterloo Bridge and through miles of streets, until, -to my astonishment, we found ourselves back in the Terrace in which he -had boarded. I could not imagine what his intention was in returning -there; but I went on and pulled up my cab a hundred yards or so from -the house. He entered it, and his hansom drove away. Give me a glass of -water, if you please. My mouth gets dry with the talking." - -I handed him the glass, and he drank it down. - -"That's better," he said. "Well, I waited for a quarter of an hour, or -more, when suddenly there came a noise like people struggling inside the -house. Next moment the door was flung open and two men appeared, one of -whom was Drebber, and the other was a young chap whom I had never seen -before. This fellow had Drebber by the collar, and when they came to -the head of the steps he gave him a shove and a kick which sent him half -across the road. 'You hound,' he cried, shaking his stick at him; 'I'll -teach you to insult an honest girl!' He was so hot that I think he would -have thrashed Drebber with his cudgel, only that the cur staggered away -down the road as fast as his legs would carry him. He ran as far as the -corner, and then, seeing my cab, he hailed me and jumped in. 'Drive me -to Halliday's Private Hotel,' said he. - -"When I had him fairly inside my cab, my heart jumped so with joy that -I feared lest at this last moment my aneurism might go wrong. I drove -along slowly, weighing in my own mind what it was best to do. I might -take him right out into the country, and there in some deserted lane -have my last interview with him. I had almost decided upon this, when he -solved the problem for me. The craze for drink had seized him again, and -he ordered me to pull up outside a gin palace. He went in, leaving word -that I should wait for him. There he remained until closing time, and -when he came out he was so far gone that I knew the game was in my own -hands. - -"Don't imagine that I intended to kill him in cold blood. It would only -have been rigid justice if I had done so, but I could not bring myself -to do it. I had long determined that he should have a show for his life -if he chose to take advantage of it. Among the many billets which I -have filled in America during my wandering life, I was once janitor and -sweeper out of the laboratory at York College. One day the professor was -lecturing on poisions, [25]_ and he showed his students some alkaloid, -as he called it, which he had extracted from some South American arrow -poison, and which was so powerful that the least grain meant instant -death. I spotted the bottle in which this preparation was kept, and when -they were all gone, I helped myself to a little of it. I was a fairly -good dispenser, so I worked this alkaloid into small, soluble pills, and -each pill I put in a box with a similar pill made without the poison. -I determined at the time that when I had my chance, my gentlemen should -each have a draw out of one of these boxes, while I ate the pill that -remained. It would be quite as deadly, and a good deal less noisy than -firing across a handkerchief. From that day I had always my pill boxes -about with me, and the time had now come when I was to use them. - -"It was nearer one than twelve, and a wild, bleak night, blowing hard -and raining in torrents. Dismal as it was outside, I was glad within--so -glad that I could have shouted out from pure exultation. If any of you -gentlemen have ever pined for a thing, and longed for it during twenty -long years, and then suddenly found it within your reach, you would -understand my feelings. I lit a cigar, and puffed at it to steady my -nerves, but my hands were trembling, and my temples throbbing with -excitement. As I drove, I could see old John Ferrier and sweet Lucy -looking at me out of the darkness and smiling at me, just as plain as I -see you all in this room. All the way they were ahead of me, one on each -side of the horse until I pulled up at the house in the Brixton Road. - -"There was not a soul to be seen, nor a sound to be heard, except the -dripping of the rain. When I looked in at the window, I found Drebber -all huddled together in a drunken sleep. I shook him by the arm, 'It's -time to get out,' I said. - -"'All right, cabby,' said he. - -"I suppose he thought we had come to the hotel that he had mentioned, -for he got out without another word, and followed me down the garden. -I had to walk beside him to keep him steady, for he was still a little -top-heavy. When we came to the door, I opened it, and led him into the -front room. I give you my word that all the way, the father and the -daughter were walking in front of us. - -"'It's infernally dark,' said he, stamping about. - -"'We'll soon have a light,' I said, striking a match and putting it to -a wax candle which I had brought with me. 'Now, Enoch Drebber,' I -continued, turning to him, and holding the light to my own face, 'who am -I?' - -"He gazed at me with bleared, drunken eyes for a moment, and then I -saw a horror spring up in them, and convulse his whole features, which -showed me that he knew me. He staggered back with a livid face, and I -saw the perspiration break out upon his brow, while his teeth chattered -in his head. At the sight, I leaned my back against the door and laughed -loud and long. I had always known that vengeance would be sweet, but I -had never hoped for the contentment of soul which now possessed me. - -"'You dog!' I said; 'I have hunted you from Salt Lake City to St. -Petersburg, and you have always escaped me. Now, at last your wanderings -have come to an end, for either you or I shall never see to-morrow's sun -rise.' He shrunk still further away as I spoke, and I could see on his -face that he thought I was mad. So I was for the time. The pulses in my -temples beat like sledge-hammers, and I believe I would have had a fit -of some sort if the blood had not gushed from my nose and relieved me. - -"'What do you think of Lucy Ferrier now?' I cried, locking the door, and -shaking the key in his face. 'Punishment has been slow in coming, but it -has overtaken you at last.' I saw his coward lips tremble as I spoke. He -would have begged for his life, but he knew well that it was useless. - -"'Would you murder me?' he stammered. - -"'There is no murder,' I answered. 'Who talks of murdering a mad dog? -What mercy had you upon my poor darling, when you dragged her from her -slaughtered father, and bore her away to your accursed and shameless -harem.' - -"'It was not I who killed her father,' he cried. - -"'But it was you who broke her innocent heart,' I shrieked, thrusting -the box before him. 'Let the high God judge between us. Choose and -eat. There is death in one and life in the other. I shall take what you -leave. Let us see if there is justice upon the earth, or if we are ruled -by chance.' - -"He cowered away with wild cries and prayers for mercy, but I drew my -knife and held it to his throat until he had obeyed me. Then I swallowed -the other, and we stood facing one another in silence for a minute or -more, waiting to see which was to live and which was to die. Shall I -ever forget the look which came over his face when the first warning -pangs told him that the poison was in his system? I laughed as I saw -it, and held Lucy's marriage ring in front of his eyes. It was but for -a moment, for the action of the alkaloid is rapid. A spasm of pain -contorted his features; he threw his hands out in front of him, -staggered, and then, with a hoarse cry, fell heavily upon the floor. I -turned him over with my foot, and placed my hand upon his heart. There -was no movement. He was dead! - -"The blood had been streaming from my nose, but I had taken no notice of -it. I don't know what it was that put it into my head to write upon the -wall with it. Perhaps it was some mischievous idea of setting the police -upon a wrong track, for I felt light-hearted and cheerful. I remembered -a German being found in New York with RACHE written up above him, and it -was argued at the time in the newspapers that the secret societies must -have done it. I guessed that what puzzled the New Yorkers would puzzle -the Londoners, so I dipped my finger in my own blood and printed it on -a convenient place on the wall. Then I walked down to my cab and found -that there was nobody about, and that the night was still very wild. I -had driven some distance when I put my hand into the pocket in which -I usually kept Lucy's ring, and found that it was not there. I was -thunderstruck at this, for it was the only memento that I had of her. -Thinking that I might have dropped it when I stooped over Drebber's -body, I drove back, and leaving my cab in a side street, I went boldly -up to the house--for I was ready to dare anything rather than lose -the ring. When I arrived there, I walked right into the arms of a -police-officer who was coming out, and only managed to disarm his -suspicions by pretending to be hopelessly drunk. - -"That was how Enoch Drebber came to his end. All I had to do then was -to do as much for Stangerson, and so pay off John Ferrier's debt. I knew -that he was staying at Halliday's Private Hotel, and I hung about all -day, but he never came out. [26]_ fancy that he suspected something when -Drebber failed to put in an appearance. He was cunning, was Stangerson, -and always on his guard. If he thought he could keep me off by staying -indoors he was very much mistaken. I soon found out which was the window -of his bedroom, and early next morning I took advantage of some ladders -which were lying in the lane behind the hotel, and so made my way into -his room in the grey of the dawn. I woke him up and told him that the -hour had come when he was to answer for the life he had taken so long -before. I described Drebber's death to him, and I gave him the same -choice of the poisoned pills. Instead of grasping at the chance of -safety which that offered him, he sprang from his bed and flew at my -throat. In self-defence I stabbed him to the heart. It would have been -the same in any case, for Providence would never have allowed his guilty -hand to pick out anything but the poison. - -"I have little more to say, and it's as well, for I am about done up. -I went on cabbing it for a day or so, intending to keep at it until I -could save enough to take me back to America. I was standing in the -yard when a ragged youngster asked if there was a cabby there called -Jefferson Hope, and said that his cab was wanted by a gentleman at 221B, -Baker Street. I went round, suspecting no harm, and the next thing I -knew, this young man here had the bracelets on my wrists, and as neatly -snackled [27]_ as ever I saw in my life. That's the whole of my story, -gentlemen. You may consider me to be a murderer; but I hold that I am -just as much an officer of justice as you are." - -So thrilling had the man's narrative been, and his manner was so -impressive that we had sat silent and absorbed. Even the professional -detectives, _blasé_ as they were in every detail of crime, appeared to -be keenly interested in the man's story. When he finished we sat for -some minutes in a stillness which was only broken by the scratching -of Lestrade's pencil as he gave the finishing touches to his shorthand -account. - -"There is only one point on which I should like a little more -information," Sherlock Holmes said at last. "Who was your accomplice who -came for the ring which I advertised?" - -The prisoner winked at my friend jocosely. "I can tell my own secrets," -he said, "but I don't get other people into trouble. I saw your -advertisement, and I thought it might be a plant, or it might be the -ring which I wanted. My friend volunteered to go and see. I think you'll -own he did it smartly." - -"Not a doubt of that," said Holmes heartily. - -"Now, gentlemen," the Inspector remarked gravely, "the forms of the law -must be complied with. On Thursday the prisoner will be brought before -the magistrates, and your attendance will be required. Until then I will -be responsible for him." He rang the bell as he spoke, and Jefferson -Hope was led off by a couple of warders, while my friend and I made our -way out of the Station and took a cab back to Baker Street. - - - - -CHAPTER VII. THE CONCLUSION. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -WE had all been warned to appear before the magistrates upon the -Thursday; but when the Thursday came there was no occasion for our -testimony. A higher Judge had taken the matter in hand, and Jefferson -Hope had been summoned before a tribunal where strict justice would -be meted out to him. On the very night after his capture the aneurism -burst, and he was found in the morning stretched upon the floor of the -cell, with a placid smile upon his face, as though he had been able -in his dying moments to look back upon a useful life, and on work well -done. - -"Gregson and Lestrade will be wild about his death," Holmes remarked, as -we chatted it over next evening. "Where will their grand advertisement -be now?" - -"I don't see that they had very much to do with his capture," I -answered. - -"What you do in this world is a matter of no consequence," returned my -companion, bitterly. "The question is, what can you make people believe -that you have done. Never mind," he continued, more brightly, after a -pause. "I would not have missed the investigation for anything. There -has been no better case within my recollection. Simple as it was, there -were several most instructive points about it." - -"Simple!" I ejaculated. - -"Well, really, it can hardly be described as otherwise," said Sherlock -Holmes, smiling at my surprise. "The proof of its intrinsic simplicity -is, that without any help save a few very ordinary deductions I was able -to lay my hand upon the criminal within three days." - -"That is true," said I. - -"I have already explained to you that what is out of the common is -usually a guide rather than a hindrance. In solving a problem of this -sort, the grand thing is to be able to reason backwards. That is a very -useful accomplishment, and a very easy one, but people do not practise -it much. In the every-day affairs of life it is more useful to reason -forwards, and so the other comes to be neglected. There are fifty who -can reason synthetically for one who can reason analytically." - -"I confess," said I, "that I do not quite follow you." - -"I hardly expected that you would. Let me see if I can make it clearer. -Most people, if you describe a train of events to them, will tell you -what the result would be. They can put those events together in their -minds, and argue from them that something will come to pass. There are -few people, however, who, if you told them a result, would be able to -evolve from their own inner consciousness what the steps were which led -up to that result. This power is what I mean when I talk of reasoning -backwards, or analytically." - -"I understand," said I. - -"Now this was a case in which you were given the result and had to -find everything else for yourself. Now let me endeavour to show you the -different steps in my reasoning. To begin at the beginning. I approached -the house, as you know, on foot, and with my mind entirely free from all -impressions. I naturally began by examining the roadway, and there, as I -have already explained to you, I saw clearly the marks of a cab, which, -I ascertained by inquiry, must have been there during the night. I -satisfied myself that it was a cab and not a private carriage by the -narrow gauge of the wheels. The ordinary London growler is considerably -less wide than a gentleman's brougham. - -"This was the first point gained. I then walked slowly down the garden -path, which happened to be composed of a clay soil, peculiarly suitable -for taking impressions. No doubt it appeared to you to be a mere -trampled line of slush, but to my trained eyes every mark upon its -surface had a meaning. There is no branch of detective science which -is so important and so much neglected as the art of tracing footsteps. -Happily, I have always laid great stress upon it, and much practice -has made it second nature to me. I saw the heavy footmarks of the -constables, but I saw also the track of the two men who had first passed -through the garden. It was easy to tell that they had been before the -others, because in places their marks had been entirely obliterated by -the others coming upon the top of them. In this way my second link was -formed, which told me that the nocturnal visitors were two in number, -one remarkable for his height (as I calculated from the length of his -stride), and the other fashionably dressed, to judge from the small and -elegant impression left by his boots. - -"On entering the house this last inference was confirmed. My well-booted -man lay before me. The tall one, then, had done the murder, if murder -there was. There was no wound upon the dead man's person, but the -agitated expression upon his face assured me that he had foreseen his -fate before it came upon him. Men who die from heart disease, or any -sudden natural cause, never by any chance exhibit agitation upon their -features. Having sniffed the dead man's lips I detected a slightly sour -smell, and I came to the conclusion that he had had poison forced upon -him. Again, I argued that it had been forced upon him from the hatred -and fear expressed upon his face. By the method of exclusion, I had -arrived at this result, for no other hypothesis would meet the facts. -Do not imagine that it was a very unheard of idea. The forcible -administration of poison is by no means a new thing in criminal annals. -The cases of Dolsky in Odessa, and of Leturier in Montpellier, will -occur at once to any toxicologist. - -"And now came the great question as to the reason why. Robbery had not -been the object of the murder, for nothing was taken. Was it politics, -then, or was it a woman? That was the question which confronted me. -I was inclined from the first to the latter supposition. Political -assassins are only too glad to do their work and to fly. This murder -had, on the contrary, been done most deliberately, and the perpetrator -had left his tracks all over the room, showing that he had been there -all the time. It must have been a private wrong, and not a political -one, which called for such a methodical revenge. When the inscription -was discovered upon the wall I was more inclined than ever to my -opinion. The thing was too evidently a blind. When the ring was found, -however, it settled the question. Clearly the murderer had used it to -remind his victim of some dead or absent woman. It was at this point -that I asked Gregson whether he had enquired in his telegram to -Cleveland as to any particular point in Mr. Drebber's former career. He -answered, you remember, in the negative. - -"I then proceeded to make a careful examination of the room, which -confirmed me in my opinion as to the murderer's height, and furnished me -with the additional details as to the Trichinopoly cigar and the length -of his nails. I had already come to the conclusion, since there were no -signs of a struggle, that the blood which covered the floor had burst -from the murderer's nose in his excitement. I could perceive that the -track of blood coincided with the track of his feet. It is seldom that -any man, unless he is very full-blooded, breaks out in this way through -emotion, so I hazarded the opinion that the criminal was probably a -robust and ruddy-faced man. Events proved that I had judged correctly. - -"Having left the house, I proceeded to do what Gregson had neglected. I -telegraphed to the head of the police at Cleveland, limiting my enquiry -to the circumstances connected with the marriage of Enoch Drebber. The -answer was conclusive. It told me that Drebber had already applied for -the protection of the law against an old rival in love, named Jefferson -Hope, and that this same Hope was at present in Europe. I knew now that -I held the clue to the mystery in my hand, and all that remained was to -secure the murderer. - -"I had already determined in my own mind that the man who had walked -into the house with Drebber, was none other than the man who had driven -the cab. The marks in the road showed me that the horse had wandered -on in a way which would have been impossible had there been anyone in -charge of it. Where, then, could the driver be, unless he were inside -the house? Again, it is absurd to suppose that any sane man would carry -out a deliberate crime under the very eyes, as it were, of a third -person, who was sure to betray him. Lastly, supposing one man wished -to dog another through London, what better means could he adopt than -to turn cabdriver. All these considerations led me to the irresistible -conclusion that Jefferson Hope was to be found among the jarveys of the -Metropolis. - -"If he had been one there was no reason to believe that he had ceased to -be. On the contrary, from his point of view, any sudden change would be -likely to draw attention to himself. He would, probably, for a time at -least, continue to perform his duties. There was no reason to suppose -that he was going under an assumed name. Why should he change his name -in a country where no one knew his original one? I therefore organized -my Street Arab detective corps, and sent them systematically to every -cab proprietor in London until they ferreted out the man that I wanted. -How well they succeeded, and how quickly I took advantage of it, are -still fresh in your recollection. The murder of Stangerson was an -incident which was entirely unexpected, but which could hardly in -any case have been prevented. Through it, as you know, I came into -possession of the pills, the existence of which I had already surmised. -You see the whole thing is a chain of logical sequences without a break -or flaw." - -"It is wonderful!" I cried. "Your merits should be publicly recognized. -You should publish an account of the case. If you won't, I will for -you." - -"You may do what you like, Doctor," he answered. "See here!" he -continued, handing a paper over to me, "look at this!" - -It was the _Echo_ for the day, and the paragraph to which he pointed was -devoted to the case in question. - -"The public," it said, "have lost a sensational treat through the sudden -death of the man Hope, who was suspected of the murder of Mr. Enoch -Drebber and of Mr. Joseph Stangerson. The details of the case will -probably be never known now, though we are informed upon good authority -that the crime was the result of an old standing and romantic feud, in -which love and Mormonism bore a part. It seems that both the victims -belonged, in their younger days, to the Latter Day Saints, and Hope, the -deceased prisoner, hails also from Salt Lake City. If the case has had -no other effect, it, at least, brings out in the most striking manner -the efficiency of our detective police force, and will serve as a lesson -to all foreigners that they will do wisely to settle their feuds at -home, and not to carry them on to British soil. It is an open secret -that the credit of this smart capture belongs entirely to the well-known -Scotland Yard officials, Messrs. Lestrade and Gregson. The man was -apprehended, it appears, in the rooms of a certain Mr. Sherlock Holmes, -who has himself, as an amateur, shown some talent in the detective -line, and who, with such instructors, may hope in time to attain to some -degree of their skill. It is expected that a testimonial of some sort -will be presented to the two officers as a fitting recognition of their -services." - -"Didn't I tell you so when we started?" cried Sherlock Holmes with a -laugh. "That's the result of all our Study in Scarlet: to get them a -testimonial!" - -"Never mind," I answered, "I have all the facts in my journal, and the -public shall know them. In the meantime you must make yourself contented -by the consciousness of success, like the Roman miser-- - - "'Populus me sibilat, at mihi plaudo - Ipse domi simul ac nummos contemplor in arca.'" - - - - - -ORIGINAL TRANSCRIBER'S NOTES: ------------------------------ - -.. [1] Frontispiece, with the caption: "He examined with his glass - the word upon the wall, going over every letter of it with the most - minute exactness." - -.. [2] "JOHN H. WATSON, M.D.": the initial letters in the name are - capitalized, the other letters in small caps. All chapter titles are in - small caps. The initial words of chapters are in small caps with first - letter capitalized. - -.. [3] "lodgings.": the period should be a comma, as in later - editions. - -.. [4] "hoemoglobin": should be haemoglobin. The o&e are - concatenated. - -.. [5] "221B": the B is in small caps - -.. [6] "THE LAURISTON GARDEN MYSTERY": the table-of-contents - lists this chapter as "...GARDENS MYSTERY"--plural, and probably more - correct. - -.. [7] "brought."": the text has an extra double-quote mark - -.. [8] "individual--": illustration this page, with the - caption: "As he spoke, his nimble fingers were flying here, there, and - everywhere." - -.. [9] "manoeuvres": the o&e are concatenated. - -.. [10] "Patent leathers": the hyphen is missing. - -.. [11] "condonment": should be condonement. - -.. [13] "wages.": ending quote is missing. - -.. [14] "the first.": ending quote is missing. - -.. [15] "make much of...": Other editions complete this sentence - with an "it." But there is a gap in the text at this point, and, given - the context, it may have actually been an interjection, a dash. The gap - is just the right size for the characters "it." and the start of a new - sentence, or for a "----" - -.. [16] "tho cushion": "tho" should be "the" - -.. [19] "shoving": later editions have "showing". The original is - clearly superior. - -.. [20] "stared about...": illustration, with the caption: "One of - them seized the little girl, and hoisted her upon his shoulder." - -.. [21] "upon the": illustration, with the caption: "As he watched - it he saw it writhe along the ground." - -.. [22] "FORMERLY...": F,S,L,C in caps, other letters in this line - in small caps. - -.. [23] "ancles": ankles. - -.. [24] "asked,": should be "asked." - -.. [25] "poisions": should be "poisons" - -.. [26] "...fancy": should be "I fancy". There is a gap in the text. - -.. [27] "snackled": "shackled" in later texts. - -.. [29] Heber C. Kemball, in one of his sermons, alludes to his hundred wives under this endearing epithet. - - -END OF THIS PROJECT GUTENBERG EBOOK A STUDY IN SCARLET ------------------------------------------------------- - -***** This file should be named 244-8.txt or 244-8.zip ***** -This and all associated files of various formats will be found in: -http://www.gutenberg.org/2/4/244/ - -Produced by Roger Squires - -Updated editions will replace the previous one--the old editions -will be renamed. - -Creating the works from public domain print editions means that no -one owns a United States copyright in these works, so the Foundation -(and you!) can copy and distribute it in the United States without -permission and without paying copyright royalties. Special rules, -set forth in the General Terms of Use part of this license, apply to -copying and distributing Project Gutenberg-tm electronic works to -protect the PROJECT GUTENBERG-tm concept and trademark. Project -Gutenberg is a registered trademark, and may not be used if you -charge for the eBooks, unless you receive specific permission. If you -do not charge anything for copies of this eBook, complying with the -rules is very easy. You may use this eBook for nearly any purpose -such as creation of derivative works, reports, performances and -research. They may be modified and printed and given away--you may do -practically ANYTHING with public domain eBooks. Redistribution is -subject to the trademark license, especially commercial -redistribution. - - - -*** START: FULL LICENSE *** - -THE FULL PROJECT GUTENBERG LICENSE -PLEASE READ THIS BEFORE YOU DISTRIBUTE OR USE THIS WORK - -To protect the Project Gutenberg-tm mission of promoting the free -distribution of electronic works, by using or distributing this work -(or any other work associated in any way with the phrase "Project -Gutenberg"), you agree to comply with all the terms of the Full Project -Gutenberg-tm License (available with this file or online at -http://gutenberg.org/license). - - -Section 1. General Terms of Use and Redistributing Project Gutenberg-tm -electronic works - -1.A. By reading or using any part of this Project Gutenberg-tm -electronic work, you indicate that you have read, understand, agree to -and accept all the terms of this license and intellectual property -(trademark/copyright) agreement. If you do not agree to abide by all -the terms of this agreement, you must cease using and return or destroy -all copies of Project Gutenberg-tm electronic works in your possession. -If you paid a fee for obtaining a copy of or access to a Project -Gutenberg-tm electronic work and you do not agree to be bound by the -terms of this agreement, you may obtain a refund from the person or -entity to whom you paid the fee as set forth in paragraph 1.E.8. - -1.B. "Project Gutenberg" is a registered trademark. It may only be -used on or associated in any way with an electronic work by people who -agree to be bound by the terms of this agreement. There are a few -things that you can do with most Project Gutenberg-tm electronic works -even without complying with the full terms of this agreement. See -paragraph 1.C below. There are a lot of things you can do with Project -Gutenberg-tm electronic works if you follow the terms of this agreement -and help preserve free future access to Project Gutenberg-tm electronic -works. See paragraph 1.E below. - -1.C. The Project Gutenberg Literary Archive Foundation ("the Foundation" -or PGLAF), owns a compilation copyright in the collection of Project -Gutenberg-tm electronic works. Nearly all the individual works in the -collection are in the public domain in the United States. If an -individual work is in the public domain in the United States and you are -located in the United States, we do not claim a right to prevent you from -copying, distributing, performing, displaying or creating derivative -works based on the work as long as all references to Project Gutenberg -are removed. Of course, we hope that you will support the Project -Gutenberg-tm mission of promoting free access to electronic works by -freely sharing Project Gutenberg-tm works in compliance with the terms of -this agreement for keeping the Project Gutenberg-tm name associated with -the work. You can easily comply with the terms of this agreement by -keeping this work in the same format with its attached full Project -Gutenberg-tm License when you share it without charge with others. - -1.D. The copyright laws of the place where you are located also govern -what you can do with this work. Copyright laws in most countries are in -a constant state of change. If you are outside the United States, check -the laws of your country in addition to the terms of this agreement -before downloading, copying, displaying, performing, distributing or -creating derivative works based on this work or any other Project -Gutenberg-tm work. The Foundation makes no representations concerning -the copyright status of any work in any country outside the United -States. - -1.E. Unless you have removed all references to Project Gutenberg: - -1.E.1. The following sentence, with active links to, or other immediate -access to, the full Project Gutenberg-tm License must appear prominently -whenever any copy of a Project Gutenberg-tm work (any work on which the -phrase "Project Gutenberg" appears, or with which the phrase "Project -Gutenberg" is associated) is accessed, displayed, performed, viewed, -copied or distributed: - -This eBook is for the use of anyone anywhere at no cost and with -almost no restrictions whatsoever. You may copy it, give it away or -re-use it under the terms of the Project Gutenberg License included -with this eBook or online at www.gutenberg.org - -1.E.2. If an individual Project Gutenberg-tm electronic work is derived -from the public domain (does not contain a notice indicating that it is -posted with permission of the copyright holder), the work can be copied -and distributed to anyone in the United States without paying any fees -or charges. If you are redistributing or providing access to a work -with the phrase "Project Gutenberg" associated with or appearing on the -work, you must comply either with the requirements of paragraphs 1.E.1 -through 1.E.7 or obtain permission for the use of the work and the -Project Gutenberg-tm trademark as set forth in paragraphs 1.E.8 or -1.E.9. - -1.E.3. If an individual Project Gutenberg-tm electronic work is posted -with the permission of the copyright holder, your use and distribution -must comply with both paragraphs 1.E.1 through 1.E.7 and any additional -terms imposed by the copyright holder. Additional terms will be linked -to the Project Gutenberg-tm License for all works posted with the -permission of the copyright holder found at the beginning of this work. - -1.E.4. Do not unlink or detach or remove the full Project Gutenberg-tm -License terms from this work, or any files containing a part of this -work or any other work associated with Project Gutenberg-tm. - -1.E.5. Do not copy, display, perform, distribute or redistribute this -electronic work, or any part of this electronic work, without -prominently displaying the sentence set forth in paragraph 1.E.1 with -active links or immediate access to the full terms of the Project -Gutenberg-tm License. - -1.E.6. You may convert to and distribute this work in any binary, -compressed, marked up, nonproprietary or proprietary form, including any -word processing or hypertext form. However, if you provide access to or -distribute copies of a Project Gutenberg-tm work in a format other than -"Plain Vanilla ASCII" or other format used in the official version -posted on the official Project Gutenberg-tm web site (www.gutenberg.org), -you must, at no additional cost, fee or expense to the user, provide a -copy, a means of exporting a copy, or a means of obtaining a copy upon -request, of the work in its original "Plain Vanilla ASCII" or other -form. Any alternate format must include the full Project Gutenberg-tm -License as specified in paragraph 1.E.1. - -1.E.7. Do not charge a fee for access to, viewing, displaying, -performing, copying or distributing any Project Gutenberg-tm works -unless you comply with paragraph 1.E.8 or 1.E.9. - -1.E.8. You may charge a reasonable fee for copies of or providing -access to or distributing Project Gutenberg-tm electronic works provided -that - -- You pay a royalty fee of 20% of the gross profits you derive from - the use of Project Gutenberg-tm works calculated using the method - you already use to calculate your applicable taxes. The fee is - owed to the owner of the Project Gutenberg-tm trademark, but he - has agreed to donate royalties under this paragraph to the - Project Gutenberg Literary Archive Foundation. Royalty payments - must be paid within 60 days following each date on which you - prepare (or are legally required to prepare) your periodic tax - returns. Royalty payments should be clearly marked as such and - sent to the Project Gutenberg Literary Archive Foundation at the - address specified in Section 4, "Information about donations to - the Project Gutenberg Literary Archive Foundation." - -- You provide a full refund of any money paid by a user who notifies - you in writing (or by e-mail) within 30 days of receipt that s/he - does not agree to the terms of the full Project Gutenberg-tm - License. You must require such a user to return or - destroy all copies of the works possessed in a physical medium - and discontinue all use of and all access to other copies of - Project Gutenberg-tm works. - -- You provide, in accordance with paragraph 1.F.3, a full refund of any - money paid for a work or a replacement copy, if a defect in the - electronic work is discovered and reported to you within 90 days - of receipt of the work. - -- You comply with all other terms of this agreement for free - distribution of Project Gutenberg-tm works. - -1.E.9. If you wish to charge a fee or distribute a Project Gutenberg-tm -electronic work or group of works on different terms than are set -forth in this agreement, you must obtain permission in writing from -both the Project Gutenberg Literary Archive Foundation and Michael -Hart, the owner of the Project Gutenberg-tm trademark. Contact the -Foundation as set forth in Section 3 below. - -1.F. - -1.F.1. Project Gutenberg volunteers and employees expend considerable -effort to identify, do copyright research on, transcribe and proofread -public domain works in creating the Project Gutenberg-tm -collection. Despite these efforts, Project Gutenberg-tm electronic -works, and the medium on which they may be stored, may contain -"Defects," such as, but not limited to, incomplete, inaccurate or -corrupt data, transcription errors, a copyright or other intellectual -property infringement, a defective or damaged disk or other medium, a -computer virus, or computer codes that damage or cannot be read by -your equipment. - -1.F.2. LIMITED WARRANTY, DISCLAIMER OF DAMAGES - Except for the "Right -of Replacement or Refund" described in paragraph 1.F.3, the Project -Gutenberg Literary Archive Foundation, the owner of the Project -Gutenberg-tm trademark, and any other party distributing a Project -Gutenberg-tm electronic work under this agreement, disclaim all -liability to you for damages, costs and expenses, including legal -fees. YOU AGREE THAT YOU HAVE NO REMEDIES FOR NEGLIGENCE, STRICT -LIABILITY, BREACH OF WARRANTY OR BREACH OF CONTRACT EXCEPT THOSE -PROVIDED IN PARAGRAPH F3. YOU AGREE THAT THE FOUNDATION, THE -TRADEMARK OWNER, AND ANY DISTRIBUTOR UNDER THIS AGREEMENT WILL NOT BE -LIABLE TO YOU FOR ACTUAL, DIRECT, INDIRECT, CONSEQUENTIAL, PUNITIVE OR -INCIDENTAL DAMAGES EVEN IF YOU GIVE NOTICE OF THE POSSIBILITY OF SUCH -DAMAGE. - -1.F.3. LIMITED RIGHT OF REPLACEMENT OR REFUND - If you discover a -defect in this electronic work within 90 days of receiving it, you can -receive a refund of the money (if any) you paid for it by sending a -written explanation to the person you received the work from. If you -received the work on a physical medium, you must return the medium with -your written explanation. The person or entity that provided you with -the defective work may elect to provide a replacement copy in lieu of a -refund. If you received the work electronically, the person or entity -providing it to you may choose to give you a second opportunity to -receive the work electronically in lieu of a refund. If the second copy -is also defective, you may demand a refund in writing without further -opportunities to fix the problem. - -1.F.4. Except for the limited right of replacement or refund set forth -in paragraph 1.F.3, this work is provided to you 'AS-IS' WITH NO OTHER -WARRANTIES OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -WARRANTIES OF MERCHANTIBILITY OR FITNESS FOR ANY PURPOSE. - -1.F.5. Some states do not allow disclaimers of certain implied -warranties or the exclusion or limitation of certain types of damages. -If any disclaimer or limitation set forth in this agreement violates the -law of the state applicable to this agreement, the agreement shall be -interpreted to make the maximum disclaimer or limitation permitted by -the applicable state law. The invalidity or unenforceability of any -provision of this agreement shall not void the remaining provisions. - -1.F.6. INDEMNITY - You agree to indemnify and hold the Foundation, the -trademark owner, any agent or employee of the Foundation, anyone -providing copies of Project Gutenberg-tm electronic works in accordance -with this agreement, and any volunteers associated with the production, -promotion and distribution of Project Gutenberg-tm electronic works, -harmless from all liability, costs and expenses, including legal fees, -that arise directly or indirectly from any of the following which you do -or cause to occur: (a) distribution of this or any Project Gutenberg-tm -work, (b) alteration, modification, or additions or deletions to any -Project Gutenberg-tm work, and (c) any Defect you cause. - - -Section 2. Information about the Mission of Project Gutenberg-tm - -Project Gutenberg-tm is synonymous with the free distribution of -electronic works in formats readable by the widest variety of computers -including obsolete, old, middle-aged and new computers. It exists -because of the efforts of hundreds of volunteers and donations from -people in all walks of life. - -Volunteers and financial support to provide volunteers with the -assistance they need, is critical to reaching Project Gutenberg-tm's -goals and ensuring that the Project Gutenberg-tm collection will -remain freely available for generations to come. In 2001, the Project -Gutenberg Literary Archive Foundation was created to provide a secure -and permanent future for Project Gutenberg-tm and future generations. -To learn more about the Project Gutenberg Literary Archive Foundation -and how your efforts and donations can help, see Sections 3 and 4 -and the Foundation web page at http://www.pglaf.org. - - -Section 3. Information about the Project Gutenberg Literary Archive -Foundation - -The Project Gutenberg Literary Archive Foundation is a non profit -501(c)(3) educational corporation organized under the laws of the -state of Mississippi and granted tax exempt status by the Internal -Revenue Service. The Foundation's EIN or federal tax identification -number is 64-6221541. Its 501(c)(3) letter is posted at -http://pglaf.org/fundraising. Contributions to the Project Gutenberg -Literary Archive Foundation are tax deductible to the full extent -permitted by U.S. federal laws and your state's laws. - -The Foundation's principal office is located at 4557 Melan Dr. S. -Fairbanks, AK, 99712., but its volunteers and employees are scattered -throughout numerous locations. Its business office is located at -809 North 1500 West, Salt Lake City, UT 84116, (801) 596-1887, email -business@pglaf.org. Email contact links and up to date contact -information can be found at the Foundation's web site and official -page at http://pglaf.org - -For additional contact information: - Dr. Gregory B. Newby - Chief Executive and Director - gbnewby@pglaf.org - - -Section 4. Information about Donations to the Project Gutenberg -Literary Archive Foundation - -Project Gutenberg-tm depends upon and cannot survive without wide -spread public support and donations to carry out its mission of -increasing the number of public domain and licensed works that can be -freely distributed in machine readable form accessible by the widest -array of equipment including outdated equipment. Many small donations -($1 to $5,000) are particularly important to maintaining tax exempt -status with the IRS. - -The Foundation is committed to complying with the laws regulating -charities and charitable donations in all 50 states of the United -States. Compliance requirements are not uniform and it takes a -considerable effort, much paperwork and many fees to meet and keep up -with these requirements. We do not solicit donations in locations -where we have not received written confirmation of compliance. To -SEND DONATIONS or determine the status of compliance for any -particular state visit http://pglaf.org - -While we cannot and do not solicit contributions from states where we -have not met the solicitation requirements, we know of no prohibition -against accepting unsolicited donations from donors in such states who -approach us with offers to donate. - -International donations are gratefully accepted, but we cannot make -any statements concerning tax treatment of donations received from -outside the United States. U.S. laws alone swamp our small staff. - -Please check the Project Gutenberg Web pages for current donation -methods and addresses. Donations are accepted in a number of other -ways including checks, online payments and credit card donations. -To donate, please visit: http://pglaf.org/donate - - -Section 5. General Information About Project Gutenberg-tm electronic -works. - -Professor Michael S. Hart is the originator of the Project Gutenberg-tm -concept of a library of electronic works that could be freely shared -with anyone. For thirty years, he produced and distributed Project -Gutenberg-tm eBooks with only a loose network of volunteer support. - - -Project Gutenberg-tm eBooks are often created from several printed -editions, all of which are confirmed as Public Domain in the U.S. -unless a copyright notice is included. Thus, we do not necessarily -keep eBooks in compliance with any particular paper edition. - - -Most people start at our Web site which has the main PG search facility: - - http://www.gutenberg.org - -This Web site includes information about Project Gutenberg-tm, -including how to make donations to the Project Gutenberg Literary -Archive Foundation, how to help produce our new eBooks, and how to -subscribe to our email newsletter to hear about new eBooks. diff --git a/nikola/data/samplesite/stories/dr-nikolas-vendetta.rst b/nikola/data/samplesite/stories/dr-nikolas-vendetta.rst new file mode 100644 index 0000000..6175355 --- /dev/null +++ b/nikola/data/samplesite/stories/dr-nikolas-vendetta.rst @@ -0,0 +1,468 @@ +.. title: A BID FOR FORTUNE OR; DR. NIKOLA'S VENDETTA
+.. template: book.tmpl
+.. hyphenate: yes
+.. filters: filters.typogrify
+
+.. class:: subtitle
+
+By `GUY BOOTHBY <http://www.gutenberg.org/ebooks/author/3587>`__
+
+Author of "Dr. Nikola," "The Beautiful White Devil," etc., etc.
+
+.. figure:: /images/frontispiece.jpg
+ :class: bookfig
+
+.. topic:: The Project Gutenberg EBook of A Bid for Fortune, by Guy Boothby
+
+ This eBook is for the use of anyone anywhere at no cost and with
+ almost no restrictions whatsoever. You may copy it, give it away or
+ re-use it under the terms of the Project Gutenberg License included
+ with this eBook or online at www.gutenberg.org
+
+
+ Title: A Bid for Fortune
+ or Dr. Nikola's Vendetta
+
+ Author: `Guy Boothby <http://www.gutenberg.org/ebooks/author/3587>`__
+
+ Release Date: May 29, 2007 [EBook #21640]
+
+ Language: English
+
+ Produced by Marilynda Fraser-Cunliffe, Mary Meehan and the
+ Online Distributed Proofreading Team at http://www.pgdp.net
+
+ Originally published by:
+
+ WARD, LOCK & CO., LIMITED
+ LONDON, MELBOURNE AND TORONTO
+ 1918
+
+.. figure:: /images/illus_001.jpg
+ :class: bookfig
+
+PART I
+======
+
+PROLOGUE
+--------
+
+.. role:: smallcaps
+
+
+:smallcaps:`The` manager of the new Imperial Restaurant on the Thames Embankment went
+into his luxurious private office and shut the door. Having done so, he
+first scratched his chin reflectively, and then took a letter from the
+drawer in which it had reposed for more than two months and perused it
+carefully. Though he was not aware of it, this was the thirtieth time he
+had read it since breakfast that morning. And yet he was not a whit
+nearer understanding it than he had been at the beginning. He turned it
+over and scrutinized the back, where not a sign of writing was to be
+seen; he held it up to the window, as if he might hope to discover
+something from the water-mark; but there was nothing in either of these
+places of a nature calculated to set his troubled mind at rest. Then he
+took a magnificent repeater watch from his waistcoat pocket and glanced
+at the dial; the hands stood at half-past seven. He immediately threw
+the letter on the table, and as he did so his anxiety found relief in
+words.
+
+"It's really the most extraordinary affair I ever had to do with," he
+remarked. "And as I've been in the business just three-and-thirty years
+at eleven a.m. next Monday morning, I ought to know something about it.
+I only hope I've done right, that's all."
+
+As he spoke, the chief bookkeeper, who had the treble advantage of being
+tall, pretty, and just eight-and-twenty years of age, entered the room.
+She noticed the open letter and the look upon her chief's face, and her
+curiosity was proportionately excited.
+
+"You seem worried, Mr. McPherson," she said tenderly, as she put down
+the papers she had brought in for his signature.
+
+"You have just hit it, Miss O'Sullivan," he answered, pushing them
+farther on to the table. "I am worried about many things, but
+particularly about this letter."
+
+He handed the epistle to her, and she, being desirous of impressing him
+with her business capabilities, read it with ostentatious care. But it
+was noticeable that when she reached the signature she too turned back
+to the beginning, and then deliberately read it over again. The manager
+rose, crossed to the mantelpiece, and rang for the head waiter. Having
+relieved his feelings in this way, he seated himself again at his
+writing-table, put on his glasses, and stared at his companion, while
+waiting for her to speak.
+
+"It's very funny," she said. "Very funny indeed!"
+
+"It's the most extraordinary communication I have ever received," he
+replied with conviction. "You see it is written from Cuyaba, Brazil. The
+date is three months ago to a day. Now I have taken the trouble to find
+out where and what Cuyaba is."
+
+He made this confession with an air of conscious pride, and having done
+so, laid himself back in his chair, stuck his thumbs into the armholes
+of his waistcoat, and looked at his fair subordinate for approval. Nor
+was he destined to be disappointed. He was a bachelor in possession of a
+snug income, and she, besides being pretty, was a lady with a keen eye
+to the main chance.
+
+"And where *is* Cuyaba?" she asked humbly.
+
+"Cuyaba," he replied, rolling his tongue with considerable relish round
+his unconscious mispronunciation of the name, "is a town almost on the
+western or Bolivian border of Brazil. It is of moderate size, is
+situated on the banks of the river Cuyaba, and is considerably connected
+with the famous Brazilian Diamond Fields."
+
+"And does the writer of this letter live there?"
+
+"I cannot say. He writes from there--that is enough for us."
+
+"And he orders dinner for four--here, in a private room overlooking the
+river, three months ahead--punctually at eight o'clock, gives you a list
+of the things he wants, and even arranges the decoration of the table.
+Says he has never seen either of his three friends before; that one of
+them hails from (here she consulted the letter again) Hang-chow, another
+from Bloemfontein, while the third resides, at present, in England. Each
+one is to present an ordinary visiting card with a red dot on it to the
+porter in the hall, and to be shown to the room at once. I don't
+understand it at all."
+
+The manager paused for a moment, and then said deliberately,--"Hang-chow
+is in China, Bloemfontein is in South Africa."
+
+"What a wonderful man you are, to be sure, Mr. McPherson! I never can
+*think* how you manage to carry so much in your head."
+
+There spoke the true woman. And it was a move in the right direction,
+for the manager was susceptible to her gentle influence, as she had
+occasion to know.
+
+At this juncture the head waiter appeared upon the scene, and took up a
+position just inside the doorway, as if he were afraid of injuring the
+carpet by coming farther.
+
+"Is No. 22 ready, Williams?"
+
+"Quite ready, sir. The wine is on the ice, and cook tells me he'll be
+ready to dish punctual to the moment."
+
+"The letter says, 'no electric light; candles with red shades.' Have you
+put on those shades I got this morning?"
+
+"Just seen it done this very minute, sir."
+
+"And let me see, there was one other thing." He took the letter from the
+chief bookkeeper's hand and glanced at it. "Ah, yes, a porcelain saucer,
+and a small jug of new milk upon the mantelpiece. An extraordinary
+request, but has it been attended to?"
+
+"I put it there myself, sir."
+
+"Who wait?"
+
+"Jones, Edmunds, Brooks, and Tomkins."
+
+"Very good. Then I think that will do. Stay! You had better tell the
+hall porter to look out for three gentlemen presenting plain visiting
+cards with a little red spot on them. Let Brooks wait in the hall, and
+when they arrive tell him to show them straight up to the room."
+
+"It shall be done, sir."
+
+The head waiter left the room, and the manager stretched himself in his
+chair, yawned by way of showing his importance, and then said
+solemnly,--
+
+"I don't believe they'll any of them turn up; but if they do, this Dr.
+Nikola, whoever he may be, won't be able to find fault with my
+arrangements."
+
+Then, leaving the dusty high road of Business, he and his companion
+wandered in the shady bridle-paths of Love--to the end that when the
+chief bookkeeper returned to her own department she had forgotten the
+strange dinner party about to take place upstairs, and was busily
+engaged upon a calculation as to how she would look in white satin and
+orange blossoms, and, that settled, fell to wondering whether it was
+true, as Miss Joyce, a subordinate, had been heard to declare, that the
+manager had once shown himself partial to a certain widow with reputed
+savings and a share in an extensive egg and dairy business.
+
+At ten minutes to eight precisely a hansom drew up at the steps of the
+hotel. As soon as it stopped, an undersized gentleman, with a clean
+shaven countenance, a canonical corporation, and bow legs, dressed in a
+decidedly clerical garb, alighted. He paid and discharged his cabman,
+and then took from his ticket pocket an ordinary white visiting card,
+which he presented to the gold-laced individual who had opened the
+apron. The latter, having noted the red spot, called a waiter, and the
+reverend gentleman was immediately escorted upstairs.
+
+Hardly had the attendant time to return to his station in the hall,
+before a second cab made its appearance, closely followed by a third.
+Out of the second jumped a tall, active, well-built man of about thirty
+years of age. He was dressed in evening dress of the latest fashion, and
+to conceal it from the vulgar gaze, wore a large Inverness cape of heavy
+texture. He also in his turn handed a white card to the porter, and,
+having done so, proceeded into the hall, followed by the occupant of the
+last cab, who had closely copied his example. This individual was also
+in evening dress, but it was of a different stamp. It was old-fashioned
+and had seen much use. The wearer, too, was taller than the ordinary run
+of men, while it was noticeable that his hair was snow-white, and that
+his face was deeply pitted with smallpox. After disposing of their hats
+and coats in an ante-room, they reached room No. 22, where they found
+the gentleman in clerical costume pacing impatiently up and down.
+
+Left alone, the tallest of the trio, who for want of a better title we
+may call the Best Dressed Man, took out his watch, and having glanced at
+it, looked at his companions. "Gentlemen," he said, with a slight
+American accent, "it is three minutes to eight o'clock. My name is
+Eastover!"
+
+"I'm glad to hear it, for I'm most uncommonly hungry," said the next
+tallest, whom I have already described as being so marked by disease.
+"My name is Prendergast!"
+
+"We only wait for our friend and host," remarked the clerical gentleman,
+as if he felt he ought to take a share in the conversation, and then, as
+an afterthought, he continued, "My name is Baxter!"
+
+They shook hands all round with marked cordiality, seated themselves
+again, and took it in turns to examine the clock.
+
+"Have you ever had the pleasure of meeting our host before?" asked Mr.
+Baxter of Mr. Prendergast.
+
+"Never," replied that gentleman, with a shake of his head. "Perhaps Mr.
+Eastover has been more fortunate?"
+
+"Not I," was the brief rejoinder. "I've had to do with him off and on
+for longer than I care to reckon, but I've never set eyes on him up to
+date."
+
+"And where may he have been the first time you heard from him?"
+
+"In Nashville, Tennessee," said Eastover. "After that, Tahupapa, New
+Zealand; after that, Papeete, in the Society Islands; then Pekin, China.
+And you?"
+
+"First time, Brussels; second, Monte Video; third, Mandalay, and then
+the Gold Coast, Africa. It's your turn, Mr. Baxter."
+
+The clergyman glanced at the timepiece. It was exactly eight o'clock.
+"First time, Cabul, Afghanistan; second, Nijni Novgorod, Russia; third,
+Wilcannia, Darling River, Australia; fourth, Valparaiso, Chili; fifth,
+Nagasaki, Japan."
+
+"He is evidently a great traveller and a most mysterious person."
+
+"He is more than that," said Eastover with conviction; "he is late for
+dinner!"
+
+Prendergast looked at his watch.
+
+"That clock is two minutes fast. Hark, there goes Big Ben! Eight
+exactly."
+
+As he spoke the door was thrown open and a voice announced "Dr. Nikola."
+
+The three men sprang to their feet simultaneously, with exclamations of
+astonishment, as the man they had been discussing made his appearance.
+
+It would take more time than I can spare the subject to give you an
+adequate and inclusive description of the person who entered the room at
+that moment. In stature he was slightly above the ordinary, his
+shoulders were broad, his limbs perfectly shaped and plainly muscular,
+but very slim. His head, which was magnificently set upon his shoulders,
+was adorned with a profusion of glossy black hair; his face was
+destitute of beard or moustache, and was of oval shape and handsome
+moulding; while his skin was of a dark olive hue, a colour which
+harmonized well with his piercing black eyes and pearly teeth. His hands
+and feet were small, and the greatest dandy must have admitted that he
+was irreproachably dressed, with a neatness that bordered on the
+puritanical. In age he might have been anything from eight-and-twenty to
+forty; in reality he was thirty-three. He advanced into the room and
+walked with out-stretched hand directly across to where Eastover was
+standing by the fireplace.
+
+"Mr. Eastover, I feel certain," he said, fixing his glittering eyes upon
+the man he addressed, and allowing a curious smile to play upon his
+face.
+
+"That is my name, Dr. Nikola," the other answered with evident surprise.
+"But how on earth can you distinguish me from your other guests?"
+
+"Ah! it would surprise you if you knew. And Mr. Prendergast, and Mr.
+Baxter. This is delightful; I hope I am not late. We had a collision in
+the Channel this morning, and I was almost afraid I might not be up to
+time. Dinner seems ready; shall we sit down to it?" They seated
+themselves, and the meal commenced. The Imperial Restaurant has earned
+an enviable reputation for doing things well, and the dinner that night
+did not in any way detract from its lustre. But, delightful as it all
+was, it was noticeable that the three guests paid more attention to
+their host than to his excellent *menu*. As they had said before his
+arrival, they had all had dealings with him for several years, but what
+those dealings were they were careful not to describe. It was more than
+possible that they hardly liked to remember them themselves.
+
+When coffee had been served and the servants had withdrawn, Dr. Nikola
+rose from the table, and went across to the massive sideboard. On it
+stood a basket of very curious shape and workmanship. This he opened,
+and as he did so, to the astonishment of his guests, an enormous cat, as
+black as his master's coat, leaped out on to the floor. The reason for
+the saucer and jug of milk became evident.
+
+Seating himself at the table again, the host followed the example of his
+guests and lit a cigar, blowing a cloud of smoke luxuriously through his
+delicately chiselled nostrils. His eyes wandered round the cornice of
+the room, took in the pictures and decorations, and then came down to
+meet the faces of his companions. As they did so, the black cat, having
+finished its meal, sprang on to his shoulder to crouch there, watching
+the three men through the curling smoke drift with its green blinking,
+fiendish eyes. Dr. Nikola smiled as he noticed the effect the animal had
+upon his guests.
+
+"Now shall we get to business?" he said briskly.
+
+The others almost simultaneously knocked the ashes off their cigars and
+brought themselves to attention. Dr. Nikola's dainty, languid manner
+seemed to drop from him like a cloak, his eyes brightened, and his
+voice, when he spoke, was clean cut as chiselled silver.
+
+"You are doubtless anxious to be informed why I summoned you from all
+parts of the globe to meet me here to-night? And it is very natural you
+should be. But then, from what you know of me, you should not be
+surprised at anything I do."
+
+His voice dropped back into its old tone of gentle languor. He drew in a
+great breath of smoke and then sent it slowly out from his lips again.
+His eyes were half closed, and he drummed with one finger on the table
+edge. The cat looked through the smoke at the three men, and it seemed
+to them that he grew every moment larger and more ferocious. Presently
+his owner took him from his perch, and seating him on his knee fell to
+stroking his fur, from head to tail, with his long slim fingers. It was
+as if he were drawing inspiration for some deadly mischief from the
+uncanny beast.
+
+"To preface what I have to say to you, let me tell you that this is by
+far the most important business for which I have ever required your
+help. (Three slow strokes down the centre of the back, and one round
+each ear.) When it first came into my mind I was at a loss who to trust
+in the matter. I thought of Vendon, but I found Vendon was dead. I
+thought of Brownlow, but Brownlow was no longer faithful. (Two strokes
+down the back and two on the throat.) Then bit by bit I remembered you.
+I was in Brazil at the time. So I sent for you. You came. So far so
+good."
+
+He rose, and crossed over to the fireplace. As he went the cat crawled
+back to its original position on his shoulder. Then his voice changed
+once more to its former business-like tone.
+
+"I am not going to tell you very much about it. But from what I do tell
+you, you will be able to gather a great deal and imagine the rest. To
+begin with, there is a man living in this world to-day who has done me a
+great and lasting injury. What that injury is is no concern of yours.
+You would not understand if I told you. So we'll leave that out of the
+question. He is immensely rich. His cheque for £300,000 would be
+honoured by his bank at any minute. Obviously he is a power. He has had
+reason to know that I am pitting my wits against his, and he flatters
+himself that so far he has got the better of me. That is because I am
+drawing him on. I am maturing a plan which will make him a poor and a
+very miserable man at one and the same time. If that scheme succeeds,
+and I am satisfied with the way you three men have performed the parts I
+shall call on you to play in it, I shall pay to each of you the sum of
+£10,000. If it doesn't succeed, then you will each receive a thousand
+and your expenses. Do you follow me?"
+
+It was evident from their faces that they hung upon his every word.
+
+"But, remember, I demand from you your whole and entire labour. While
+you are serving me you are mine body and soul. I know you are
+trustworthy. I have had good proof that you are--pardon the
+expression--unscrupulous, and I flatter myself you are silent. What is
+more, I shall tell you nothing beyond what is necessary for the carrying
+out of my scheme, so that you could not betray me if you would. Now for
+my plans!"
+
+He sat down again and took a paper from his pocket. Having perused it,
+he turned to Eastover.
+
+"You will leave at once--that is to say, by the boat on Wednesday--for
+Sydney. You will book your passage to-morrow morning, first thing, and
+join her in Plymouth. You will meet me to-morrow evening at an address I
+will send you, and receive your final instructions. Good-night."
+
+Seeing that he was expected to go, Eastover rose, shook hands, and left
+the room without a word. He was too astonished to hesitate or to say
+anything.
+
+Nikola took another letter from his pocket and turned to Prendergast.
+"*You* will go down to Dover to-night, cross to Paris to-morrow morning,
+and leave this letter personally at the address you will find written on
+it. On Thursday, at half-past two precisely, you will deliver me an
+answer in the porch at Charing Cross. You will find sufficient money in
+that envelope to pay all your expenses. Now go!"
+
+"At half-past two you shall have your answer. Good-night."
+
+"Good-night."
+
+When Prendergast had left the room, Dr. Nikola lit another cigar and
+turned his attentions to Mr. Baxter.
+
+"Six months ago, Mr. Baxter, I found for you a situation as tutor to the
+young Marquis of Beckenham. You still hold it, I suppose?"
+
+"I do."
+
+"Is the father well disposed towards you?"
+
+"In every way. I have done my best to ingratiate myself with him. That
+was one of your instructions."
+
+"Yes, yes! But I was not certain that you would succeed. If the old man
+is anything like what he was when I last met him he must still be a
+difficult person to deal with. Does the boy like you?"
+
+"I hope so."
+
+"Have you brought me his photograph as I directed?"
+
+"I have. Here it is."
+
+Baxter took a photograph from his pocket and handed it across the table.
+
+"Good. You have done very well, Mr. Baxter. I am pleased with you.
+To-morrow morning you will go back to Yorkshire----"
+
+"I beg your pardon, Bournemouth. His Grace owns a house near
+Bournemouth, which he occupies during the summer months."
+
+"Very well--then to-morrow morning you will go back to Bournemouth and
+continue to ingratiate yourself with father and son. You will also begin
+to implant in the boy's mind a desire for travel. Don't let him become
+aware that his desire has its source in you--but do not fail to foster
+it all you can. I will communicate with you further in a day or two. Now
+go."
+
+Baxter in his turn left the room. The door closed. Dr. Nikola picked up
+the photograph and studied it.
+
+"The likeness is unmistakable--or it ought to be. My friend, my very
+dear friend, Wetherell, my toils are closing on you. My arrangements are
+perfecting themselves admirably. Presently, when all is complete, I
+shall press the lever, the machinery will be set in motion, and you will
+find yourself being slowly but surely ground into powder. Then you will
+hand over what I want, and be sorry you thought fit to baulk Dr.
+Nikola!"
+
+He rang the bell and ordered his bill. This duty discharged, he placed
+the cat back in its prison, shut the lid, descended with the basket to
+the hall, and called a hansom. The porter inquired to what address he
+should order the cabman to drive. Dr. Nikola did not reply for a moment,
+then he said, as if he had been thinking something out: "The *Green
+Sailor* public-house, East India Dock Road."
+
+
+------------------------
+
+You can read the rest of "A Bid For Fortune; Or, Dr. Nikola's Vendetta" at `Open Library <https://archive.org/stream/bidforfortunenov00bootiala#page/12/mode/2up>`__
diff --git a/nikola/data/samplesite/stories/path_handlers.txt b/nikola/data/samplesite/stories/path_handlers.txt new file mode 120000 index 0000000..cce056b --- /dev/null +++ b/nikola/data/samplesite/stories/path_handlers.txt @@ -0,0 +1 @@ +../../../../docs/path_handlers.txt
\ No newline at end of file diff --git a/nikola/data/samplesite/stories/quickref.rst b/nikola/data/samplesite/stories/quickref.rst index bf5324f..7cc91bd 100644 --- a/nikola/data/samplesite/stories/quickref.rst +++ b/nikola/data/samplesite/stories/quickref.rst @@ -1,9 +1,10 @@ .. title: A reStructuredText Reference .. slug: quickref .. date: 2012-03-30 23:00:00 UTC-03:00 -.. tags: -.. link: -.. description: +.. tags: +.. link: +.. description: +.. author: docutils contributors .. raw:: html @@ -78,7 +79,7 @@ character styles (like italics and boldface) and functionality (like hyperlinks). - <p><table border="1" bgcolor="#ffffcc" cellpadding="3"> + <p><table class="table" class="table" border="1" bgcolor="#ffffcc" cellpadding="3"> <thead> <tr align="left" bgcolor="#99CCFF"> <th>Plain text @@ -212,7 +213,7 @@ themselves. To get a literal backslash, use an escaped backslash ("\\"). For example: - <p><table border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> + <p><table class="table" border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> <thead> <tr align="left" bgcolor="#99CCFF"> <th width="50%">Raw reStructuredText @@ -232,7 +233,7 @@ <em>reach</em> reStructuredText. The simplest way to do this is to use raw strings: - <p><table border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> + <p><table class="table" border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> <thead> <tr align="left" bgcolor="#99CCFF"> <th width="50%">Python string @@ -255,7 +256,7 @@ <p>(<a href="http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#sections">details</a>) - <p><table border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> + <p><table class="table" border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> <thead> <tr align="left" bgcolor="#99CCFF"> <th width="50%">Plain text @@ -301,7 +302,7 @@ <p>(<a href="http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#paragraphs">details</a>) - <p><table border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> + <p><table class="table" border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> <thead> <tr align="left" bgcolor="#99CCFF"> <th width="50%">Plain text @@ -329,7 +330,7 @@ <p>(<a href="http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#bullet-lists">details</a>) - <p><table border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> + <p><table class="table" border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> <thead> <tr align="left" bgcolor="#99CCFF"> <th width="50%">Plain text @@ -367,7 +368,7 @@ <p>(<a href="http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#enumerated-lists">details</a>) - <p><table border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> + <p><table class="table" border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> <thead> <tr align="left" bgcolor="#99CCFF"> <th width="50%">Plain text @@ -405,7 +406,7 @@ <p>(<a href="http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#definition-lists">details</a>) - <p><table border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> + <p><table class="table" border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> <thead> <tr align="left" bgcolor="#99CCFF"> <th width="50%">Plain text @@ -446,7 +447,7 @@ <p>(<a href="http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#field-lists">details</a>) - <p><table border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> + <p><table class="table" border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> <thead> <tr align="left" bgcolor="#99CCFF"> <th width="50%">Plain text @@ -464,7 +465,7 @@ <p><samp>:Version: 1.0 of 2001/08/08</samp> <br><samp>:Dedication: To my father.</samp> <td> - <table> + <table class="table" > <tr valign="top"> <td><strong>Authors:</strong> <td>Tony J. (Tibs) Ibbs, @@ -485,7 +486,7 @@ <p>(<a href="http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#option-lists">details</a>) - <p><table border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> + <p><table class="table" border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> <thead> <tr align="left" bgcolor="#99CCFF"> <th width="50%">Plain text @@ -505,7 +506,7 @@ </samp> <td> - <table border="0" width="100%"> + <table class="table" border="0" width="100%"> <tbody valign="top"> <tr> <td width="30%"><samp>-a</samp> @@ -533,7 +534,7 @@ <p>(<a href="http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#literal-blocks">details</a>) - <p><table border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> + <p><table class="table" border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> <thead> <tr align="left" bgcolor="#99CCFF"> <th width="50%">Plain text @@ -622,7 +623,7 @@ <p>(<a href="http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#line-blocks">details</a>) - <p><table border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> + <p><table class="table" border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> <thead> <tr align="left" bgcolor="#99CCFF"> <th width="50%">Plain text @@ -664,7 +665,7 @@ <p>(<a href="http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#block-quotes">details</a>) - <p><table border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> + <p><table class="table" border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> <thead> <tr align="left" bgcolor="#99CCFF"> <th width="50%">Plain text @@ -696,7 +697,7 @@ <p>(<a href="http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#doctest-blocks">details</a>) - <p><table border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> + <p><table class="table" border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> <thead> <tr align="left" bgcolor="#99CCFF"> <th width="50%">Plain text @@ -736,7 +737,7 @@ tables are complete but cumbersome to create. Simple tables are easy to create but limited (no row spans, etc.).</p> - <p><table border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> + <p><table class="table" border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> <thead> <tr align="left" bgcolor="#99CCFF"> <th width="50%">Plain text @@ -760,7 +761,7 @@ <br><samp>+------------+------------+-----------+</samp></p> <td> <p>Grid table:</p> - <table border="1"> + <table class="table" border="1"> <thead valign="bottom"> <tr> <th>Header 1 @@ -809,7 +810,7 @@ <td> <p>Simple table:</p> - <table border="1"> + <table class="table" border="1"> <colgroup> <col width="31%"> <col width="31%"> @@ -849,7 +850,7 @@ <p>(<a href="http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#transitions">details</a>) - <p><table border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> + <p><table class="table" border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> <thead> <tr align="left" bgcolor="#99CCFF"> <th width="50%">Plain text @@ -899,7 +900,7 @@ <p>(<a href="http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#footnotes">details</a>) - <p><table border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> + <p><table class="table" border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> <thead> <tr align="left" bgcolor="#99CCFF"> <th width="50%">Plain text @@ -922,7 +923,7 @@ Note that footnotes may get rearranged, e.g., to the bottom of the "page". - <p><table> + <p><table class="table" > <tr><td colspan="2"><hr> <!-- <tr><td colspan="2">Footnotes: --> <tr><td><a name="5"><strong>[5]</strong></a><td> A numerical footnote. @@ -950,7 +951,7 @@ <sup><a href="#fourth">4</a></sup> and <sup><a href="#third">3</a></sup>. - <p><table> + <p><table class="table" > <tr><td colspan="2"><hr> <!-- <tr><td colspan="2">Footnotes: --> <tr><td><a name="auto1"><strong>[1]</strong></a><td> This is the first one. @@ -971,7 +972,7 @@ possible, like this: <sup><a href="#symbol1">*</a></sup> and <sup><a href="#symbol2">†</a></sup>. - <p><table> + <p><table class="table" > <tr><td colspan="2"><hr> <!-- <tr><td colspan="2">Footnotes: --> <tr><td><a name="symbol1"><strong>[*]</strong></a><td> This is the first symbol footnote @@ -992,7 +993,7 @@ <p>(<a href="http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#citations">details</a>) - <p><table border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> + <p><table class="table" border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> <thead> <tr align="left" bgcolor="#99CCFF"> <th width="50%">Plain text @@ -1030,7 +1031,7 @@ <p>Given a citation like <a href="#this">[this]</a>, one can also refer to it like <a href="#this">this</a>. - <p><table> + <p><table class="table" > <tr><td colspan="2"><hr> <!-- <tr><td colspan="2">Citations: --> <tr><td><a name="cit2002"><strong>[CIT2002]</strong></a><td> A citation @@ -1048,7 +1049,7 @@ <h4><a href="#contents" name="external-hyperlink-targets" class="backref" >External Hyperlink Targets</a></h4> - <p><table border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> + <p><table class="table" border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> <thead> <tr align="left" bgcolor="#99CCFF"> <th width="50%">Plain text @@ -1062,19 +1063,19 @@ <p><samp>.. _Python: http://www.python.org/</samp> <td> - <table width="100%"> + <table class="table" width="100%"> <tr bgcolor="#99CCFF"><td><em>Fold-in form</em> <tr><td>External hyperlinks, like <a href="http://www.python.org/">Python</a>. </table> <tr valign="top"> <td> - <table width="100%"> + <table class="table" width="100%"> <tr bgcolor="#99CCFF"><td><em>Call-out form</em> <tr><td>External hyperlinks, like <a href="#labPython"><i>Python</i></a>. - <p><table> + <p><table class="table" > <tr><td colspan="2"><hr> <tr><td><a name="labPython"><i>Python:</i></a> <td> <a href="http://www.python.org/">http://www.python.org/</a> @@ -1097,7 +1098,7 @@ reference may directly embed a target URI inline, within angle brackets. The following is exactly equivalent to the example above: - <p><table border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> + <p><table class="table" border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> <thead> <tr align="left" bgcolor="#99CCFF"> <th width="50%">Plain text @@ -1116,7 +1117,7 @@ <h4><a href="#contents" name="internal-hyperlink-targets" class="backref" >Internal Hyperlink Targets</a></h4> - <p><table border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> + <p><table class="table" border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> <thead> <tr align="left" bgcolor="#99CCFF"> <th width="50%">Plain text @@ -1131,7 +1132,7 @@ <p><samp>This is an example crossreference target.</samp> <td> - <table width="100%"> + <table class="table" width="100%"> <tr bgcolor="#99CCFF"><td><em>Fold-in form</em> <!-- Note that some browsers may not like an "a" tag that --> <!-- does not have any content, so we could arbitrarily --> @@ -1143,7 +1144,7 @@ </table> <tr valign="top"> <td> - <table width="100%"> + <table class="table" width="100%"> <tr><td bgcolor="#99CCFF"><em>Call-out form</em> <tr><td>Internal crossreferences, like <a href="#example-callout">example</a> @@ -1158,7 +1159,7 @@ <p>(<a href="http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#indirect-hyperlink-targets">details</a>) - <p><table border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> + <p><table class="table" border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> <thead> <tr align="left" bgcolor="#99CCFF"> <th width="50%">Plain text @@ -1202,7 +1203,7 @@ hyperlink targets (the title text or footnote/citation label is used as the hyperlink name). - <p><table border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> + <p><table class="table" border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> <thead><tr align="left" bgcolor="#99CCFF"> <th width="50%">Plain text <th width="50%">Typical result @@ -1232,7 +1233,7 @@ href="http://docutils.sourceforge.net/docs/ref/rst/directives.html" >reStructuredText Directives</a>. - <p><table border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> + <p><table class="table" border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> <thead> <tr align="left" bgcolor="#99CCFF"> <th width="50%">Plain text @@ -1257,7 +1258,7 @@ <p>Substitutions are like inline directives, allowing graphics and arbitrary constructs within text. - <p><table border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> + <p><table class="table" border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> <thead> <tr align="left" bgcolor="#99CCFF"> <th width="50%">Plain text @@ -1287,7 +1288,7 @@ <p>Any text which begins with an explicit markup start but doesn't use the syntax of any of the constructs above, is a comment. - <p><table border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> + <p><table class="table" border="1" width="100%" bgcolor="#ffffcc" cellpadding="3"> <thead> <tr align="left" bgcolor="#99CCFF"> <th width="50%">Plain text diff --git a/nikola/data/samplesite/templates/book.tmpl b/nikola/data/samplesite/templates/book.tmpl new file mode 100644 index 0000000..a44f088 --- /dev/null +++ b/nikola/data/samplesite/templates/book.tmpl @@ -0,0 +1,117 @@ +## -*- coding: utf-8 -*- +<%namespace name="helper" file="post_helper.tmpl"/> +<%namespace name="pheader" file="post_header.tmpl"/> +<%namespace name="comments" file="comments_helper.tmpl"/> +<%inherit file="post.tmpl"/> + +<%block name="extra_head"> + ${parent.extra_head()} + <link href='https://fonts.googleapis.com/css?family=Gentium+Book+Basic' rel='stylesheet' type='text/css'> + <style> + .smallcaps { + font-variant: small-caps; + } + .chapter { + width: 100%; + padding: 10px; + -webkit-column-gap: 40px; + -moz-column-gap: 40px; + column-gap: 40px; + -webkit-column-width: 400px; + -moz-column-width: 400px; + column-width: 400px; + -webkit-column-count: 2; + -moz-column-count: 2; + column-count: 2; + -webkit-column-rule: 1px solid #ddd; + -moz-column-rule: 1px solid #ddd; + column-rule: 1px solid #ddd; + height: 90vh; + font-family: 'Gentium Book Basic', serif; + color: #2d2e2e; + font-weight: 500; + } + div.frame { + overflow: hidden; + padding: 0; + margin: 0; + } + div.scrolling-cont { + overflow-x: scroll; + padding: 0; + margin: 0; + } + h1, h2, h3, h4 { + text-align: center; + width: 100%; + font-family: 'Gentium Book Basic', serif; + font-size: 120%; + font-weight: 900; + } + h1 { + font-size: 150%; + } + .subtitle { + text-align: center; + width: 100%; + } + .bookfig { + width: 100%; + height: auto; + max-width: 100%; + max-height: 100%; + } + div.figure { + height: 88vh; + margin: 0; + } + div.topic { + margin: 0; + } + div.section > p { + text-indent: 1em; + margin-bottom: 0; + text-align: justify; + } + </style> +</%block> + +<%block name="content"> +<article class="storypage" itemscope="itemscope" itemtype="http://schema.org/Article"> + <div class="frame"> + <div class="scrolling-cont" id="scrolling-cont" name="scrolling-cont"> + <div class="e-content entry-content chapter" itemprop="articleBody text"> + <h1>${post.title()}</h1> + ${post.text()} + </div> + </div> + </div> +</article> +</%block> + +<%block name="extra_js"> + <script src="//cdnjs.cloudflare.com/ajax/libs/Flowtype.js/1.1.0/flowtype.min.js"></script> + <script> + $('#scrolling-cont').flowtype({ + minimum: 500, + maximum: 1200, + minFont: 20, + maxFont: 40, + fontRatio: 50 + }); + $(document).ready(function() { + var elem = $('#scrolling-cont'); + elem.click(function(event) { + var x1 = elem.position().left; + var pw = elem.width() + 20; + var x2 = event.pageX; + if (x2 - x1 < pw / 2) { + pw = -pw; + } + elem.animate({ + scrollLeft: '+=' + pw + }, 500) + }); + }); + </script> +</%block> diff --git a/nikola/data/symlinked.txt b/nikola/data/symlinked.txt index ce34ddf..a5d48b2 100644 --- a/nikola/data/symlinked.txt +++ b/nikola/data/symlinked.txt @@ -3,12 +3,14 @@ docs/sphinx/creating-a-theme.txt docs/sphinx/extending.txt docs/sphinx/internals.txt docs/sphinx/manual.txt +docs/sphinx/path_handlers.txt docs/sphinx/social_buttons.txt docs/sphinx/theming.txt nikola/data/samplesite/stories/creating-a-theme.rst nikola/data/samplesite/stories/extending.txt nikola/data/samplesite/stories/internals.txt nikola/data/samplesite/stories/manual.rst +nikola/data/samplesite/stories/path_handlers.txt nikola/data/samplesite/stories/social_buttons.txt nikola/data/samplesite/stories/theming.rst nikola/data/symlink-test-link.txt diff --git a/nikola/data/themes/base-jinja/templates/author.tmpl b/nikola/data/themes/base-jinja/templates/author.tmpl new file mode 100644 index 0000000..1604939 --- /dev/null +++ b/nikola/data/themes/base-jinja/templates/author.tmpl @@ -0,0 +1,43 @@ +{# -*- coding: utf-8 -*- #} +{% extends 'list_post.tmpl' %} + +{% block extra_head %} + {{ super() }} + {% if translations|length > 1 and generate_rss %} + {% for language in translations|sort %} + <link rel="alternate" type="application/rss+xml" title="RSS for {{ kind }} {{ author|e }} ({{ language }})" href="{{ _link(kind + "_rss", author, language) }}"> + {% endfor %} + {% elif generate_rss %} + <link rel="alternate" type="application/rss+xml" title="RSS for {{ kind }} {{ author|e }}" href="{{ _link(kind + "_rss", author) }}"> + {% endif %} +{% endblock %} + + +{% block content %} +<article class="authorpage"> + <header> + <h1>{{ title|e }}</h1> + {% if description %} + <p>{{ description }}</p> + {% endif %} + <div class="metadata"> + {% if translations|length > 1 and generate_rss %} + {% for language in translations|sort %} + <p class="feedlink"> + <a href="{{ _link(kind + "_rss", author, language) }}" hreflang="{{ language }}" type="application/rss+xml">{{ messages('RSS feed', language) }} ({{ language }})</a> + </p> + {% endfor %} + {% elif generate_rss %} + <p class="feedlink"><a href="{{ _link(kind + "_rss", author) }}" type="application/rss+xml">{{ messages('RSS feed') }}</a></p> + {% endif %} + </div> + </header> + {% if posts %} + <ul class="postlist"> + {% for post in posts %} + <li><a href="{{ post.permalink() }}" class="listtitle">{{ post.title()|e }}</a><time class="listdate" datetime="{{ post.formatted_date('webiso') }}" title="{{ post.formatted_date(date_format)|e }}">{{ post.formatted_date(date_format)|e }}</time></li> + {% endfor %} + </ul> + {% endif %} +</article> +{% endblock %} diff --git a/nikola/data/themes/base-jinja/templates/authorindex.tmpl b/nikola/data/themes/base-jinja/templates/authorindex.tmpl new file mode 100644 index 0000000..3c40ee1 --- /dev/null +++ b/nikola/data/themes/base-jinja/templates/authorindex.tmpl @@ -0,0 +1,13 @@ +{# -*- coding: utf-8 -*- #} +{% extends 'index.tmpl' %} + +{% block extra_head %} + {{ super() }} + {% if tranlations|length > 1 and generate_atom %} + {% for language in translations|sort %} + <link rel="alternate" type="application/atom+xml" title="Atom for the {{ author|e }} section ({{ language }})" href="{{ _link(kind + "_atom", author, language) }}"> + {% endfor %} + {% elif generate_atom %} + <link rel="alternate" type="application/atom+xml" title="Atom for the {{ author|e }} section" href="{{ _link("author" + "_atom", author) }}"> + {% endif %} +{% endblock %} diff --git a/nikola/data/themes/base-jinja/templates/authors.tmpl b/nikola/data/themes/base-jinja/templates/authors.tmpl new file mode 100644 index 0000000..8b6ea64 --- /dev/null +++ b/nikola/data/themes/base-jinja/templates/authors.tmpl @@ -0,0 +1,17 @@ +{# -*- coding: utf-8 -*- #} +{% extends 'base.tmpl' %} + +{% block content %} +<article class="authorindex"> + {% if items %} + <h2>{{ messages("Authors") }}</h2> + <ul class="postlist"> + {% for text, link in items %} + {% if text not in hidden_authors %} + <li><a class="reference listtitle" href="{{ link }}">{{ text|e }}</a></li> + {% endif %} + {% endfor %} + </ul> + {% endif %} +</article> +{% endblock %} diff --git a/nikola/data/themes/base-jinja/templates/base_header.tmpl b/nikola/data/themes/base-jinja/templates/base_header.tmpl index 092abd8..d9370d7 100644 --- a/nikola/data/themes/base-jinja/templates/base_header.tmpl +++ b/nikola/data/themes/base-jinja/templates/base_header.tmpl @@ -16,13 +16,13 @@ {% endmacro %} {% macro html_site_title() %} - <h1 id="brand"><a href="{{ abs_link(_link("root", None, lang)) }}" title="{{ blog_title }}" rel="home"> + <h1 id="brand"><a href="{{ abs_link(_link("root", None, lang)) }}" title="{{ blog_title|e }}" rel="home"> {% if logo_url %} - <img src="{{ logo_url }}" alt="{{ blog_title }}" id="logo"> + <img src="{{ logo_url }}" alt="{{ blog_title|e }}" id="logo"> {% endif %} {% if show_blog_title %} - <span id="blog-title">{{ blog_title }}</span> + <span id="blog-title">{{ blog_title|e }}</span> {% endif %} </a></h1> {% endmacro %} diff --git a/nikola/data/themes/base-jinja/templates/base_helper.tmpl b/nikola/data/themes/base-jinja/templates/base_helper.tmpl index 8b4ed97..66d82e9 100644 --- a/nikola/data/themes/base-jinja/templates/base_helper.tmpl +++ b/nikola/data/themes/base-jinja/templates/base_helper.tmpl @@ -21,9 +21,11 @@ dir="rtl" lang="{{ lang }}"> <head> <meta charset="utf-8"> + {% if use_base_tag %} <base href="{{ abs_link(permalink) }}"> + {% endif %} {% if description %} - <meta name="description" content="{{ description }}"> + <meta name="description" content="{{ description|e }}"> {% endif %} <meta name="viewport" content="width=device-width"> {% if title == blog_title %} @@ -33,6 +35,7 @@ lang="{{ lang }}"> {% endif %} {{ html_stylesheets() }} + <meta content="{{ theme_color }}" name="theme-color"> {{ html_feedlinks() }} <link rel="canonical" href="{{ abs_link(permalink) }}"> diff --git a/nikola/data/themes/base-jinja/templates/gallery.tmpl b/nikola/data/themes/base-jinja/templates/gallery.tmpl index 0a53ebe..01452e4 100644 --- a/nikola/data/themes/base-jinja/templates/gallery.tmpl +++ b/nikola/data/themes/base-jinja/templates/gallery.tmpl @@ -17,8 +17,8 @@ {% if folders %} <ul> {% for folder, ftitle in folders %} - <li><a href="{{ folder }}"><i - class="icon-folder-open"></i> {{ ftitle }}</a></li> + <li><a href="{{ folder|urlencode }}"><i + class="icon-folder-open"></i> {{ ftitle|e }}</a></li> {% endfor %} </ul> {% endif %} @@ -26,7 +26,7 @@ <ul class="thumbnails"> {% for image in photo_array %} <li><a href="{{ image['url'] }}" class="thumbnail image-reference" title="{{ image['title'] }}"> - <img src="{{ image['url_thumb'] }}" alt="{{ image['title'] }}" /></a> + <img src="{{ image['url_thumb'] }}" alt="{{ image['title']|e }}" /></a> {% endfor %} </ul> {% endif %} diff --git a/nikola/data/themes/base-jinja/templates/index.tmpl b/nikola/data/themes/base-jinja/templates/index.tmpl index 222da43..a36bac3 100644 --- a/nikola/data/themes/base-jinja/templates/index.tmpl +++ b/nikola/data/themes/base-jinja/templates/index.tmpl @@ -18,8 +18,14 @@ <header> <h1 class="p-name entry-title"><a href="{{ post.permalink() }}" class="u-url">{{ post.title()|e }}</a></h1> <div class="metadata"> - <p class="byline author vcard"><span class="byline-name fn">{{ post.author() }}</span></p> - <p class="dateline"><a href="{{ post.permalink() }}" rel="bookmark"><time class="published dt-published" datetime="{{ post.formatted_date('webiso') }}" title="{{ post.formatted_date(date_format) }}">{{ post.formatted_date(date_format) }}</time></a></p> + <p class="byline author vcard"><span class="byline-name fn"> + {% if author_pages_generated %} + <a href="{{ _link('author', post.author()) }}">{{ post.author()|e }}</a> + {% else %} + {{ post.author()|e }} + {% endif %} + </span></p> + <p class="dateline"><a href="{{ post.permalink() }}" rel="bookmark"><time class="published dt-published" datetime="{{ post.formatted_date('webiso') }}" title="{{ post.formatted_date(date_format)|e }}">{{ post.formatted_date(date_format)|e }}</time></a></p> {% if not post.meta('nocomments') and site_has_comments %} <p class="commentline">{{ comments.comment_link(post.permalink(), post._base_path) }} {% endif %} diff --git a/nikola/data/themes/base-jinja/templates/index_helper.tmpl b/nikola/data/themes/base-jinja/templates/index_helper.tmpl index 2f9e8ea..64b4d12 100644 --- a/nikola/data/themes/base-jinja/templates/index_helper.tmpl +++ b/nikola/data/themes/base-jinja/templates/index_helper.tmpl @@ -20,8 +20,17 @@ {% macro mathjax_script(posts) %} {% if posts|selectattr("is_mathjax")|list %} - <script type="text/x-mathjax-config"> - MathJax.Hub.Config({tex2jax: {inlineMath: [['$latex ','$'], ['\\(','\\)']]}});</script> - <script src="/assets/js/mathjax.js"></script> + {% if use_katex %} + <script src="//cdnjs.cloudflare.com/ajax/libs/KaTeX/0.3.0/katex.min.js"></script> + <script src="//cdnjs.cloudflare.com/ajax/libs/KaTeX/0.3.0/contrib/auto-render.min.js"></script> + <script> + renderMathInElement(document.body); + </script> + {% else %} + <script type="text/javascript" src="//cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"> </script> + <script type="text/x-mathjax-config"> + MathJax.Hub.Config({tex2jax: {inlineMath: [['$latex ','$'], ['\\(','\\)']]}}); + </script> + {% endif %} {% endif %} {% endmacro %} diff --git a/nikola/data/themes/base-jinja/templates/list.tmpl b/nikola/data/themes/base-jinja/templates/list.tmpl index e442864..c9c330b 100644 --- a/nikola/data/themes/base-jinja/templates/list.tmpl +++ b/nikola/data/themes/base-jinja/templates/list.tmpl @@ -4,12 +4,15 @@ {% block content %} <article class="listpage"> <header> - <h1>{{ title }}</h1> + <h1>{{ title|e }}</h1> </header> {% if items %} <ul class="postlist"> - {% for text, link in items %} - <li><a href="{{ link }}">{{ text }}</a> + {% for text, link, count in items %} + <li><a href="{{ link }}">{{ text|e }}</a> + {% if count %} + ({{ count }}) + {% endif %} {% endfor %} </ul> {% else %} diff --git a/nikola/data/themes/base-jinja/templates/list_post.tmpl b/nikola/data/themes/base-jinja/templates/list_post.tmpl index 919d6f0..83f4bcf 100644 --- a/nikola/data/themes/base-jinja/templates/list_post.tmpl +++ b/nikola/data/themes/base-jinja/templates/list_post.tmpl @@ -4,12 +4,12 @@ {% block content %} <article class="listpage"> <header> - <h1>{{ title }}</h1> + <h1>{{ title|e }}</h1> </header> {% if posts %} <ul class="postlist"> {% for post in posts %} - <li><a href="{{ post.permalink() }}" class="listtitle">{{ post.title()|e }}</a> <time class="listdate" datetime="{{ post.formatted_date('webiso') }}" title="{{ post.formatted_date(date_format) }}">{{ post.formatted_date(date_format) }}</time></li> + <li><a href="{{ post.permalink() }}" class="listtitle">{{ post.title()|e }}</a> <time class="listdate" datetime="{{ post.formatted_date('webiso') }}" title="{{ post.formatted_date(date_format)|e }}">{{ post.formatted_date(date_format)|e }}</time></li> {% endfor %} </ul> {% else %} diff --git a/nikola/data/themes/base-jinja/templates/listing.tmpl b/nikola/data/themes/base-jinja/templates/listing.tmpl index ccbc5ba..281630d 100644 --- a/nikola/data/themes/base-jinja/templates/listing.tmpl +++ b/nikola/data/themes/base-jinja/templates/listing.tmpl @@ -6,10 +6,10 @@ {% if folders or files %} <ul> {% for name in folders %} - <li><a href="{{ name }}"><i class="icon-folder-open"></i> {{ name }}</a> + <li><a href="{{ name|urlencode }}"><i class="icon-folder-open"></i> {{ name|e }}</a> {% endfor %} {% for name in files %} - <li><a href="{{ name }}.html"><i class="icon-file"></i> {{ name }}</a> + <li><a href="{{ name|urlencode }}.html"><i class="icon-file"></i> {{ name|e }}</a> {% endfor %} </ul> {% endif %} diff --git a/nikola/data/themes/base-jinja/templates/post.tmpl b/nikola/data/themes/base-jinja/templates/post.tmpl index 28da35e..5e4d9a5 100644 --- a/nikola/data/themes/base-jinja/templates/post.tmpl +++ b/nikola/data/themes/base-jinja/templates/post.tmpl @@ -10,9 +10,9 @@ <meta name="keywords" content="{{ post.meta('keywords')|e }}"> {% endif %} {% if post.description() %} - <meta name="description" content="{{ post.description() }}"> + <meta name="description" content="{{ post.description()|e }}"> {% endif %} - <meta name="author" content="{{ post.author() }}"> + <meta name="author" content="{{ post.author()|e }}"> {% if post.prev_post %} <link rel="prev" href="{{ post.prev_post.permalink() }}" title="{{ post.prev_post.title()|e }}" type="text/html"> {% endif %} diff --git a/nikola/data/themes/base-jinja/templates/post_header.tmpl b/nikola/data/themes/base-jinja/templates/post_header.tmpl index dc844e3..6b81120 100644 --- a/nikola/data/themes/base-jinja/templates/post_header.tmpl +++ b/nikola/data/themes/base-jinja/templates/post_header.tmpl @@ -31,17 +31,23 @@ <header> {{ html_title() }} <div class="metadata"> - <p class="byline author vcard"><span class="byline-name fn">{{ post.author() }}</span></p> - <p class="dateline"><a href="{{ post.permalink() }}" rel="bookmark"><time class="published dt-published" datetime="{{ post.formatted_date('webiso') }}" itemprop="datePublished" title="{{ post.formatted_date(date_format) }}">{{ post.formatted_date(date_format) }}</time></a></p> + <p class="byline author vcard"><span class="byline-name fn"> + {% if author_pages_generated %} + <a href="{{ _link('author', post.author()) }}">{{ post.author()|e }}</a> + {% else %} + {{ post.author()|e }} + {% endif %} + </span></p> + <p class="dateline"><a href="{{ post.permalink() }}" rel="bookmark"><time class="published dt-published" datetime="{{ post.formatted_date('webiso') }}" itemprop="datePublished" title="{{ post.formatted_date(date_format)|e }}">{{ post.formatted_date(date_format)|e }}</time></a></p> {% if not post.meta('nocomments') and site_has_comments %} <p class="commentline">{{ comments.comment_link(post.permalink(), post._base_path) }} {% endif %} {{ html_sourcelink() }} {% if post.meta('link') %} - <p class="linkline"><a href='{{ post.meta('link') }}'>{{ messages("Original site") }}</a></p> + <p class="linkline"><a href="{{ post.meta('link') }}">{{ messages("Original site") }}</a></p> {% endif %} {% if post.description() %} - <meta name="description" itemprop="description" content="{{ post.description() }}"> + <meta name="description" itemprop="description" content="{{ post.description()|e }}"> {% endif %} </div> {{ html_translations(post) }} diff --git a/nikola/data/themes/base-jinja/templates/post_helper.tmpl b/nikola/data/themes/base-jinja/templates/post_helper.tmpl index b4826eb..f5de54a 100644 --- a/nikola/data/themes/base-jinja/templates/post_helper.tmpl +++ b/nikola/data/themes/base-jinja/templates/post_helper.tmpl @@ -15,7 +15,7 @@ <ul itemprop="keywords" class="tags"> {% for tag in post.tags %} {% if tag not in hidden_tags %} - <li><a class="tag p-category" href="{{ _link('tag', tag) }}" rel="tag">{{ tag }}</a></li> + <li><a class="tag p-category" href="{{ _link('tag', tag) }}" rel="tag">{{ tag|e }}</a></li> {% endif %} {% endfor %} </ul> @@ -55,14 +55,14 @@ <meta property="og:type" content="article"> {# Will only work with Pintrest and breaks everywhere else who expect a [Facebook] URI. #} {# %if post.author(): #} -{# <meta property="article:author" content="{{ post.author() }}"> #} +{# <meta property="article:author" content="{{ post.author()|e }}"> #} {# %endif #} {% if post.date.isoformat() %} <meta property="article:published_time" content="{{ post.formatted_date('webiso') }}"> {% endif %} {% if post.tags %} {% for tag in post.tags %} - <meta property="article:tag" content="{{ tag }}"> + <meta property="article:tag" content="{{ tag|e }}"> {% endfor %} {% endif %} {% endif %} @@ -86,8 +86,17 @@ {% macro mathjax_script(post) %} {% if post.is_mathjax %} - <script type="text/x-mathjax-config"> - MathJax.Hub.Config({tex2jax: {inlineMath: [['$latex ','$'], ['\\(','\\)']]}});</script> - <script src="/assets/js/mathjax.js"></script> + {% if use_katex %} + <script src="//cdnjs.cloudflare.com/ajax/libs/KaTeX/0.3.0/katex.min.js"></script> + <script src="//cdnjs.cloudflare.com/ajax/libs/KaTeX/0.3.0/contrib/auto-render.min.js"></script> + <script> + renderMathInElement(document.body); + </script> + {% else %} + <script type="text/javascript" src="//cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"> </script> + <script type="text/x-mathjax-config"> + MathJax.Hub.Config({tex2jax: {inlineMath: [['$latex ','$'], ['\\(','\\)']]}}); + </script> + {% endif %} {% endif %} {% endmacro %} diff --git a/nikola/data/themes/base-jinja/templates/post_list_directive.tmpl b/nikola/data/themes/base-jinja/templates/post_list_directive.tmpl index 92c1b7d..f8c53cc 100644 --- a/nikola/data/themes/base-jinja/templates/post_list_directive.tmpl +++ b/nikola/data/themes/base-jinja/templates/post_list_directive.tmpl @@ -6,7 +6,7 @@ <ul class="post-list"> {% for post in posts %} <li class="post-list-item"> - {{ post.formatted_date(date_format) }} + {{ post.formatted_date(date_format)|e }} <a href="{{ post.permalink(lang) }}">{{ post.title(lang)|e }}</a> </li> diff --git a/nikola/data/themes/base-jinja/templates/sectionindex.tmpl b/nikola/data/themes/base-jinja/templates/sectionindex.tmpl new file mode 100644 index 0000000..f1d3d5b --- /dev/null +++ b/nikola/data/themes/base-jinja/templates/sectionindex.tmpl @@ -0,0 +1,21 @@ +{# -*- coding: utf-8 -*- #} +{% extends 'index.tmpl' %} + +{% block extra_head %} + {{ super() }} + {% if generate_atom %} + <link rel="alternate" type="application/atom+xml" title="Atom for the {{ posts[0].section_name()|e }} section" href="{{ _link('section_index_atom', posts[0].section_slug()) }}"> + {% endif %} +{% endblock %} + +{% block content %} +<div class="sectionindex"> + <header> + <h2><a href="{{ _link('section_index', posts[0].section_slug()) }}">{{ title|e }}</a></h2> + {% if generate_atom %} + <p class="feedlink"><a href="{{ _link('section_index_atom', posts[0].section_slug()) }}" type="application/atom+xml">{{ messages('Updates') }}</a></p> + {% endif %} + </header> + {{ super() }} +</div> +{% endblock %} diff --git a/nikola/data/themes/base-jinja/templates/tag.tmpl b/nikola/data/themes/base-jinja/templates/tag.tmpl index 0928a5f..f9adc63 100644 --- a/nikola/data/themes/base-jinja/templates/tag.tmpl +++ b/nikola/data/themes/base-jinja/templates/tag.tmpl @@ -5,10 +5,10 @@ {{ super() }} {% if translations|length > 1 and generate_rss %} {% for language in translations|sort %} - <link rel="alternate" type="application/rss+xml" type="application/rss+xml" title="RSS for {{ kind }} {{ tag }} ({{ language }})" href="{{ _link(kind + "_rss", tag, language) }}"> + <link rel="alternate" type="application/rss+xml" title="RSS for {{ kind }} {{ tag|e }} ({{ language }})" href="{{ _link(kind + "_rss", tag, language) }}"> {% endfor %} {% elif generate_rss %} - <link rel="alternate" type="application/rss+xml" type="application/rss+xml" title="RSS for {{ kind }} {{ tag }}" href="{{ _link(kind + "_rss", tag) }}"> + <link rel="alternate" type="application/rss+xml" title="RSS for {{ kind }} {{ tag|e }}" href="{{ _link(kind + "_rss", tag) }}"> {% endif %} {% endblock %} @@ -24,7 +24,7 @@ {{ messages('Subcategories:') }} <ul> {% for name, link in subcategories %} - <li><a href="{{ link }}">{{ name }}</a></li> + <li><a href="{{ link }}">{{ name|e }}</a></li> {% endfor %} </ul> {% endif %} @@ -43,7 +43,7 @@ {% if posts %} <ul class="postlist"> {% for post in posts %} - <li><a href="{{ post.permalink() }}" class="listtitle">{{ post.title()|e }}</a> <time class="listdate" datetime="{{ post.formatted_date('webiso') }}" title="{{ post.formatted_date(date_format) }}">{{ post.formatted_date(date_format) }}</time></li> + <li><time class="listdate" datetime="{{ post.formatted_date('webiso') }}" title="{{ post.formatted_date(date_format)|e }}">{{ post.formatted_date(date_format)|e }}</time><a href="{{ post.permalink() }}" class="listtitle">{{ post.title()|e }}<a></li> {% endfor %} </ul> {% endif %} diff --git a/nikola/data/themes/base-jinja/templates/tagindex.tmpl b/nikola/data/themes/base-jinja/templates/tagindex.tmpl index f2d2d7a..bc1b02d 100644 --- a/nikola/data/themes/base-jinja/templates/tagindex.tmpl +++ b/nikola/data/themes/base-jinja/templates/tagindex.tmpl @@ -6,7 +6,7 @@ {{ messages('Subcategories:') }} <ul> {% for name, link in subcategories %} - <li><a href="{{ link }}">{{ name }}</a></li> + <li><a href="{{ link }}">{{ name|e }}</a></li> {% endfor %} </ul> {% endif %} @@ -16,9 +16,9 @@ {{ super() }} {% if translations|length > 1 and generate_atom %} {% for language in translations|sort %} - <link rel="alternate" type="application/atom+xml" title="Atom for the {{ tag }} section ({{ language }})" href="{{ _link(kind + "_atom", tag, language) }}"> + <link rel="alternate" type="application/atom+xml" title="Atom for the {{ tag|e }} section ({{ language }})" href="{{ _link(kind + "_atom", tag, language) }}"> {% endfor %} {% elif generate_atom %} - <link rel="alternate" type="application/atom+xml" title="Atom for the {{ tag }} section" href="{{ _link("tag" + "_atom", tag) }}"> + <link rel="alternate" type="application/atom+xml" title="Atom for the {{ tag|e }} section" href="{{ _link("tag" + "_atom", tag) }}"> {% endif %} {% endblock %} diff --git a/nikola/data/themes/base-jinja/templates/tags.tmpl b/nikola/data/themes/base-jinja/templates/tags.tmpl index 4605fc9..936ed21 100644 --- a/nikola/data/themes/base-jinja/templates/tags.tmpl +++ b/nikola/data/themes/base-jinja/templates/tags.tmpl @@ -4,7 +4,7 @@ {% block content %} <article class="tagindex"> <header> - <h1>{{ title }}</h1> + <h1>{{ title|e }}</h1> </header> {% if cat_items %} {% if items %} @@ -33,7 +33,7 @@ <ul class="postlist"> {% for text, link in items %} {% if text not in hidden_tags %} - <li><a class="reference listtitle" href="{{ link }}">{{ text }}</a></li> + <li><a class="reference listtitle" href="{{ link }}">{{ text|e }}</a></li> {% endif %} {% endfor %} </ul> diff --git a/nikola/data/themes/base/assets/css/ipython.min.css b/nikola/data/themes/base/assets/css/ipython.min.css new file mode 100644 index 0000000..f9934c2 --- /dev/null +++ b/nikola/data/themes/base/assets/css/ipython.min.css @@ -0,0 +1,9 @@ +/*! +* +* IPython base +* +*/.modal.fade .modal-dialog{-webkit-transform:translate(0, 0);-ms-transform:translate(0, 0);-o-transform:translate(0, 0);transform:translate(0, 0)}code{color:#000}pre{font-size:inherit;line-height:inherit}label{font-weight:normal}.border-box-sizing{box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box}.corner-all{border-radius:2px}.no-padding{padding:0}.hbox{display:-webkit-box;-webkit-box-orient:horizontal;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:horizontal;-moz-box-align:stretch;display:box;box-orient:horizontal;box-align:stretch;display:flex;flex-direction:row;align-items:stretch}.hbox>*{-webkit-box-flex:0;-moz-box-flex:0;box-flex:0;flex:none}.vbox{display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:vertical;-moz-box-align:stretch;display:box;box-orient:vertical;box-align:stretch;display:flex;flex-direction:column;align-items:stretch}.vbox>*{-webkit-box-flex:0;-moz-box-flex:0;box-flex:0;flex:none}.hbox.reverse,.vbox.reverse,.reverse{-webkit-box-direction:reverse;-moz-box-direction:reverse;box-direction:reverse;flex-direction:row-reverse}.hbox.box-flex0,.vbox.box-flex0,.box-flex0{-webkit-box-flex:0;-moz-box-flex:0;box-flex:0;flex:none;width:auto}.hbox.box-flex1,.vbox.box-flex1,.box-flex1{-webkit-box-flex:1;-moz-box-flex:1;box-flex:1;flex:1}.hbox.box-flex,.vbox.box-flex,.box-flex{-webkit-box-flex:1;-moz-box-flex:1;box-flex:1;flex:1}.hbox.box-flex2,.vbox.box-flex2,.box-flex2{-webkit-box-flex:2;-moz-box-flex:2;box-flex:2;flex:2}.box-group1{-webkit-box-flex-group:1;-moz-box-flex-group:1;box-flex-group:1}.box-group2{-webkit-box-flex-group:2;-moz-box-flex-group:2;box-flex-group:2}.hbox.start,.vbox.start,.start{-webkit-box-pack:start;-moz-box-pack:start;box-pack:start;justify-content:flex-start}.hbox.end,.vbox.end,.end{-webkit-box-pack:end;-moz-box-pack:end;box-pack:end;justify-content:flex-end}.hbox.center,.vbox.center,.center{-webkit-box-pack:center;-moz-box-pack:center;box-pack:center;justify-content:center}.hbox.baseline,.vbox.baseline,.baseline{-webkit-box-pack:baseline;-moz-box-pack:baseline;box-pack:baseline;justify-content:baseline}.hbox.stretch,.vbox.stretch,.stretch{-webkit-box-pack:stretch;-moz-box-pack:stretch;box-pack:stretch;justify-content:stretch}.hbox.align-start,.vbox.align-start,.align-start{-webkit-box-align:start;-moz-box-align:start;box-align:start;align-items:flex-start}.hbox.align-end,.vbox.align-end,.align-end{-webkit-box-align:end;-moz-box-align:end;box-align:end;align-items:flex-end}.hbox.align-center,.vbox.align-center,.align-center{-webkit-box-align:center;-moz-box-align:center;box-align:center;align-items:center}.hbox.align-baseline,.vbox.align-baseline,.align-baseline{-webkit-box-align:baseline;-moz-box-align:baseline;box-align:baseline;align-items:baseline}.hbox.align-stretch,.vbox.align-stretch,.align-stretch{-webkit-box-align:stretch;-moz-box-align:stretch;box-align:stretch;align-items:stretch}div.error{margin:2em;text-align:center}div.error>h1{font-size:500%;line-height:normal}div.error>p{font-size:200%;line-height:normal}div.traceback-wrapper{text-align:left;max-width:800px;margin:auto}/*! +* +* IPython notebook +* +*/.ansibold{font-weight:bold}.ansiblack{color:black}.ansired{color:darkred}.ansigreen{color:darkgreen}.ansiyellow{color:#c4a000}.ansiblue{color:darkblue}.ansipurple{color:darkviolet}.ansicyan{color:steelblue}.ansigray{color:gray}.ansibgblack{background-color:black}.ansibgred{background-color:red}.ansibggreen{background-color:green}.ansibgyellow{background-color:yellow}.ansibgblue{background-color:blue}.ansibgpurple{background-color:magenta}.ansibgcyan{background-color:cyan}.ansibggray{background-color:gray}div.cell{border:1px solid transparent;display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:vertical;-moz-box-align:stretch;display:box;box-orient:vertical;box-align:stretch;display:flex;flex-direction:column;align-items:stretch;border-radius:2px;box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;border-width:thin;border-style:solid;width:100%;padding:5px;margin:0;outline:none}div.cell.selected{border-color:#ababab}@media print{div.cell.selected{border-color:transparent}}.edit_mode div.cell.selected{border-color:green}@media print{.edit_mode div.cell.selected{border-color:transparent}}.prompt{min-width:14ex;padding:.4em;margin:0;font-family:monospace;text-align:right;line-height:1.21429em}@media (max-width:540px){.prompt{text-align:left}}div.inner_cell{display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:vertical;-moz-box-align:stretch;display:box;box-orient:vertical;box-align:stretch;display:flex;flex-direction:column;align-items:stretch;-webkit-box-flex:1;-moz-box-flex:1;box-flex:1;flex:1}@-moz-document url-prefix(){div.inner_cell{overflow-x:hidden}}div.input_area{border:1px solid #cfcfcf;border-radius:2px;background:#f7f7f7;line-height:1.21429em}div.prompt:empty{padding-top:0;padding-bottom:0}div.unrecognized_cell{padding:5px 5px 5px 0;display:-webkit-box;-webkit-box-orient:horizontal;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:horizontal;-moz-box-align:stretch;display:box;box-orient:horizontal;box-align:stretch;display:flex;flex-direction:row;align-items:stretch}div.unrecognized_cell .inner_cell{border-radius:2px;padding:5px;font-weight:bold;color:red;border:1px solid #cfcfcf;background:#eaeaea}div.unrecognized_cell .inner_cell a{color:inherit;text-decoration:none}div.unrecognized_cell .inner_cell a:hover{color:inherit;text-decoration:none}@media (max-width:540px){div.unrecognized_cell>div.prompt{display:none}}@media print{div.code_cell{page-break-inside:avoid}}div.input{page-break-inside:avoid;display:-webkit-box;-webkit-box-orient:horizontal;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:horizontal;-moz-box-align:stretch;display:box;box-orient:horizontal;box-align:stretch;display:flex;flex-direction:row;align-items:stretch}@media (max-width:540px){div.input{display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:vertical;-moz-box-align:stretch;display:box;box-orient:vertical;box-align:stretch;display:flex;flex-direction:column;align-items:stretch}}div.input_prompt{color:navy;border-top:1px solid transparent}div.input_area>div.highlight{margin:.4em;border:none;padding:0;background-color:transparent}div.input_area>div.highlight>pre{margin:0;border:none;padding:0;background-color:transparent}.CodeMirror{line-height:1.21429em;font-size:14px;height:auto;background:none}.CodeMirror-scroll{overflow-y:hidden;overflow-x:auto}.CodeMirror-lines{padding:.4em}.CodeMirror-linenumber{padding:0 8px 0 4px}.CodeMirror-gutters{border-bottom-left-radius:2px;border-top-left-radius:2px}.CodeMirror pre{padding:0;border:0;border-radius:0}.highlight-base{color:#000}.highlight-variable{color:#000}.highlight-variable-2{color:#1a1a1a}.highlight-variable-3{color:#333}.highlight-string{color:#ba2121}.highlight-comment{color:#408080;font-style:italic}.highlight-number{color:#080}.highlight-atom{color:#88f}.highlight-keyword{color:#008000;font-weight:bold}.highlight-builtin{color:#008000}.highlight-error{color:#f00}.highlight-operator{color:#a2f;font-weight:bold}.highlight-meta{color:#a2f}.highlight-def{color:#00f}.highlight-string-2{color:#f50}.highlight-qualifier{color:#555}.highlight-bracket{color:#997}.highlight-tag{color:#170}.highlight-attribute{color:#00c}.highlight-header{color:blue}.highlight-quote{color:#090}.highlight-link{color:#00c}.cm-s-ipython span.cm-keyword{color:#008000;font-weight:bold}.cm-s-ipython span.cm-atom{color:#88f}.cm-s-ipython span.cm-number{color:#080}.cm-s-ipython span.cm-def{color:#00f}.cm-s-ipython span.cm-variable{color:#000}.cm-s-ipython span.cm-operator{color:#a2f;font-weight:bold}.cm-s-ipython span.cm-variable-2{color:#1a1a1a}.cm-s-ipython span.cm-variable-3{color:#333}.cm-s-ipython span.cm-comment{color:#408080;font-style:italic}.cm-s-ipython span.cm-string{color:#ba2121}.cm-s-ipython span.cm-string-2{color:#f50}.cm-s-ipython span.cm-meta{color:#a2f}.cm-s-ipython span.cm-qualifier{color:#555}.cm-s-ipython span.cm-builtin{color:#008000}.cm-s-ipython span.cm-bracket{color:#997}.cm-s-ipython span.cm-tag{color:#170}.cm-s-ipython span.cm-attribute{color:#00c}.cm-s-ipython span.cm-header{color:blue}.cm-s-ipython span.cm-quote{color:#090}.cm-s-ipython span.cm-link{color:#00c}.cm-s-ipython span.cm-error{color:#f00}.cm-s-ipython span.cm-tab{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAMCAYAAAAkuj5RAAAAAXNSR0IArs4c6QAAAGFJREFUSMft1LsRQFAQheHPowAKoACx3IgEKtaEHujDjORSgWTH/ZOdnZOcM/sgk/kFFWY0qV8foQwS4MKBCS3qR6ixBJvElOobYAtivseIE120FaowJPN75GMu8j/LfMwNjh4HUpwg4LUAAAAASUVORK5CYII=);background-position:right;background-repeat:no-repeat}div.output_wrapper{position:relative;display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:vertical;-moz-box-align:stretch;display:box;box-orient:vertical;box-align:stretch;display:flex;flex-direction:column;align-items:stretch;z-index:1}div.output_scroll{height:24em;width:100%;overflow:auto;border-radius:2px;-webkit-box-shadow:inset 0 2px 8px rgba(0,0,0,0.8);box-shadow:inset 0 2px 8px rgba(0,0,0,0.8);display:block}div.output_collapsed{margin:0;padding:0;display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:vertical;-moz-box-align:stretch;display:box;box-orient:vertical;box-align:stretch;display:flex;flex-direction:column;align-items:stretch}div.out_prompt_overlay{height:100%;padding:0 .4em;position:absolute;border-radius:2px}div.out_prompt_overlay:hover{-webkit-box-shadow:inset 0 0 1px #000;box-shadow:inset 0 0 1px #000;background:rgba(240,240,240,0.5)}div.output_prompt{color:darkred}div.output_area{padding:0;page-break-inside:avoid;display:-webkit-box;-webkit-box-orient:horizontal;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:horizontal;-moz-box-align:stretch;display:box;box-orient:horizontal;box-align:stretch;display:flex;flex-direction:row;align-items:stretch}div.output_area .MathJax_Display{text-align:left !important}div.output_area .rendered_html table{margin-left:0;margin-right:0}div.output_area .rendered_html img{margin-left:0;margin-right:0}div.output_area img,div.output_area svg{max-width:100%;height:auto}div.output_area img.unconfined,div.output_area svg.unconfined{max-width:none}.output{display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:vertical;-moz-box-align:stretch;display:box;box-orient:vertical;box-align:stretch;display:flex;flex-direction:column;align-items:stretch}@media (max-width:540px){div.output_area{display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:vertical;-moz-box-align:stretch;display:box;box-orient:vertical;box-align:stretch;display:flex;flex-direction:column;align-items:stretch}}div.output_area pre{margin:0;padding:0;border:0;vertical-align:baseline;color:black;background-color:transparent;border-radius:0}div.output_subarea{overflow-x:auto;padding:.4em;-webkit-box-flex:1;-moz-box-flex:1;box-flex:1;flex:1;max-width:calc(100% - 14ex)}div.output_text{text-align:left;color:#000;line-height:1.21429em}div.output_stderr{background:#fdd}div.output_latex{text-align:left}div.output_javascript:empty{padding:0}.js-error{color:darkred}div.raw_input_container{font-family:monospace;padding-top:5px}input.raw_input{font-family:inherit;font-size:inherit;color:inherit;width:auto;vertical-align:baseline;padding:0 .25em;margin:0 .25em}input.raw_input:focus{box-shadow:none}p.p-space{margin-bottom:10px}div.output_unrecognized{padding:5px;font-weight:bold;color:red}div.output_unrecognized a{color:inherit;text-decoration:none}div.output_unrecognized a:hover{color:inherit;text-decoration:none}.rendered_html{color:#000}.rendered_html em{font-style:italic}.rendered_html strong{font-weight:bold}.rendered_html u{text-decoration:underline}.rendered_html :link{text-decoration:underline}.rendered_html :visited{text-decoration:underline}.rendered_html h1{font-size:185.7%;margin:1.08em 0 0 0;font-weight:bold;line-height:1}.rendered_html h2{font-size:157.1%;margin:1.27em 0 0 0;font-weight:bold;line-height:1}.rendered_html h3{font-size:128.6%;margin:1.55em 0 0 0;font-weight:bold;line-height:1}.rendered_html h4{font-size:100%;margin:2em 0 0 0;font-weight:bold;line-height:1}.rendered_html h5{font-size:100%;margin:2em 0 0 0;font-weight:bold;line-height:1;font-style:italic}.rendered_html h6{font-size:100%;margin:2em 0 0 0;font-weight:bold;line-height:1;font-style:italic}.rendered_html h1:first-child{margin-top:.538em}.rendered_html h2:first-child{margin-top:.636em}.rendered_html h3:first-child{margin-top:.777em}.rendered_html h4:first-child{margin-top:1em}.rendered_html h5:first-child{margin-top:1em}.rendered_html h6:first-child{margin-top:1em}.rendered_html ul{list-style:disc;margin:0 2em;padding-left:0}.rendered_html ul ul{list-style:square;margin:0 2em}.rendered_html ul ul ul{list-style:circle;margin:0 2em}.rendered_html ol{list-style:decimal;margin:0 2em;padding-left:0}.rendered_html ol ol{list-style:upper-alpha;margin:0 2em}.rendered_html ol ol ol{list-style:lower-alpha;margin:0 2em}.rendered_html ol ol ol ol{list-style:lower-roman;margin:0 2em}.rendered_html ol ol ol ol ol{list-style:decimal;margin:0 2em}.rendered_html *+ul{margin-top:1em}.rendered_html *+ol{margin-top:1em}.rendered_html hr{color:black;background-color:black}.rendered_html pre{margin:1em 2em}.rendered_html pre,.rendered_html code{border:0;background-color:#fff;color:#000;font-size:100%;padding:0}.rendered_html blockquote{margin:1em 2em}.rendered_html table{margin-left:auto;margin-right:auto;border:1px solid black;border-collapse:collapse}.rendered_html tr,.rendered_html th,.rendered_html td{border:1px solid black;border-collapse:collapse;margin:1em 2em}.rendered_html td,.rendered_html th{text-align:left;vertical-align:middle;padding:4px}.rendered_html th{font-weight:bold}.rendered_html *+table{margin-top:1em}.rendered_html p{text-align:left}.rendered_html *+p{margin-top:1em}.rendered_html img{display:block;margin-left:auto;margin-right:auto}.rendered_html *+img{margin-top:1em}.rendered_html img,.rendered_html svg{max-width:100%;height:auto}.rendered_html img.unconfined,.rendered_html svg.unconfined{max-width:none}div.text_cell{display:-webkit-box;-webkit-box-orient:horizontal;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:horizontal;-moz-box-align:stretch;display:box;box-orient:horizontal;box-align:stretch;display:flex;flex-direction:row;align-items:stretch}@media (max-width:540px){div.text_cell>div.prompt{display:none}}div.text_cell_render{outline:none;resize:none;width:inherit;border-style:none;padding:.5em .5em .5em .4em;color:#000;box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box}a.anchor-link:link{text-decoration:none;padding:0 20px;visibility:hidden}h1:hover .anchor-link,h2:hover .anchor-link,h3:hover .anchor-link,h4:hover .anchor-link,h5:hover .anchor-link,h6:hover .anchor-link{visibility:visible}.text_cell.rendered .input_area{display:none}.text_cell.rendered .rendered_html{overflow-x:auto}.text_cell.unrendered .text_cell_render{display:none}.cm-header-1,.cm-header-2,.cm-header-3,.cm-header-4,.cm-header-5,.cm-header-6{font-weight:bold;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif}.cm-header-1{font-size:185.7%}.cm-header-2{font-size:157.1%}.cm-header-3{font-size:128.6%}.cm-header-4{font-size:110%}.cm-header-5{font-size:100%;font-style:italic}.cm-header-6{font-size:100%;font-style:italic}.widget-interact>div,.widget-interact>input{padding:2.5px}.widget-area{page-break-inside:avoid;display:-webkit-box;-webkit-box-orient:horizontal;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:horizontal;-moz-box-align:stretch;display:box;box-orient:horizontal;box-align:stretch;display:flex;flex-direction:row;align-items:stretch}.widget-area .widget-subarea{padding:.44em .4em .4em 1px;margin-left:6px;box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:vertical;-moz-box-align:stretch;display:box;box-orient:vertical;box-align:stretch;display:flex;flex-direction:column;align-items:stretch;-webkit-box-flex:2;-moz-box-flex:2;box-flex:2;flex:2;-webkit-box-align:start;-moz-box-align:start;box-align:start;align-items:flex-start}.widget-area.connection-problems .prompt:after{content:"\f127";font-family:'FontAwesome';color:#d9534f;font-size:14px;top:3px;padding:3px}.slide-track{border:1px solid #ccc;background:#fff;border-radius:2px}.widget-hslider{padding-left:8px;padding-right:2px;overflow:visible;width:350px;height:5px;max-height:5px;margin-top:13px;margin-bottom:10px;border:1px solid #ccc;background:#fff;border-radius:2px;display:-webkit-box;-webkit-box-orient:horizontal;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:horizontal;-moz-box-align:stretch;display:box;box-orient:horizontal;box-align:stretch;display:flex;flex-direction:row;align-items:stretch}.widget-hslider .ui-slider{border:0;background:none;display:-webkit-box;-webkit-box-orient:horizontal;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:horizontal;-moz-box-align:stretch;display:box;box-orient:horizontal;box-align:stretch;display:flex;flex-direction:row;align-items:stretch;-webkit-box-flex:1;-moz-box-flex:1;box-flex:1;flex:1}.widget-hslider .ui-slider .ui-slider-handle{width:12px;height:28px;margin-top:-8px;border-radius:2px}.widget-hslider .ui-slider .ui-slider-range{height:12px;margin-top:-4px;background:#eee}.widget-vslider{padding-bottom:5px;overflow:visible;width:5px;max-width:5px;height:250px;margin-left:12px;border:1px solid #ccc;background:#fff;border-radius:2px;display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:vertical;-moz-box-align:stretch;display:box;box-orient:vertical;box-align:stretch;display:flex;flex-direction:column;align-items:stretch}.widget-vslider .ui-slider{border:0;background:none;margin-left:-4px;margin-top:5px;display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:vertical;-moz-box-align:stretch;display:box;box-orient:vertical;box-align:stretch;display:flex;flex-direction:column;align-items:stretch;-webkit-box-flex:1;-moz-box-flex:1;box-flex:1;flex:1}.widget-vslider .ui-slider .ui-slider-handle{width:28px;height:12px;margin-left:-9px;border-radius:2px}.widget-vslider .ui-slider .ui-slider-range{width:12px;margin-left:-1px;background:#eee}.widget-text{width:350px;margin:0}.widget-listbox{width:350px;margin-bottom:0}.widget-numeric-text{width:150px;margin:0}.widget-progress{margin-top:6px;min-width:350px}.widget-progress .progress-bar{-webkit-transition:none;-moz-transition:none;-ms-transition:none;-o-transition:none;transition:none}.widget-combo-btn{min-width:125px}.widget_item .dropdown-menu li a{color:inherit}.widget-hbox{display:-webkit-box;-webkit-box-orient:horizontal;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:horizontal;-moz-box-align:stretch;display:box;box-orient:horizontal;box-align:stretch;display:flex;flex-direction:row;align-items:stretch}.widget-hbox input[type="checkbox"]{margin-top:9px;margin-bottom:10px}.widget-hbox .widget-label{min-width:10ex;padding-right:8px;padding-top:5px;text-align:right;vertical-align:text-top}.widget-hbox .widget-readout{padding-left:8px;padding-top:5px;text-align:left;vertical-align:text-top}.widget-vbox{display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:vertical;-moz-box-align:stretch;display:box;box-orient:vertical;box-align:stretch;display:flex;flex-direction:column;align-items:stretch}.widget-vbox .widget-label{padding-bottom:5px;text-align:center;vertical-align:text-bottom}.widget-vbox .widget-readout{padding-top:5px;text-align:center;vertical-align:text-top}.widget-box{box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;-webkit-box-align:start;-moz-box-align:start;box-align:start;align-items:flex-start}.widget-radio-box{display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:vertical;-moz-box-align:stretch;display:box;box-orient:vertical;box-align:stretch;display:flex;flex-direction:column;align-items:stretch;box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding-top:4px}.widget-radio-box label{margin-top:0;margin-left:20px}/*# sourceMappingURL=ipython.min.css.map */
\ No newline at end of file diff --git a/nikola/data/themes/base/assets/css/ipython.min.css.map b/nikola/data/themes/base/assets/css/ipython.min.css.map new file mode 100644 index 0000000..3e36e5e --- /dev/null +++ b/nikola/data/themes/base/assets/css/ipython.min.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["../base/less/variables.less","../components/bootstrap/less/mixins/vendor-prefixes.less","../base/less/mixins.less","../base/less/flexbox.less","../base/less/error.less","../notebook/less/ansicolors.less","../notebook/less/cell.less","../notebook/less/codecell.less","../notebook/less/codemirror.less","../notebook/less/highlight.less","../components/codemirror/lib/codemirror.css","../notebook/less/outputarea.less","../notebook/less/renderedhtml.less","../notebook/less/textcell.less","../components/bootstrap/less/variables.less","../widgets/less/widgets.less","../components/font-awesome/less/variables.less"],"names":[],"mappings":"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","file":"ipython.min.css"}
\ No newline at end of file diff --git a/nikola/data/themes/base/assets/js/jquery.min.map b/nikola/data/themes/base/assets/js/jquery.min.map new file mode 100644 index 0000000..1528143 --- /dev/null +++ b/nikola/data/themes/base/assets/js/jquery.min.map @@ -0,0 +1 @@ +{"version":3,"file":"jquery.min.js","sources":["jquery.js"],"names":["global","factory","module","exports","document","w","Error","window","this","noGlobal","deletedIds","slice","concat","push","indexOf","class2type","toString","hasOwn","hasOwnProperty","support","version","jQuery","selector","context","fn","init","rtrim","rmsPrefix","rdashAlpha","fcamelCase","all","letter","toUpperCase","prototype","jquery","constructor","length","toArray","call","get","num","pushStack","elems","ret","merge","prevObject","each","callback","args","map","elem","i","apply","arguments","first","eq","last","len","j","end","sort","splice","extend","src","copyIsArray","copy","name","options","clone","target","deep","isFunction","isPlainObject","isArray","undefined","expando","Math","random","replace","isReady","error","msg","noop","obj","type","Array","isWindow","isNumeric","parseFloat","isEmptyObject","key","nodeType","e","ownLast","globalEval","data","trim","execScript","camelCase","string","nodeName","toLowerCase","value","isArraylike","text","makeArray","arr","results","Object","inArray","max","second","grep","invert","callbackInverse","matches","callbackExpect","arg","guid","proxy","tmp","now","Date","split","Sizzle","Expr","getText","isXML","tokenize","compile","select","outermostContext","sortInput","hasDuplicate","setDocument","docElem","documentIsHTML","rbuggyQSA","rbuggyMatches","contains","preferredDoc","dirruns","done","classCache","createCache","tokenCache","compilerCache","sortOrder","a","b","MAX_NEGATIVE","pop","push_native","list","booleans","whitespace","characterEncoding","identifier","attributes","pseudos","rwhitespace","RegExp","rcomma","rcombinators","rattributeQuotes","rpseudo","ridentifier","matchExpr","ID","CLASS","TAG","ATTR","PSEUDO","CHILD","bool","needsContext","rinputs","rheader","rnative","rquickExpr","rsibling","rescape","runescape","funescape","_","escaped","escapedWhitespace","high","String","fromCharCode","unloadHandler","childNodes","els","seed","match","m","groups","old","nid","newContext","newSelector","ownerDocument","exec","getElementById","parentNode","id","getElementsByTagName","getElementsByClassName","qsa","test","getAttribute","setAttribute","toSelector","testContext","join","querySelectorAll","qsaError","removeAttribute","keys","cache","cacheLength","shift","markFunction","assert","div","createElement","removeChild","addHandle","attrs","handler","attrHandle","siblingCheck","cur","diff","sourceIndex","nextSibling","createInputPseudo","createButtonPseudo","createPositionalPseudo","argument","matchIndexes","documentElement","node","hasCompare","parent","doc","defaultView","top","addEventListener","attachEvent","className","appendChild","createComment","getById","getElementsByName","find","filter","attrId","getAttributeNode","tag","innerHTML","input","matchesSelector","webkitMatchesSelector","mozMatchesSelector","oMatchesSelector","msMatchesSelector","disconnectedMatch","compareDocumentPosition","adown","bup","compare","sortDetached","aup","ap","bp","unshift","expr","elements","attr","val","specified","uniqueSort","duplicates","detectDuplicates","sortStable","textContent","firstChild","nodeValue","selectors","createPseudo","relative",">","dir"," ","+","~","preFilter","excess","unquoted","nodeNameSelector","pattern","operator","check","result","what","simple","forward","ofType","xml","outerCache","nodeIndex","start","useCache","lastChild","pseudo","setFilters","idx","matched","not","matcher","unmatched","has","innerText","lang","elemLang","hash","location","root","focus","activeElement","hasFocus","href","tabIndex","enabled","disabled","checked","selected","selectedIndex","empty","header","button","even","odd","lt","gt","radio","checkbox","file","password","image","submit","reset","filters","parseOnly","tokens","soFar","preFilters","cached","addCombinator","combinator","base","checkNonElements","doneName","oldCache","newCache","elementMatcher","matchers","multipleContexts","contexts","condense","newUnmatched","mapped","setMatcher","postFilter","postFinder","postSelector","temp","preMap","postMap","preexisting","matcherIn","matcherOut","matcherFromTokens","checkContext","leadingRelative","implicitRelative","matchContext","matchAnyContext","matcherFromGroupMatchers","elementMatchers","setMatchers","bySet","byElement","superMatcher","outermost","matchedCount","setMatched","contextBackup","dirrunsUnique","token","compiled","div1","defaultValue","unique","isXMLDoc","rneedsContext","rsingleTag","risSimple","winnow","qualifier","self","is","rootjQuery","charAt","parseHTML","ready","rparentsprev","guaranteedUnique","children","contents","next","prev","until","sibling","n","r","targets","closest","l","pos","index","prevAll","add","addBack","parents","parentsUntil","nextAll","nextUntil","prevUntil","siblings","contentDocument","contentWindow","reverse","rnotwhite","optionsCache","createOptions","object","flag","Callbacks","firing","memory","fired","firingLength","firingIndex","firingStart","stack","once","fire","stopOnFalse","disable","remove","lock","locked","fireWith","Deferred","func","tuples","state","promise","always","deferred","fail","then","fns","newDefer","tuple","returned","resolve","reject","progress","notify","pipe","stateString","when","subordinate","resolveValues","remaining","updateFunc","values","progressValues","notifyWith","resolveWith","progressContexts","resolveContexts","readyList","readyWait","holdReady","hold","wait","body","setTimeout","triggerHandler","off","detach","removeEventListener","completed","detachEvent","event","readyState","frameElement","doScroll","doScrollCheck","strundefined","inlineBlockNeedsLayout","container","style","cssText","zoom","offsetWidth","deleteExpando","acceptData","noData","rbrace","rmultiDash","dataAttr","parseJSON","isEmptyDataObject","internalData","pvt","thisCache","internalKey","isNode","toJSON","internalRemoveData","cleanData","applet ","embed ","object ","hasData","removeData","_data","_removeData","queue","dequeue","startLength","hooks","_queueHooks","stop","setter","clearQueue","count","defer","pnum","source","cssExpand","isHidden","el","css","access","chainable","emptyGet","raw","bulk","rcheckableType","fragment","createDocumentFragment","leadingWhitespace","tbody","htmlSerialize","html5Clone","cloneNode","outerHTML","appendChecked","noCloneChecked","checkClone","noCloneEvent","click","eventName","change","focusin","rformElems","rkeyEvent","rmouseEvent","rfocusMorph","rtypenamespace","returnTrue","returnFalse","safeActiveElement","err","types","events","t","handleObjIn","special","eventHandle","handleObj","handlers","namespaces","origType","elemData","handle","triggered","dispatch","delegateType","bindType","namespace","delegateCount","setup","mappedTypes","origCount","teardown","removeEvent","trigger","onlyHandlers","ontype","bubbleType","eventPath","Event","isTrigger","namespace_re","noBubble","parentWindow","isPropagationStopped","preventDefault","isDefaultPrevented","_default","fix","handlerQueue","delegateTarget","preDispatch","currentTarget","isImmediatePropagationStopped","stopPropagation","postDispatch","sel","prop","originalEvent","fixHook","fixHooks","mouseHooks","keyHooks","props","srcElement","metaKey","original","which","charCode","keyCode","eventDoc","fromElement","pageX","clientX","scrollLeft","clientLeft","pageY","clientY","scrollTop","clientTop","relatedTarget","toElement","load","blur","beforeunload","returnValue","simulate","bubble","isSimulated","defaultPrevented","timeStamp","cancelBubble","stopImmediatePropagation","mouseenter","mouseleave","pointerenter","pointerleave","orig","related","submitBubbles","form","_submit_bubble","changeBubbles","propertyName","_just_changed","focusinBubbles","attaches","on","one","origFn","createSafeFragment","nodeNames","safeFrag","rinlinejQuery","rnoshimcache","rleadingWhitespace","rxhtmlTag","rtagName","rtbody","rhtml","rnoInnerhtml","rchecked","rscriptType","rscriptTypeMasked","rcleanScript","wrapMap","option","legend","area","param","thead","tr","col","td","safeFragment","fragmentDiv","optgroup","tfoot","colgroup","caption","th","getAll","found","fixDefaultChecked","defaultChecked","manipulationTarget","content","disableScript","restoreScript","setGlobalEval","refElements","cloneCopyEvent","dest","oldData","curData","fixCloneNodeIssues","defaultSelected","dataAndEvents","deepDataAndEvents","destElements","srcElements","inPage","buildFragment","scripts","selection","wrap","safe","nodes","createTextNode","append","domManip","prepend","insertBefore","before","after","keepData","html","replaceWith","replaceChild","hasScripts","set","iNoClone","_evalUrl","appendTo","prependTo","insertAfter","replaceAll","insert","iframe","elemdisplay","actualDisplay","display","getDefaultComputedStyle","defaultDisplay","write","close","shrinkWrapBlocksVal","shrinkWrapBlocks","width","rmargin","rnumnonpx","getStyles","curCSS","rposition","getComputedStyle","opener","computed","minWidth","maxWidth","getPropertyValue","currentStyle","left","rs","rsLeft","runtimeStyle","pixelLeft","addGetHookIf","conditionFn","hookFn","condition","pixelPositionVal","boxSizingReliableVal","reliableHiddenOffsetsVal","reliableMarginRightVal","opacity","cssFloat","backgroundClip","clearCloneStyle","boxSizing","MozBoxSizing","WebkitBoxSizing","reliableHiddenOffsets","computeStyleTests","boxSizingReliable","pixelPosition","reliableMarginRight","marginRight","offsetHeight","swap","ralpha","ropacity","rdisplayswap","rnumsplit","rrelNum","cssShow","position","visibility","cssNormalTransform","letterSpacing","fontWeight","cssPrefixes","vendorPropName","capName","origName","showHide","show","hidden","setPositiveNumber","subtract","augmentWidthOrHeight","extra","isBorderBox","styles","getWidthOrHeight","valueIsBorderBox","cssHooks","cssNumber","columnCount","fillOpacity","flexGrow","flexShrink","lineHeight","order","orphans","widows","zIndex","cssProps","float","$1","margin","padding","border","prefix","suffix","expand","expanded","parts","hide","toggle","Tween","easing","unit","propHooks","run","percent","eased","duration","step","tween","fx","linear","p","swing","cos","PI","fxNow","timerId","rfxtypes","rfxnum","rrun","animationPrefilters","defaultPrefilter","tweeners","*","createTween","scale","maxIterations","createFxNow","genFx","includeWidth","height","animation","collection","opts","oldfire","checkDisplay","anim","dataShow","unqueued","overflow","overflowX","overflowY","propFilter","specialEasing","Animation","properties","stopped","tick","currentTime","startTime","tweens","originalProperties","originalOptions","gotoEnd","rejectWith","timer","complete","tweener","prefilter","speed","opt","speeds","fadeTo","to","animate","optall","doAnimation","finish","stopQueue","timers","cssFn","slideDown","slideUp","slideToggle","fadeIn","fadeOut","fadeToggle","interval","setInterval","clearInterval","slow","fast","delay","time","timeout","clearTimeout","getSetAttribute","hrefNormalized","checkOn","optSelected","enctype","optDisabled","radioValue","rreturn","valHooks","optionSet","scrollHeight","nodeHook","boolHook","ruseDefault","getSetInput","removeAttr","nType","attrHooks","propName","attrNames","propFix","getter","setAttributeNode","createAttribute","coords","contenteditable","rfocusable","rclickable","removeProp","for","class","notxml","tabindex","parseInt","rclass","addClass","classes","clazz","finalValue","proceed","removeClass","toggleClass","stateVal","classNames","hasClass","hover","fnOver","fnOut","bind","unbind","delegate","undelegate","nonce","rquery","rvalidtokens","JSON","parse","requireNonComma","depth","str","comma","open","Function","parseXML","DOMParser","parseFromString","ActiveXObject","async","loadXML","ajaxLocParts","ajaxLocation","rhash","rts","rheaders","rlocalProtocol","rnoContent","rprotocol","rurl","prefilters","transports","allTypes","addToPrefiltersOrTransports","structure","dataTypeExpression","dataType","dataTypes","inspectPrefiltersOrTransports","jqXHR","inspected","seekingTransport","inspect","prefilterOrFactory","dataTypeOrTransport","ajaxExtend","flatOptions","ajaxSettings","ajaxHandleResponses","s","responses","firstDataType","ct","finalDataType","mimeType","getResponseHeader","converters","ajaxConvert","response","isSuccess","conv2","current","conv","responseFields","dataFilter","active","lastModified","etag","url","isLocal","processData","contentType","accepts","json","* text","text html","text json","text xml","ajaxSetup","settings","ajaxPrefilter","ajaxTransport","ajax","cacheURL","responseHeadersString","timeoutTimer","fireGlobals","transport","responseHeaders","callbackContext","globalEventContext","completeDeferred","statusCode","requestHeaders","requestHeadersNames","strAbort","getAllResponseHeaders","setRequestHeader","lname","overrideMimeType","code","status","abort","statusText","finalText","success","method","crossDomain","traditional","hasContent","ifModified","headers","beforeSend","send","nativeStatusText","modified","getJSON","getScript","throws","wrapAll","wrapInner","unwrap","visible","r20","rbracket","rCRLF","rsubmitterTypes","rsubmittable","buildParams","v","encodeURIComponent","serialize","serializeArray","xhr","createStandardXHR","createActiveXHR","xhrId","xhrCallbacks","xhrSupported","cors","username","xhrFields","isAbort","onreadystatechange","responseText","XMLHttpRequest","script","text script","head","scriptCharset","charset","onload","oldCallbacks","rjsonp","jsonp","jsonpCallback","originalSettings","callbackName","overwritten","responseContainer","jsonProp","keepScripts","parsed","_load","params","animated","getWindow","offset","setOffset","curPosition","curLeft","curCSSTop","curTop","curOffset","curCSSLeft","calculatePosition","curElem","using","win","box","getBoundingClientRect","pageYOffset","pageXOffset","offsetParent","parentOffset","scrollTo","Height","Width","defaultExtra","funcName","size","andSelf","define","amd","_jQuery","_$","$","noConflict"],"mappings":"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"}
\ No newline at end of file diff --git a/nikola/data/themes/base/assets/js/mathjax.js b/nikola/data/themes/base/assets/js/mathjax.js deleted file mode 100644 index 6ef2dd7..0000000 --- a/nikola/data/themes/base/assets/js/mathjax.js +++ /dev/null @@ -1,11 +0,0 @@ -// We wait for the onload function to load MathJax after the page is completely loaded. -// MathJax is loaded 1 unit of time after the page is ready. -// This hack prevent problems when you use social button from addthis. -// -window.onload = function () { - setTimeout(function () { - var script = document.createElement("script"); - script.src = "https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"; - document.getElementsByTagName("body")[0].appendChild(script); - },1) -} diff --git a/nikola/data/themes/base/assets/js/moment-with-locales.min.js b/nikola/data/themes/base/assets/js/moment-with-locales.min.js new file mode 120000 index 0000000..1caedc6 --- /dev/null +++ b/nikola/data/themes/base/assets/js/moment-with-locales.min.js @@ -0,0 +1 @@ +../../../../../../bower_components/moment/min/moment-with-locales.min.js
\ No newline at end of file diff --git a/nikola/data/themes/base/messages/messages_ar.py b/nikola/data/themes/base/messages/messages_ar.py index 07c38b2..6796b6b 100644 --- a/nikola/data/themes/base/messages/messages_ar.py +++ b/nikola/data/themes/base/messages/messages_ar.py @@ -6,6 +6,7 @@ MESSAGES = { "(active)": "", "Also available in:": "أيضا متوفر في:", "Archive": "الأرشيف", + "Authors": "", "Categories": "فئات", "Comments": "التّعليقات", "LANGUAGE": "العربيّة", @@ -19,6 +20,7 @@ MESSAGES = { "Original site": "الموقع الأصلي", "Posted:": "نشر:", "Posts about %s": "مقالات عن s%", + "Posts by %s": "", "Posts for year %s": "مقالات سنة s%", "Posts for {month} {day}, {year}": "", "Posts for {month} {year}": "", @@ -32,6 +34,8 @@ MESSAGES = { "Subcategories:": "", "Tags and Categories": "تصنيفات و فئات", "Tags": "تصنيفات", + "Uncategorized": "", + "Updates": "", "Write your page here.": "", "Write your post here.": "", "old posts, page %d": "مقالات قديمة, صفحة d%", diff --git a/nikola/data/themes/base/messages/messages_az.py b/nikola/data/themes/base/messages/messages_az.py index 76c15fc..15b2341 100644 --- a/nikola/data/themes/base/messages/messages_az.py +++ b/nikola/data/themes/base/messages/messages_az.py @@ -6,6 +6,7 @@ MESSAGES = { "(active)": "", "Also available in:": "Həmçinin mövcuddur:", "Archive": "Arxiv", + "Authors": "", "Categories": "Kateqoriyalar", "Comments": "Şərhlər", "LANGUAGE": "Azərbaycan dili", @@ -19,6 +20,7 @@ MESSAGES = { "Original site": "Original sayt", "Posted:": "yazılma tarixi:", "Posts about %s": "%s ilə bağlı yazılar", + "Posts by %s": "", "Posts for year %s": "%s ilindəki yazılar", "Posts for {month} {day}, {year}": "{month} {day}, {year} üçün yazılar", "Posts for {month} {year}": "{month} {year} üçün yazılar", @@ -32,6 +34,8 @@ MESSAGES = { "Subcategories:": "", "Tags and Categories": "Teqlər və Kateqoriyalar", "Tags": "Teqlər", + "Uncategorized": "", + "Updates": "", "Write your page here.": "", "Write your post here.": "", "old posts, page %d": "köhnə yazılar, səhifə %s", diff --git a/nikola/data/themes/base/messages/messages_bg.py b/nikola/data/themes/base/messages/messages_bg.py index 96ac878..bd6d301 100644 --- a/nikola/data/themes/base/messages/messages_bg.py +++ b/nikola/data/themes/base/messages/messages_bg.py @@ -6,6 +6,7 @@ MESSAGES = { "(active)": "", "Also available in:": "Също достъпно в:", "Archive": "Архив", + "Authors": "", "Categories": "Категории", "Comments": "", "LANGUAGE": "Български", @@ -19,6 +20,7 @@ MESSAGES = { "Original site": "Оригиналния сайт", "Posted:": "Публиковано:", "Posts about %s": "Публикации относно %s", + "Posts by %s": "", "Posts for year %s": "Публикации за %s година", "Posts for {month} {day}, {year}": "", "Posts for {month} {year}": "Публикации за {month} {year}", @@ -32,6 +34,8 @@ MESSAGES = { "Subcategories:": "", "Tags and Categories": "Тагове и Категории", "Tags": "Тагове", + "Uncategorized": "", + "Updates": "", "Write your page here.": "", "Write your post here.": "", "old posts, page %d": "стари публикации, страница %d", diff --git a/nikola/data/themes/base/messages/messages_bs.py b/nikola/data/themes/base/messages/messages_bs.py index 23a1f64..d2bef18 100644 --- a/nikola/data/themes/base/messages/messages_bs.py +++ b/nikola/data/themes/base/messages/messages_bs.py @@ -6,23 +6,25 @@ MESSAGES = { "(active)": "(aktivno)", "Also available in:": "Takođe dostupan u:", "Archive": "Arhiva", + "Authors": "Autori", "Categories": "Kategorije", "Comments": "Komentari", "LANGUAGE": "Bosanski", "Languages:": "Jezici:", - "More posts about %s": "Više objava o %s", + "More posts about %s": "Više članaka o %s", "Newer posts": "Novije objave", - "Next post": "Naredna objava", - "No posts found.": "Nema objava.", + "Next post": "Naredni članak", + "No posts found.": "Nema članaka.", "Nothing found.": "Ništa nije pronađeno.", "Older posts": "Starije objave", "Original site": "Izvorni sajt", "Posted:": "Objavljeno:", "Posts about %s": "Objave o %s", + "Posts by %s": "Objave prema %s", "Posts for year %s": "Objave u godini %s", - "Posts for {month} {day}, {year}": "Objave za {month} {day}, {year}", + "Posts for {month} {day}, {year}": "Objave za {day}.{month}.{year}", "Posts for {month} {year}": "Objave za {month} {year}", - "Previous post": "Prethodne objave", + "Previous post": "Prethodni članak", "Publication date": "Datum objavljivanja", "RSS feed": "RSS feed", "Read in English": "Pročitaj na bosanskom", @@ -32,8 +34,10 @@ MESSAGES = { "Subcategories:": "Podkategorije:", "Tags and Categories": "Oznake i kategorije", "Tags": "Oznake", + "Uncategorized": "Bez kategorije", + "Updates": "Ažuriranja", "Write your page here.": "Vašu stranicu napišite ovdje.", - "Write your post here.": "Vašu objavu napišite ovdje.", + "Write your post here.": "Vaš članak napišite ovdje.", "old posts, page %d": "stare objave, strana %d", "page %d": "strana %d", } diff --git a/nikola/data/themes/base/messages/messages_ca.py b/nikola/data/themes/base/messages/messages_ca.py index f45b1a6..bd4cacc 100644 --- a/nikola/data/themes/base/messages/messages_ca.py +++ b/nikola/data/themes/base/messages/messages_ca.py @@ -6,6 +6,7 @@ MESSAGES = { "(active)": "", "Also available in:": "També disponibles en:", "Archive": "Arxiu", + "Authors": "", "Categories": "Categories", "Comments": "Comentaris", "LANGUAGE": "Català", @@ -19,6 +20,7 @@ MESSAGES = { "Original site": "Lloc original", "Posted:": "Publicat:", "Posts about %s": "Entrades sobre %s", + "Posts by %s": "", "Posts for year %s": "Entrades de l'any %s", "Posts for {month} {day}, {year}": "", "Posts for {month} {year}": "Publicat en {month} {year}", @@ -32,6 +34,8 @@ MESSAGES = { "Subcategories:": "", "Tags and Categories": "Etiquetes i Categories", "Tags": "Etiquetes", + "Uncategorized": "", + "Updates": "", "Write your page here.": "", "Write your post here.": "", "old posts, page %d": "entrades antigues, pàgina %d", diff --git a/nikola/data/themes/base/messages/messages_cs.py b/nikola/data/themes/base/messages/messages_cs.py index 6e1bdd2..b509043 100644 --- a/nikola/data/themes/base/messages/messages_cs.py +++ b/nikola/data/themes/base/messages/messages_cs.py @@ -6,6 +6,7 @@ MESSAGES = { "(active)": "", "Also available in:": "Dostupné také v", "Archive": "Archiv", + "Authors": "", "Categories": "Kategorie", "Comments": "Komentáře", "LANGUAGE": "Čeština", @@ -19,6 +20,7 @@ MESSAGES = { "Original site": "Původní stránka", "Posted:": "Zveřejněno:", "Posts about %s": "Příspěvky o %s", + "Posts by %s": "", "Posts for year %s": "Příspěvky v roce %s", "Posts for {month} {day}, {year}": "Příspěvky v {month} {day}, {year}", "Posts for {month} {year}": "Příspěvky v {month} {year}", @@ -32,6 +34,8 @@ MESSAGES = { "Subcategories:": "", "Tags and Categories": "Štítky a kategorie", "Tags": "Štítky", + "Uncategorized": "", + "Updates": "", "Write your page here.": "", "Write your post here.": "", "old posts, page %d": "staré příspěvky, strana %d", diff --git a/nikola/data/themes/base/messages/messages_da.py b/nikola/data/themes/base/messages/messages_da.py index 73b71cb..34a92f5 100644 --- a/nikola/data/themes/base/messages/messages_da.py +++ b/nikola/data/themes/base/messages/messages_da.py @@ -6,6 +6,7 @@ MESSAGES = { "(active)": "", "Also available in:": "Fås også i:", "Archive": "Arkiv", + "Authors": "", "Categories": "Kategorier", "Comments": "Kommentarer", "LANGUAGE": "Dansk", @@ -19,6 +20,7 @@ MESSAGES = { "Original site": "Oprindeligt hjemmeside", "Posted:": "Opslået:", "Posts about %s": "Indlæg om %s", + "Posts by %s": "", "Posts for year %s": "Indlæg for %s", "Posts for {month} {day}, {year}": "Indlæs for {month} {day}, {year}", "Posts for {month} {year}": "Indlæg for {month} {year}", @@ -32,6 +34,8 @@ MESSAGES = { "Subcategories:": "", "Tags and Categories": "Nøgleord og kategorier", "Tags": "Nøgleord", + "Uncategorized": "", + "Updates": "", "Write your page here.": "", "Write your post here.": "", "old posts, page %d": "gamle indlæg, side %d", diff --git a/nikola/data/themes/base/messages/messages_de.py b/nikola/data/themes/base/messages/messages_de.py index 324dd14..fd2c293 100644 --- a/nikola/data/themes/base/messages/messages_de.py +++ b/nikola/data/themes/base/messages/messages_de.py @@ -3,9 +3,10 @@ from __future__ import unicode_literals MESSAGES = { "%d min remaining to read": "%d min verbleiben zum Lesen", - "(active)": "", + "(active)": "(aktiv)", "Also available in:": "Auch verfügbar in:", "Archive": "Archiv", + "Authors": "Autoren", "Categories": "Kategorien", "Comments": "Kommentare", "LANGUAGE": "Deutsch", @@ -19,6 +20,7 @@ MESSAGES = { "Original site": "Original-Seite", "Posted:": "Veröffentlicht:", "Posts about %s": "Einträge über %s", + "Posts by %s": "Einträge von %s", "Posts for year %s": "Einträge aus dem Jahr %s", "Posts for {month} {day}, {year}": "Einträge vom {day}. {month} {year}", "Posts for {month} {year}": "Einträge aus {month} {year}", @@ -32,8 +34,10 @@ MESSAGES = { "Subcategories:": "Unterkategorien:", "Tags and Categories": "Tags und Kategorien", "Tags": "Tags", - "Write your page here.": "", - "Write your post here.": "", + "Uncategorized": "Nicht kategorisiert", + "Updates": "Updates", + "Write your page here.": "Schreibe hier deinen Seiteninhalt hin.", + "Write your post here.": "Schreibe hier deinen Eintrag hin.", "old posts, page %d": "Ältere Einträge, Seite %d", "page %d": "Seite %d", } diff --git a/nikola/data/themes/base/messages/messages_el.py b/nikola/data/themes/base/messages/messages_el.py index 68cf91f..9775b30 100644 --- a/nikola/data/themes/base/messages/messages_el.py +++ b/nikola/data/themes/base/messages/messages_el.py @@ -6,6 +6,7 @@ MESSAGES = { "(active)": "", "Also available in:": "Διαθέσιμο και στα:", "Archive": "Αρχείο", + "Authors": "", "Categories": "Κατηγορίες", "Comments": "Σχόλια", "LANGUAGE": "Ελληνικά", @@ -19,6 +20,7 @@ MESSAGES = { "Original site": "Ιστοσελίδα αρχικής ανάρτησης", "Posted:": "Αναρτήθηκε:", "Posts about %s": "Αναρτήσεις για %s", + "Posts by %s": "", "Posts for year %s": "Αναρτήσεις για το έτος %s", "Posts for {month} {day}, {year}": "Αναρτήσεις στις {day} {month}, {year}", "Posts for {month} {year}": "Αναρτήσεις για τον {month} του {year}", @@ -32,6 +34,8 @@ MESSAGES = { "Subcategories:": "", "Tags and Categories": "Ετικέτες και κατηγορίες", "Tags": "Ετικέτες", + "Uncategorized": "", + "Updates": "", "Write your page here.": "", "Write your post here.": "", "old posts, page %d": "σελίδα παλαιότερων αναρτήσεων %d", diff --git a/nikola/data/themes/base/messages/messages_en.py b/nikola/data/themes/base/messages/messages_en.py index 29af257..f17ce18 100644 --- a/nikola/data/themes/base/messages/messages_en.py +++ b/nikola/data/themes/base/messages/messages_en.py @@ -6,6 +6,7 @@ MESSAGES = { "(active)": "(active)", "Also available in:": "Also available in:", "Archive": "Archive", + "Authors": "Authors", "Categories": "Categories", "Comments": "Comments", "LANGUAGE": "English", @@ -19,6 +20,7 @@ MESSAGES = { "Original site": "Original site", "Posted:": "Posted:", "Posts about %s": "Posts about %s", + "Posts by %s": "Posts by %s", "Posts for year %s": "Posts for year %s", "Posts for {month} {day}, {year}": "Posts for {month} {day}, {year}", "Posts for {month} {year}": "Posts for {month} {year}", @@ -32,6 +34,8 @@ MESSAGES = { "Subcategories:": "Subcategories:", "Tags and Categories": "Tags and Categories", "Tags": "Tags", + "Uncategorized": "Uncategorized", + "Updates": "Updates", "Write your page here.": "Write your page here.", "Write your post here.": "Write your post here.", "old posts, page %d": "old posts, page %d", diff --git a/nikola/data/themes/base/messages/messages_eo.py b/nikola/data/themes/base/messages/messages_eo.py index 8938707..126c98f 100644 --- a/nikola/data/themes/base/messages/messages_eo.py +++ b/nikola/data/themes/base/messages/messages_eo.py @@ -2,38 +2,42 @@ from __future__ import unicode_literals MESSAGES = { - "%d min remaining to read": "", - "(active)": "", + "%d min remaining to read": "%d minutoj por legi", + "(active)": "(aktiva)", "Also available in:": "Ankaŭ disponebla en:", - "Archive": "Arĥivo", + "Archive": "Arkivo", + "Authors": "Aŭtoroj", "Categories": "Kategorioj", - "Comments": "", - "LANGUAGE": "Anglalingve", - "Languages:": "", - "More posts about %s": "Pli artikoloj pri %s", + "Comments": "Komentoj", + "LANGUAGE": "Esperante", + "Languages:": "Lingvoj:", + "More posts about %s": "Pli da artikoloj pri %s", "Newer posts": "Pli novaj artikoloj", "Next post": "Venonta artikolo", - "No posts found.": "", - "Nothing found.": "", + "No posts found.": "Neniu artikoloj trovitaj.", + "Nothing found.": "Nenio trovita.", "Older posts": "Pli malnovaj artikoloj", - "Original site": "Originala interretejo", + "Original site": "Originala retejo", "Posted:": "Skribita:", "Posts about %s": "Artikoloj pri %s", + "Posts by %s": "Artikoloj de %s", "Posts for year %s": "Artikoloj de la jaro %s", - "Posts for {month} {day}, {year}": "", - "Posts for {month} {year}": "Artikoloj skribitaj en {month} {year}", + "Posts for {month} {day}, {year}": "Artikoloj de la {day}a de {month} {year}", + "Posts for {month} {year}": "Artikoloj de {month} {year}", "Previous post": "Antaŭa artikolo", - "Publication date": "", - "RSS feed": "", + "Publication date": "Eldona dato", + "RSS feed": "RSS fluo", "Read in English": "Legu ĝin en Esperanto", - "Read more": "Legu plu", - "Skip to main content": "", + "Read more": "Legu pli", + "Skip to main content": "Salti al ĉefenhavo", "Source": "Fonto", - "Subcategories:": "", - "Tags and Categories": "Etikedoj kaj Kategorioj", + "Subcategories:": "Subkategorioj:", + "Tags and Categories": "Etikedoj kaj kategorioj", "Tags": "Etikedoj", - "Write your page here.": "", - "Write your post here.": "", - "old posts, page %d": "paĝo de malnovaj artikoloj %d", + "Uncategorized": "Sen kategorioj", + "Updates": "Ĝisdatigoj", + "Write your page here.": "Skribu tie vian paĝon.", + "Write your post here.": "Skribu tie vian artikolon.", + "old posts, page %d": "%da paĝo de malnovaj artikoloj", "page %d": "paĝo %d", } diff --git a/nikola/data/themes/base/messages/messages_es.py b/nikola/data/themes/base/messages/messages_es.py index e3f0f03..7457c89 100644 --- a/nikola/data/themes/base/messages/messages_es.py +++ b/nikola/data/themes/base/messages/messages_es.py @@ -6,6 +6,7 @@ MESSAGES = { "(active)": "(activo)", "Also available in:": "También disponible en:", "Archive": "Archivo", + "Authors": "Autores", "Categories": "Categorías", "Comments": "Comentarios", "LANGUAGE": "Español", @@ -19,9 +20,10 @@ MESSAGES = { "Original site": "Sitio original", "Posted:": "Publicado:", "Posts about %s": "Publicaciones sobre %s", + "Posts by %s": "Publicaciones de %s", "Posts for year %s": "Publicaciones del año %s", - "Posts for {month} {day}, {year}": "Publicaciones de {day}, {month}, {year}", - "Posts for {month} {year}": "Posts de {month} {year}", + "Posts for {month} {day}, {year}": "Publicaciones del {day} de {month} de {year}", + "Posts for {month} {year}": "Posts de {month} de {year}", "Previous post": "Publicación anterior", "Publication date": "Fecha de publicación", "RSS feed": "Canal RSS", @@ -32,8 +34,10 @@ MESSAGES = { "Subcategories:": "Subcategorías:", "Tags and Categories": "Etiquetas y Categorías", "Tags": "Etiquetas", - "Write your page here.": "", - "Write your post here.": "", + "Uncategorized": "Sin categoría", + "Updates": "Actualizaciones", + "Write your page here.": "Escriba su página aquí.", + "Write your post here.": "Escriba su publicación aquí.", "old posts, page %d": "publicaciones antiguas, página %d", "page %d": "página %d", } diff --git a/nikola/data/themes/base/messages/messages_et.py b/nikola/data/themes/base/messages/messages_et.py index c67cbb2..bfe58e4 100644 --- a/nikola/data/themes/base/messages/messages_et.py +++ b/nikola/data/themes/base/messages/messages_et.py @@ -6,6 +6,7 @@ MESSAGES = { "(active)": "", "Also available in:": "Saadaval ka:", "Archive": "Arhiiv", + "Authors": "", "Categories": "Kategooriad", "Comments": "", "LANGUAGE": "Eesti", @@ -19,6 +20,7 @@ MESSAGES = { "Original site": "Algallikas", "Posted:": "Postitatud:", "Posts about %s": "Postitused %s kohta", + "Posts by %s": "", "Posts for year %s": "Postitused aastast %s", "Posts for {month} {day}, {year}": "", "Posts for {month} {year}": "Postitused {year} aasta kuust {month} ", @@ -32,6 +34,8 @@ MESSAGES = { "Subcategories:": "", "Tags and Categories": "Sildid ja kategooriad", "Tags": "Märksõnad", + "Uncategorized": "", + "Updates": "", "Write your page here.": "", "Write your post here.": "", "old posts, page %d": "vanade postituste, leht %d", diff --git a/nikola/data/themes/base/messages/messages_eu.py b/nikola/data/themes/base/messages/messages_eu.py index 1588cd6..8635f21 100644 --- a/nikola/data/themes/base/messages/messages_eu.py +++ b/nikola/data/themes/base/messages/messages_eu.py @@ -6,6 +6,7 @@ MESSAGES = { "(active)": "", "Also available in:": "Eskuragarria hemen ere:", "Archive": "Artxiboa", + "Authors": "", "Categories": "Kategoriak", "Comments": "", "LANGUAGE": "Euskara", @@ -19,6 +20,7 @@ MESSAGES = { "Original site": "Jatorrizko orria", "Posted:": "Argitaratuta:", "Posts about %s": "%s-ri buruzko postak", + "Posts by %s": "", "Posts for year %s": "%s. urteko postak", "Posts for {month} {day}, {year}": "", "Posts for {month} {year}": "{year}ko {month}ren postak", @@ -32,6 +34,8 @@ MESSAGES = { "Subcategories:": "", "Tags and Categories": "Etiketak eta Kategoriak", "Tags": "Etiketak", + "Uncategorized": "", + "Updates": "", "Write your page here.": "", "Write your post here.": "", "old posts, page %d": "Post zaharren, orria %d", diff --git a/nikola/data/themes/base/messages/messages_fa.py b/nikola/data/themes/base/messages/messages_fa.py index b69d57a..1d96da8 100644 --- a/nikola/data/themes/base/messages/messages_fa.py +++ b/nikola/data/themes/base/messages/messages_fa.py @@ -6,6 +6,7 @@ MESSAGES = { "(active)": "", "Also available in:": "همچنین قابل دسترس از:", "Archive": "آرشیو", + "Authors": "", "Categories": "دستهها", "Comments": "دیدگاهها", "LANGUAGE": "فارسی", @@ -19,6 +20,7 @@ MESSAGES = { "Original site": "سایت اصلی", "Posted:": "ارسال شده:", "Posts about %s": "ارسالها دربارهٔ %s", + "Posts by %s": "", "Posts for year %s": "ارسالها برای سال %s", "Posts for {month} {day}, {year}": "ارسال برای {month} {day}. {year}", "Posts for {month} {year}": "ارسال برای {month} {year}", @@ -32,6 +34,8 @@ MESSAGES = { "Subcategories:": "", "Tags and Categories": "برچسبها و دستهها", "Tags": "برچسبها", + "Uncategorized": "", + "Updates": "", "Write your page here.": "", "Write your post here.": "", "old posts, page %d": "صفحهٔ ارسالهای قدیمی %d", diff --git a/nikola/data/themes/base/messages/messages_fi.py b/nikola/data/themes/base/messages/messages_fi.py index 1bcd6ad..b7ba141 100644 --- a/nikola/data/themes/base/messages/messages_fi.py +++ b/nikola/data/themes/base/messages/messages_fi.py @@ -6,6 +6,7 @@ MESSAGES = { "(active)": "(aktiivinen)", "Also available in:": "Saatavilla myös:", "Archive": "Arkisto", + "Authors": "", "Categories": "Kategoriat", "Comments": "Kommentit", "LANGUAGE": "Suomi", @@ -19,6 +20,7 @@ MESSAGES = { "Original site": "Alkuperäinen sivusto", "Posted:": "Postattu:", "Posts about %s": "Postauksia aiheesta %s", + "Posts by %s": "", "Posts for year %s": "Postauksia vuodelta %s", "Posts for {month} {day}, {year}": "", "Posts for {month} {year}": "Postauksia ajalle {month} {year}", @@ -32,6 +34,8 @@ MESSAGES = { "Subcategories:": "Alakategoriat:", "Tags and Categories": "Tagit ja kategoriat", "Tags": "Tagit", + "Uncategorized": "", + "Updates": "", "Write your page here.": "", "Write your post here.": "", "old posts, page %d": "vanhoja postauksia, sivu %d", diff --git a/nikola/data/themes/base/messages/messages_fil.py b/nikola/data/themes/base/messages/messages_fil.py index fd26d77..f661c0e 100644 --- a/nikola/data/themes/base/messages/messages_fil.py +++ b/nikola/data/themes/base/messages/messages_fil.py @@ -6,6 +6,7 @@ MESSAGES = { "(active)": "", "Also available in:": "", "Archive": "", + "Authors": "", "Categories": "", "Comments": "", "LANGUAGE": "", @@ -19,6 +20,7 @@ MESSAGES = { "Original site": "", "Posted:": "", "Posts about %s": "", + "Posts by %s": "", "Posts for year %s": "", "Posts for {month} {day}, {year}": "", "Posts for {month} {year}": "", @@ -32,6 +34,8 @@ MESSAGES = { "Subcategories:": "", "Tags and Categories": "", "Tags": "", + "Uncategorized": "", + "Updates": "", "Write your page here.": "", "Write your post here.": "", "old posts, page %d": "", diff --git a/nikola/data/themes/base/messages/messages_fr.py b/nikola/data/themes/base/messages/messages_fr.py index 327b3f7..1c2025c 100644 --- a/nikola/data/themes/base/messages/messages_fr.py +++ b/nikola/data/themes/base/messages/messages_fr.py @@ -4,12 +4,13 @@ from __future__ import unicode_literals MESSAGES = { "%d min remaining to read": "Il reste encore %d min. de lecture", "(active)": "(actif)", - "Also available in:": "Également disponible en:", + "Also available in:": "Également disponible en :", "Archive": "Archives", + "Authors": "Auteurs", "Categories": "Catégories", "Comments": "Commentaires", "LANGUAGE": "Français", - "Languages:": "Langues:", + "Languages:": "Langues :", "More posts about %s": "Plus d'articles sur %s", "Newer posts": "Articles récents", "Next post": "Article suivant", @@ -17,8 +18,9 @@ MESSAGES = { "Nothing found.": "Pas de résultats.", "Older posts": "Anciens articles", "Original site": "Site d'origine", - "Posted:": "Publié:", + "Posted:": "Publié :", "Posts about %s": "Articles sur %s", + "Posts by %s": "Publiés par %s", "Posts for year %s": "Articles de l'année %s", "Posts for {month} {day}, {year}": "Articles du {day} {month} {year}", "Posts for {month} {year}": "Articles de {month} {year}", @@ -32,8 +34,10 @@ MESSAGES = { "Subcategories:": "Sous-catégories", "Tags and Categories": "Étiquettes et catégories", "Tags": "Étiquettes", - "Write your page here.": "", - "Write your post here.": "", + "Uncategorized": "Sans catégorie", + "Updates": "Mises à jour", + "Write your page here.": "Écrivez votre page ici.", + "Write your post here.": "Écrivez votre billet ici.", "old posts, page %d": "anciens articles, page %d", "page %d": "page %d", } diff --git a/nikola/data/themes/base/messages/messages_gl.py b/nikola/data/themes/base/messages/messages_gl.py index fd26d77..f661c0e 100644 --- a/nikola/data/themes/base/messages/messages_gl.py +++ b/nikola/data/themes/base/messages/messages_gl.py @@ -6,6 +6,7 @@ MESSAGES = { "(active)": "", "Also available in:": "", "Archive": "", + "Authors": "", "Categories": "", "Comments": "", "LANGUAGE": "", @@ -19,6 +20,7 @@ MESSAGES = { "Original site": "", "Posted:": "", "Posts about %s": "", + "Posts by %s": "", "Posts for year %s": "", "Posts for {month} {day}, {year}": "", "Posts for {month} {year}": "", @@ -32,6 +34,8 @@ MESSAGES = { "Subcategories:": "", "Tags and Categories": "", "Tags": "", + "Uncategorized": "", + "Updates": "", "Write your page here.": "", "Write your post here.": "", "old posts, page %d": "", diff --git a/nikola/data/themes/base/messages/messages_hi.py b/nikola/data/themes/base/messages/messages_hi.py index 7804b60..afbd8d5 100644 --- a/nikola/data/themes/base/messages/messages_hi.py +++ b/nikola/data/themes/base/messages/messages_hi.py @@ -6,6 +6,7 @@ MESSAGES = { "(active)": "", "Also available in:": "उपलब्ध भाषाएँ:", "Archive": "आर्काइव", + "Authors": "", "Categories": "श्रेणियाँ", "Comments": "टिप्पणियाँ", "LANGUAGE": "हिन्दी", @@ -19,6 +20,7 @@ MESSAGES = { "Original site": "असली साइट", "Posted:": "पोस्टेड:", "Posts about %s": "%s के बारे में पोस्टें", + "Posts by %s": "", "Posts for year %s": "साल %s की पोस्टें", "Posts for {month} {day}, {year}": "{day} {month} {year} की पोस्टें", "Posts for {month} {year}": "{month} {year} की पोस्टें", @@ -32,6 +34,8 @@ MESSAGES = { "Subcategories:": "", "Tags and Categories": "टैग्स और श्रेणियाँ", "Tags": "टैग्स", + "Uncategorized": "", + "Updates": "", "Write your page here.": "", "Write your post here.": "", "old posts, page %d": "पुरानी पोस्टें, पृष्ठ %d", diff --git a/nikola/data/themes/base/messages/messages_hr.py b/nikola/data/themes/base/messages/messages_hr.py index 11e07d8..79bd050 100644 --- a/nikola/data/themes/base/messages/messages_hr.py +++ b/nikola/data/themes/base/messages/messages_hr.py @@ -6,6 +6,7 @@ MESSAGES = { "(active)": "(aktivno)", "Also available in:": "Također dostupno i u:", "Archive": "Arhiva", + "Authors": "", "Categories": "Kategorije", "Comments": "Komentari", "LANGUAGE": "hrvatski", @@ -19,6 +20,7 @@ MESSAGES = { "Original site": "Izvorna stranica", "Posted:": "Objavljeno:", "Posts about %s": "Postovi o %s", + "Posts by %s": "", "Posts for year %s": "Postovi za godinu %s", "Posts for {month} {day}, {year}": "", "Posts for {month} {year}": "Postovi za {month} {year}", @@ -32,6 +34,8 @@ MESSAGES = { "Subcategories:": "Podkategorije:", "Tags and Categories": "Tagovi i kategorije", "Tags": "Tagovi", + "Uncategorized": "", + "Updates": "", "Write your page here.": "", "Write your post here.": "", "old posts, page %d": "stari postovi, stranice %d", diff --git a/nikola/data/themes/base/messages/messages_hu.py b/nikola/data/themes/base/messages/messages_hu.py new file mode 100644 index 0000000..e1dc354 --- /dev/null +++ b/nikola/data/themes/base/messages/messages_hu.py @@ -0,0 +1,43 @@ +# -*- encoding:utf-8 -*- +from __future__ import unicode_literals + +MESSAGES = { + "%d min remaining to read": "%d perc van hátra olvasni", + "(active)": "(aktív)", + "Also available in:": "Olvasható még:", + "Archive": "Archív", + "Authors": "Szerzők", + "Categories": "Kategóriák", + "Comments": "Hozzászólások", + "LANGUAGE": "Magyarul", + "Languages:": "Nyelvek:", + "More posts about %s": "Több bejegyzés erről: %s", + "Newer posts": "Újabb bejegyzések", + "Next post": "A következő bejegyzés", + "No posts found.": "Nincs ilyen bejegyzés.", + "Nothing found.": "Nincs találat.", + "Older posts": "Régebbi bejegyzések", + "Original site": "Eredeti honlap", + "Posted:": "Megjelent:", + "Posts about %s": "Bejegyzések erről: %s", + "Posts by %s": "Bejegyzések %s által", + "Posts for year %s": "%s. bejegyzések", + "Posts for {month} {day}, {year}": "{year}. {month}. {day}.-i bejegyzések", + "Posts for {month} {year}": "{year}. {month}.-i bejegyzések", + "Previous post": "Az előző bejegyzés ", + "Publication date": "A megjelenés dátuma", + "RSS feed": "RSS", + "Read in English": "Olvass magyarul", + "Read more": "Olvass tovább", + "Skip to main content": "Ugrás a főtartalomra", + "Source": "Forrás", + "Subcategories:": "Alkategóriák:", + "Tags and Categories": "Címkék és kategóriák", + "Tags": "Címkék", + "Uncategorized": "Nincs kategorizálva", + "Updates": "Frissítések", + "Write your page here.": "Ide írd az oldalad.", + "Write your post here.": "Ide írd a bejegyzésed.", + "old posts, page %d": "régi bejegyzések, %d. oldal", + "page %d": "%d. oldal", +} diff --git a/nikola/data/themes/base/messages/messages_id.py b/nikola/data/themes/base/messages/messages_id.py index 475299f..3682b5b 100644 --- a/nikola/data/themes/base/messages/messages_id.py +++ b/nikola/data/themes/base/messages/messages_id.py @@ -6,6 +6,7 @@ MESSAGES = { "(active)": "(aktif)", "Also available in:": "Juga tersedia dalam:", "Archive": "Arsip", + "Authors": "", "Categories": "Kategori", "Comments": "Komentar", "LANGUAGE": "Inggris", @@ -19,6 +20,7 @@ MESSAGES = { "Original site": "Situs orisinal", "Posted:": "Ditulis oleh:", "Posts about %s": "Tulisan tentang %s", + "Posts by %s": "", "Posts for year %s": "Tulisan untuk tahun %s", "Posts for {month} {day}, {year}": "Tulisan untuk {month} {day}, {year}", "Posts for {month} {year}": "Tulisan untuk {month} {year}", @@ -32,6 +34,8 @@ MESSAGES = { "Subcategories:": "Sub kategori:", "Tags and Categories": "Tag dan Kategori", "Tags": "Tag", + "Uncategorized": "", + "Updates": "", "Write your page here.": "", "Write your post here.": "", "old posts, page %d": "tulisan lama, halaman %d", diff --git a/nikola/data/themes/base/messages/messages_it.py b/nikola/data/themes/base/messages/messages_it.py index 9f6d8be..65481e0 100644 --- a/nikola/data/themes/base/messages/messages_it.py +++ b/nikola/data/themes/base/messages/messages_it.py @@ -6,6 +6,7 @@ MESSAGES = { "(active)": "(attivo)", "Also available in:": "Disponibile anche in:", "Archive": "Archivio", + "Authors": "Autori", "Categories": "Categorie", "Comments": "Commenti", "LANGUAGE": "Italiano", @@ -19,6 +20,7 @@ MESSAGES = { "Original site": "Sito originale", "Posted:": "Pubblicato:", "Posts about %s": "Articoli su %s", + "Posts by %s": "Articoli di %s", "Posts for year %s": "Articoli per l'anno %s", "Posts for {month} {day}, {year}": "Articoli per il {day} {month} {year}", "Posts for {month} {year}": "Articoli per {month} {year}", @@ -32,6 +34,8 @@ MESSAGES = { "Subcategories:": "Sottocategorie:", "Tags and Categories": "Tag e categorie", "Tags": "Tag", + "Uncategorized": "Senza categorie", + "Updates": "Aggiornamenti", "Write your page here.": "Scrivi qui la tua pagina.", "Write your post here.": "Scrivi qui il tuo post.", "old posts, page %d": "vecchi articoli, pagina %d", diff --git a/nikola/data/themes/base/messages/messages_ja.py b/nikola/data/themes/base/messages/messages_ja.py index 972794d..c94c79c 100644 --- a/nikola/data/themes/base/messages/messages_ja.py +++ b/nikola/data/themes/base/messages/messages_ja.py @@ -2,38 +2,42 @@ from __future__ import unicode_literals MESSAGES = { - "%d min remaining to read": "読込むまで残り %d 分", - "(active)": "", - "Also available in:": "他の言語で読む:", - "Archive": "過去の記事", - "Categories": "カテゴリー", + "%d min remaining to read": "残りを読むのに必要な時間は%d分", + "(active)": "(有効)", + "Also available in:": "他の言語で読む:", + "Archive": "文書一覧", + "Authors": "著者一覧", + "Categories": "カテゴリ", "Comments": "コメント", "LANGUAGE": "日本語", - "Languages:": "言語 :", - "More posts about %s": "タグ: %s", - "Newer posts": "新しい記事", - "Next post": "次の記事", - "No posts found.": "記事はありません", - "Nothing found.": "なにも見つかりませんでした", - "Older posts": "過去の記事", - "Original site": "元のサイト", - "Posted:": "投稿日時:", - "Posts about %s": "%sについての記事", - "Posts for year %s": "%s年の記事", - "Posts for {month} {day}, {year}": "{year}年{month}月{day}日の記事", - "Posts for {month} {year}": "{year}年{month}月の記事", - "Previous post": "前の記事", - "Publication date": "投稿日", - "RSS feed": "RSS フィード", + "Languages:": "言語:", + "More posts about %s": "%sに関する文書一覧", + "Newer posts": "新しい文書", + "Next post": "次の文書", + "No posts found.": "文書はありません。", + "Nothing found.": "なにも見つかりませんでした。", + "Older posts": "過去の文書", + "Original site": "翻訳元のサイト", + "Posted:": "公開日時:", + "Posts about %s": "%sについての文書", + "Posts by %s": "%sの文書一覧", + "Posts for year %s": "%s年の文書", + "Posts for {month} {day}, {year}": "{year}年{month}{day}日の文書", + "Posts for {month} {year}": "{year}年{month}の文書", + "Previous post": "一つ前の文書", + "Publication date": "公開日", + "RSS feed": "RSSフィード", "Read in English": "日本語で読む", "Read more": "続きを読む", - "Skip to main content": "メインコンテンツをスキップ", + "Skip to main content": "本文を読み飛ばす", "Source": "ソース", - "Subcategories:": "", - "Tags and Categories": "タグとカテゴリー", + "Subcategories:": "サブカテゴリ", + "Tags and Categories": "カテゴリおよびタグ一覧", "Tags": "タグ", - "Write your page here.": "", - "Write your post here.": "", - "old posts, page %d": "前の記事 %dページ目", - "page %d": "ページ %d", + "Uncategorized": "uncategorized", + "Updates": "フィード", + "Write your page here.": "ここに文書を記述してください。", + "Write your post here.": "ここに文書を記述してください。", + "old posts, page %d": "過去の文書 %dページ目", + "page %d": "ページ%d", } diff --git a/nikola/data/themes/base/messages/messages_ko.py b/nikola/data/themes/base/messages/messages_ko.py index 6904492..7f06c28 100644 --- a/nikola/data/themes/base/messages/messages_ko.py +++ b/nikola/data/themes/base/messages/messages_ko.py @@ -6,6 +6,7 @@ MESSAGES = { "(active)": "", "Also available in:": "", "Archive": "저장소", + "Authors": "", "Categories": "분류", "Comments": "댓글", "LANGUAGE": "영어", @@ -19,6 +20,7 @@ MESSAGES = { "Original site": "출처", "Posted:": "", "Posts about %s": "%s에 대한 포스트", + "Posts by %s": "", "Posts for year %s": "%s년도 포스트", "Posts for {month} {day}, {year}": "", "Posts for {month} {year}": "{year}년 {month}월에 쓴 포스트", @@ -32,6 +34,8 @@ MESSAGES = { "Subcategories:": "", "Tags and Categories": "태그와 분류", "Tags": "태그", + "Uncategorized": "", + "Updates": "", "Write your page here.": "", "Write your post here.": "", "old posts, page %d": "이전 포스트, 페이지 %d", diff --git a/nikola/data/themes/base/messages/messages_nb.py b/nikola/data/themes/base/messages/messages_nb.py index 30b798e..fd98f88 100644 --- a/nikola/data/themes/base/messages/messages_nb.py +++ b/nikola/data/themes/base/messages/messages_nb.py @@ -6,6 +6,7 @@ MESSAGES = { "(active)": "(aktiv)", "Also available in:": "Også tilgjengelig på:", "Archive": "Arkiv", + "Authors": "", "Categories": "Kategorier", "Comments": "Kommentarer", "LANGUAGE": "norsk", @@ -19,6 +20,7 @@ MESSAGES = { "Original site": "Opprinnelig side", "Posted:": "Publisert:", "Posts about %s": "Innlegg om %s", + "Posts by %s": "", "Posts for year %s": "Innlegg fra %s", "Posts for {month} {day}, {year}": "Innlegg fra {day}. {month} {year}", "Posts for {month} {year}": "Innlegg fra {month} {year}", @@ -32,6 +34,8 @@ MESSAGES = { "Subcategories:": "Underkategorier:", "Tags and Categories": "Merker og kategorier", "Tags": "Merker", + "Uncategorized": "", + "Updates": "", "Write your page here.": "Skriv siden din her.", "Write your post here.": "Skriv innlegget din her.", "old posts, page %d": "eldre innlegg, side %d", diff --git a/nikola/data/themes/base/messages/messages_nl.py b/nikola/data/themes/base/messages/messages_nl.py index 0661ce1..42becd9 100644 --- a/nikola/data/themes/base/messages/messages_nl.py +++ b/nikola/data/themes/base/messages/messages_nl.py @@ -6,6 +6,7 @@ MESSAGES = { "(active)": "(actief)", "Also available in:": "Ook beschikbaar in:", "Archive": "Archief", + "Authors": "Auteurs", "Categories": "Categorieën", "Comments": "Commentaar", "LANGUAGE": "Nederlands", @@ -19,6 +20,7 @@ MESSAGES = { "Original site": "Originele site", "Posted:": "Geplaatst:", "Posts about %s": "Berichten over %s", + "Posts by %s": "Berichten van %s", "Posts for year %s": "Berichten voor het jaar %s", "Posts for {month} {day}, {year}": "Berichten voor {month} {day}, {year}", "Posts for {month} {year}": "Berichten voor {month} {year}", @@ -32,8 +34,10 @@ MESSAGES = { "Subcategories:": "Subcategorieën", "Tags and Categories": "Tags en Categorieën", "Tags": "Tags", - "Write your page here.": "", - "Write your post here.": "", + "Uncategorized": "Ongeordend", + "Updates": "Bijgewerkte versies", + "Write your page here.": "Schrijf hier je pagina.", + "Write your post here.": "Schrijf hier je bericht.", "old posts, page %d": "oude berichten, pagina %d", "page %d": "pagina %d", } diff --git a/nikola/data/themes/base/messages/messages_pa.py b/nikola/data/themes/base/messages/messages_pa.py index 392a38b..13996ac 100644 --- a/nikola/data/themes/base/messages/messages_pa.py +++ b/nikola/data/themes/base/messages/messages_pa.py @@ -6,6 +6,7 @@ MESSAGES = { "(active)": "(ਚਲੰਤ)", "Also available in:": "ਹੋਰ ਉਪਲਬਧ ਬੋਲੀਆਂ:", "Archive": "ਆਰਕਾਈਵ", + "Authors": "", "Categories": "ਸ਼੍ਰੇਣੀ", "Comments": "ਟਿੱਪਣੀਆਂ", "LANGUAGE": "ਅੰਗਰੇਜ਼ੀ", @@ -19,6 +20,7 @@ MESSAGES = { "Original site": "ਅਸਲ ਸਾਈਟ", "Posted:": "ਲਿਖਤ ਛਪੀ:", "Posts about %s": "%s ਬਾਰੇ ਲਿਖਤਾਂ", + "Posts by %s": "", "Posts for year %s": "ਸਾਲ %s ਦੀਆਂ ਲਿਖਤਾਂ", "Posts for {month} {day}, {year}": "{day} {month} {year} ਦੀਆਂ ਲਿਖਤਾਂ", "Posts for {month} {year}": "{month} {year} ਦੀਆਂ ਲਿਖਤਾਂ", @@ -32,6 +34,8 @@ MESSAGES = { "Subcategories:": "ਉਪਸ਼੍ਰੇਣੀਆਂ:", "Tags and Categories": "ਟੈਗ ਅਤੇ ਸ਼੍ਰੇਣੀਆਂ", "Tags": "ਟੈਗ", + "Uncategorized": "", + "Updates": "", "Write your page here.": "ਆਪਣਾ ਸਫ਼ਾ ਏਥੇ ਲਿਖੋ |", "Write your post here.": "ਆਪਣੀ ਲਿਖਤ ਏਥੇ ਲਿਖੋ |", "old posts, page %d": "ਪੁਰਾਣੀਆਂ ਲਿਖਤਾਂ , ਸਫ਼ਾ %d", diff --git a/nikola/data/themes/base/messages/messages_pl.py b/nikola/data/themes/base/messages/messages_pl.py index 89aae0f..3e24bfc 100644 --- a/nikola/data/themes/base/messages/messages_pl.py +++ b/nikola/data/themes/base/messages/messages_pl.py @@ -6,6 +6,7 @@ MESSAGES = { "(active)": "(aktywne)", "Also available in:": "Również dostępny w językach:", "Archive": "Archiwum", + "Authors": "Autorzy", "Categories": "Kategorie", "Comments": "Komentarze", "LANGUAGE": "Polski", @@ -19,6 +20,7 @@ MESSAGES = { "Original site": "Oryginalna strona", "Posted:": "Opublikowano:", "Posts about %s": "Posty o %s", + "Posts by %s": "Posty autora %s", "Posts for year %s": "Posty z roku %s", "Posts for {month} {day}, {year}": "Posty z {day} {month} {year}", "Posts for {month} {year}": "Posty z {month} {year}", @@ -32,6 +34,8 @@ MESSAGES = { "Subcategories:": "Podkategorie:", "Tags and Categories": "Tagi i Kategorie", "Tags": "Tagi", + "Uncategorized": "Nieskategoryzowane", + "Updates": "Aktualności", "Write your page here.": "Tu wpisz treść strony.", "Write your post here.": "Tu wpisz treść postu.", "old posts, page %d": "stare posty, strona %d", diff --git a/nikola/data/themes/base/messages/messages_pt.py b/nikola/data/themes/base/messages/messages_pt.py index 91ea52e..3c4bd55 100644 --- a/nikola/data/themes/base/messages/messages_pt.py +++ b/nikola/data/themes/base/messages/messages_pt.py @@ -6,6 +6,7 @@ MESSAGES = { "(active)": "(ativo)", "Also available in:": "Também disponível em:", "Archive": "Arquivo", + "Authors": "", "Categories": "Categorias", "Comments": "Comentários", "LANGUAGE": "Português", @@ -19,6 +20,7 @@ MESSAGES = { "Original site": "Sítio original", "Posted:": "Publicado:", "Posts about %s": "Textos publicados sobre %s", + "Posts by %s": "", "Posts for year %s": "Textos publicados do ano %s", "Posts for {month} {day}, {year}": "Textos publicados de {day} {month} {year}", "Posts for {month} {year}": "Textos publicados de {month} {year}", @@ -32,6 +34,8 @@ MESSAGES = { "Subcategories:": "Sub-Categorias:", "Tags and Categories": "Etiquetas e Categorias", "Tags": "Etiqueta", + "Uncategorized": "", + "Updates": "", "Write your page here.": "Escreva a sua página aqui.", "Write your post here.": "Escreva o seu texto para publicar aqui.", "old posts, page %d": "Textos publicados antigos, página %d", diff --git a/nikola/data/themes/base/messages/messages_pt_br.py b/nikola/data/themes/base/messages/messages_pt_br.py index 58ad63f..7dcc8a8 100644 --- a/nikola/data/themes/base/messages/messages_pt_br.py +++ b/nikola/data/themes/base/messages/messages_pt_br.py @@ -6,6 +6,7 @@ MESSAGES = { "(active)": "", "Also available in:": "Também disponível em:", "Archive": "Arquivo", + "Authors": "", "Categories": "Categorias", "Comments": "Comentários", "LANGUAGE": "Português", @@ -19,6 +20,7 @@ MESSAGES = { "Original site": "Site original", "Posted:": "Publicado:", "Posts about %s": "Posts sobre %s", + "Posts by %s": "", "Posts for year %s": "Posts do ano %s", "Posts for {month} {day}, {year}": "Posts do {day} {month}, {year}", "Posts for {month} {year}": "Posts de {month} {year}", @@ -32,6 +34,8 @@ MESSAGES = { "Subcategories:": "", "Tags and Categories": "Tags e Categorias", "Tags": "Tags", + "Uncategorized": "", + "Updates": "", "Write your page here.": "", "Write your post here.": "", "old posts, page %d": "Posts antigos, página %d", diff --git a/nikola/data/themes/base/messages/messages_ru.py b/nikola/data/themes/base/messages/messages_ru.py index cfbe536..bdf3873 100644 --- a/nikola/data/themes/base/messages/messages_ru.py +++ b/nikola/data/themes/base/messages/messages_ru.py @@ -3,9 +3,10 @@ from __future__ import unicode_literals MESSAGES = { "%d min remaining to read": "%d минут чтения осталось", - "(active)": "", + "(active)": "(активная)", "Also available in:": "Также доступно на:", "Archive": "Архив", + "Authors": "", "Categories": "Категории", "Comments": "Комментарии", "LANGUAGE": "Русский", @@ -19,8 +20,9 @@ MESSAGES = { "Original site": "Оригинальный сайт", "Posted:": "Опубликовано:", "Posts about %s": "Записи о %s", + "Posts by %s": "", "Posts for year %s": "Записи за %s год", - "Posts for {month} {day}, {year}": "", + "Posts for {month} {day}, {year}": "Записи за {day} {month} {year}", "Posts for {month} {year}": "Записи за {month} {year}", "Previous post": "Предыдущая запись", "Publication date": "Дата опубликования", @@ -29,11 +31,13 @@ MESSAGES = { "Read more": "Читать далее", "Skip to main content": "Перейти к главному содержимому", "Source": "Источник", - "Subcategories:": "", + "Subcategories:": "Подкатегории:", "Tags and Categories": "Тэги и категории", "Tags": "Тэги", - "Write your page here.": "", - "Write your post here.": "", + "Uncategorized": "", + "Updates": "", + "Write your page here.": "Создайте Вашу страницу здесь.", + "Write your post here.": "Создайте Вашу запись здесь.", "old posts, page %d": "%d страница со старыми записями", "page %d": "%d страница", } diff --git a/nikola/data/themes/base/messages/messages_si_lk.py b/nikola/data/themes/base/messages/messages_si_lk.py index fd26d77..f661c0e 100644 --- a/nikola/data/themes/base/messages/messages_si_lk.py +++ b/nikola/data/themes/base/messages/messages_si_lk.py @@ -6,6 +6,7 @@ MESSAGES = { "(active)": "", "Also available in:": "", "Archive": "", + "Authors": "", "Categories": "", "Comments": "", "LANGUAGE": "", @@ -19,6 +20,7 @@ MESSAGES = { "Original site": "", "Posted:": "", "Posts about %s": "", + "Posts by %s": "", "Posts for year %s": "", "Posts for {month} {day}, {year}": "", "Posts for {month} {year}": "", @@ -32,6 +34,8 @@ MESSAGES = { "Subcategories:": "", "Tags and Categories": "", "Tags": "", + "Uncategorized": "", + "Updates": "", "Write your page here.": "", "Write your post here.": "", "old posts, page %d": "", diff --git a/nikola/data/themes/base/messages/messages_sk.py b/nikola/data/themes/base/messages/messages_sk.py index acd7364..03cce43 100644 --- a/nikola/data/themes/base/messages/messages_sk.py +++ b/nikola/data/themes/base/messages/messages_sk.py @@ -6,6 +6,7 @@ MESSAGES = { "(active)": "(aktívne)", "Also available in:": "Tiež dostupné v:", "Archive": "Archív", + "Authors": "", "Categories": "Kategórie", "Comments": "Komentáre", "LANGUAGE": "Slovenčina", @@ -19,6 +20,7 @@ MESSAGES = { "Original site": "Pôvodná stránka", "Posted:": "Zverejnené:", "Posts about %s": "Príspevky o %s", + "Posts by %s": "", "Posts for year %s": "Príspevky z roku %s", "Posts for {month} {day}, {year}": "Príspevky zo dňa {day}. {month} {year}", "Posts for {month} {year}": "Príspevky za mesiac {month} z roku {year}", @@ -32,6 +34,8 @@ MESSAGES = { "Subcategories:": "Podkategórie:", "Tags and Categories": "Štítky a kategórie", "Tags": "Štítky", + "Uncategorized": "", + "Updates": "", "Write your page here.": "Tu napíšte svoju stránku.", "Write your post here.": "Tu napíšte svoj príspevok.", "old posts, page %d": "staré príspevky, strana %d", diff --git a/nikola/data/themes/base/messages/messages_sl.py b/nikola/data/themes/base/messages/messages_sl.py index 992787b..a531ca5 100644 --- a/nikola/data/themes/base/messages/messages_sl.py +++ b/nikola/data/themes/base/messages/messages_sl.py @@ -6,6 +6,7 @@ MESSAGES = { "(active)": "", "Also available in:": "Na voljo tudi v:", "Archive": "Arhiv", + "Authors": "", "Categories": "Kategorije", "Comments": "Komentarji", "LANGUAGE": "Slovenščina", @@ -19,6 +20,7 @@ MESSAGES = { "Original site": "Izvorna spletna stran", "Posted:": "Objavljeno:", "Posts about %s": "Objave o %s", + "Posts by %s": "", "Posts for year %s": "Objave za leto %s", "Posts for {month} {day}, {year}": "Objave za {day}. {month}, {year}", "Posts for {month} {year}": "Objave za {month} {year}", @@ -32,6 +34,8 @@ MESSAGES = { "Subcategories:": "", "Tags and Categories": "Značke in kategorije", "Tags": "Značke", + "Uncategorized": "", + "Updates": "", "Write your page here.": "", "Write your post here.": "", "old posts, page %d": "stare objave, stran %d", diff --git a/nikola/data/themes/base/messages/messages_sr.py b/nikola/data/themes/base/messages/messages_sr.py index 2b0aaf5..13137c3 100644 --- a/nikola/data/themes/base/messages/messages_sr.py +++ b/nikola/data/themes/base/messages/messages_sr.py @@ -6,6 +6,7 @@ MESSAGES = { "(active)": "(активно)", "Also available in:": "Такође доступан у:", "Archive": "Архива", + "Authors": "", "Categories": "Категорије", "Comments": "Коментари", "LANGUAGE": "Српски", @@ -19,6 +20,7 @@ MESSAGES = { "Original site": "Оригинал сајт", "Posted:": "Објављено:", "Posts about %s": "Постови о %s", + "Posts by %s": "", "Posts for year %s": "Постови за годину %s", "Posts for {month} {day}, {year}": "Објаве за {month} {day}, {year}", "Posts for {month} {year}": "Постови за {month} {year}", @@ -32,6 +34,8 @@ MESSAGES = { "Subcategories:": "Подкатегорије:", "Tags and Categories": "Тагови и категорије", "Tags": "Тагови", + "Uncategorized": "", + "Updates": "", "Write your page here.": "Вашу страницу напишите овдје.", "Write your post here.": "Вашу објаву напишите овдје.", "old posts, page %d": "стари постови, страна %d", diff --git a/nikola/data/themes/base/messages/messages_sr@latin.py b/nikola/data/themes/base/messages/messages_sr_latin.py index 23a1f64..db60c76 100644 --- a/nikola/data/themes/base/messages/messages_sr@latin.py +++ b/nikola/data/themes/base/messages/messages_sr_latin.py @@ -6,6 +6,7 @@ MESSAGES = { "(active)": "(aktivno)", "Also available in:": "Takođe dostupan u:", "Archive": "Arhiva", + "Authors": "", "Categories": "Kategorije", "Comments": "Komentari", "LANGUAGE": "Bosanski", @@ -19,6 +20,7 @@ MESSAGES = { "Original site": "Izvorni sajt", "Posted:": "Objavljeno:", "Posts about %s": "Objave o %s", + "Posts by %s": "", "Posts for year %s": "Objave u godini %s", "Posts for {month} {day}, {year}": "Objave za {month} {day}, {year}", "Posts for {month} {year}": "Objave za {month} {year}", @@ -32,6 +34,8 @@ MESSAGES = { "Subcategories:": "Podkategorije:", "Tags and Categories": "Oznake i kategorije", "Tags": "Oznake", + "Uncategorized": "", + "Updates": "", "Write your page here.": "Vašu stranicu napišite ovdje.", "Write your post here.": "Vašu objavu napišite ovdje.", "old posts, page %d": "stare objave, strana %d", diff --git a/nikola/data/themes/base/messages/messages_sv.py b/nikola/data/themes/base/messages/messages_sv.py index 52df00d..d1357e7 100644 --- a/nikola/data/themes/base/messages/messages_sv.py +++ b/nikola/data/themes/base/messages/messages_sv.py @@ -6,6 +6,7 @@ MESSAGES = { "(active)": "(aktiv)", "Also available in:": "Även tillgänglig på:", "Archive": "Arkiv", + "Authors": "", "Categories": "Kategorier", "Comments": "Kommentarer", "LANGUAGE": "Svenska", @@ -19,6 +20,7 @@ MESSAGES = { "Original site": "Originalsida", "Posted:": "Publicerad:", "Posts about %s": "Inlägg om %s", + "Posts by %s": "", "Posts for year %s": "Inlägg för år %s", "Posts for {month} {day}, {year}": "Inlägg för {month} {day}, {year}", "Posts for {month} {year}": "Inlägg för {month} {year}", @@ -32,6 +34,8 @@ MESSAGES = { "Subcategories:": "Underkategorier:", "Tags and Categories": "Taggar och Kategorier", "Tags": "Taggar", + "Uncategorized": "", + "Updates": "", "Write your page here.": "Skriv din sida här.", "Write your post here.": "Skriv ditt inlägg här.", "old posts, page %d": "gamla inlägg, sida %d", diff --git a/nikola/data/themes/base/messages/messages_tl.py b/nikola/data/themes/base/messages/messages_tl.py index f748edd..303ea36 100644 --- a/nikola/data/themes/base/messages/messages_tl.py +++ b/nikola/data/themes/base/messages/messages_tl.py @@ -6,6 +6,7 @@ MESSAGES = { "(active)": "", "Also available in:": "", "Archive": "", + "Authors": "", "Categories": "", "Comments": "", "LANGUAGE": "Ingles", @@ -19,6 +20,7 @@ MESSAGES = { "Original site": "", "Posted:": "", "Posts about %s": "", + "Posts by %s": "", "Posts for year %s": "", "Posts for {month} {day}, {year}": "", "Posts for {month} {year}": "", @@ -32,6 +34,8 @@ MESSAGES = { "Subcategories:": "", "Tags and Categories": "", "Tags": "Mga Tag", + "Uncategorized": "", + "Updates": "", "Write your page here.": "", "Write your post here.": "", "old posts, page %d": "", diff --git a/nikola/data/themes/base/messages/messages_tr.py b/nikola/data/themes/base/messages/messages_tr.py index 21eb534..e173a40 100644 --- a/nikola/data/themes/base/messages/messages_tr.py +++ b/nikola/data/themes/base/messages/messages_tr.py @@ -6,6 +6,7 @@ MESSAGES = { "(active)": "", "Also available in:": "Şu dilde de mevcut:", "Archive": "Arşiv", + "Authors": "", "Categories": "Kategoriler", "Comments": "Yorumlar", "LANGUAGE": "Türkçe", @@ -19,6 +20,7 @@ MESSAGES = { "Original site": "Orjinal web sayfası", "Posted:": "Yayın tarihi:", "Posts about %s": "%s ile ilgili yazılar", + "Posts by %s": "", "Posts for year %s": "%s yılındaki yazılar", "Posts for {month} {day}, {year}": "{month} {day}, {year} 'den beri olan yazılar", "Posts for {month} {year}": "{month} {year} göre yazılar", @@ -32,6 +34,8 @@ MESSAGES = { "Subcategories:": "", "Tags and Categories": "Etiketler ve Kategoriler", "Tags": "Etiketler", + "Uncategorized": "", + "Updates": "", "Write your page here.": "", "Write your post here.": "", "old posts, page %d": "eski yazılar, sayfa %d", diff --git a/nikola/data/themes/base/messages/messages_uk.py b/nikola/data/themes/base/messages/messages_uk.py index 16913da..4f19568 100644 --- a/nikola/data/themes/base/messages/messages_uk.py +++ b/nikola/data/themes/base/messages/messages_uk.py @@ -3,15 +3,16 @@ from __future__ import unicode_literals MESSAGES = { "%d min remaining to read": "Залишилось читати %d хвилин", - "(active)": "", + "(active)": "(активне)", "Also available in:": "Іншою мовою:", "Archive": "Архів", + "Authors": "", "Categories": "Категорії", "Comments": "Коментарі", "LANGUAGE": "Українська", "Languages:": "Мови:", "More posts about %s": "Більше статей про %s", - "Newer posts": "Нові статті", + "Newer posts": "Новіші статті", "Next post": "Наступна стаття", "No posts found.": "Не знайдено жодної статті", "Nothing found.": "Нічого не знайдено", @@ -19,6 +20,7 @@ MESSAGES = { "Original site": "Оригінал сайту", "Posted:": "Опублікована:", "Posts about %s": "Статті про %s", + "Posts by %s": "", "Posts for year %s": "Статті за %s рік", "Posts for {month} {day}, {year}": "Статті за {month} {day}, {year}", "Posts for {month} {year}": "Статті за {month} {year}", @@ -29,11 +31,13 @@ MESSAGES = { "Read more": "Читати далі", "Skip to main content": "Перейти до основного матеріалу", "Source": "Джерело", - "Subcategories:": "", + "Subcategories:": "Підкатегорії:", "Tags and Categories": "Теги і категорії", "Tags": "Теги", - "Write your page here.": "", - "Write your post here.": "", + "Uncategorized": "", + "Updates": "", + "Write your page here.": "Напишіть Вашу сторінку тут.", + "Write your post here.": "Напишить Вашу статтю тут.", "old posts, page %d": "старі статті, сторінка %d", "page %d": "сторінка %d", } diff --git a/nikola/data/themes/base/messages/messages_ur.py b/nikola/data/themes/base/messages/messages_ur.py index 074cb06..92cdccd 100644 --- a/nikola/data/themes/base/messages/messages_ur.py +++ b/nikola/data/themes/base/messages/messages_ur.py @@ -4,8 +4,9 @@ from __future__ import unicode_literals MESSAGES = { "%d min remaining to read": "%d منٹ کا مطالعہ باقی", "(active)": "(فعال)", - "Also available in:": "ان زبانوں میں بھی دستیاب:", + "Also available in:": "ان میں بھی دستیاب:", "Archive": "آرکائیو", + "Authors": "مصنفین", "Categories": "زمرے", "Comments": "تبصرے", "LANGUAGE": "اردو", @@ -19,6 +20,7 @@ MESSAGES = { "Original site": "اصلی سائٹ", "Posted:": "اشاعت:", "Posts about %s": "%s کے بارے میں تحاریر", + "Posts by %s": "%s کی تحاریر", "Posts for year %s": "سال %s کی تحاریر", "Posts for {month} {day}, {year}": "{day} {month}، {year} کی تحاریر", "Posts for {month} {year}": "{month} {year} کی تحاریر", @@ -32,6 +34,8 @@ MESSAGES = { "Subcategories:": "ذیلی زمرے", "Tags and Categories": "ٹیگز اور زمرے", "Tags": "ٹیگز", + "Uncategorized": "", + "Updates": "تازہ ترین", "Write your page here.": "اپنے صفحے کا متن یہاں لکھیں۔", "Write your post here.": "اپنی تحریر یہاں لکھیں۔", "old posts, page %d": "پرانی تحاریر صفحہ %d", diff --git a/nikola/data/themes/base/messages/messages_zh_cn.py b/nikola/data/themes/base/messages/messages_zh_cn.py index c21b4f0..888b50e 100644 --- a/nikola/data/themes/base/messages/messages_zh_cn.py +++ b/nikola/data/themes/base/messages/messages_zh_cn.py @@ -6,6 +6,7 @@ MESSAGES = { "(active)": "", "Also available in:": "其他语言版本:", "Archive": "文章存档", + "Authors": "", "Categories": "分类", "Comments": "", "LANGUAGE": "简体中文", @@ -19,6 +20,7 @@ MESSAGES = { "Original site": "原文地址", "Posted:": "发表于:", "Posts about %s": "文章分类:%s", + "Posts by %s": "", "Posts for year %s": "%s年文章", "Posts for {month} {day}, {year}": "", "Posts for {month} {year}": "{year}年{month}月文章", @@ -32,6 +34,8 @@ MESSAGES = { "Subcategories:": "", "Tags and Categories": "标签和分类", "Tags": "标签", + "Uncategorized": "", + "Updates": "", "Write your page here.": "", "Write your post here.": "", "old posts, page %d": "旧文章页 %d", diff --git a/nikola/data/themes/base/messages/messages_zh_tw.py b/nikola/data/themes/base/messages/messages_zh_tw.py index fd26d77..f661c0e 100644 --- a/nikola/data/themes/base/messages/messages_zh_tw.py +++ b/nikola/data/themes/base/messages/messages_zh_tw.py @@ -6,6 +6,7 @@ MESSAGES = { "(active)": "", "Also available in:": "", "Archive": "", + "Authors": "", "Categories": "", "Comments": "", "LANGUAGE": "", @@ -19,6 +20,7 @@ MESSAGES = { "Original site": "", "Posted:": "", "Posts about %s": "", + "Posts by %s": "", "Posts for year %s": "", "Posts for {month} {day}, {year}": "", "Posts for {month} {year}": "", @@ -32,6 +34,8 @@ MESSAGES = { "Subcategories:": "", "Tags and Categories": "", "Tags": "", + "Uncategorized": "", + "Updates": "", "Write your page here.": "", "Write your post here.": "", "old posts, page %d": "", diff --git a/nikola/data/themes/base/templates/author.tmpl b/nikola/data/themes/base/templates/author.tmpl new file mode 100644 index 0000000..3ad5140 --- /dev/null +++ b/nikola/data/themes/base/templates/author.tmpl @@ -0,0 +1,43 @@ +## -*- coding: utf-8 -*- +<%inherit file="list_post.tmpl"/> + +<%block name="extra_head"> + ${parent.extra_head()} + %if len(translations) > 1 and generate_rss: + %for language in sorted(translations): + <link rel="alternate" type="application/rss+xml" title="RSS for ${kind} ${author|h} (${language})" href="${_link(kind + "_rss", author, language)}"> + %endfor + %elif generate_rss: + <link rel="alternate" type="application/rss+xml" title="RSS for ${kind} ${author|h}" href="${_link(kind + "_rss", author)}"> + %endif +</%block> + + +<%block name="content"> +<article class="authorpage"> + <header> + <h1>${title|h}</h1> + %if description: + <p>${description}</p> + %endif + <div class="metadata"> + %if len(translations) > 1 and generate_rss: + %for language in sorted(translations): + <p class="feedlink"> + <a href="${_link(kind + "_rss", author, language)}" hreflang="${language}" type="application/rss+xml">${messages('RSS feed', language)} (${language})</a> + </p> + %endfor + %elif generate_rss: + <p class="feedlink"><a href="${_link(kind + "_rss", author)}" type="application/rss+xml">${messages('RSS feed')}</a></p> + %endif + </div> + </header> + %if posts: + <ul class="postlist"> + % for post in posts: + <li><a href="${post.permalink()}" class="listtitle">${post.title()|h}</a><time class="listdate" datetime="${post.formatted_date('webiso')}" title="${post.formatted_date(date_format)|h}">${post.formatted_date(date_format)|h}</time></li> + % endfor + </ul> + %endif +</article> +</%block> diff --git a/nikola/data/themes/base/templates/authorindex.tmpl b/nikola/data/themes/base/templates/authorindex.tmpl new file mode 100644 index 0000000..34cb20b --- /dev/null +++ b/nikola/data/themes/base/templates/authorindex.tmpl @@ -0,0 +1,13 @@ +## -*- coding: utf-8 -*- +<%inherit file="index.tmpl"/> + +<%block name="extra_head"> + ${parent.extra_head()} + %if len(tranlations) > 1 and generate_atom: + %for language in sorted(translations): + <link rel="alternate" type="application/atom+xml" title="Atom for the ${author|h} section (${language})" href="${_link(kind + "_atom", author, language)}"> + %endfor + %elif generate_atom: + <link rel="alternate" type="application/atom+xml" title="Atom for the ${author|h} section" href="${_link("author" + "_atom", author)}"> + %endif +</%block> diff --git a/nikola/data/themes/base/templates/authors.tmpl b/nikola/data/themes/base/templates/authors.tmpl new file mode 100644 index 0000000..141c560 --- /dev/null +++ b/nikola/data/themes/base/templates/authors.tmpl @@ -0,0 +1,17 @@ +## -*- coding: utf-8 -*- +<%inherit file="base.tmpl"/> + +<%block name="content"> +<article class="authorindex"> + %if items: + <h2>${messages("Authors")}</h2> + <ul class="postlist"> + % for text, link in items: + % if text not in hidden_authors: + <li><a class="reference listtitle" href="${link}">${text|h}</a></li> + % endif + % endfor + </ul> + %endif +</article> +</%block> diff --git a/nikola/data/themes/base/templates/base_header.tmpl b/nikola/data/themes/base/templates/base_header.tmpl index e29e2b3..2ffcfee 100644 --- a/nikola/data/themes/base/templates/base_header.tmpl +++ b/nikola/data/themes/base/templates/base_header.tmpl @@ -16,13 +16,13 @@ </%def> <%def name="html_site_title()"> - <h1 id="brand"><a href="${abs_link(_link("root", None, lang))}" title="${blog_title}" rel="home"> + <h1 id="brand"><a href="${abs_link(_link("root", None, lang))}" title="${blog_title|h}" rel="home"> %if logo_url: - <img src="${logo_url}" alt="${blog_title}" id="logo"> + <img src="${logo_url}" alt="${blog_title|h}" id="logo"> %endif % if show_blog_title: - <span id="blog-title">${blog_title}</span> + <span id="blog-title">${blog_title|h}</span> % endif </a></h1> </%def> diff --git a/nikola/data/themes/base/templates/base_helper.tmpl b/nikola/data/themes/base/templates/base_helper.tmpl index 948cfba..35ed983 100644 --- a/nikola/data/themes/base/templates/base_helper.tmpl +++ b/nikola/data/themes/base/templates/base_helper.tmpl @@ -21,9 +21,11 @@ dir="rtl" \ lang="${lang}"> <head> <meta charset="utf-8"> + % if use_base_tag: <base href="${abs_link(permalink)}"> + % endif %if description: - <meta name="description" content="${description}"> + <meta name="description" content="${description|h}"> %endif <meta name="viewport" content="width=device-width"> %if title == blog_title: @@ -33,6 +35,7 @@ lang="${lang}"> %endif ${html_stylesheets()} + <meta content="${theme_color}" name="theme-color"> ${html_feedlinks()} <link rel="canonical" href="${abs_link(permalink)}"> diff --git a/nikola/data/themes/base/templates/gallery.tmpl b/nikola/data/themes/base/templates/gallery.tmpl index 0ee07ac..126d51c 100644 --- a/nikola/data/themes/base/templates/gallery.tmpl +++ b/nikola/data/themes/base/templates/gallery.tmpl @@ -17,8 +17,8 @@ %if folders: <ul> % for folder, ftitle in folders: - <li><a href="${folder}"><i - class="icon-folder-open"></i> ${ftitle}</a></li> + <li><a href="${folder|u}"><i + class="icon-folder-open"></i> ${ftitle|h}</a></li> % endfor </ul> %endif @@ -26,7 +26,7 @@ <ul class="thumbnails"> %for image in photo_array: <li><a href="${image['url']}" class="thumbnail image-reference" title="${image['title']}"> - <img src="${image['url_thumb']}" alt="${image['title']}" /></a> + <img src="${image['url_thumb']}" alt="${image['title']|h}" /></a> %endfor </ul> %endif diff --git a/nikola/data/themes/base/templates/index.tmpl b/nikola/data/themes/base/templates/index.tmpl index 88bb25c..294bdaf 100644 --- a/nikola/data/themes/base/templates/index.tmpl +++ b/nikola/data/themes/base/templates/index.tmpl @@ -18,8 +18,14 @@ <header> <h1 class="p-name entry-title"><a href="${post.permalink()}" class="u-url">${post.title()|h}</a></h1> <div class="metadata"> - <p class="byline author vcard"><span class="byline-name fn">${post.author()}</span></p> - <p class="dateline"><a href="${post.permalink()}" rel="bookmark"><time class="published dt-published" datetime="${post.formatted_date('webiso')}" title="${post.formatted_date(date_format)}">${post.formatted_date(date_format)}</time></a></p> + <p class="byline author vcard"><span class="byline-name fn"> + % if author_pages_generated: + <a href="${_link('author', post.author())}">${post.author()|h}</a> + % else: + ${post.author()|h} + % endif + </span></p> + <p class="dateline"><a href="${post.permalink()}" rel="bookmark"><time class="published dt-published" datetime="${post.formatted_date('webiso')}" title="${post.formatted_date(date_format)|h}">${post.formatted_date(date_format)|h}</time></a></p> % if not post.meta('nocomments') and site_has_comments: <p class="commentline">${comments.comment_link(post.permalink(), post._base_path)} % endif diff --git a/nikola/data/themes/base/templates/index_helper.tmpl b/nikola/data/themes/base/templates/index_helper.tmpl index 9331b93..fae046d 100644 --- a/nikola/data/themes/base/templates/index_helper.tmpl +++ b/nikola/data/themes/base/templates/index_helper.tmpl @@ -20,8 +20,17 @@ <%def name="mathjax_script(posts)"> %if any(post.is_mathjax for post in posts): - <script type="text/x-mathjax-config"> - MathJax.Hub.Config({tex2jax: {inlineMath: [['$latex ','$'], ['\\(','\\)']]}});</script> - <script src="/assets/js/mathjax.js"></script> + %if use_katex: + <script src="//cdnjs.cloudflare.com/ajax/libs/KaTeX/0.3.0/katex.min.js"></script> + <script src="//cdnjs.cloudflare.com/ajax/libs/KaTeX/0.3.0/contrib/auto-render.min.js"></script> + <script> + renderMathInElement(document.body); + </script> + %else: + <script type="text/javascript" src="//cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"> </script> + <script type="text/x-mathjax-config"> + MathJax.Hub.Config({tex2jax: {inlineMath: [['$latex ','$'], ['\\(','\\)']]}}); + </script> + %endif %endif </%def> diff --git a/nikola/data/themes/base/templates/list.tmpl b/nikola/data/themes/base/templates/list.tmpl index 4082516..5a8843d 100644 --- a/nikola/data/themes/base/templates/list.tmpl +++ b/nikola/data/themes/base/templates/list.tmpl @@ -4,12 +4,15 @@ <%block name="content"> <article class="listpage"> <header> - <h1>${title}</h1> + <h1>${title|h}</h1> </header> %if items: <ul class="postlist"> - % for text, link in items: - <li><a href="${link}">${text}</a> + % for text, link, count in items: + <li><a href="${link}">${text|h}</a> + % if count: + (${count}) + % endif % endfor </ul> %else: diff --git a/nikola/data/themes/base/templates/list_post.tmpl b/nikola/data/themes/base/templates/list_post.tmpl index 3335fa6..5fd4df1 100644 --- a/nikola/data/themes/base/templates/list_post.tmpl +++ b/nikola/data/themes/base/templates/list_post.tmpl @@ -4,12 +4,12 @@ <%block name="content"> <article class="listpage"> <header> - <h1>${title}</h1> + <h1>${title|h}</h1> </header> %if posts: <ul class="postlist"> % for post in posts: - <li><a href="${post.permalink()}" class="listtitle">${post.title()|h}</a> <time class="listdate" datetime="${post.formatted_date('webiso')}" title="${post.formatted_date(date_format)}">${post.formatted_date(date_format)}</time></li> + <li><a href="${post.permalink()}" class="listtitle">${post.title()|h}</a> <time class="listdate" datetime="${post.formatted_date('webiso')}" title="${post.formatted_date(date_format)|h}">${post.formatted_date(date_format)|h}</time></li> % endfor </ul> %else: diff --git a/nikola/data/themes/base/templates/listing.tmpl b/nikola/data/themes/base/templates/listing.tmpl index e0bf91b..1800bd9 100644 --- a/nikola/data/themes/base/templates/listing.tmpl +++ b/nikola/data/themes/base/templates/listing.tmpl @@ -6,10 +6,10 @@ ${ui.bar(crumbs)} %if folders or files: <ul> % for name in folders: - <li><a href="${name}"><i class="icon-folder-open"></i> ${name}</a> + <li><a href="${name|u}"><i class="icon-folder-open"></i> ${name|h}</a> % endfor % for name in files: - <li><a href="${name}.html"><i class="icon-file"></i> ${name}</a> + <li><a href="${name|u}.html"><i class="icon-file"></i> ${name|h}</a> % endfor </ul> %endif diff --git a/nikola/data/themes/base/templates/post.tmpl b/nikola/data/themes/base/templates/post.tmpl index cbb81ef..da616bf 100644 --- a/nikola/data/themes/base/templates/post.tmpl +++ b/nikola/data/themes/base/templates/post.tmpl @@ -10,9 +10,9 @@ <meta name="keywords" content="${post.meta('keywords')|h}"> % endif %if post.description(): - <meta name="description" content="${post.description()}"> + <meta name="description" content="${post.description()|h}"> %endif - <meta name="author" content="${post.author()}"> + <meta name="author" content="${post.author()|h}"> %if post.prev_post: <link rel="prev" href="${post.prev_post.permalink()}" title="${post.prev_post.title()|h}" type="text/html"> %endif diff --git a/nikola/data/themes/base/templates/post_header.tmpl b/nikola/data/themes/base/templates/post_header.tmpl index 75383cb..480c36a 100644 --- a/nikola/data/themes/base/templates/post_header.tmpl +++ b/nikola/data/themes/base/templates/post_header.tmpl @@ -31,17 +31,23 @@ <header> ${html_title()} <div class="metadata"> - <p class="byline author vcard"><span class="byline-name fn">${post.author()}</span></p> - <p class="dateline"><a href="${post.permalink()}" rel="bookmark"><time class="published dt-published" datetime="${post.formatted_date('webiso')}" itemprop="datePublished" title="${post.formatted_date(date_format)}">${post.formatted_date(date_format)}</time></a></p> + <p class="byline author vcard"><span class="byline-name fn"> + % if author_pages_generated: + <a href="${_link('author', post.author())}">${post.author()|h}</a> + % else: + ${post.author()|h} + % endif + </span></p> + <p class="dateline"><a href="${post.permalink()}" rel="bookmark"><time class="published dt-published" datetime="${post.formatted_date('webiso')}" itemprop="datePublished" title="${post.formatted_date(date_format)|h}">${post.formatted_date(date_format)|h}</time></a></p> % if not post.meta('nocomments') and site_has_comments: <p class="commentline">${comments.comment_link(post.permalink(), post._base_path)} % endif ${html_sourcelink()} % if post.meta('link'): - <p class="linkline"><a href='${post.meta('link')}'>${messages("Original site")}</a></p> + <p class="linkline"><a href="${post.meta('link')}">${messages("Original site")}</a></p> % endif %if post.description(): - <meta name="description" itemprop="description" content="${post.description()}"> + <meta name="description" itemprop="description" content="${post.description()|h}"> %endif </div> ${html_translations(post)} diff --git a/nikola/data/themes/base/templates/post_helper.tmpl b/nikola/data/themes/base/templates/post_helper.tmpl index 424d134..f8572ec 100644 --- a/nikola/data/themes/base/templates/post_helper.tmpl +++ b/nikola/data/themes/base/templates/post_helper.tmpl @@ -15,7 +15,7 @@ <ul itemprop="keywords" class="tags"> %for tag in post.tags: % if tag not in hidden_tags: - <li><a class="tag p-category" href="${_link('tag', tag)}" rel="tag">${tag}</a></li> + <li><a class="tag p-category" href="${_link('tag', tag)}" rel="tag">${tag|h}</a></li> % endif %endfor </ul> @@ -41,7 +41,7 @@ <%def name="open_graph_metadata(post)"> %if use_open_graph: - <meta property="og:site_name" content="${blog_title|striphtml}"> + <meta property="og:site_name" content="${blog_title|h}"> <meta property="og:title" content="${post.title()[:70]|h}"> <meta property="og:url" content="${abs_link(permalink)}"> %if post.description(): @@ -55,14 +55,14 @@ <meta property="og:type" content="article"> ### Will only work with Pintrest and breaks everywhere else who expect a [Facebook] URI. ### %if post.author(): -### <meta property="article:author" content="${post.author()}"> +### <meta property="article:author" content="${post.author()|h}"> ### %endif %if post.date.isoformat(): <meta property="article:published_time" content="${post.formatted_date('webiso')}"> %endif %if post.tags: %for tag in post.tags: - <meta property="article:tag" content="${tag}"> + <meta property="article:tag" content="${tag|h}"> %endfor %endif %endif @@ -86,8 +86,17 @@ <%def name="mathjax_script(post)"> %if post.is_mathjax: - <script type="text/x-mathjax-config"> - MathJax.Hub.Config({tex2jax: {inlineMath: [['$latex ','$'], ['\\(','\\)']]}});</script> - <script src="/assets/js/mathjax.js"></script> + %if use_katex: + <script src="//cdnjs.cloudflare.com/ajax/libs/KaTeX/0.3.0/katex.min.js"></script> + <script src="//cdnjs.cloudflare.com/ajax/libs/KaTeX/0.3.0/contrib/auto-render.min.js"></script> + <script> + renderMathInElement(document.body); + </script> + %else: + <script type="text/javascript" src="//cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"> </script> + <script type="text/x-mathjax-config"> + MathJax.Hub.Config({tex2jax: {inlineMath: [['$latex ','$'], ['\\(','\\)']]}}); + </script> + %endif %endif </%def> diff --git a/nikola/data/themes/base/templates/post_list_directive.tmpl b/nikola/data/themes/base/templates/post_list_directive.tmpl index e080a81..4a136a1 100644 --- a/nikola/data/themes/base/templates/post_list_directive.tmpl +++ b/nikola/data/themes/base/templates/post_list_directive.tmpl @@ -6,7 +6,7 @@ <ul class="post-list"> % for post in posts: <li class="post-list-item"> - ${post.formatted_date(date_format)} + ${post.formatted_date(date_format)|h} <a href="${post.permalink(lang)}">${post.title(lang)|h}</a> </li> diff --git a/nikola/data/themes/base/templates/sectionindex.tmpl b/nikola/data/themes/base/templates/sectionindex.tmpl new file mode 100644 index 0000000..7fb4f1e --- /dev/null +++ b/nikola/data/themes/base/templates/sectionindex.tmpl @@ -0,0 +1,21 @@ +## -*- coding: utf-8 -*- +<%inherit file="index.tmpl"/> + +<%block name="extra_head"> + ${parent.extra_head()} + % if generate_atom: + <link rel="alternate" type="application/atom+xml" title="Atom for the ${posts[0].section_name()|h} section" href="${_link('section_index_atom', posts[0].section_slug())}"> + % endif +</%block> + +<%block name="content"> +<div class="sectionindex"> + <header> + <h2><a href="${_link('section_index', posts[0].section_slug())}">${title|h}</a></h2> + % if generate_atom: + <p class="feedlink"><a href="${_link('section_index_atom', posts[0].section_slug())}" type="application/atom+xml">${messages('Updates')}</a></p> + % endif + </header> + ${parent.content()} +</div> +</%block> diff --git a/nikola/data/themes/base/templates/tag.tmpl b/nikola/data/themes/base/templates/tag.tmpl index 7509f3e..d07c434 100644 --- a/nikola/data/themes/base/templates/tag.tmpl +++ b/nikola/data/themes/base/templates/tag.tmpl @@ -5,10 +5,10 @@ ${parent.extra_head()} %if len(translations) > 1 and generate_rss: %for language in sorted(translations): - <link rel="alternate" type="application/rss+xml" type="application/rss+xml" title="RSS for ${kind} ${tag} (${language})" href="${_link(kind + "_rss", tag, language)}"> + <link rel="alternate" type="application/rss+xml" title="RSS for ${kind} ${tag|h} (${language})" href="${_link(kind + "_rss", tag, language)}"> %endfor %elif generate_rss: - <link rel="alternate" type="application/rss+xml" type="application/rss+xml" title="RSS for ${kind} ${tag}" href="${_link(kind + "_rss", tag)}"> + <link rel="alternate" type="application/rss+xml" title="RSS for ${kind} ${tag|h}" href="${_link(kind + "_rss", tag)}"> %endif </%block> @@ -24,7 +24,7 @@ ${messages('Subcategories:')} <ul> %for name, link in subcategories: - <li><a href="${link}">${name}</a></li> + <li><a href="${link}">${name|h}</a></li> %endfor </ul> %endif @@ -43,7 +43,7 @@ %if posts: <ul class="postlist"> % for post in posts: - <li><a href="${post.permalink()}" class="listtitle">${post.title()|h}</a> <time class="listdate" datetime="${post.formatted_date('webiso')}" title="${post.formatted_date(date_format)}">${post.formatted_date(date_format)}</time></li> + <li><time class="listdate" datetime="${post.formatted_date('webiso')}" title="${post.formatted_date(date_format)|h}">${post.formatted_date(date_format)|h}</time><a href="${post.permalink()}" class="listtitle">${post.title()|h}<a></li> % endfor </ul> %endif diff --git a/nikola/data/themes/base/templates/tagindex.tmpl b/nikola/data/themes/base/templates/tagindex.tmpl index 0834411..047d638 100644 --- a/nikola/data/themes/base/templates/tagindex.tmpl +++ b/nikola/data/themes/base/templates/tagindex.tmpl @@ -6,7 +6,7 @@ ${messages('Subcategories:')} <ul> %for name, link in subcategories: - <li><a href="${link}">${name}</a></li> + <li><a href="${link}">${name|h}</a></li> %endfor </ul> %endif @@ -16,9 +16,9 @@ ${parent.extra_head()} %if len(translations) > 1 and generate_atom: %for language in sorted(translations): - <link rel="alternate" type="application/atom+xml" title="Atom for the ${tag} section (${language})" href="${_link(kind + "_atom", tag, language)}"> + <link rel="alternate" type="application/atom+xml" title="Atom for the ${tag|h} section (${language})" href="${_link(kind + "_atom", tag, language)}"> %endfor %elif generate_atom: - <link rel="alternate" type="application/atom+xml" title="Atom for the ${tag} section" href="${_link("tag" + "_atom", tag)}"> + <link rel="alternate" type="application/atom+xml" title="Atom for the ${tag|h} section" href="${_link("tag" + "_atom", tag)}"> %endif </%block> diff --git a/nikola/data/themes/base/templates/tags.tmpl b/nikola/data/themes/base/templates/tags.tmpl index a9d2dd7..6c329d9 100644 --- a/nikola/data/themes/base/templates/tags.tmpl +++ b/nikola/data/themes/base/templates/tags.tmpl @@ -4,7 +4,7 @@ <%block name="content"> <article class="tagindex"> <header> - <h1>${title}</h1> + <h1>${title|h}</h1> </header> % if cat_items: % if items: @@ -33,7 +33,7 @@ <ul class="postlist"> % for text, link in items: % if text not in hidden_tags: - <li><a class="reference listtitle" href="${link}">${text}</a></li> + <li><a class="reference listtitle" href="${link}">${text|h}</a></li> % endif % endfor </ul> diff --git a/nikola/data/themes/bootstrap3-jinja/assets/css/bootstrap-theme.css b/nikola/data/themes/bootstrap3-jinja/assets/css/bootstrap-theme.css new file mode 120000 index 0000000..78d39af --- /dev/null +++ b/nikola/data/themes/bootstrap3-jinja/assets/css/bootstrap-theme.css @@ -0,0 +1 @@ +../../../../../../bower_components/bootstrap/dist/css/bootstrap-theme.css
\ No newline at end of file diff --git a/nikola/data/themes/bootstrap3-jinja/assets/css/bootstrap-theme.min.css b/nikola/data/themes/bootstrap3-jinja/assets/css/bootstrap-theme.min.css new file mode 120000 index 0000000..200c765 --- /dev/null +++ b/nikola/data/themes/bootstrap3-jinja/assets/css/bootstrap-theme.min.css @@ -0,0 +1 @@ +../../../../../../bower_components/bootstrap/dist/css/bootstrap-theme.min.css
\ No newline at end of file diff --git a/nikola/data/themes/bootstrap3-jinja/assets/css/bootstrap.css b/nikola/data/themes/bootstrap3-jinja/assets/css/bootstrap.css new file mode 120000 index 0000000..013623e --- /dev/null +++ b/nikola/data/themes/bootstrap3-jinja/assets/css/bootstrap.css @@ -0,0 +1 @@ +../../../../../../bower_components/bootstrap/dist/css/bootstrap.css
\ No newline at end of file diff --git a/nikola/data/themes/bootstrap3-jinja/assets/css/bootstrap.min.css b/nikola/data/themes/bootstrap3-jinja/assets/css/bootstrap.min.css new file mode 120000 index 0000000..5bc6076 --- /dev/null +++ b/nikola/data/themes/bootstrap3-jinja/assets/css/bootstrap.min.css @@ -0,0 +1 @@ +../../../../../../bower_components/bootstrap/dist/css/bootstrap.min.css
\ No newline at end of file diff --git a/nikola/data/themes/bootstrap3-jinja/assets/js/bootstrap.js b/nikola/data/themes/bootstrap3-jinja/assets/js/bootstrap.js new file mode 120000 index 0000000..26aa1fd --- /dev/null +++ b/nikola/data/themes/bootstrap3-jinja/assets/js/bootstrap.js @@ -0,0 +1 @@ +../../../../../../bower_components/bootstrap/dist/js/bootstrap.js
\ No newline at end of file diff --git a/nikola/data/themes/bootstrap3-jinja/assets/js/bootstrap.min.js b/nikola/data/themes/bootstrap3-jinja/assets/js/bootstrap.min.js new file mode 120000 index 0000000..c4cdf6c --- /dev/null +++ b/nikola/data/themes/bootstrap3-jinja/assets/js/bootstrap.min.js @@ -0,0 +1 @@ +../../../../../../bower_components/bootstrap/dist/js/bootstrap.min.js
\ No newline at end of file diff --git a/nikola/data/themes/bootstrap3-jinja/assets/js/flowr.plugin.js b/nikola/data/themes/bootstrap3-jinja/assets/js/flowr.plugin.js index c0d986b..a6ab900 100644 --- a/nikola/data/themes/bootstrap3-jinja/assets/js/flowr.plugin.js +++ b/nikola/data/themes/bootstrap3-jinja/assets/js/flowr.plugin.js @@ -1,265 +1,271 @@ /** - * Flowr.js - Simple jQuery plugin to emulate Flickr's justified view - * For usage information refer to http://github.com/kalyan02/flowr-js - * - * - * @author: Kalyan Chakravarthy (http://KalyanChakravarthy.net) - * @version: v0.1 - */ -(function($){ - //$("#container2").css( 'border', '1px solid #ccc'); - $.fn.flowr = function(options) { - - $this = this; - var ROW_CLASS_NAME = 'flowr-row'; // Class name for the row of flowy - var MAX_LAST_ROW_GAP = 25; // If the width of last row is lesser than max-width, recalculation is needed - var NO_COPY_FIELDS = [ 'complete', 'data', 'responsive' ]; // these attributes will not be carried forward for append related calls - var DEFAULTS = { - 'data' : [], - 'padding' : 5, // whats the padding between flowy items - 'height' : 240, // Minimum height an image row should take - 'render' : null, // callback function to get the tag - 'append' : false, // TODO - 'widthAttr' : 'width', // a custom data structure can specify which attribute refers to height/width - 'heightAttr' : 'height', - 'maxScale' : 1.5, // In case there is only 1 elment in last row - 'maxWidth' : this.width()-1, // 1px is just for offset - 'itemWidth' : null, // callback function for width - 'itemHeight' : null, // callback function for height - 'complete' : null, // complete callback - 'rowClassName' : ROW_CLASS_NAME, - 'rows' : -1, // Maximum number of rows to render. -1 for no limit. - 'responsive' : true // make content responsive - }; - var settings = $.extend( DEFAULTS, options); - - // If data is being appended, we already have settings - // If we already have settings, retrieve them - if( settings.append && $this.data('lastSettings') ) { - lastSettings = $this.data('lastSettings'); - - // Copy over the settings from previous init - for( attr in DEFAULTS ) { - if( NO_COPY_FIELDS.indexOf(attr)<0 && settings[attr] == DEFAULTS[attr] ) { - settings[attr] = lastSettings[attr]; - } - } - - // Check if we have an incomplete last row - lastRow = $this.data('lastRow'); - if( lastRow.data.length > 0 && settings.maxWidth-lastRow.width > MAX_LAST_ROW_GAP ) { - // Prepend the incomplete row to newly loaded data and redraw - lastRowData = lastSettings.data.slice( lastSettings.data.length - lastRow.data.length - 1 ); - settings.data = lastRowData.concat(settings.data); - - // Remove the incomplete row - // TODO: Don't reload this stuff later. Reattach to new row. - $( '.' + settings.rowClassName + ':last', $this ).detach(); - } else { - // console.log( lastRow.data.length ); - // console.log( lastRow.width ); - } - } - - // only on the first initial call - if( !settings.responsive && !settings.append ) - $this.width( $this.width() ); - - // Basic sanity checks - if( !(settings.data instanceof Array) ) - return; - - if( typeof(settings.padding) != 'number' ) - settings.padding = parseInt( settings.padding ); - - if( typeof(settings.itemWidth) != 'function' ) { - settings.itemWidth = function(data) { - return data[ settings.widthAttr ]; - } - } - - if( typeof(settings.itemHeight) != 'function' ) { - settings.itemHeight = function(data) { - return data[ settings.heightAttr ]; - } - } - - // A standalone utility to calculate the item widths for a particular row - // Returns rowWidth: width occupied & data : the items in the new row - var utils = { - getNextRow : function( data, settings ) { - var itemIndex = 0; - var itemsLength = data.length; - var lineItems = []; - var lineWidth = 0; - var maxWidth = settings.maxWidth; - var paddingSize = settings.padding; - - // console.log( 'maxItems=' + data.length ); - - requiredPadding = function() { - var extraPads = arguments.length == 1 ? arguments[0] : 0; - return (lineItems.length - 1 + extraPads) * settings.padding; - } - - while( lineWidth + requiredPadding() < settings.maxWidth && (itemIndex < itemsLength) ) { - var itemData = data[ itemIndex ]; - var itemWidth = settings.itemWidth.call( $this, itemData ); - var itemHeight = settings.itemHeight.call( $this, itemData ); - - var minHeight = settings.height; - var minWidth = Math.floor( itemWidth * settings.height / itemHeight ); - - var newLineWidth = lineWidth + minWidth + requiredPadding(1); - - if (minWidth > settings.maxWidth) { - // very short+wide images like panoramas - // show them even if ugly, as wide as possible - minWidth = settings.maxWidth-1; - minHeight = settings.height * minHeight / minWidth; - } - - // console.log( 'lineWidth = ' + lineWidth ); - // console.log( 'newLineWidth = ' + newLineWidth ); - if( newLineWidth < settings.maxWidth ) { - lineItems.push({ - 'height' : minHeight, - 'width' : minWidth, - 'itemData' : itemData - }); - - lineWidth += minWidth; - itemIndex ++; - } else { - // We'd have exceeded width. So break off to scale. - // console.log( 'breaking off = ' + itemIndex ); - // console.log( 'leave off size = ' + lineItems.length ); - break; - } - } //while - - // Scale the size to max width - testWidth=0; - if( lineWidth < settings.maxWidth ) { - var fullScaleWidth = settings.maxWidth - requiredPadding() - 10; - var currScaleWidth = lineWidth; - var scaleFactor = fullScaleWidth / currScaleWidth; - if( scaleFactor > settings.maxScale ) - scaleFactor = 1; - - var newHeight = Math.round( settings.height * scaleFactor ); - for( i=0; i<lineItems.length; i++ ) { - var lineItem = lineItems[ i ]; - lineItem.width = Math.floor(lineItem.width * scaleFactor); - lineItem.height = newHeight; - - testWidth += lineItem.width; - } - } - - return { - data : lineItems, - width : testWidth + requiredPadding() - }; - }, //getNextRow - reorderContent : function(){ - /* - TODO: optimize for faster resizing by reusing dom objects instead of killing the dom - */ - var _initialWidth = $this.data('width'); - var _newWidth = $this.width(); - var _change = _initialWidth - _newWidth; - - if(_initialWidth!=_newWidth) { - $this.html(''); - var _settings = $this.data( 'lastSettings' ); - _settings.data = $this.data( 'data' ); - _settings.maxWidth = $this.width() - 1; - $this.flowr( _settings ); - } - } - } //utils - - // If the resposive var is set to true then listen for resize method - // and prevent resizing from happening twice if responsive is set again during append phase! - if( settings.responsive && !$this.data('__responsive') ) { - $(window).resize(function(){ - initialWidth = $this.data('width'); - newWidth = $this.width(); - - //initiate resize - if( initialWidth != newWidth ) { - var task_id = $this.data('task_id'); - if( task_id ) { - task_id = clearTimeout( task_id ); - task_id = null; - } - task_id = setTimeout( utils.reorderContent, 80 ); - $this.data('task_id', task_id ); - } - }); - $this.data('__responsive',true); - } - - - return this.each(function(){ - - // Get a copy of original data. 1 level deep copy is sufficient. - var data = settings.data.slice(0); - var rowData = null; - var currentRow = 0; - var currentItem = 0; - - // Store all the data - var allData = $this.data( 'data' ) || []; - for(i=0;i<data.length;i++) { - allData.push( data[i] ); - } - $this.data( 'data', allData ); - - // While we have a new row - while( ( rowData = utils.getNextRow(data,settings) ) != null && rowData.data.length > 0 ) { - if( settings.rows > 0 && currentRow >= settings.rows ) - break; - // remove the number of elements in the new row from the top of data stack - data.splice( 0, rowData.data.length ); - - // Create a new row div, add class, append the htmls and insert the flowy items - var $row = $('<div>').addClass(settings.rowClassName); - for( i=0; i<rowData.data.length; i++ ) { - var displayData = rowData.data[i]; - // Get the HTML object from custom render function passed as argument - var displayObject = settings.render.call( $this, displayData ); - displayObject = $(displayObject); - // Set some basic stuff - displayObject - .css( 'width', displayData.width ) - .css( 'height', displayData.height ) - .css( 'margin-left', i==0 ? '0' : settings.padding + "px" ); //TODO:Refactor - $row.append( displayObject ); - - currentItem++; - } - $this.append( $row ); - // console.log ( "I> rowData.data.length="+rowData.data.length +" rowData.width="+rowData.width ); - - currentRow++; - $this.data('lastRow', rowData ); - } - // store the current state of settings and the items in last row - // we'll need this info when we append more items - $this.data('lastSettings', settings ); - - // onComplete callback - // pass back info about list of rows and items rendered - if( typeof (settings.complete) == 'function' ) { - var completeData = { - renderedRows : currentRow, - renderedItems : currentItem - } - settings.complete.call( $this, completeData ); - } - }); - }; + * Flowr.js - Simple jQuery plugin to emulate Flickr's justified view + * For usage information refer to http://github.com/kalyan02/flowr-js + * + * + * @author: Kalyan Chakravarthy (http://KalyanChakravarthy.net) + * @version: v0.1 + */ +(function($) { + //$("#container2").css( 'border', '1px solid #ccc'); + $.fn.flowr = function(options) { + + $this = this; + var ROW_CLASS_NAME = 'flowr-row'; // Class name for the row of flowy + var MAX_LAST_ROW_GAP = 25; // If the width of last row is lesser than max-width, recalculation is needed + var NO_COPY_FIELDS = ['complete', 'data', 'responsive']; // these attributes will not be carried forward for append related calls + var DEFAULTS = { + 'data': [], + 'padding': 5, // whats the padding between flowy items + 'height': 240, // Minimum height an image row should take + 'render': null, // callback function to get the tag + 'append': false, // TODO + 'widthAttr': 'width', // a custom data structure can specify which attribute refers to height/width + 'heightAttr': 'height', + 'maxScale': 1.5, // In case there is only 1 elment in last row + 'maxWidth': this.width() - 1, // 1px is just for offset + 'itemWidth': null, // callback function for width + 'itemHeight': null, // callback function for height + 'complete': null, // complete callback + 'rowClassName': ROW_CLASS_NAME, + 'rows': -1, // Maximum number of rows to render. -1 for no limit. + 'responsive': true // make content responsive + }; + var settings = $.extend(DEFAULTS, options); + + // If data is being appended, we already have settings + // If we already have settings, retrieve them + if (settings.append && $this.data('lastSettings')) { + lastSettings = $this.data('lastSettings'); + + // Copy over the settings from previous init + for (attr in DEFAULTS) { + if (NO_COPY_FIELDS.indexOf(attr) < 0 && settings[attr] == DEFAULTS[attr]) { + settings[attr] = lastSettings[attr]; + } + } + + // Check if we have an incomplete last row + lastRow = $this.data('lastRow'); + if (lastRow.data.length > 0 && settings.maxWidth - lastRow.width > MAX_LAST_ROW_GAP) { + // Prepend the incomplete row to newly loaded data and redraw + lastRowData = lastSettings.data.slice(lastSettings.data.length - lastRow.data.length - 1); + settings.data = lastRowData.concat(settings.data); + + // Remove the incomplete row + // TODO: Don't reload this stuff later. Reattach to new row. + $('.' + settings.rowClassName + ':last', $this).detach(); + } else { + // console.log( lastRow.data.length ); + // console.log( lastRow.width ); + } + } + + // only on the first initial call + if (!settings.responsive && !settings.append) + $this.width($this.width()); + + // Basic sanity checks + if (!(settings.data instanceof Array)) + return; + + if (typeof(settings.padding) != 'number') + settings.padding = parseInt(settings.padding); + + if (typeof(settings.itemWidth) != 'function') { + settings.itemWidth = function(data) { + return data[settings.widthAttr]; + } + } + + if (typeof(settings.itemHeight) != 'function') { + settings.itemHeight = function(data) { + return data[settings.heightAttr]; + } + } + + // A standalone utility to calculate the item widths for a particular row + // Returns rowWidth: width occupied & data : the items in the new row + var utils = { + getNextRow: function(data, settings) { + var itemIndex = 0; + var itemsLength = data.length; + var lineItems = []; + var lineWidth = 0; + var maxWidth = settings.maxWidth; + var paddingSize = settings.padding; + + // console.log( 'maxItems=' + data.length ); + + requiredPadding = function() { + var extraPads = arguments.length == 1 ? arguments[0] : 0; + return (lineItems.length - 1 + extraPads) * settings.padding; + } + + while (lineWidth + requiredPadding() < settings.maxWidth && (itemIndex < itemsLength)) { + var itemData = data[itemIndex]; + var itemWidth = settings.itemWidth.call($this, itemData); + var itemHeight = settings.itemHeight.call($this, itemData); + + var minHeight = settings.height; + var minWidth = Math.floor(itemWidth * settings.height / itemHeight); + + + if (minWidth > settings.maxWidth) { + // very short+wide images like panoramas + // show them even if ugly, as wide as possible + minWidth = settings.maxWidth - 1 - requiredPadding(1); + minHeight = settings.height * minHeight / minWidth; + } + var newLineWidth = lineWidth + minWidth; + + // console.log( 'lineWidth = ' + lineWidth ); + // console.log( 'newLineWidth = ' + newLineWidth ); + if (newLineWidth < settings.maxWidth) { + lineItems.push({ + 'height': minHeight, + 'width': minWidth, + 'itemData': itemData + }); + + lineWidth += minWidth; + itemIndex++; + } else { + // We'd have exceeded width. So break off to scale. + // console.log( 'breaking off = ' + itemIndex ); + // console.log( 'leave off size = ' + lineItems.length ); + break; + } + } //while + + // Scale the size to max width + testWidth = 0; + if (lineWidth < settings.maxWidth) { + var fullScaleWidth = settings.maxWidth - requiredPadding() - 10; + var currScaleWidth = lineWidth; + var scaleFactor = fullScaleWidth / currScaleWidth; + if (scaleFactor > settings.maxScale) + scaleFactor = 1; + + var newHeight = Math.round(settings.height * scaleFactor); + for (i = 0; i < lineItems.length; i++) { + var lineItem = lineItems[i]; + lineItem.width = Math.floor(lineItem.width * scaleFactor); + lineItem.height = newHeight; + + testWidth += lineItem.width; + } + } + + return { + data: lineItems, + width: testWidth + requiredPadding() + }; + }, //getNextRow + reorderContent: function() { + /* + TODO: optimize for faster resizing by reusing dom objects instead of killing the dom + */ + var _initialWidth = $this.data('width'); + var _newWidth = $this.width(); + var _change = _initialWidth - _newWidth; + + if (_initialWidth != _newWidth) { + $this.html(''); + var _settings = $this.data('lastSettings'); + _settings.data = $this.data('data'); + _settings.maxWidth = $this.width() - 1; + $this.flowr(_settings); + } + } + } //utils + + // If the resposive var is set to true then listen for resize method + // and prevent resizing from happening twice if responsive is set again during append phase! + if (settings.responsive && !$this.data('__responsive')) { + $(window).resize(function() { + initialWidth = $this.data('width'); + newWidth = $this.width(); + + //initiate resize + if (initialWidth != newWidth) { + var task_id = $this.data('task_id'); + if (task_id) { + task_id = clearTimeout(task_id); + task_id = null; + } + task_id = setTimeout(utils.reorderContent, 80); + $this.data('task_id', task_id); + } + }); + $this.data('__responsive', true); + } + + + return this.each(function() { + + // Get a copy of original data. 1 level deep copy is sufficient. + var data = settings.data.slice(0); + var rowData = null; + var currentRow = 0; + var currentItem = 0; + + // Store all the data + var allData = $this.data('data') || []; + for (i = 0; i < data.length; i++) { + allData.push(data[i]); + } + $this.data('data', allData); + + // While we have a new row + while ((rowData = utils.getNextRow(data, settings)) != null && rowData.data.length > 0) { + if (settings.rows > 0 && currentRow >= settings.rows) + break; + // remove the number of elements in the new row from the top of data stack + data.splice(0, rowData.data.length); + + // Create a new row div, add class, append the htmls and insert the flowy items + var $row = $('<div>').addClass(settings.rowClassName); + var slack = $this[0].clientWidth - rowData.width - 2 * settings.padding + for (i = 0; i < rowData.data.length; i++) { + var displayData = rowData.data[i]; + // Get the HTML object from custom render function passed as argument + var displayObject = settings.render.call($this, displayData); + displayObject = $(displayObject); + extraw = Math.floor(slack/rowData.data.length) + if (i == 0) { + extraw += slack % rowData.data.length + } + // Set some basic stuff + displayObject + .css('width', displayData.width + extraw) + .css('height', displayData.height) + .css('margin-bottom', settings.padding + "px") + .css('margin-left', i == 0 ? '0' : settings.padding + "px"); //TODO:Refactor + $row.append(displayObject); + + currentItem++; + } + $this.append($row); + // console.log ( "I> rowData.data.length="+rowData.data.length +" rowData.width="+rowData.width ); + + currentRow++; + $this.data('lastRow', rowData); + } + // store the current state of settings and the items in last row + // we'll need this info when we append more items + $this.data('lastSettings', settings); + + // onComplete callback + // pass back info about list of rows and items rendered + if (typeof(settings.complete) == 'function') { + var completeData = { + renderedRows: currentRow, + renderedItems: currentItem + } + settings.complete.call($this, completeData); + } + }); + }; })(jQuery); diff --git a/nikola/data/themes/bootstrap3-jinja/assets/js/jquery.colorbox-min.js b/nikola/data/themes/bootstrap3-jinja/assets/js/jquery.colorbox-min.js new file mode 120000 index 0000000..9e40fd4 --- /dev/null +++ b/nikola/data/themes/bootstrap3-jinja/assets/js/jquery.colorbox-min.js @@ -0,0 +1 @@ +../../../../../../bower_components/jquery-colorbox/jquery.colorbox-min.js
\ No newline at end of file diff --git a/nikola/data/themes/bootstrap3-jinja/assets/js/jquery.min.js b/nikola/data/themes/bootstrap3-jinja/assets/js/jquery.min.js new file mode 120000 index 0000000..5c080da --- /dev/null +++ b/nikola/data/themes/bootstrap3-jinja/assets/js/jquery.min.js @@ -0,0 +1 @@ +../../../../../../bower_components/jquery/dist/jquery.min.js
\ No newline at end of file diff --git a/nikola/data/themes/bootstrap3-jinja/assets/js/jquery.min.map b/nikola/data/themes/bootstrap3-jinja/assets/js/jquery.min.map new file mode 120000 index 0000000..7e2c217 --- /dev/null +++ b/nikola/data/themes/bootstrap3-jinja/assets/js/jquery.min.map @@ -0,0 +1 @@ +../../../../../../bower_components/jquery/dist/jquery.min.map
\ No newline at end of file diff --git a/nikola/data/themes/bootstrap3-jinja/templates/authors.tmpl b/nikola/data/themes/bootstrap3-jinja/templates/authors.tmpl new file mode 100644 index 0000000..d65c727 --- /dev/null +++ b/nikola/data/themes/bootstrap3-jinja/templates/authors.tmpl @@ -0,0 +1,17 @@ +{# -*- coding: utf-8 -*- #} +{% extends 'base.tmpl' %} + +{% block content %} +{% if items %} + <h2>{{ messages("Authors") }}</h2> +{% endif %} +{% if items %} + <ul class="list-inline"> + {% for text, link in items %} + {% if text not in hidden_authors %} + <li><a class="reference badge" href="{{ link }}">{{ text|e }}</a></li> + {% endif %} + {% endfor %} + </ul> +{% endif %} +{% endblock %} diff --git a/nikola/data/themes/bootstrap3-jinja/templates/base.tmpl b/nikola/data/themes/bootstrap3-jinja/templates/base.tmpl index 46d145a..21b93cd 100644 --- a/nikola/data/themes/bootstrap3-jinja/templates/base.tmpl +++ b/nikola/data/themes/bootstrap3-jinja/templates/base.tmpl @@ -24,11 +24,11 @@ </button> <a class="navbar-brand" href="{{ abs_link(_link("root", None, lang)) }}"> {% if logo_url %} - <img src="{{ logo_url }}" alt="{{ blog_title }}" id="logo"> + <img src="{{ logo_url }}" alt="{{ blog_title|e }}" id="logo"> {% endif %} {% if show_blog_title %} - <span id="blog-title">{{ blog_title }}</span> + <span id="blog-title">{{ blog_title|e }}</span> {% endif %} </a> </div><!-- /.navbar-header --> diff --git a/nikola/data/themes/bootstrap3-jinja/templates/base_helper.tmpl b/nikola/data/themes/bootstrap3-jinja/templates/base_helper.tmpl index 5ab4dcb..d9ea683 100644 --- a/nikola/data/themes/bootstrap3-jinja/templates/base_helper.tmpl +++ b/nikola/data/themes/bootstrap3-jinja/templates/base_helper.tmpl @@ -26,9 +26,11 @@ dir="rtl" lang="{{ lang }}"> <head> <meta charset="utf-8"> + {% if use_base_tag %} <base href="{{ abs_link(permalink) }}"> + {% endif %} {% if description %} - <meta name="description" content="{{ description }}"> + <meta name="description" content="{{ description|e }}"> {% endif %} <meta name="viewport" content="width=device-width, initial-scale=1"> {% if title == blog_title %} @@ -38,6 +40,7 @@ lang="{{ lang }}"> {% endif %} {{ html_stylesheets() }} + <meta content="{{ theme_color }}" name="theme-color"> {{ html_feedlinks() }} <link rel="canonical" href="{{ abs_link(permalink) }}"> diff --git a/nikola/data/themes/bootstrap3-jinja/templates/gallery.tmpl b/nikola/data/themes/bootstrap3-jinja/templates/gallery.tmpl index 00fda11..2ae0457 100644 --- a/nikola/data/themes/bootstrap3-jinja/templates/gallery.tmpl +++ b/nikola/data/themes/bootstrap3-jinja/templates/gallery.tmpl @@ -17,7 +17,7 @@ {% if folders %} <ul> {% for folder, ftitle in folders %} - <li><a href="{{ folder }}"><i class="glyphicon glyphicon-folder-open"></i> {{ ftitle }}</a></li> + <li><a href="{{ folder }}"><i class="glyphicon glyphicon-folder-open"></i> {{ ftitle|e }}</a></li> {% endfor %} </ul> {% endif %} @@ -27,8 +27,8 @@ <noscript> <ul class="thumbnails"> {% for image in photo_array %} - <li><a href="{{ image['url'] }}" class="thumbnail image-reference" title="{{ image['title'] }}"> - <img src="{{ image['url_thumb'] }}" alt="{{ image['title'] }}" /></a> + <li><a href="{{ image['url'] }}" class="thumbnail image-reference" title="{{ image['title']|e }}"> + <img src="{{ image['url_thumb'] }}" alt="{{ image['title']|e }}" /></a> {% endfor %} </ul> </noscript> diff --git a/nikola/data/themes/bootstrap3-jinja/templates/listing.tmpl b/nikola/data/themes/bootstrap3-jinja/templates/listing.tmpl index 634c482..f0d8050 100644 --- a/nikola/data/themes/bootstrap3-jinja/templates/listing.tmpl +++ b/nikola/data/themes/bootstrap3-jinja/templates/listing.tmpl @@ -7,10 +7,10 @@ {% if folders or files %} <ul class="list-unstyled"> {% for name in folders %} - <li><a href="{{ name }}"><i class="glyphicon glyphicon-folder-open"></i> {{ name }}</a> + <li><a href="{{ name|urlencode }}"><i class="glyphicon glyphicon-folder-open"></i> {{ name|e }}</a> {% endfor %} {% for name in files %} - <li><a href="{{ name }}.html"><i class="glyphicon glyphicon-file"></i> {{ name }}</a> + <li><a href="{{ name|urlencode }}.html"><i class="glyphicon glyphicon-file"></i> {{ name|e }}</a> {% endfor %} </ul> {% endif %} diff --git a/nikola/data/themes/bootstrap3-jinja/templates/post.tmpl b/nikola/data/themes/bootstrap3-jinja/templates/post.tmpl index df4109c..3cf4c4b 100644 --- a/nikola/data/themes/bootstrap3-jinja/templates/post.tmpl +++ b/nikola/data/themes/bootstrap3-jinja/templates/post.tmpl @@ -10,9 +10,9 @@ <meta name="keywords" content="{{ post.meta('keywords')|e }}"> {% endif %} {% if post.description() %} - <meta name="description" itemprop="description" content="{{ post.description() }}"> + <meta name="description" itemprop="description" content="{{ post.description()|e }}"> {% endif %} - <meta name="author" content="{{ post.author() }}"> + <meta name="author" content="{{ post.author()|e }}"> {% if post.prev_post %} <link rel="prev" href="{{ post.prev_post.permalink() }}" title="{{ post.prev_post.title()|e }}" type="text/html"> {% endif %} diff --git a/nikola/data/themes/bootstrap3-jinja/templates/tags.tmpl b/nikola/data/themes/bootstrap3-jinja/templates/tags.tmpl index c308f19..4afd4d2 100644 --- a/nikola/data/themes/bootstrap3-jinja/templates/tags.tmpl +++ b/nikola/data/themes/bootstrap3-jinja/templates/tags.tmpl @@ -2,16 +2,16 @@ {% extends 'base.tmpl' %} {% block content %} -<h1>{{ title }}</h1> +<h1>{{ title|e }}</h1> {% if cat_items %} {% if items %} <h2>{{ messages("Categories") }}</h2> {% endif %} {% for text, full_name, path, link, indent_levels, indent_change_before, indent_change_after in cat_hierarchy %} {% for i in range(indent_change_before) %} - <ul class="unstyled"> + <ul class="list-inline"> {% endfor %} - <li><a class="reference badge" href="{{ link }}">{{ text }}</a> + <li><a class="reference badge" href="{{ link }}">{{ text|e }}</a> {% if indent_change_after <= 0 %} </li> {% endif %} @@ -30,7 +30,7 @@ <ul class="list-inline"> {% for text, link in items %} {% if text not in hidden_tags %} - <li><a class="reference badge" href="{{ link }}">{{ text }}</a></li> + <li><a class="reference badge" href="{{ link }}">{{ text|e }}</a></li> {% endif %} {% endfor %} </ul> diff --git a/nikola/data/themes/bootstrap3/assets/css/bootstrap-theme.css b/nikola/data/themes/bootstrap3/assets/css/bootstrap-theme.css new file mode 120000 index 0000000..78d39af --- /dev/null +++ b/nikola/data/themes/bootstrap3/assets/css/bootstrap-theme.css @@ -0,0 +1 @@ +../../../../../../bower_components/bootstrap/dist/css/bootstrap-theme.css
\ No newline at end of file diff --git a/nikola/data/themes/bootstrap3/assets/css/bootstrap-theme.min.css b/nikola/data/themes/bootstrap3/assets/css/bootstrap-theme.min.css new file mode 120000 index 0000000..200c765 --- /dev/null +++ b/nikola/data/themes/bootstrap3/assets/css/bootstrap-theme.min.css @@ -0,0 +1 @@ +../../../../../../bower_components/bootstrap/dist/css/bootstrap-theme.min.css
\ No newline at end of file diff --git a/nikola/data/themes/bootstrap3/assets/css/bootstrap.css b/nikola/data/themes/bootstrap3/assets/css/bootstrap.css new file mode 120000 index 0000000..013623e --- /dev/null +++ b/nikola/data/themes/bootstrap3/assets/css/bootstrap.css @@ -0,0 +1 @@ +../../../../../../bower_components/bootstrap/dist/css/bootstrap.css
\ No newline at end of file diff --git a/nikola/data/themes/bootstrap3/assets/css/bootstrap.min.css b/nikola/data/themes/bootstrap3/assets/css/bootstrap.min.css new file mode 120000 index 0000000..5bc6076 --- /dev/null +++ b/nikola/data/themes/bootstrap3/assets/css/bootstrap.min.css @@ -0,0 +1 @@ +../../../../../../bower_components/bootstrap/dist/css/bootstrap.min.css
\ No newline at end of file diff --git a/nikola/data/themes/bootstrap3/assets/js/bootstrap.js b/nikola/data/themes/bootstrap3/assets/js/bootstrap.js new file mode 120000 index 0000000..26aa1fd --- /dev/null +++ b/nikola/data/themes/bootstrap3/assets/js/bootstrap.js @@ -0,0 +1 @@ +../../../../../../bower_components/bootstrap/dist/js/bootstrap.js
\ No newline at end of file diff --git a/nikola/data/themes/bootstrap3/assets/js/bootstrap.min.js b/nikola/data/themes/bootstrap3/assets/js/bootstrap.min.js new file mode 120000 index 0000000..c4cdf6c --- /dev/null +++ b/nikola/data/themes/bootstrap3/assets/js/bootstrap.min.js @@ -0,0 +1 @@ +../../../../../../bower_components/bootstrap/dist/js/bootstrap.min.js
\ No newline at end of file diff --git a/nikola/data/themes/bootstrap3/assets/js/flowr.plugin.js b/nikola/data/themes/bootstrap3/assets/js/flowr.plugin.js index c0d986b..a6ab900 100644 --- a/nikola/data/themes/bootstrap3/assets/js/flowr.plugin.js +++ b/nikola/data/themes/bootstrap3/assets/js/flowr.plugin.js @@ -1,265 +1,271 @@ /** - * Flowr.js - Simple jQuery plugin to emulate Flickr's justified view - * For usage information refer to http://github.com/kalyan02/flowr-js - * - * - * @author: Kalyan Chakravarthy (http://KalyanChakravarthy.net) - * @version: v0.1 - */ -(function($){ - //$("#container2").css( 'border', '1px solid #ccc'); - $.fn.flowr = function(options) { - - $this = this; - var ROW_CLASS_NAME = 'flowr-row'; // Class name for the row of flowy - var MAX_LAST_ROW_GAP = 25; // If the width of last row is lesser than max-width, recalculation is needed - var NO_COPY_FIELDS = [ 'complete', 'data', 'responsive' ]; // these attributes will not be carried forward for append related calls - var DEFAULTS = { - 'data' : [], - 'padding' : 5, // whats the padding between flowy items - 'height' : 240, // Minimum height an image row should take - 'render' : null, // callback function to get the tag - 'append' : false, // TODO - 'widthAttr' : 'width', // a custom data structure can specify which attribute refers to height/width - 'heightAttr' : 'height', - 'maxScale' : 1.5, // In case there is only 1 elment in last row - 'maxWidth' : this.width()-1, // 1px is just for offset - 'itemWidth' : null, // callback function for width - 'itemHeight' : null, // callback function for height - 'complete' : null, // complete callback - 'rowClassName' : ROW_CLASS_NAME, - 'rows' : -1, // Maximum number of rows to render. -1 for no limit. - 'responsive' : true // make content responsive - }; - var settings = $.extend( DEFAULTS, options); - - // If data is being appended, we already have settings - // If we already have settings, retrieve them - if( settings.append && $this.data('lastSettings') ) { - lastSettings = $this.data('lastSettings'); - - // Copy over the settings from previous init - for( attr in DEFAULTS ) { - if( NO_COPY_FIELDS.indexOf(attr)<0 && settings[attr] == DEFAULTS[attr] ) { - settings[attr] = lastSettings[attr]; - } - } - - // Check if we have an incomplete last row - lastRow = $this.data('lastRow'); - if( lastRow.data.length > 0 && settings.maxWidth-lastRow.width > MAX_LAST_ROW_GAP ) { - // Prepend the incomplete row to newly loaded data and redraw - lastRowData = lastSettings.data.slice( lastSettings.data.length - lastRow.data.length - 1 ); - settings.data = lastRowData.concat(settings.data); - - // Remove the incomplete row - // TODO: Don't reload this stuff later. Reattach to new row. - $( '.' + settings.rowClassName + ':last', $this ).detach(); - } else { - // console.log( lastRow.data.length ); - // console.log( lastRow.width ); - } - } - - // only on the first initial call - if( !settings.responsive && !settings.append ) - $this.width( $this.width() ); - - // Basic sanity checks - if( !(settings.data instanceof Array) ) - return; - - if( typeof(settings.padding) != 'number' ) - settings.padding = parseInt( settings.padding ); - - if( typeof(settings.itemWidth) != 'function' ) { - settings.itemWidth = function(data) { - return data[ settings.widthAttr ]; - } - } - - if( typeof(settings.itemHeight) != 'function' ) { - settings.itemHeight = function(data) { - return data[ settings.heightAttr ]; - } - } - - // A standalone utility to calculate the item widths for a particular row - // Returns rowWidth: width occupied & data : the items in the new row - var utils = { - getNextRow : function( data, settings ) { - var itemIndex = 0; - var itemsLength = data.length; - var lineItems = []; - var lineWidth = 0; - var maxWidth = settings.maxWidth; - var paddingSize = settings.padding; - - // console.log( 'maxItems=' + data.length ); - - requiredPadding = function() { - var extraPads = arguments.length == 1 ? arguments[0] : 0; - return (lineItems.length - 1 + extraPads) * settings.padding; - } - - while( lineWidth + requiredPadding() < settings.maxWidth && (itemIndex < itemsLength) ) { - var itemData = data[ itemIndex ]; - var itemWidth = settings.itemWidth.call( $this, itemData ); - var itemHeight = settings.itemHeight.call( $this, itemData ); - - var minHeight = settings.height; - var minWidth = Math.floor( itemWidth * settings.height / itemHeight ); - - var newLineWidth = lineWidth + minWidth + requiredPadding(1); - - if (minWidth > settings.maxWidth) { - // very short+wide images like panoramas - // show them even if ugly, as wide as possible - minWidth = settings.maxWidth-1; - minHeight = settings.height * minHeight / minWidth; - } - - // console.log( 'lineWidth = ' + lineWidth ); - // console.log( 'newLineWidth = ' + newLineWidth ); - if( newLineWidth < settings.maxWidth ) { - lineItems.push({ - 'height' : minHeight, - 'width' : minWidth, - 'itemData' : itemData - }); - - lineWidth += minWidth; - itemIndex ++; - } else { - // We'd have exceeded width. So break off to scale. - // console.log( 'breaking off = ' + itemIndex ); - // console.log( 'leave off size = ' + lineItems.length ); - break; - } - } //while - - // Scale the size to max width - testWidth=0; - if( lineWidth < settings.maxWidth ) { - var fullScaleWidth = settings.maxWidth - requiredPadding() - 10; - var currScaleWidth = lineWidth; - var scaleFactor = fullScaleWidth / currScaleWidth; - if( scaleFactor > settings.maxScale ) - scaleFactor = 1; - - var newHeight = Math.round( settings.height * scaleFactor ); - for( i=0; i<lineItems.length; i++ ) { - var lineItem = lineItems[ i ]; - lineItem.width = Math.floor(lineItem.width * scaleFactor); - lineItem.height = newHeight; - - testWidth += lineItem.width; - } - } - - return { - data : lineItems, - width : testWidth + requiredPadding() - }; - }, //getNextRow - reorderContent : function(){ - /* - TODO: optimize for faster resizing by reusing dom objects instead of killing the dom - */ - var _initialWidth = $this.data('width'); - var _newWidth = $this.width(); - var _change = _initialWidth - _newWidth; - - if(_initialWidth!=_newWidth) { - $this.html(''); - var _settings = $this.data( 'lastSettings' ); - _settings.data = $this.data( 'data' ); - _settings.maxWidth = $this.width() - 1; - $this.flowr( _settings ); - } - } - } //utils - - // If the resposive var is set to true then listen for resize method - // and prevent resizing from happening twice if responsive is set again during append phase! - if( settings.responsive && !$this.data('__responsive') ) { - $(window).resize(function(){ - initialWidth = $this.data('width'); - newWidth = $this.width(); - - //initiate resize - if( initialWidth != newWidth ) { - var task_id = $this.data('task_id'); - if( task_id ) { - task_id = clearTimeout( task_id ); - task_id = null; - } - task_id = setTimeout( utils.reorderContent, 80 ); - $this.data('task_id', task_id ); - } - }); - $this.data('__responsive',true); - } - - - return this.each(function(){ - - // Get a copy of original data. 1 level deep copy is sufficient. - var data = settings.data.slice(0); - var rowData = null; - var currentRow = 0; - var currentItem = 0; - - // Store all the data - var allData = $this.data( 'data' ) || []; - for(i=0;i<data.length;i++) { - allData.push( data[i] ); - } - $this.data( 'data', allData ); - - // While we have a new row - while( ( rowData = utils.getNextRow(data,settings) ) != null && rowData.data.length > 0 ) { - if( settings.rows > 0 && currentRow >= settings.rows ) - break; - // remove the number of elements in the new row from the top of data stack - data.splice( 0, rowData.data.length ); - - // Create a new row div, add class, append the htmls and insert the flowy items - var $row = $('<div>').addClass(settings.rowClassName); - for( i=0; i<rowData.data.length; i++ ) { - var displayData = rowData.data[i]; - // Get the HTML object from custom render function passed as argument - var displayObject = settings.render.call( $this, displayData ); - displayObject = $(displayObject); - // Set some basic stuff - displayObject - .css( 'width', displayData.width ) - .css( 'height', displayData.height ) - .css( 'margin-left', i==0 ? '0' : settings.padding + "px" ); //TODO:Refactor - $row.append( displayObject ); - - currentItem++; - } - $this.append( $row ); - // console.log ( "I> rowData.data.length="+rowData.data.length +" rowData.width="+rowData.width ); - - currentRow++; - $this.data('lastRow', rowData ); - } - // store the current state of settings and the items in last row - // we'll need this info when we append more items - $this.data('lastSettings', settings ); - - // onComplete callback - // pass back info about list of rows and items rendered - if( typeof (settings.complete) == 'function' ) { - var completeData = { - renderedRows : currentRow, - renderedItems : currentItem - } - settings.complete.call( $this, completeData ); - } - }); - }; + * Flowr.js - Simple jQuery plugin to emulate Flickr's justified view + * For usage information refer to http://github.com/kalyan02/flowr-js + * + * + * @author: Kalyan Chakravarthy (http://KalyanChakravarthy.net) + * @version: v0.1 + */ +(function($) { + //$("#container2").css( 'border', '1px solid #ccc'); + $.fn.flowr = function(options) { + + $this = this; + var ROW_CLASS_NAME = 'flowr-row'; // Class name for the row of flowy + var MAX_LAST_ROW_GAP = 25; // If the width of last row is lesser than max-width, recalculation is needed + var NO_COPY_FIELDS = ['complete', 'data', 'responsive']; // these attributes will not be carried forward for append related calls + var DEFAULTS = { + 'data': [], + 'padding': 5, // whats the padding between flowy items + 'height': 240, // Minimum height an image row should take + 'render': null, // callback function to get the tag + 'append': false, // TODO + 'widthAttr': 'width', // a custom data structure can specify which attribute refers to height/width + 'heightAttr': 'height', + 'maxScale': 1.5, // In case there is only 1 elment in last row + 'maxWidth': this.width() - 1, // 1px is just for offset + 'itemWidth': null, // callback function for width + 'itemHeight': null, // callback function for height + 'complete': null, // complete callback + 'rowClassName': ROW_CLASS_NAME, + 'rows': -1, // Maximum number of rows to render. -1 for no limit. + 'responsive': true // make content responsive + }; + var settings = $.extend(DEFAULTS, options); + + // If data is being appended, we already have settings + // If we already have settings, retrieve them + if (settings.append && $this.data('lastSettings')) { + lastSettings = $this.data('lastSettings'); + + // Copy over the settings from previous init + for (attr in DEFAULTS) { + if (NO_COPY_FIELDS.indexOf(attr) < 0 && settings[attr] == DEFAULTS[attr]) { + settings[attr] = lastSettings[attr]; + } + } + + // Check if we have an incomplete last row + lastRow = $this.data('lastRow'); + if (lastRow.data.length > 0 && settings.maxWidth - lastRow.width > MAX_LAST_ROW_GAP) { + // Prepend the incomplete row to newly loaded data and redraw + lastRowData = lastSettings.data.slice(lastSettings.data.length - lastRow.data.length - 1); + settings.data = lastRowData.concat(settings.data); + + // Remove the incomplete row + // TODO: Don't reload this stuff later. Reattach to new row. + $('.' + settings.rowClassName + ':last', $this).detach(); + } else { + // console.log( lastRow.data.length ); + // console.log( lastRow.width ); + } + } + + // only on the first initial call + if (!settings.responsive && !settings.append) + $this.width($this.width()); + + // Basic sanity checks + if (!(settings.data instanceof Array)) + return; + + if (typeof(settings.padding) != 'number') + settings.padding = parseInt(settings.padding); + + if (typeof(settings.itemWidth) != 'function') { + settings.itemWidth = function(data) { + return data[settings.widthAttr]; + } + } + + if (typeof(settings.itemHeight) != 'function') { + settings.itemHeight = function(data) { + return data[settings.heightAttr]; + } + } + + // A standalone utility to calculate the item widths for a particular row + // Returns rowWidth: width occupied & data : the items in the new row + var utils = { + getNextRow: function(data, settings) { + var itemIndex = 0; + var itemsLength = data.length; + var lineItems = []; + var lineWidth = 0; + var maxWidth = settings.maxWidth; + var paddingSize = settings.padding; + + // console.log( 'maxItems=' + data.length ); + + requiredPadding = function() { + var extraPads = arguments.length == 1 ? arguments[0] : 0; + return (lineItems.length - 1 + extraPads) * settings.padding; + } + + while (lineWidth + requiredPadding() < settings.maxWidth && (itemIndex < itemsLength)) { + var itemData = data[itemIndex]; + var itemWidth = settings.itemWidth.call($this, itemData); + var itemHeight = settings.itemHeight.call($this, itemData); + + var minHeight = settings.height; + var minWidth = Math.floor(itemWidth * settings.height / itemHeight); + + + if (minWidth > settings.maxWidth) { + // very short+wide images like panoramas + // show them even if ugly, as wide as possible + minWidth = settings.maxWidth - 1 - requiredPadding(1); + minHeight = settings.height * minHeight / minWidth; + } + var newLineWidth = lineWidth + minWidth; + + // console.log( 'lineWidth = ' + lineWidth ); + // console.log( 'newLineWidth = ' + newLineWidth ); + if (newLineWidth < settings.maxWidth) { + lineItems.push({ + 'height': minHeight, + 'width': minWidth, + 'itemData': itemData + }); + + lineWidth += minWidth; + itemIndex++; + } else { + // We'd have exceeded width. So break off to scale. + // console.log( 'breaking off = ' + itemIndex ); + // console.log( 'leave off size = ' + lineItems.length ); + break; + } + } //while + + // Scale the size to max width + testWidth = 0; + if (lineWidth < settings.maxWidth) { + var fullScaleWidth = settings.maxWidth - requiredPadding() - 10; + var currScaleWidth = lineWidth; + var scaleFactor = fullScaleWidth / currScaleWidth; + if (scaleFactor > settings.maxScale) + scaleFactor = 1; + + var newHeight = Math.round(settings.height * scaleFactor); + for (i = 0; i < lineItems.length; i++) { + var lineItem = lineItems[i]; + lineItem.width = Math.floor(lineItem.width * scaleFactor); + lineItem.height = newHeight; + + testWidth += lineItem.width; + } + } + + return { + data: lineItems, + width: testWidth + requiredPadding() + }; + }, //getNextRow + reorderContent: function() { + /* + TODO: optimize for faster resizing by reusing dom objects instead of killing the dom + */ + var _initialWidth = $this.data('width'); + var _newWidth = $this.width(); + var _change = _initialWidth - _newWidth; + + if (_initialWidth != _newWidth) { + $this.html(''); + var _settings = $this.data('lastSettings'); + _settings.data = $this.data('data'); + _settings.maxWidth = $this.width() - 1; + $this.flowr(_settings); + } + } + } //utils + + // If the resposive var is set to true then listen for resize method + // and prevent resizing from happening twice if responsive is set again during append phase! + if (settings.responsive && !$this.data('__responsive')) { + $(window).resize(function() { + initialWidth = $this.data('width'); + newWidth = $this.width(); + + //initiate resize + if (initialWidth != newWidth) { + var task_id = $this.data('task_id'); + if (task_id) { + task_id = clearTimeout(task_id); + task_id = null; + } + task_id = setTimeout(utils.reorderContent, 80); + $this.data('task_id', task_id); + } + }); + $this.data('__responsive', true); + } + + + return this.each(function() { + + // Get a copy of original data. 1 level deep copy is sufficient. + var data = settings.data.slice(0); + var rowData = null; + var currentRow = 0; + var currentItem = 0; + + // Store all the data + var allData = $this.data('data') || []; + for (i = 0; i < data.length; i++) { + allData.push(data[i]); + } + $this.data('data', allData); + + // While we have a new row + while ((rowData = utils.getNextRow(data, settings)) != null && rowData.data.length > 0) { + if (settings.rows > 0 && currentRow >= settings.rows) + break; + // remove the number of elements in the new row from the top of data stack + data.splice(0, rowData.data.length); + + // Create a new row div, add class, append the htmls and insert the flowy items + var $row = $('<div>').addClass(settings.rowClassName); + var slack = $this[0].clientWidth - rowData.width - 2 * settings.padding + for (i = 0; i < rowData.data.length; i++) { + var displayData = rowData.data[i]; + // Get the HTML object from custom render function passed as argument + var displayObject = settings.render.call($this, displayData); + displayObject = $(displayObject); + extraw = Math.floor(slack/rowData.data.length) + if (i == 0) { + extraw += slack % rowData.data.length + } + // Set some basic stuff + displayObject + .css('width', displayData.width + extraw) + .css('height', displayData.height) + .css('margin-bottom', settings.padding + "px") + .css('margin-left', i == 0 ? '0' : settings.padding + "px"); //TODO:Refactor + $row.append(displayObject); + + currentItem++; + } + $this.append($row); + // console.log ( "I> rowData.data.length="+rowData.data.length +" rowData.width="+rowData.width ); + + currentRow++; + $this.data('lastRow', rowData); + } + // store the current state of settings and the items in last row + // we'll need this info when we append more items + $this.data('lastSettings', settings); + + // onComplete callback + // pass back info about list of rows and items rendered + if (typeof(settings.complete) == 'function') { + var completeData = { + renderedRows: currentRow, + renderedItems: currentItem + } + settings.complete.call($this, completeData); + } + }); + }; })(jQuery); diff --git a/nikola/data/themes/bootstrap3/assets/js/jquery.colorbox-min.js b/nikola/data/themes/bootstrap3/assets/js/jquery.colorbox-min.js new file mode 120000 index 0000000..9e40fd4 --- /dev/null +++ b/nikola/data/themes/bootstrap3/assets/js/jquery.colorbox-min.js @@ -0,0 +1 @@ +../../../../../../bower_components/jquery-colorbox/jquery.colorbox-min.js
\ No newline at end of file diff --git a/nikola/data/themes/bootstrap3/assets/js/jquery.min.js b/nikola/data/themes/bootstrap3/assets/js/jquery.min.js new file mode 120000 index 0000000..5c080da --- /dev/null +++ b/nikola/data/themes/bootstrap3/assets/js/jquery.min.js @@ -0,0 +1 @@ +../../../../../../bower_components/jquery/dist/jquery.min.js
\ No newline at end of file diff --git a/nikola/data/themes/bootstrap3/assets/js/jquery.min.map b/nikola/data/themes/bootstrap3/assets/js/jquery.min.map new file mode 120000 index 0000000..7e2c217 --- /dev/null +++ b/nikola/data/themes/bootstrap3/assets/js/jquery.min.map @@ -0,0 +1 @@ +../../../../../../bower_components/jquery/dist/jquery.min.map
\ No newline at end of file diff --git a/nikola/data/themes/bootstrap3/templates/authors.tmpl b/nikola/data/themes/bootstrap3/templates/authors.tmpl new file mode 100644 index 0000000..2d3bbf5 --- /dev/null +++ b/nikola/data/themes/bootstrap3/templates/authors.tmpl @@ -0,0 +1,17 @@ +## -*- coding: utf-8 -*- +<%inherit file="base.tmpl"/> + +<%block name="content"> +% if items: + <h2>${messages("Authors")}</h2> +% endif +% if items: + <ul class="list-inline"> + % for text, link in items: + % if text not in hidden_authors: + <li><a class="reference badge" href="${link}">${text|h}</a></li> + % endif + % endfor + </ul> +% endif +</%block> diff --git a/nikola/data/themes/bootstrap3/templates/base.tmpl b/nikola/data/themes/bootstrap3/templates/base.tmpl index 5f90733..ab7805b 100644 --- a/nikola/data/themes/bootstrap3/templates/base.tmpl +++ b/nikola/data/themes/bootstrap3/templates/base.tmpl @@ -24,11 +24,11 @@ ${template_hooks['extra_head']()} </button> <a class="navbar-brand" href="${abs_link(_link("root", None, lang))}"> %if logo_url: - <img src="${logo_url}" alt="${blog_title}" id="logo"> + <img src="${logo_url}" alt="${blog_title|h}" id="logo"> %endif % if show_blog_title: - <span id="blog-title">${blog_title}</span> + <span id="blog-title">${blog_title|h}</span> % endif </a> </div><!-- /.navbar-header --> diff --git a/nikola/data/themes/bootstrap3/templates/base_helper.tmpl b/nikola/data/themes/bootstrap3/templates/base_helper.tmpl index a1e7508..587a933 100644 --- a/nikola/data/themes/bootstrap3/templates/base_helper.tmpl +++ b/nikola/data/themes/bootstrap3/templates/base_helper.tmpl @@ -26,9 +26,11 @@ dir="rtl" \ lang="${lang}"> <head> <meta charset="utf-8"> + % if use_base_tag: <base href="${abs_link(permalink)}"> + % endif %if description: - <meta name="description" content="${description}"> + <meta name="description" content="${description|h}"> %endif <meta name="viewport" content="width=device-width, initial-scale=1"> %if title == blog_title: @@ -38,6 +40,7 @@ lang="${lang}"> %endif ${html_stylesheets()} + <meta content="${theme_color}" name="theme-color"> ${html_feedlinks()} <link rel="canonical" href="${abs_link(permalink)}"> diff --git a/nikola/data/themes/bootstrap3/templates/gallery.tmpl b/nikola/data/themes/bootstrap3/templates/gallery.tmpl index 3d6c01a..af0c194 100644 --- a/nikola/data/themes/bootstrap3/templates/gallery.tmpl +++ b/nikola/data/themes/bootstrap3/templates/gallery.tmpl @@ -17,7 +17,7 @@ %if folders: <ul> % for folder, ftitle in folders: - <li><a href="${folder}"><i class="glyphicon glyphicon-folder-open"></i> ${ftitle}</a></li> + <li><a href="${folder}"><i class="glyphicon glyphicon-folder-open"></i> ${ftitle|h}</a></li> % endfor </ul> %endif @@ -27,8 +27,8 @@ <noscript> <ul class="thumbnails"> %for image in photo_array: - <li><a href="${image['url']}" class="thumbnail image-reference" title="${image['title']}"> - <img src="${image['url_thumb']}" alt="${image['title']}" /></a> + <li><a href="${image['url']}" class="thumbnail image-reference" title="${image['title']|h}"> + <img src="${image['url_thumb']}" alt="${image['title']|h}" /></a> %endfor </ul> </noscript> diff --git a/nikola/data/themes/bootstrap3/templates/listing.tmpl b/nikola/data/themes/bootstrap3/templates/listing.tmpl index 7b09e3e..8c43806 100644 --- a/nikola/data/themes/bootstrap3/templates/listing.tmpl +++ b/nikola/data/themes/bootstrap3/templates/listing.tmpl @@ -7,10 +7,10 @@ ${ui.bar(crumbs)} %if folders or files: <ul class="list-unstyled"> % for name in folders: - <li><a href="${name}"><i class="glyphicon glyphicon-folder-open"></i> ${name}</a> + <li><a href="${name|u}"><i class="glyphicon glyphicon-folder-open"></i> ${name|h}</a> % endfor % for name in files: - <li><a href="${name}.html"><i class="glyphicon glyphicon-file"></i> ${name}</a> + <li><a href="${name|u}.html"><i class="glyphicon glyphicon-file"></i> ${name|h}</a> % endfor </ul> %endif diff --git a/nikola/data/themes/bootstrap3/templates/post.tmpl b/nikola/data/themes/bootstrap3/templates/post.tmpl index 8d9f88a..469c1e1 100644 --- a/nikola/data/themes/bootstrap3/templates/post.tmpl +++ b/nikola/data/themes/bootstrap3/templates/post.tmpl @@ -10,9 +10,9 @@ <meta name="keywords" content="${post.meta('keywords')|h}"> % endif %if post.description(): - <meta name="description" itemprop="description" content="${post.description()}"> + <meta name="description" itemprop="description" content="${post.description()|h}"> %endif - <meta name="author" content="${post.author()}"> + <meta name="author" content="${post.author()|h}"> %if post.prev_post: <link rel="prev" href="${post.prev_post.permalink()}" title="${post.prev_post.title()|h}" type="text/html"> %endif diff --git a/nikola/data/themes/bootstrap3/templates/tags.tmpl b/nikola/data/themes/bootstrap3/templates/tags.tmpl index ead3b0a..061bb39 100644 --- a/nikola/data/themes/bootstrap3/templates/tags.tmpl +++ b/nikola/data/themes/bootstrap3/templates/tags.tmpl @@ -2,16 +2,16 @@ <%inherit file="base.tmpl"/> <%block name="content"> -<h1>${title}</h1> +<h1>${title|h}</h1> % if cat_items: % if items: <h2>${messages("Categories")}</h2> % endif % for text, full_name, path, link, indent_levels, indent_change_before, indent_change_after in cat_hierarchy: % for i in range(indent_change_before): - <ul class="unstyled"> + <ul class="list-inline"> % endfor - <li><a class="reference badge" href="${link}">${text}</a> + <li><a class="reference badge" href="${link}">${text|h}</a> % if indent_change_after <= 0: </li> % endif @@ -30,7 +30,7 @@ <ul class="list-inline"> % for text, link in items: % if text not in hidden_tags: - <li><a class="reference badge" href="${link}">${text}</a></li> + <li><a class="reference badge" href="${link}">${text|h}</a></li> % endif % endfor </ul> diff --git a/nikola/filters.py b/nikola/filters.py index 4304860..0c477c2 100644 --- a/nikola/filters.py +++ b/nikola/filters.py @@ -26,19 +26,23 @@ """Utility functions to help run filters on files.""" -from .utils import req_missing from functools import wraps import os import io +import json import shutil import subprocess import tempfile import shlex +import lxml try: import typogrify.filters as typo except ImportError: typo = None # NOQA +import requests + +from .utils import req_missing, LOGGER def apply_to_binary_file(f): @@ -232,8 +236,10 @@ def minify_lines(data): def typogrify(data): """Prettify text with typogrify.""" if typo is None: - req_missing(['typogrify'], 'use the typogrify filter') + req_missing(['typogrify'], 'use the typogrify filter', optional=True) + return data + data = _normalize_html(data) data = typo.amp(data) data = typo.widont(data) data = typo.smartypants(data) @@ -251,6 +257,7 @@ def typogrify_sans_widont(data): if typo is None: req_missing(['typogrify'], 'use the typogrify_sans_widont filter') + data = _normalize_html(data) data = typo.amp(data) data = typo.smartypants(data) # Disabled because of typogrify bug where it breaks <title> @@ -274,3 +281,56 @@ def php_template_injection(data): return phpdata else: return data + + +@apply_to_text_file +def cssminify(data): + """Minify CSS using http://cssminifier.com/.""" + try: + url = 'http://cssminifier.com/raw' + _data = {'input': data} + response = requests.post(url, data=_data) + return response.text + except Exception as exc: + LOGGER.error("can't use cssminifier.com: {}", exc) + return data + + +@apply_to_text_file +def jsminify(data): + """Minify JS using http://javascript-minifier.com/.""" + try: + url = 'http://javascript-minifier.com/raw' + _data = {'input': data} + response = requests.post(url, data=_data) + return response.text + except Exception as exc: + LOGGER.error("can't use javascript-minifier.com: {}", exc) + return data + + +@apply_to_text_file +def jsonminify(data): + """Minify JSON files (strip whitespace and use minimal separators).""" + data = json.dumps(json.loads(data), indent=None, separators=(',', ':')) + return data + + +@apply_to_binary_file +def xmlminify(data): + """Minify XML files (strip whitespace and use minimal separators).""" + parser = lxml.etree.XMLParser(remove_blank_text=True) + newdata = lxml.etree.XML(data, parser=parser) + return lxml.etree.tostring(newdata, encoding='utf-8', method='xml', xml_declaration=True) + + +def _normalize_html(data): + """Pass HTML through LXML to clean it up, if possible.""" + try: + data = lxml.html.tostring(lxml.html.fromstring(data), encoding='unicode') + except: + pass + return data + + +normalize_html = apply_to_text_file(_normalize_html) diff --git a/nikola/image_processing.py b/nikola/image_processing.py index 0ba139f..b6f8215 100644 --- a/nikola/image_processing.py +++ b/nikola/image_processing.py @@ -29,6 +29,9 @@ from __future__ import unicode_literals import datetime import os +import lxml +import re +import gzip from nikola import utils @@ -45,15 +48,14 @@ except ImportError: class ImageProcessor(object): - """Apply image operations.""" image_ext_list_builtin = ['.jpg', '.png', '.jpeg', '.gif', '.svg', '.bmp', '.tiff'] def resize_image(self, src, dst, max_size, bigger_panoramas=True): """Make a copy of the image in the requested size.""" - if not Image: - utils.copy_file(src, dst) + if not Image or os.path.splitext(src)[1] in ['.svg', '.svgz']: + self.resize_svg(src, dst, max_size, bigger_panoramas) return im = Image.open(src) w, h = im.size @@ -90,6 +92,48 @@ class ImageProcessor(object): else: # Image is small utils.copy_file(src, dst) + def resize_svg(self, src, dst, max_size, bigger_panoramas): + """Make a copy of an svg at the requested size.""" + try: + # Resize svg based on viewport hacking. + # note that this can also lead to enlarged svgs + if src.endswith('.svgz'): + with gzip.GzipFile(src) as op: + xml = op.read() + else: + with open(src) as op: + xml = op.read() + tree = lxml.etree.XML(xml) + width = tree.attrib['width'] + height = tree.attrib['height'] + w = int(re.search("[0-9]+", width).group(0)) + h = int(re.search("[0-9]+", height).group(0)) + # calculate new size preserving aspect ratio. + ratio = float(w) / h + # Panoramas get larger thumbnails because they look *awful* + if bigger_panoramas and w > 2 * h: + max_size = max_size * 4 + if w > h: + w = max_size + h = max_size / ratio + else: + w = max_size * ratio + h = max_size + w = int(w) + h = int(h) + tree.attrib.pop("width") + tree.attrib.pop("height") + tree.attrib['viewport'] = "0 0 %ipx %ipx" % (w, h) + if dst.endswith('.svgz'): + op = gzip.GzipFile(dst, 'w') + else: + op = open(dst, 'w') + op.write(lxml.etree.tostring(tree)) + op.close() + except (KeyError, AttributeError) as e: + self.logger.warn("No width/height in %s. Original exception: %s" % (src, e)) + utils.copy_file(src, dst) + def image_date(self, src): """Try to figure out the date of the image.""" if src not in self.dates: @@ -103,8 +147,10 @@ class ImageProcessor(object): decoded = ExifTags.TAGS.get(tag, tag) if decoded in ('DateTimeOriginal', 'DateTimeDigitized'): try: + if isinstance(value, tuple): + value = value[0] self.dates[src] = datetime.datetime.strptime( - value, r'%Y:%m:%d %H:%M:%S') + value, '%Y:%m:%d %H:%M:%S') break except ValueError: # Invalid EXIF date. pass diff --git a/nikola/nikola.py b/nikola/nikola.py index e0af7ad..9e9b849 100644 --- a/nikola/nikola.py +++ b/nikola/nikola.py @@ -80,7 +80,7 @@ else: # Default "Read more..." link DEFAULT_INDEX_READ_MORE_LINK = '<p class="more"><a href="{link}">{read_more}…</a></p>' -DEFAULT_RSS_READ_MORE_LINK = '<p><a href="{link}">{read_more}…</a> ({min_remaining_read})</p>' +DEFAULT_FEED_READ_MORE_LINK = '<p><a href="{link}">{read_more}…</a> ({min_remaining_read})</p>' # Default pattern for translation files' names DEFAULT_TRANSLATIONS_PATTERN = '{path}.{lang}.{ext}' @@ -105,6 +105,7 @@ LEGAL_VALUES = { 'ar': 'Arabic', 'az': 'Azerbaijani', 'bg': 'Bulgarian', + 'bs': 'Bosnian', 'ca': 'Catalan', ('cs', 'cz'): 'Czech', 'da': 'Danish', @@ -120,6 +121,7 @@ LEGAL_VALUES = { 'fr': 'French', 'hi': 'Hindi', 'hr': 'Croatian', + 'hu': 'Hungarian', 'id': 'Indonesian', 'it': 'Italian', ('ja', '!jp'): 'Japanese', @@ -128,11 +130,13 @@ LEGAL_VALUES = { 'nl': 'Dutch', 'pa': 'Punjabi', 'pl': 'Polish', - 'pt_br': 'Portuguese (Brasil)', + 'pt': 'Portuguese', + 'pt_br': 'Portuguese (Brazil)', 'ru': 'Russian', 'sk': 'Slovak', 'sl': 'Slovene', 'sr': 'Serbian (Cyrillic)', + 'sr_latin': 'Serbian (Latin)', 'sv': 'Swedish', ('tr', '!tr_TR'): 'Turkish', 'ur': 'Urdu', @@ -152,6 +156,7 @@ LEGAL_VALUES = { "fa": "Farsi", # Persian "fr": "French", "hr": "Croatian", + "hu": "Hungarian", "it": "Italian", "jp": "Japanese", "nl": "Dutch", @@ -167,7 +172,6 @@ LEGAL_VALUES = { # country specifier. If there is no other locale that has the same # language with a different country, ``nikola init`` (but nobody else!) # will accept it, warning the user about it. - 'pt': 'pt_br', 'zh': 'zh_cn', }, 'RTL_LANGUAGES': ('ar', 'fa', 'ur'), @@ -187,6 +191,7 @@ LEGAL_VALUES = { fi='fi', fr='fr', hr='hr', + hu='hu', id='id', it='it', ja='ja', @@ -194,11 +199,13 @@ LEGAL_VALUES = { nb='no', nl='nl', pl='pl', + pt='pt-BR', # hope nobody will mind pt_br='pt-BR', ru='ru', sk='sk', sl='si', # country code is si, language code is sl, colorbox is wrong sr='sr', # warning: this is serbian in Latin alphabet + sr_latin='sr', sv='sv', tr='tr', uk='uk', @@ -207,20 +214,27 @@ LEGAL_VALUES = { 'MOMENTJS_LOCALES': defaultdict( str, ar='ar', + az='az', bg='bg', bn='bn', + bs='bs', ca='ca', cs='cs', cz='cs', da='da', de='de', - en='', + el='el', + en='en', + eo='eo', es='es', et='et', + eu='eu', fa='fa', fi='fi', fr='fr', + hi='hi', hr='hr', + hu='hu', id='id', it='it', ja='ja', @@ -228,13 +242,16 @@ LEGAL_VALUES = { nb='nb', nl='nl', pl='pl', + pt='pt', pt_br='pt-br', ru='ru', sk='sk', sl='sl', sr='sr-cyrl', + sr_latin='sr', sv='sv', tr='tr', + uk='uk', zh_cn='zh-cn' ), 'PYPHEN_LOCALES': { @@ -250,16 +267,19 @@ LEGAL_VALUES = { 'et': 'et', 'fr': 'fr', 'hr': 'hr', + 'hu': 'hu', 'it': 'it', 'nb': 'nb', 'nl': 'nl', 'pl': 'pl', + 'pt': 'pt', 'pt_br': 'pt_BR', 'ru': 'ru', 'sk': 'sk', 'sl': 'sl', 'sr': 'sr', 'sv': 'sv', + 'uk': 'uk', }, } @@ -275,7 +295,6 @@ def _enclosure(post, lang): class Nikola(object): - """Class that handles site generation. Takes a site config as argument on creation. @@ -335,6 +354,10 @@ class Nikola(object): 'ARCHIVE_PATH': "", 'ARCHIVE_FILENAME': "archive.html", 'ARCHIVES_ARE_INDEXES': False, + 'AUTHOR_PATH': 'authors', + 'AUTHOR_PAGES_ARE_INDEXES': False, + 'AUTHOR_PAGES_DESCRIPTIONS': {}, + 'AUTHORLIST_MINIMUM_POSTS': 1, 'BLOG_AUTHOR': 'Default Author', 'BLOG_TITLE': 'Default Title', 'BLOG_DESCRIPTION': 'Default Description', @@ -343,6 +366,7 @@ class Nikola(object): 'CATEGORY_PATH': None, # None means: same as TAG_PATH 'CATEGORY_PAGES_ARE_INDEXES': None, # None means: same as TAG_PAGES_ARE_INDEXES 'CATEGORY_PAGES_DESCRIPTIONS': {}, + 'CATEGORY_PAGES_TITLES': {}, 'CATEGORY_PREFIX': 'cat_', 'CATEGORY_ALLOW_HIERARCHIES': False, 'CATEGORY_OUTPUT_FLAT_HIERARCHY': False, @@ -375,6 +399,7 @@ class Nikola(object): 'DISABLED_PLUGINS': [], 'EXTRA_PLUGINS_DIRS': [], 'COMMENT_SYSTEM_ID': 'nikolademo', + 'ENABLE_AUTHOR_PAGES': True, 'EXTRA_HEAD_DATA': '', 'FAVICONS': (), 'FEED_LENGTH': 10, @@ -389,6 +414,7 @@ class Nikola(object): 'GZIP_COMMAND': None, 'GZIP_FILES': False, 'GZIP_EXTENSIONS': ('.txt', '.htm', '.html', '.css', '.js', '.json', '.xml'), + 'HIDDEN_AUTHORS': [], 'HIDDEN_TAGS': [], 'HIDDEN_CATEGORIES': [], 'HYPHENATE': False, @@ -417,27 +443,35 @@ class Nikola(object): 'OLD_THEME_SUPPORT': True, 'OUTPUT_FOLDER': 'output', 'POSTS': (("posts/*.txt", "posts", "post.tmpl"),), + 'POSTS_SECTIONS': True, + 'POSTS_SECTION_ARE_INDEXES': True, + 'POSTS_SECTION_DESCRIPTIONS': "", + 'POSTS_SECTION_FROM_META': False, + 'POSTS_SECTION_NAME': "", + 'POSTS_SECTION_TITLE': "{name}", 'PAGES': (("stories/*.txt", "stories", "story.tmpl"),), 'PANDOC_OPTIONS': [], 'PRETTY_URLS': False, 'FUTURE_IS_NOW': False, 'INDEX_READ_MORE_LINK': DEFAULT_INDEX_READ_MORE_LINK, - 'RSS_READ_MORE_LINK': DEFAULT_RSS_READ_MORE_LINK, - 'RSS_LINKS_APPEND_QUERY': False, 'REDIRECTIONS': [], 'ROBOTS_EXCLUSIONS': [], 'GENERATE_ATOM': False, + 'FEED_TEASERS': True, + 'FEED_PLAIN': False, + 'FEED_PREVIEWIMAGE': True, + 'FEED_READ_MORE_LINK': DEFAULT_FEED_READ_MORE_LINK, + 'FEED_LINKS_APPEND_QUERY': False, 'GENERATE_RSS': True, 'RSS_LINK': None, 'RSS_PATH': '', - 'RSS_PLAIN': False, - 'RSS_TEASERS': True, 'SASS_COMPILER': 'sass', 'SASS_OPTIONS': [], 'SEARCH_FORM': '', 'SHOW_BLOG_TITLE': True, 'SHOW_SOURCELINK': True, 'SHOW_UNTRANSLATED_POSTS': True, + 'SLUG_AUTHOR_PATH': True, 'SLUG_TAG_PATH': True, 'SOCIAL_BUTTONS_CODE': '', 'SITE_URL': 'https://example.com/', @@ -447,18 +481,23 @@ class Nikola(object): 'TAG_PATH': 'categories', 'TAG_PAGES_ARE_INDEXES': False, 'TAG_PAGES_DESCRIPTIONS': {}, + 'TAG_PAGES_TITLES': {}, + 'TAGS_INDEX_PATH': '', 'TAGLIST_MINIMUM_POSTS': 1, 'TEMPLATE_FILTERS': {}, 'THEME': 'bootstrap3', + 'THEME_COLOR': '#5670d4', # light "corporate blue" 'THEME_REVEAL_CONFIG_SUBTHEME': 'sky', 'THEME_REVEAL_CONFIG_TRANSITION': 'cube', 'THUMBNAIL_SIZE': 180, 'UNSLUGIFY_TITLES': False, # WARNING: conf.py.in overrides this with True for backwards compatibility 'URL_TYPE': 'rel_path', + 'USE_BASE_TAG': True, 'USE_BUNDLES': True, 'USE_CDN': False, 'USE_CDN_WARNING': True, 'USE_FILENAME_AS_TITLE': True, + 'USE_KATEX': False, 'USE_OPEN_GRAPH': True, 'USE_SLUGIFY': True, 'TIMEZONE': 'UTC', @@ -510,10 +549,21 @@ class Nikola(object): 'EXTRA_HEAD_DATA', 'NAVIGATION_LINKS', 'INDEX_READ_MORE_LINK', - 'RSS_READ_MORE_LINK', + 'FEED_READ_MORE_LINK', 'INDEXES_TITLE', + 'POSTS_SECTION_COLORS', + 'POSTS_SECTION_DESCRIPTIONS', + 'POSTS_SECTION_NAME', + 'POSTS_SECTION_TITLE', 'INDEXES_PAGES', - 'INDEXES_PRETTY_PAGE_URL',) + 'INDEXES_PRETTY_PAGE_URL', + # PATH options (Issue #1914) + 'TAG_PATH', + 'TAGS_INDEX_PATH', + 'CATEGORY_PATH', + 'DATE_FORMAT', + 'JS_DATE_FORMAT', + ) self._GLOBAL_CONTEXT_TRANSLATABLE = ('blog_author', 'blog_title', @@ -524,10 +574,20 @@ class Nikola(object): 'social_buttons_code', 'search_form', 'body_end', - 'extra_head_data',) + 'extra_head_data', + 'date_format', + 'js_date_format',) # WARNING: navigation_links SHOULD NOT be added to the list above. # Themes ask for [lang] there and we should provide it. + # We first have to massage JS_DATE_FORMAT, otherwise we run into trouble + if 'JS_DATE_FORMAT' in self.config: + if isinstance(self.config['JS_DATE_FORMAT'], dict): + for k in self.config['JS_DATE_FORMAT']: + self.config['JS_DATE_FORMAT'][k] = json.dumps(self.config['JS_DATE_FORMAT'][k]) + else: + self.config['JS_DATE_FORMAT'] = json.dumps(self.config['JS_DATE_FORMAT']) + for i in self.TRANSLATABLE_SETTINGS: try: self.config[i] = utils.TranslatableSetting(i, self.config[i], self.config['TRANSLATIONS']) @@ -555,6 +615,38 @@ class Nikola(object): for i1, i2, i3 in self.config['PAGES']: self.config['post_pages'].append([i1, i2, i3, False]) + # RSS_TEASERS has been replaced with FEED_TEASERS + # TODO: remove on v8 + if 'RSS_TEASERS' in config: + utils.LOGGER.warn('The RSS_TEASERS option is deprecated, use FEED_TEASERS instead.') + if 'FEED_TEASERS' in config: + utils.LOGGER.warn('FEED_TEASERS conflicts with RSS_TEASERS, ignoring RSS_TEASERS.') + self.config['FEED_TEASERS'] = config['RSS_TEASERS'] + + # RSS_PLAIN has been replaced with FEED_PLAIN + # TODO: remove on v8 + if 'RSS_PLAIN' in config: + utils.LOGGER.warn('The RSS_PLAIN option is deprecated, use FEED_PLAIN instead.') + if 'FEED_PLAIN' in config: + utils.LOGGER.warn('FEED_PLIN conflicts with RSS_PLAIN, ignoring RSS_PLAIN.') + self.config['FEED_PLAIN'] = config['RSS_PLAIN'] + + # RSS_LINKS_APPEND_QUERY has been replaced with FEED_LINKS_APPEND_QUERY + # TODO: remove on v8 + if 'RSS_LINKS_APPEND_QUERY' in config: + utils.LOGGER.warn('The RSS_LINKS_APPEND_QUERY option is deprecated, use FEED_LINKS_APPEND_QUERY instead.') + if 'FEED_TEASERS' in config: + utils.LOGGER.warn('FEED_LINKS_APPEND_QUERY conflicts with RSS_LINKS_APPEND_QUERY, ignoring RSS_LINKS_APPEND_QUERY.') + self.config['FEED_LINKS_APPEND_QUERY'] = config['RSS_LINKS_APPEND_QUERY'] + + # RSS_READ_MORE_LINK has been replaced with FEED_READ_MORE_LINK + # TODO: remove on v8 + if 'RSS_READ_MORE_LINK' in config: + utils.LOGGER.warn('The RSS_READ_MORE_LINK option is deprecated, use FEED_READ_MORE_LINK instead.') + if 'FEED_READ_MORE_LINK' in config: + utils.LOGGER.warn('FEED_READ_MORE_LINK conflicts with RSS_READ_MORE_LINK, ignoring RSS_READ_MORE_LINK') + self.config['FEED_READ_MORE_LINK'] = utils.TranslatableSetting('FEED_READ_MORE_LINK', config['RSS_READ_MORE_LINK'], self.config['TRANSLATIONS']) + # DEFAULT_TRANSLATIONS_PATTERN was changed from "p.e.l" to "p.l.e" # TODO: remove on v8 if 'TRANSLATIONS_PATTERN' not in self.config: @@ -634,7 +726,7 @@ class Nikola(object): if not self.config.get('COPY_SOURCES'): self.config['SHOW_SOURCELINK'] = False - if self.config['CATEGORY_PATH'] is None: + if self.config['CATEGORY_PATH']._inp is None: self.config['CATEGORY_PATH'] = self.config['TAG_PATH'] if self.config['CATEGORY_PAGES_ARE_INDEXES'] is None: self.config['CATEGORY_PAGES_ARE_INDEXES'] = self.config['TAG_PAGES_ARE_INDEXES'] @@ -775,7 +867,7 @@ class Nikola(object): # Remove compiler extensions we don't need if p[-1].details.has_option('Nikola', 'compiler') and p[-1].details.get('Nikola', 'compiler') in self.disabled_compilers: bad_candidates.add(p) - utils.LOGGER.debug('Not loading comopiler extension {}', p[-1].name) + utils.LOGGER.debug('Not loading compiler extension {}', p[-1].name) self.plugin_manager._candidates = list(set(self.plugin_manager._candidates) - bad_candidates) self.plugin_manager.loadPlugins() @@ -828,13 +920,16 @@ class Nikola(object): self._GLOBAL_CONTEXT['rel_link'] = self.rel_link self._GLOBAL_CONTEXT['abs_link'] = self.abs_link self._GLOBAL_CONTEXT['exists'] = self.file_exists + self._GLOBAL_CONTEXT['SLUG_AUTHOR_PATH'] = self.config['SLUG_AUTHOR_PATH'] self._GLOBAL_CONTEXT['SLUG_TAG_PATH'] = self.config['SLUG_TAG_PATH'] self._GLOBAL_CONTEXT['annotations'] = self.config['ANNOTATIONS'] self._GLOBAL_CONTEXT['index_display_post_count'] = self.config[ 'INDEX_DISPLAY_POST_COUNT'] self._GLOBAL_CONTEXT['index_file'] = self.config['INDEX_FILE'] + self._GLOBAL_CONTEXT['use_base_tag'] = self.config['USE_BASE_TAG'] self._GLOBAL_CONTEXT['use_bundles'] = self.config['USE_BUNDLES'] self._GLOBAL_CONTEXT['use_cdn'] = self.config.get("USE_CDN") + self._GLOBAL_CONTEXT['theme_color'] = self.config.get("THEME_COLOR") self._GLOBAL_CONTEXT['favicons'] = self.config['FAVICONS'] self._GLOBAL_CONTEXT['date_format'] = self.config.get('DATE_FORMAT') self._GLOBAL_CONTEXT['blog_author'] = self.config.get('BLOG_AUTHOR') @@ -842,6 +937,8 @@ class Nikola(object): self._GLOBAL_CONTEXT['show_blog_title'] = self.config.get('SHOW_BLOG_TITLE') self._GLOBAL_CONTEXT['logo_url'] = self.config.get('LOGO_URL') self._GLOBAL_CONTEXT['blog_description'] = self.config.get('BLOG_DESCRIPTION') + self._GLOBAL_CONTEXT['color_hsl_adjust_hex'] = utils.color_hsl_adjust_hex + self._GLOBAL_CONTEXT['colorize_str_from_base_color'] = utils.colorize_str_from_base_color # TODO: remove in v8 self._GLOBAL_CONTEXT['blog_desc'] = self.config.get('BLOG_DESCRIPTION') @@ -858,6 +955,7 @@ class Nikola(object): self._GLOBAL_CONTEXT['site_has_comments'] = bool(self.config.get('COMMENT_SYSTEM')) self._GLOBAL_CONTEXT['mathjax_config'] = self.config.get( 'MATHJAX_CONFIG') + self._GLOBAL_CONTEXT['use_katex'] = self.config.get('USE_KATEX') self._GLOBAL_CONTEXT['subtheme'] = self.config.get('THEME_REVEAL_CONFIG_SUBTHEME') self._GLOBAL_CONTEXT['transition'] = self.config.get('THEME_REVEAL_CONFIG_TRANSITION') self._GLOBAL_CONTEXT['content_footer'] = self.config.get( @@ -879,12 +977,20 @@ class Nikola(object): 'SHOW_SOURCELINK') self._GLOBAL_CONTEXT['extra_head_data'] = self.config.get('EXTRA_HEAD_DATA') self._GLOBAL_CONTEXT['date_fanciness'] = self.config.get('DATE_FANCINESS') - self._GLOBAL_CONTEXT['js_date_format'] = json.dumps(self.config.get('JS_DATE_FORMAT')) + self._GLOBAL_CONTEXT['js_date_format'] = self.config.get('JS_DATE_FORMAT') self._GLOBAL_CONTEXT['colorbox_locales'] = LEGAL_VALUES['COLORBOX_LOCALES'] self._GLOBAL_CONTEXT['momentjs_locales'] = LEGAL_VALUES['MOMENTJS_LOCALES'] self._GLOBAL_CONTEXT['hidden_tags'] = self.config.get('HIDDEN_TAGS') self._GLOBAL_CONTEXT['hidden_categories'] = self.config.get('HIDDEN_CATEGORIES') + self._GLOBAL_CONTEXT['hidden_authors'] = self.config.get('HIDDEN_AUTHORS') self._GLOBAL_CONTEXT['url_replacer'] = self.url_replacer + self._GLOBAL_CONTEXT['posts_sections'] = self.config.get('POSTS_SECTIONS') + self._GLOBAL_CONTEXT['posts_section_are_indexes'] = self.config.get('POSTS_SECTION_ARE_INDEXES') + self._GLOBAL_CONTEXT['posts_section_colors'] = self.config.get('POSTS_SECTION_COLORS') + self._GLOBAL_CONTEXT['posts_section_descriptions'] = self.config.get('POSTS_SECTION_DESCRIPTIONS') + self._GLOBAL_CONTEXT['posts_section_from_meta'] = self.config.get('POSTS_SECTION_FROM_META') + self._GLOBAL_CONTEXT['posts_section_name'] = self.config.get('POSTS_SECTION_NAME') + self._GLOBAL_CONTEXT['posts_section_title'] = self.config.get('POSTS_SECTION_TITLE') # IPython theme configuration. If a website has ipynb enabled in post_pages # we should enable the IPython CSS (leaving that up to the theme itself). @@ -1064,7 +1170,7 @@ class Nikola(object): doc.rewrite_links(lambda dst: self.url_replacer(src, dst, lang), resolve_base_href=False) # lxml ignores srcset in img and source elements, so do that by hand - objs = list(doc.findall('*//img')) + list(doc.findall('*//source')) + objs = list(doc.xpath('(*//img|*//source)')) for obj in objs: if 'srcset' in obj.attrib: urls = [u.strip() for u in obj.attrib['srcset'].split(',')] @@ -1192,7 +1298,7 @@ class Nikola(object): """Take all necessary data, and render a RSS feed in output_path.""" rss_obj = utils.ExtendedRSS2( title=title, - link=link, + link=utils.encodelink(link), description=description, lastBuildDate=datetime.datetime.utcnow(), generator='https://getnikola.com/', @@ -1213,10 +1319,12 @@ class Nikola(object): for post in timeline[:feed_length]: data = post.text(lang, teaser_only=rss_teasers, strip_html=rss_plain, - rss_read_more_link=True, rss_links_append_query=feed_append_query) + feed_read_more_link=True, feed_links_append_query=feed_append_query) if feed_url is not None and data: # Massage the post's HTML (unless plain) if not rss_plain: + if self.config["FEED_PREVIEWIMAGE"] and 'previewimage' in post.meta[lang] and post.meta[lang]['previewimage'] not in data: + data = "<figure><img src=\"{}\"></figure> {}".format(post.meta[lang]['previewimage'], data) # FIXME: this is duplicated with code in Post.text() try: doc = lxml.html.document_fromstring(data) @@ -1308,7 +1416,6 @@ class Nikola(object): try: path = self.path_handlers[kind](name, lang) path = [os.path.normpath(p) for p in path if p != '.'] # Fix Issue #1028 - if is_link: link = '/' + ('/'.join(path)) index_len = len(self.config['INDEX_FILE']) @@ -1324,13 +1431,25 @@ class Nikola(object): return "" def post_path(self, name, lang): - """Handle post_path paths.""" + """Link to the destination of an element in the POSTS/PAGES settings. + + Example: + + link://post_path/posts => /blog + """ return [_f for _f in [self.config['TRANSLATIONS'][lang], os.path.dirname(name), self.config['INDEX_FILE']] if _f] def root_path(self, name, lang): - """Handle root_path paths.""" + """Link to the current language's root. + + Example: + + link://root_path => / + + link://root_path => /translations/spanish/ + """ d = self.config['TRANSLATIONS'][lang] if d: return [d, ''] @@ -1338,7 +1457,12 @@ class Nikola(object): return [] def slug_path(self, name, lang): - """Handle slug paths.""" + """A link to a post with given slug, if not ambiguous. + + Example: + + links://slug/yellow-camaro => /posts/cars/awful/yellow-camaro/index.html + """ results = [p for p in self.timeline if p.meta('slug') == name] if not results: utils.LOGGER.warning("Cannot resolve path request for slug: {0}".format(name)) @@ -1348,7 +1472,12 @@ class Nikola(object): return [_f for _f in results[0].permalink(lang).split('/') if _f] def filename_path(self, name, lang): - """Handle filename paths.""" + """Link to post or story by source filename. + + Example: + + link://filename/manual.txt => /docs/handbook.html + """ results = [p for p in self.timeline if p.source_path == name] if not results: utils.LOGGER.warning("Cannot resolve path request for filename: {0}".format(name)) @@ -1366,7 +1495,9 @@ class Nikola(object): def link(self, *args): """Create a link.""" - return self.path(*args, is_link=True) + url = self.path(*args, is_link=True) + url = utils.encodelink(url) + return url def abs_link(self, dst, protocol_relative=False): """Get an absolute link.""" @@ -1378,6 +1509,7 @@ class Nikola(object): url = urlparse(dst).geturl() if protocol_relative: url = url.split(":", 1)[1] + url = utils.encodelink(url) return url def rel_link(self, src, dst): @@ -1392,7 +1524,7 @@ class Nikola(object): parsed_src = urlsplit(src) parsed_dst = urlsplit(dst) if parsed_src[:2] != parsed_dst[:2]: - return dst + return utils.encodelink(dst) # Now both paths are on the same site and absolute src_elems = parsed_src.path.split('/')[1:] dst_elems = parsed_dst.path.split('/')[1:] @@ -1403,7 +1535,9 @@ class Nikola(object): else: i += 1 # Now i is the longest common prefix - return '/'.join(['..'] * (len(src_elems) - i - 1) + dst_elems[i:]) + url = '/'.join(['..'] * (len(src_elems) - i - 1) + dst_elems[i:]) + url = utils.encodelink(url) + return url def file_exists(self, path, not_empty=False): """Check if the file exists. If not_empty is True, it also must not be empty.""" @@ -1554,7 +1688,7 @@ class Nikola(object): utils.LOGGER.error('Tag {0} is used in: {1}'.format(other_tag, ', '.join([p.source_path for p in self.posts_per_tag[other_tag]]))) quit = True else: - slugged_tags.add(utils.slugify(tag, force=True)) + slugged_tags.add(utils.slugify(tag)) self.posts_per_tag[tag].append(post) for lang in self.config['TRANSLATIONS'].keys(): self.tags_per_language[lang].extend(post.tags_for_language(lang)) @@ -1606,6 +1740,7 @@ class Nikola(object): def generic_page_renderer(self, lang, post, filters, context=None): """Render post fragments to final HTML pages.""" + utils.LocaleBorg().set_locale(lang) context = context.copy() if context else {} deps = post.deps(lang) + \ self.template_system.template_deps(post.template_name) @@ -1711,9 +1846,10 @@ class Nikola(object): link = lxml.etree.Element("link") link.set("rel", link_rel) link.set("type", link_type) - link.set("href", link_href) + link.set("href", utils.encodelink(link_href)) return link + utils.LocaleBorg().set_locale(lang) deps = [] uptodate_deps = [] for post in posts: @@ -1739,15 +1875,13 @@ class Nikola(object): deps_context['navigation_links'] = deps_context['global']['navigation_links'](lang) nslist = {} - if context["is_feed_stale"] or (not context["feedpagenum"] == context["feedpagecount"] - 1 and not context["feedpagenum"] == 0): + if context["is_feed_stale"] or "feedpagenum" in context and (not context["feedpagenum"] == context["feedpagecount"] - 1 and not context["feedpagenum"] == 0): nslist["fh"] = "http://purl.org/syndication/history/1.0" - if not self.config["RSS_TEASERS"]: - nslist["xh"] = "http://www.w3.org/1999/xhtml" feed_xsl_link = self.abs_link("/assets/xml/atom.xsl") feed_root = lxml.etree.Element("feed", nsmap=nslist) feed_root.addprevious(lxml.etree.ProcessingInstruction( "xml-stylesheet", - 'href="' + feed_xsl_link + '" type="text/xsl media="all"')) + 'href="' + utils.encodelink(feed_xsl_link) + '" type="text/xsl media="all"')) feed_root.set("{http://www.w3.org/XML/1998/namespace}lang", lang) feed_root.set("xmlns", "http://www.w3.org/2005/Atom") feed_title = lxml.etree.SubElement(feed_root, "title") @@ -1755,7 +1889,7 @@ class Nikola(object): feed_id = lxml.etree.SubElement(feed_root, "id") feed_id.text = self.abs_link(context["feedlink"]) feed_updated = lxml.etree.SubElement(feed_root, "updated") - feed_updated.text = post.formatted_date('webiso', datetime.datetime.now(tz=dateutil.tz.tzutc())) + feed_updated.text = utils.LocaleBorg().formatted_date('webiso', datetime.datetime.now(tz=dateutil.tz.tzutc())) feed_author = lxml.etree.SubElement(feed_root, "author") feed_author_name = lxml.etree.SubElement(feed_author, "name") feed_author_name.text = self.config["BLOG_AUTHOR"](lang) @@ -1768,50 +1902,64 @@ class Nikola(object): if "prevfeedlink" in context: feed_root.append(atom_link("previous", "application/atom+xml", self.abs_link(context["prevfeedlink"]))) - if context["is_feed_stale"] or not context["feedpagenum"] == 0: + if context["is_feed_stale"] or "feedpagenum" in context and not context["feedpagenum"] == 0: feed_root.append(atom_link("current", "application/atom+xml", self.abs_link(context["currentfeedlink"]))) # Older is "prev-archive" and newer is "next-archive" in archived feeds (opposite of paginated) - if "prevfeedlink" in context and (context["is_feed_stale"] or not context["feedpagenum"] == context["feedpagecount"] - 1): + if "prevfeedlink" in context and (context["is_feed_stale"] or "feedpagenum" in context and not context["feedpagenum"] == context["feedpagecount"] - 1): feed_root.append(atom_link("next-archive", "application/atom+xml", self.abs_link(context["prevfeedlink"]))) if "nextfeedlink" in context: feed_root.append(atom_link("prev-archive", "application/atom+xml", self.abs_link(context["nextfeedlink"]))) - if context["is_feed_stale"] or not context["feedpagenum"] == context["feedpagecount"] - 1: + if context["is_feed_stale"] or "feedpagenum" and not context["feedpagenum"] == context["feedpagecount"] - 1: lxml.etree.SubElement(feed_root, "{http://purl.org/syndication/history/1.0}archive") feed_root.append(atom_link("alternate", "text/html", self.abs_link(context["permalink"]))) feed_generator = lxml.etree.SubElement(feed_root, "generator") - feed_generator.set("uri", "http://getnikola.com/") + feed_generator.set("uri", "https://getnikola.com/") feed_generator.text = "Nikola" feed_append_query = None - if self.config["RSS_LINKS_APPEND_QUERY"]: - feed_append_query = self.config["RSS_LINKS_APPEND_QUERY"].format( + if self.config["FEED_LINKS_APPEND_QUERY"]: + feed_append_query = self.config["FEED_LINKS_APPEND_QUERY"].format( feedRelUri=context["feedlink"], feedFormat="atom") - for post in posts: - data = post.text(lang, teaser_only=self.config["RSS_TEASERS"], strip_html=self.config["RSS_TEASERS"], - rss_read_more_link=True, rss_links_append_query=feed_append_query) - if not self.config["RSS_TEASERS"]: + def atom_post_text(post, text): + if not self.config["FEED_PLAIN"]: + if self.config["FEED_PREVIEWIMAGE"] and 'previewimage' in post.meta[lang] and post.meta[lang]['previewimage'] not in text: + text = "<figure><img src=\"{}\"></figure> {}".format(post.meta[lang]['previewimage'], text) + # FIXME: this is duplicated with code in Post.text() and generic_rss_renderer try: - doc = lxml.html.document_fromstring(data) - doc.rewrite_links(lambda dst: self.url_replacer(post.permalink(), dst, lang, 'absolute')) + doc = lxml.html.document_fromstring(text) + doc.rewrite_links(lambda dst: self.url_replacer(post.permalink(lang), dst, lang, 'absolute')) try: body = doc.body - data = (body.text or '') + ''.join( + text = (body.text or '') + ''.join( [lxml.html.tostring(child, encoding='unicode') for child in body.iterchildren()]) except IndexError: # No body there, it happens sometimes - data = '' + text = '' except lxml.etree.ParserError as e: if str(e) == "Document is empty": - data = "" + text = "" else: # let other errors raise raise(e) + return text.strip() + + for post in posts: + summary = atom_post_text(post, post.text(lang, teaser_only=True, + strip_html=self.config["FEED_PLAIN"], + feed_read_more_link=True, + feed_links_append_query=feed_append_query)) + content = None + if not self.config["FEED_TEASERS"]: + content = atom_post_text(post, post.text(lang, teaser_only=self.config["FEED_TEASERS"], + strip_html=self.config["FEED_PLAIN"], + feed_read_more_link=True, + feed_links_append_query=feed_append_query)) entry_root = lxml.etree.SubElement(feed_root, "entry") entry_title = lxml.etree.SubElement(entry_root, "title") @@ -1828,15 +1976,20 @@ class Nikola(object): entry_root.append(atom_link("alternate", "text/html", post.permalink(lang, absolute=True, query=feed_append_query))) - if self.config["RSS_TEASERS"]: - entry_summary = lxml.etree.SubElement(entry_root, "summary") - entry_summary.text = data + entry_summary = lxml.etree.SubElement(entry_root, "summary") + if not self.config["FEED_PLAIN"]: + entry_summary.set("type", "html") else: + entry_summary.set("type", "text") + entry_summary.text = summary + if content: entry_content = lxml.etree.SubElement(entry_root, "content") - entry_content.set("type", "xhtml") - entry_content_nsdiv = lxml.etree.SubElement(entry_content, "{http://www.w3.org/1999/xhtml}div") - entry_content_nsdiv.text = data - for category in post.tags: + if not self.config["FEED_PLAIN"]: + entry_content.set("type", "html") + else: + entry_content.set("type", "text") + entry_content.text = content + for category in post.tags_for_language(lang): entry_category = lxml.etree.SubElement(entry_root, "category") entry_category.set("term", utils.slugify(category)) entry_category.set("label", category) @@ -1886,8 +2039,7 @@ class Nikola(object): kw['indexes_prety_page_url'] = self.config["INDEXES_PRETTY_PAGE_URL"] kw['demote_headers'] = self.config['DEMOTE_HEADERS'] kw['generate_atom'] = self.config["GENERATE_ATOM"] - kw['feed_link_append_query'] = self.config["RSS_LINKS_APPEND_QUERY"] - kw['feed_teasers'] = self.config["RSS_TEASERS"] + kw['feed_link_append_query'] = self.config["FEED_LINKS_APPEND_QUERY"] kw['currentfeed'] = None # Split in smaller lists @@ -1978,6 +2130,9 @@ class Nikola(object): context["currentfeedlink"] = kw["currentfeed"] context["feedpagenum"] = i context["feedpagecount"] = num_pages + kw['feed_teasers'] = self.config['FEED_TEASERS'] + kw['feed_plain'] = self.config['FEED_PLAIN'] + kw['feed_previewimage'] = self.config['FEED_PREVIEWIMAGE'] atom_task = { "basename": basename, "name": atom_output_name, @@ -2105,7 +2260,7 @@ def valid_locale_fallback(desired_locale=None): """ # Whenever fallbacks change, adjust test TestHarcodedFallbacksWork candidates_windows = [str('English'), str('C')] - candidates_posix = [str('en_US.utf8'), str('C')] + candidates_posix = [str('en_US.UTF-8'), str('C')] candidates = candidates_windows if sys.platform == 'win32' else candidates_posix if desired_locale: candidates = list(candidates) @@ -2139,7 +2294,7 @@ def guess_locale_from_lang_posix(lang): locale_n = str(lang) else: # this works in Travis when locale support set by Travis suggestion - locale_n = str((locale.normalize(lang).split('.')[0]) + '.utf8') + locale_n = str((locale.normalize(lang).split('.')[0]) + '.UTF-8') if not is_valid_locale(locale_n): # http://thread.gmane.org/gmane.comp.web.nikola/337/focus=343 locale_n = str((locale.normalize(lang).split('.')[0])) diff --git a/nikola/plugin_categories.py b/nikola/plugin_categories.py index ecbbb82..b96de4f 100644 --- a/nikola/plugin_categories.py +++ b/nikola/plugin_categories.py @@ -52,7 +52,6 @@ __all__ = ( class BasePlugin(IPlugin): - """Base plugin class.""" def set_site(self, site): @@ -84,7 +83,6 @@ class BasePlugin(IPlugin): class PostScanner(BasePlugin): - """The scan method of these plugins is called by Nikola.scan_posts.""" def scan(self): @@ -93,7 +91,6 @@ class PostScanner(BasePlugin): class Command(BasePlugin, DoitCommand): - """Doit command implementation.""" name = "dummy_command" @@ -156,7 +153,6 @@ DoitCommand.help = help class BaseTask(BasePlugin): - """Base for task generators.""" name = "dummy_task" @@ -179,21 +175,18 @@ class BaseTask(BasePlugin): class Task(BaseTask): - """Task generator.""" name = "dummy_task" class LateTask(BaseTask): - """Late task generator (plugin executed after all Task plugins).""" name = "dummy_latetask" class TemplateSystem(BasePlugin): - """Provide support for templating systems.""" name = "dummy_templates" @@ -224,7 +217,6 @@ class TemplateSystem(BasePlugin): class TaskMultiplier(BasePlugin): - """Take a task and return *more* tasks.""" name = "dummy multiplier" @@ -235,7 +227,6 @@ class TaskMultiplier(BasePlugin): class PageCompiler(BasePlugin): - """Compile text files into HTML.""" name = "dummy_compiler" @@ -300,7 +291,6 @@ class PageCompiler(BasePlugin): class CompilerExtension(BasePlugin): - """An extension for a Nikola compiler. If you intend to implement those in your own compiler, you can: @@ -319,7 +309,6 @@ class CompilerExtension(BasePlugin): class RestExtension(CompilerExtension): - """Extensions for reStructuredText.""" name = "dummy_rest_extension" @@ -327,7 +316,6 @@ class RestExtension(CompilerExtension): class MarkdownExtension(CompilerExtension): - """Extensions for Markdown.""" name = "dummy_markdown_extension" @@ -335,21 +323,18 @@ class MarkdownExtension(CompilerExtension): class SignalHandler(BasePlugin): - """Signal handlers.""" name = "dummy_signal_handler" class ConfigPlugin(BasePlugin): - """A plugin that can edit config (or modify the site) on-the-fly.""" name = "dummy_config_plugin" class Importer(Command): - """Basic structure for importing data into Nikola. The flow is: diff --git a/nikola/plugins/basic_import.py b/nikola/plugins/basic_import.py index 073a539..04f1091 100644 --- a/nikola/plugins/basic_import.py +++ b/nikola/plugins/basic_import.py @@ -48,7 +48,6 @@ links = {} class ImportMixin(object): - """Mixin with common used methods.""" name = "import_mixin" diff --git a/nikola/plugins/command/auto.plugin b/nikola/plugins/command/auto.plugin index 3e2b17d..1081c78 100644 --- a/nikola/plugins/command/auto.plugin +++ b/nikola/plugins/command/auto.plugin @@ -5,7 +5,7 @@ module = auto [Documentation] author = Roberto Alsina version = 2.1.0 -website = http://getnikola.com +website = https://getnikola.com/ description = Automatically detect site changes, rebuild and optionally refresh a browser. [Nikola] diff --git a/nikola/plugins/command/auto/__init__.py b/nikola/plugins/command/auto/__init__.py index 71f9624..e339c06 100644 --- a/nikola/plugins/command/auto/__init__.py +++ b/nikola/plugins/command/auto/__init__.py @@ -63,7 +63,7 @@ except ImportError: from nikola.plugin_categories import Command -from nikola.utils import req_missing, get_logger, get_theme_path, STDERR_HANDLER +from nikola.utils import dns_sd, req_missing, get_logger, get_theme_path, STDERR_HANDLER LRJS_PATH = os.path.join(os.path.dirname(__file__), 'livereload.js') error_signal = signal('error') refresh_signal = signal('refresh') @@ -79,13 +79,14 @@ ERROR {} class CommandAuto(Command): - """Automatic rebuilds for Nikola.""" name = "auto" logger = None has_server = True doc_purpose = "builds and serves a site; automatically detects site changes, rebuilds, and optionally refreshes a browser" + dns_sd = None + cmd_options = [ { 'name': 'port', @@ -156,7 +157,7 @@ class CommandAuto(Command): # Do not duplicate entries -- otherwise, multiple rebuilds are triggered watched = set([ - 'templates/', + 'templates/', 'plugins/', ] + [get_theme_path(name) for name in self.site.THEMES]) for item in self.site.config['post_pages']: watched.add(os.path.dirname(item[0])) @@ -208,7 +209,6 @@ class CommandAuto(Command): parent = self class Mixed(WebSocketWSGIApplication): - """A class that supports WS and HTTP protocols on the same port.""" def __call__(self, environ, start_response): @@ -235,9 +235,12 @@ class CommandAuto(Command): webbrowser.open('http://{0}:{1}'.format(host, port)) try: + self.dns_sd = dns_sd(port, (options['ipv6'] or '::' in host)) ws.serve_forever() except KeyboardInterrupt: self.logger.info("Server is shutting down.") + if self.dns_sd: + self.dns_sd.Reset() # This is a hack, but something is locking up in a futex # and exit() doesn't work. os.kill(os.getpid(), 15) @@ -262,6 +265,8 @@ class CommandAuto(Command): fname = os.path.basename(event_path) if (fname.endswith('~') or fname.startswith('.') or + '__pycache__' in event_path or + event_path.endswith(('.pyc', '.pyo', '.pyd')) or os.path.isdir(event_path)): # Skip on folders, these are usually duplicates return self.logger.info('REBUILDING SITE (from {0})'.format(event_path)) @@ -300,11 +305,14 @@ class CommandAuto(Command): mimetype = 'text/html' if p_uri.path == '/robots.txt': - start_response('200 OK', [('Content-type', 'text/plain')]) + start_response('200 OK', [('Content-type', 'text/plain; charset=UTF-8')]) return ['User-Agent: *\nDisallow: /\n'.encode('utf-8')] elif os.path.isfile(f_path): with open(f_path, 'rb') as fd: - start_response('200 OK', [('Content-type', mimetype)]) + if mimetype.startswith('text/') or mimetype.endswith('+xml'): + start_response('200 OK', [('Content-type', "{0}; charset=UTF-8".format(mimetype))]) + else: + start_response('200 OK', [('Content-type', mimetype)]) return [self.file_filter(mimetype, fd.read())] elif p_uri.path == '/livereload.js': with open(LRJS_PATH, 'rb') as fd: @@ -337,7 +345,6 @@ pending = [] class LRSocket(WebSocket): - """Speak Livereload protocol.""" def __init__(self, *a, **kw): @@ -410,7 +417,6 @@ class LRSocket(WebSocket): class OurWatchHandler(FileSystemEventHandler): - """A Nikola-specific handler for Watchdog.""" def __init__(self, function): @@ -424,7 +430,6 @@ class OurWatchHandler(FileSystemEventHandler): class ConfigWatchHandler(FileSystemEventHandler): - """A Nikola-specific handler for Watchdog that handles the config file (as a workaround).""" def __init__(self, configuration_filename, function): diff --git a/nikola/plugins/command/bootswatch_theme.plugin b/nikola/plugins/command/bootswatch_theme.plugin index fc25045..51e6718 100644 --- a/nikola/plugins/command/bootswatch_theme.plugin +++ b/nikola/plugins/command/bootswatch_theme.plugin @@ -5,7 +5,7 @@ module = bootswatch_theme [Documentation] author = Roberto Alsina version = 1.0 -website = http://getnikola.com +website = https://getnikola.com/ description = Given a swatch name and a parent theme, creates a custom theme. [Nikola] diff --git a/nikola/plugins/command/bootswatch_theme.py b/nikola/plugins/command/bootswatch_theme.py index b5644a1..afd15af 100644 --- a/nikola/plugins/command/bootswatch_theme.py +++ b/nikola/plugins/command/bootswatch_theme.py @@ -37,7 +37,6 @@ LOGGER = utils.get_logger('bootswatch_theme', utils.STDERR_HANDLER) class CommandBootswatchTheme(Command): - """Given a swatch name from bootswatch.com and a parent theme, creates a custom theme.""" name = "bootswatch_theme" @@ -91,12 +90,16 @@ class CommandBootswatchTheme(Command): LOGGER.info("Creating '{0}' theme from '{1}' and '{2}'".format(name, swatch, parent)) utils.makedirs(os.path.join('themes', name, 'assets', 'css')) for fname in ('bootstrap.min.css', 'bootstrap.css'): - url = 'http://bootswatch.com' + url = 'https://bootswatch.com' if version: url += '/' + version url = '/'.join((url, swatch, fname)) LOGGER.info("Downloading: " + url) - data = requests.get(url).text + r = requests.get(url) + if r.status_code > 299: + LOGGER.error('Error {} getting {}', r.status_code, url) + exit(1) + data = r.text with open(os.path.join('themes', name, 'assets', 'css', fname), 'wb+') as output: output.write(data.encode('utf-8')) diff --git a/nikola/plugins/command/check.plugin b/nikola/plugins/command/check.plugin index e380e64..6d2df82 100644 --- a/nikola/plugins/command/check.plugin +++ b/nikola/plugins/command/check.plugin @@ -5,7 +5,7 @@ module = check [Documentation] author = Roberto Alsina version = 1.0 -website = http://getnikola.com +website = https://getnikola.com/ description = Check the generated site [Nikola] diff --git a/nikola/plugins/command/check.py b/nikola/plugins/command/check.py index abf183e..bfc6ee2 100644 --- a/nikola/plugins/command/check.py +++ b/nikola/plugins/command/check.py @@ -46,7 +46,10 @@ from nikola.plugin_categories import Command from nikola.utils import get_logger, STDERR_HANDLER -def _call_nikola_list(site): +def _call_nikola_list(site, cache=None): + if cache is not None: + if 'files' in cache and 'deps' in cache: + return cache['files'], cache['deps'] files = [] deps = defaultdict(list) for task in generate_tasks('render_site', site.gen_tasks('render_site', "Task", '')): @@ -57,16 +60,19 @@ def _call_nikola_list(site): files.extend(task.targets) for target in task.targets: deps[target].extend(task.file_dep) + if cache is not None: + cache['files'] = files + cache['deps'] = deps return files, deps -def real_scan_files(site): +def real_scan_files(site, cache=None): """Scan for files.""" task_fnames = set([]) real_fnames = set([]) output_folder = site.config['OUTPUT_FOLDER'] # First check that all targets are generated in the right places - for fname in _call_nikola_list(site)[0]: + for fname in _call_nikola_list(site, cache)[0]: fname = fname.strip() if fname.startswith(output_folder): task_fnames.add(fname) @@ -94,7 +100,6 @@ def fs_relpath_from_url_path(url_path): class CommandCheck(Command): - """Check the generated site.""" name = "check" @@ -162,22 +167,25 @@ class CommandCheck(Command): self.logger.level = 1 else: self.logger.level = 4 + failure = False if options['links']: - failure = self.scan_links(options['find_sources'], options['remote']) + failure |= self.scan_links(options['find_sources'], options['remote']) if options['files']: - failure = self.scan_files() + failure |= self.scan_files() if options['clean']: - failure = self.clean_files() + failure |= self.clean_files() if failure: return 1 existing_targets = set([]) checked_remote_targets = {} + cache = {} def analyze(self, fname, find_sources=False, check_remote=False): """Analyze links on a page.""" rv = False self.whitelist = [re.compile(x) for x in self.site.config['LINK_CHECK_WHITELIST']] + self.internal_redirects = [urljoin('/', _[0]) for _ in self.site.config['REDIRECTIONS']] base_url = urlparse(self.site.config['BASE_URL']) self.existing_targets.add(self.site.config['SITE_URL']) self.existing_targets.add(self.site.config['BASE_URL']) @@ -185,7 +193,7 @@ class CommandCheck(Command): deps = {} if find_sources: - deps = _call_nikola_list(self.site)[1] + deps = _call_nikola_list(self.site, self.cache)[1] if url_type in ('absolute', 'full_path'): url_netloc_to_root = urlparse(self.site.config['BASE_URL']).path @@ -203,31 +211,70 @@ class CommandCheck(Command): # Quietly ignore files that don’t exist; use `nikola check -f` instead (Issue #1831) return False - d = lxml.html.fromstring(open(filename, 'rb').read()) - for l in d.iterlinks(): + if '.html' == fname[-5:]: + d = lxml.html.fromstring(open(filename, 'rb').read()) + extra_objs = lxml.html.fromstring('<html/>') + + # Turn elements with a srcset attribute into individual img elements with src attributes + for obj in list(d.xpath('(*//img|*//source)')): + if 'srcset' in obj.attrib: + for srcset_item in obj.attrib['srcset'].split(','): + extra_objs.append(lxml.etree.Element('img', src=srcset_item.strip().split(' ')[0])) + link_elements = list(d.iterlinks()) + list(extra_objs.iterlinks()) + # Extract links from XML formats to minimal HTML, allowing those to go through the link checks + elif '.atom' == filename[-5:]: + d = lxml.etree.parse(filename) + link_elements = lxml.html.fromstring('<html/>') + for elm in d.findall('*//{http://www.w3.org/2005/Atom}link'): + feed_link = elm.attrib['href'].split('?')[0].strip() # strip FEED_LINKS_APPEND_QUERY + link_elements.append(lxml.etree.Element('a', href=feed_link)) + link_elements = list(link_elements.iterlinks()) + elif filename.endswith('sitemap.xml') or filename.endswith('sitemapindex.xml'): + d = lxml.etree.parse(filename) + link_elements = lxml.html.fromstring('<html/>') + for elm in d.getroot().findall("*//{http://www.sitemaps.org/schemas/sitemap/0.9}loc"): + link_elements.append(lxml.etree.Element('a', href=elm.text.strip())) + link_elements = list(link_elements.iterlinks()) + else: # unsupported file type + return False + + for l in link_elements: target = l[2] if target == "#": continue - target, _ = urldefrag(target) + target = urldefrag(target)[0] + + if any([urlparse(target).netloc.endswith(_) for _ in ['example.com', 'example.net', 'example.org']]): + self.logger.info("Not testing example address \"{0}\".".format(target)) + continue + + # absolute URL to root-relative + if target.startswith(base_url.geturl()): + target = target.replace(base_url.geturl(), '/') + parsed = urlparse(target) # Warn about links from https to http (mixed-security) if base_url.netloc == parsed.netloc and base_url.scheme == "https" and parsed.scheme == "http": self.logger.warn("Mixed-content security for link in {0}: {1}".format(filename, target)) + # Link to an internal REDIRECTIONS page + if target in self.internal_redirects: + redir_status_code = 301 + redir_target = [_dest for _target, _dest in self.site.config['REDIRECTIONS'] if urljoin('/', _target) == target][0] + self.logger.warn("Remote link moved PERMANENTLY to \"{0}\" and should be updated in {1}: {2} [HTTP: 301]".format(redir_target, filename, target)) + # Absolute links to other domains, skip # Absolute links when using only paths, skip. if ((parsed.scheme or target.startswith('//')) and parsed.netloc != base_url.netloc) or \ ((parsed.scheme or target.startswith('//')) and url_type in ('rel_path', 'full_path')): if not check_remote or parsed.scheme not in ["http", "https"]: continue - if parsed.netloc == base_url.netloc: # absolute URL to self.site - continue if target in self.checked_remote_targets: # already checked this exact target - if self.checked_remote_targets[target] in [301, 307]: + if self.checked_remote_targets[target] in [301, 308]: self.logger.warn("Remote link PERMANENTLY redirected in {0}: {1} [Error {2}]".format(filename, target, self.checked_remote_targets[target])) - elif self.checked_remote_targets[target] in [302, 308]: - self.logger.info("Remote link temporarily redirected in {1}: {2} [HTTP: {3}]".format(filename, target, self.checked_remote_targets[target])) + elif self.checked_remote_targets[target] in [302, 307]: + self.logger.notice("Remote link temporarily redirected in {1}: {2} [HTTP: {3}]".format(filename, target, self.checked_remote_targets[target])) elif self.checked_remote_targets[target] > 399: self.logger.error("Broken link in {0}: {1} [Error {2}]".format(filename, target, self.checked_remote_targets[target])) continue @@ -255,7 +302,7 @@ class CommandCheck(Command): if redir_status_code in [301, 308]: self.logger.warn("Remote link moved PERMANENTLY to \"{0}\" and should be updated in {1}: {2} [HTTP: {3}]".format(resp.url, filename, target, redir_status_code)) if redir_status_code in [302, 307]: - self.logger.info("Remote link temporarily redirected to \"{0}\" in {1}: {2} [HTTP: {3}]".format(resp.url, filename, target, redir_status_code)) + self.logger.notice("Remote link temporarily redirected to \"{0}\" in {1}: {2} [HTTP: {3}]".format(resp.url, filename, target, redir_status_code)) self.checked_remote_targets[resp.url] = resp.status_code self.checked_remote_targets[target] = redir_status_code else: @@ -275,8 +322,9 @@ class CommandCheck(Command): target_filename = os.path.abspath( os.path.join(self.site.config['OUTPUT_FOLDER'], unquote(target.lstrip('/')))) else: # Relative path + unquoted_target = unquote(target).encode('utf-8') if sys.version_info.major >= 3 else unquote(target).decode('utf-8') target_filename = os.path.abspath( - os.path.join(os.path.dirname(filename), unquote(target))) + os.path.join(os.path.dirname(filename).encode('utf-8'), unquoted_target)) elif url_type in ('full_path', 'absolute'): if url_type == 'absolute': @@ -292,9 +340,10 @@ class CommandCheck(Command): if any(re.search(x, target_filename) for x in self.whitelist): continue + elif target_filename not in self.existing_targets: if os.path.exists(target_filename): - self.logger.notice("Good link {0} => {1}".format(target, target_filename)) + self.logger.info(u"Good link {0} => {1}".format(target, target_filename)) self.existing_targets.add(target_filename) else: rv = True @@ -304,7 +353,7 @@ class CommandCheck(Command): self.logger.warn("\n".join(deps[filename])) self.logger.warn("===============================\n") except Exception as exc: - self.logger.error("Error with: {0} {1}".format(filename, exc)) + self.logger.error(u"Error with: {0} {1}".format(filename, exc)) return rv def scan_links(self, find_sources=False, check_remote=False): @@ -315,10 +364,21 @@ class CommandCheck(Command): failure = False # Maybe we should just examine all HTML files output_folder = self.site.config['OUTPUT_FOLDER'] - for fname in _call_nikola_list(self.site)[0]: - if fname.startswith(output_folder) and '.html' == fname[-5:]: - if self.analyze(fname, find_sources, check_remote): - failure = True + + if urlparse(self.site.config['BASE_URL']).netloc == 'example.com': + self.logger.error("You've not changed the SITE_URL (or BASE_URL) setting from \"example.com\"!") + + for fname in _call_nikola_list(self.site, self.cache)[0]: + if fname.startswith(output_folder): + if '.html' == fname[-5:]: + if self.analyze(fname, find_sources, check_remote): + failure = True + if '.atom' == fname[-5:]: + if self.analyze(fname, find_sources, False): + failure = True + if fname.endswith('sitemap.xml') or fname.endswith('sitemapindex.xml'): + if self.analyze(fname, find_sources, False): + failure = True if not failure: self.logger.info("All links checked.") return failure @@ -328,7 +388,7 @@ class CommandCheck(Command): failure = False self.logger.info("Checking Files:") self.logger.info("===============\n") - only_on_output, only_on_input = real_scan_files(self.site) + only_on_output, only_on_input = real_scan_files(self.site, self.cache) # Ignore folders only_on_output = [p for p in only_on_output if not os.path.isdir(p)] @@ -351,7 +411,7 @@ class CommandCheck(Command): def clean_files(self): """Remove orphaned files.""" - only_on_output, _ = real_scan_files(self.site) + only_on_output, _ = real_scan_files(self.site, self.cache) for f in only_on_output: self.logger.info('removed: {0}'.format(f)) os.unlink(f) diff --git a/nikola/plugins/command/console.plugin b/nikola/plugins/command/console.plugin index 333762c..9bcc909 100644 --- a/nikola/plugins/command/console.plugin +++ b/nikola/plugins/command/console.plugin @@ -5,7 +5,7 @@ module = console [Documentation] author = Chris Warrick, Roberto Alsina version = 1.0 -website = http://getnikola.com +website = https://getnikola.com/ description = Start a debugging python console [Nikola] diff --git a/nikola/plugins/command/console.py b/nikola/plugins/command/console.py index 539fa08..3d3daab 100644 --- a/nikola/plugins/command/console.py +++ b/nikola/plugins/command/console.py @@ -38,7 +38,6 @@ LOGGER = get_logger('console', STDERR_HANDLER) class CommandConsole(Command): - """Start debugging console.""" name = "console" diff --git a/nikola/plugins/command/deploy.plugin b/nikola/plugins/command/deploy.plugin index 4743ca2..8bdc0e2 100644 --- a/nikola/plugins/command/deploy.plugin +++ b/nikola/plugins/command/deploy.plugin @@ -5,7 +5,7 @@ module = deploy [Documentation] author = Roberto Alsina version = 1.0 -website = http://getnikola.com +website = https://getnikola.com/ description = Deploy the site [Nikola] diff --git a/nikola/plugins/command/deploy.py b/nikola/plugins/command/deploy.py index 821ea11..757c0d2 100644 --- a/nikola/plugins/command/deploy.py +++ b/nikola/plugins/command/deploy.py @@ -41,7 +41,6 @@ from nikola.utils import get_logger, remove_file, unicode_str, makedirs, STDERR_ class CommandDeploy(Command): - """Deploy site.""" name = "deploy" diff --git a/nikola/plugins/command/github_deploy.plugin b/nikola/plugins/command/github_deploy.plugin index e793548..21e246c 100644 --- a/nikola/plugins/command/github_deploy.plugin +++ b/nikola/plugins/command/github_deploy.plugin @@ -5,7 +5,7 @@ module = github_deploy [Documentation] author = Puneeth Chaganti version = 1,0 -website = http://getnikola.com +website = https://getnikola.com/ description = Deploy the site to GitHub pages. [Nikola] diff --git a/nikola/plugins/command/github_deploy.py b/nikola/plugins/command/github_deploy.py index 0ab9332..2fe0d4e 100644 --- a/nikola/plugins/command/github_deploy.py +++ b/nikola/plugins/command/github_deploy.py @@ -57,7 +57,6 @@ def check_ghp_import_installed(): class CommandGitHubDeploy(Command): - """Deploy site to GitHub Pages.""" name = 'github_deploy' diff --git a/nikola/plugins/command/import_wordpress.plugin b/nikola/plugins/command/import_wordpress.plugin index 6c4384e..eab9d17 100644 --- a/nikola/plugins/command/import_wordpress.plugin +++ b/nikola/plugins/command/import_wordpress.plugin @@ -5,7 +5,7 @@ module = import_wordpress [Documentation] author = Roberto Alsina version = 1.0 -website = http://getnikola.com +website = https://getnikola.com/ description = Import a wordpress site from a XML dump (requires markdown). [Nikola] diff --git a/nikola/plugins/command/import_wordpress.py b/nikola/plugins/command/import_wordpress.py index a652ec8..69ef144 100644 --- a/nikola/plugins/command/import_wordpress.py +++ b/nikola/plugins/command/import_wordpress.py @@ -50,7 +50,7 @@ except ImportError: from nikola.plugin_categories import Command from nikola import utils -from nikola.utils import req_missing +from nikola.utils import req_missing, unicode_str from nikola.plugins.basic_import import ImportMixin, links from nikola.nikola import DEFAULT_TRANSLATIONS_PATTERN from nikola.plugins.command.init import SAMPLE_CONF, prepare_config, format_default_translations_config @@ -88,7 +88,6 @@ def install_plugin(site, plugin_name, output_dir=None, show_install_notes=False) class CommandImportWordpress(Command, ImportMixin): - """Import a WordPress dump.""" name = "import_wordpress" @@ -191,6 +190,12 @@ class CommandImportWordpress(Command, ImportMixin): 'type': bool, 'help': "Automatically installs the WordPress page compiler (either locally or in the new site) if required by other options.\nWarning: the compiler is GPL software!", }, + { + 'name': 'tag_saniziting_strategy', + 'long': 'tag-saniziting-strategy', + 'default': 'first', + 'help': 'lower: Convert all tag and category names to lower case\nfirst: Keep first spelling of tag or category name', + }, ] all_tags = set([]) @@ -239,6 +244,8 @@ class CommandImportWordpress(Command, ImportMixin): self.install_wordpress_compiler = options.get('install_wordpress_compiler', False) self.wordpress_page_compiler = None + self.tag_saniziting_strategy = options.get('tag_saniziting_strategy', 'first') + self.auth = None if options.get('download_auth') is not None: username_password = options.get('download_auth') @@ -337,7 +344,10 @@ class CommandImportWordpress(Command, ImportMixin): # Add tag redirects for tag in self.all_tags: try: - tag_str = tag.decode('utf8') + if isinstance(tag, utils.bytes_str): + tag_str = tag.decode('utf8', 'replace') + else: + tag_str = tag except AttributeError: tag_str = tag tag = utils.slugify(tag_str) @@ -604,7 +614,7 @@ class CommandImportWordpress(Command, ImportMixin): def transform_code(self, content): """Transform code blocks.""" - # http://en.support.wordpress.com/code/posting-source-code/. There are + # https://en.support.wordpress.com/code/posting-source-code/. There are # a ton of things not supported here. We only do a basic [code # lang="x"] -> ```x translation, and remove quoted html entities (<, # >, &, and "). @@ -686,7 +696,7 @@ class CommandImportWordpress(Command, ImportMixin): elif approved == 'spam' or approved == 'trash': pass else: - LOGGER.warn("Unknown comment approved status: " + str(approved)) + LOGGER.warn("Unknown comment approved status: {0}".format(approved)) parent = int(get_text_tag(comment, "{{{0}}}comment_parent".format(wordpress_namespace), 0)) if parent == 0: parent = None @@ -707,7 +717,7 @@ class CommandImportWordpress(Command, ImportMixin): """Write comment header line.""" if header_content is None: return - header_content = str(header_content).replace('\n', ' ') + header_content = unicode_str(header_content).replace('\n', ' ') line = '.. ' + header_field + ': ' + header_content + '\n' fd.write(line.encode('utf8')) @@ -747,6 +757,24 @@ class CommandImportWordpress(Command, ImportMixin): tags_cats = tags + categories return tags_cats, other_meta + _tag_sanitize_map = {True: {}, False: {}} + + def _sanitize(self, tag, is_category): + if self.tag_saniziting_strategy == 'lower': + return tag.lower() + if tag.lower() not in self._tag_sanitize_map[is_category]: + self._tag_sanitize_map[is_category][tag.lower()] = [tag] + return tag + previous = self._tag_sanitize_map[is_category][tag.lower()] + if self.tag_saniziting_strategy == 'first': + if tag != previous[0]: + LOGGER.warn("Changing spelling of {0} name '{1}' to {2}.".format('category' if is_category else 'tag', tag, previous[0])) + return previous[0] + else: + LOGGER.error("Unknown tag sanitizing strategy '{0}'!".format(self.tag_saniziting_strategy)) + sys.exit(1) + return tag + def import_postpage_item(self, item, wordpress_namespace, out_folder=None, attachments=None): """Take an item from the feed and creates a post file.""" if out_folder is None: @@ -760,7 +788,10 @@ class CommandImportWordpress(Command, ImportMixin): path = unquote(parsed.path.strip('/')) try: - path = path.decode('utf8') + if isinstance(path, utils.bytes_str): + path = path.decode('utf8', 'replace') + else: + path = path except AttributeError: pass @@ -831,15 +862,24 @@ class CommandImportWordpress(Command, ImportMixin): type = tag.attrib['domain'] if text == 'Uncategorized' and type == 'category': continue - self.all_tags.add(text) if type == 'category': - categories.append(type) + categories.append(text) else: tags.append(text) if '$latex' in content: tags.append('mathjax') + for i, cat in enumerate(categories[:]): + cat = self._sanitize(cat, True) + categories[i] = cat + self.all_tags.add(cat) + + for i, tag in enumerate(tags[:]): + tag = self._sanitize(tag, False) + tags[i] = tag + self.all_tags.add(tag) + # Find post format if it's there post_format = 'wp' format_tag = [x for x in item.findall('*//{%s}meta_key' % wordpress_namespace) if x.text == '_tc_post_format'] @@ -905,7 +945,7 @@ class CommandImportWordpress(Command, ImportMixin): comments.append(comment) for comment in comments: - comment_filename = slug + "." + str(comment['id']) + ".wpcomment" + comment_filename = "{0}.{1}.wpcomment".format(slug, comment['id']) self._write_comment(os.path.join(self.output_folder, out_folder, comment_filename), comment) return (out_folder, slug) diff --git a/nikola/plugins/command/init.plugin b/nikola/plugins/command/init.plugin index a5404c4..a8b1523 100644 --- a/nikola/plugins/command/init.plugin +++ b/nikola/plugins/command/init.plugin @@ -5,7 +5,7 @@ module = init [Documentation] author = Roberto Alsina version = 1.0 -website = http://getnikola.com +website = https://getnikola.com/ description = Create a new site. [Nikola] diff --git a/nikola/plugins/command/init.py b/nikola/plugins/command/init.py index 91ccdb4..2dbee43 100644 --- a/nikola/plugins/command/init.py +++ b/nikola/plugins/command/init.py @@ -41,7 +41,7 @@ from pkg_resources import resource_filename import tarfile import nikola -from nikola.nikola import DEFAULT_TRANSLATIONS_PATTERN, DEFAULT_INDEX_READ_MORE_LINK, DEFAULT_RSS_READ_MORE_LINK, LEGAL_VALUES, urlsplit, urlunsplit +from nikola.nikola import DEFAULT_TRANSLATIONS_PATTERN, DEFAULT_INDEX_READ_MORE_LINK, DEFAULT_FEED_READ_MORE_LINK, LEGAL_VALUES, urlsplit, urlunsplit from nikola.plugin_categories import Command from nikola.utils import ask, ask_yesno, get_logger, makedirs, STDERR_HANDLER, load_messages from nikola.packages.tzlocal import get_localzone @@ -71,7 +71,7 @@ SAMPLE_CONF = { 'CATEGORY_OUTPUT_FLAT_HIERARCHY': False, 'TRANSLATIONS_PATTERN': DEFAULT_TRANSLATIONS_PATTERN, 'INDEX_READ_MORE_LINK': DEFAULT_INDEX_READ_MORE_LINK, - 'RSS_READ_MORE_LINK': DEFAULT_RSS_READ_MORE_LINK, + 'FEED_READ_MORE_LINK': DEFAULT_FEED_READ_MORE_LINK, 'POSTS': """( ("posts/*.rst", "posts", "post.tmpl"), ("posts/*.txt", "posts", "post.tmpl"), @@ -210,17 +210,16 @@ def prepare_config(config): """Parse sample config with JSON.""" p = config.copy() p.update({k: json.dumps(v, ensure_ascii=False) for k, v in p.items() - if k not in ('POSTS', 'PAGES', 'COMPILERS', 'TRANSLATIONS', 'NAVIGATION_LINKS', '_SUPPORTED_LANGUAGES', '_SUPPORTED_COMMENT_SYSTEMS', 'INDEX_READ_MORE_LINK', 'RSS_READ_MORE_LINK')}) + if k not in ('POSTS', 'PAGES', 'COMPILERS', 'TRANSLATIONS', 'NAVIGATION_LINKS', '_SUPPORTED_LANGUAGES', '_SUPPORTED_COMMENT_SYSTEMS', 'INDEX_READ_MORE_LINK', 'FEED_READ_MORE_LINK')}) # READ_MORE_LINKs require some special treatment. p['INDEX_READ_MORE_LINK'] = "'" + p['INDEX_READ_MORE_LINK'].replace("'", "\\'") + "'" - p['RSS_READ_MORE_LINK'] = "'" + p['RSS_READ_MORE_LINK'].replace("'", "\\'") + "'" + p['FEED_READ_MORE_LINK'] = "'" + p['FEED_READ_MORE_LINK'].replace("'", "\\'") + "'" # fix booleans and None p.update({k: str(v) for k, v in config.items() if isinstance(v, bool) or v is None}) return p class CommandInit(Command): - """Create a new site.""" name = "init" @@ -358,7 +357,7 @@ class CommandInit(Command): def tzhandler(default, toconf): print("\nPlease choose the correct time zone for your blog. Nikola uses the tz database.") print("You can find your time zone here:") - print("http://en.wikipedia.org/wiki/List_of_tz_database_time_zones") + print("https://en.wikipedia.org/wiki/List_of_tz_database_time_zones") print("") answered = False while not answered: diff --git a/nikola/plugins/command/install_theme.plugin b/nikola/plugins/command/install_theme.plugin index 8434f2e..aa68773 100644 --- a/nikola/plugins/command/install_theme.plugin +++ b/nikola/plugins/command/install_theme.plugin @@ -5,7 +5,7 @@ module = install_theme [Documentation] author = Roberto Alsina version = 1.0 -website = http://getnikola.com +website = https://getnikola.com/ description = Install a theme into the current site. [Nikola] diff --git a/nikola/plugins/command/install_theme.py b/nikola/plugins/command/install_theme.py index f02252e..bad335c 100644 --- a/nikola/plugins/command/install_theme.py +++ b/nikola/plugins/command/install_theme.py @@ -43,7 +43,6 @@ LOGGER = utils.get_logger('install_theme', utils.STDERR_HANDLER) class CommandInstallTheme(Command): - """Install a theme.""" name = "install_theme" diff --git a/nikola/plugins/command/new_page.plugin b/nikola/plugins/command/new_page.plugin index 145a419..3eaecb4 100644 --- a/nikola/plugins/command/new_page.plugin +++ b/nikola/plugins/command/new_page.plugin @@ -5,7 +5,7 @@ module = new_page [Documentation] author = Roberto Alsina, Chris Warrick version = 1.0 -website = http://getnikola.com +website = https://getnikola.com/ description = Create a new page. [Nikola] diff --git a/nikola/plugins/command/new_page.py b/nikola/plugins/command/new_page.py index 811e28b..8843421 100644 --- a/nikola/plugins/command/new_page.py +++ b/nikola/plugins/command/new_page.py @@ -32,7 +32,6 @@ from nikola.plugin_categories import Command class CommandNewPage(Command): - """Create a new page.""" name = "new_page" diff --git a/nikola/plugins/command/new_post.plugin b/nikola/plugins/command/new_post.plugin index d88469f..e9c3af5 100644 --- a/nikola/plugins/command/new_post.plugin +++ b/nikola/plugins/command/new_post.plugin @@ -5,7 +5,7 @@ module = new_post [Documentation] author = Roberto Alsina version = 1.0 -website = http://getnikola.com +website = https://getnikola.com/ description = Create a new post. [Nikola] diff --git a/nikola/plugins/command/new_post.py b/nikola/plugins/command/new_post.py index f9fe3ff..30d009d 100644 --- a/nikola/plugins/command/new_post.py +++ b/nikola/plugins/command/new_post.py @@ -114,7 +114,6 @@ def get_date(schedule=False, rule=None, last_date=None, tz=None, iso8601=False): class CommandNewPost(Command): - """Create a new post.""" name = "new_post" diff --git a/nikola/plugins/command/orphans.plugin b/nikola/plugins/command/orphans.plugin index 669429d..d20c539 100644 --- a/nikola/plugins/command/orphans.plugin +++ b/nikola/plugins/command/orphans.plugin @@ -5,7 +5,7 @@ module = orphans [Documentation] author = Roberto Alsina, Chris Warrick version = 1.0 -website = http://getnikola.com +website = https://getnikola.com/ description = List all orphans [Nikola] diff --git a/nikola/plugins/command/orphans.py b/nikola/plugins/command/orphans.py index b12cc67..f061d39 100644 --- a/nikola/plugins/command/orphans.py +++ b/nikola/plugins/command/orphans.py @@ -34,7 +34,6 @@ from nikola.plugins.command.check import real_scan_files class CommandOrphans(Command): - """List all orphans.""" name = "orphans" diff --git a/nikola/plugins/command/plugin.plugin b/nikola/plugins/command/plugin.plugin index d44dcf3..016bcaa 100644 --- a/nikola/plugins/command/plugin.plugin +++ b/nikola/plugins/command/plugin.plugin @@ -5,7 +5,7 @@ module = plugin [Documentation] author = Roberto Alsina and Chris Warrick version = 1.0 -website = http://getnikola.com +website = https://getnikola.com/ description = Manage Nikola plugins [Nikola] diff --git a/nikola/plugins/command/plugin.py b/nikola/plugins/command/plugin.py index f892ee9..1df7b71 100644 --- a/nikola/plugins/command/plugin.py +++ b/nikola/plugins/command/plugin.py @@ -45,7 +45,6 @@ LOGGER = utils.get_logger('plugin', utils.STDERR_HANDLER) class CommandPlugin(Command): - """Manage plugins.""" json = None diff --git a/nikola/plugins/command/rst2html.plugin b/nikola/plugins/command/rst2html.plugin index 02c9276..a095705 100644 --- a/nikola/plugins/command/rst2html.plugin +++ b/nikola/plugins/command/rst2html.plugin @@ -5,7 +5,7 @@ module = rst2html [Documentation] author = Chris Warrick version = 1.0 -website = http://getnikola.com +website = https://getnikola.com/ description = Compile reStructuredText to HTML using the Nikola architecture [Nikola] diff --git a/nikola/plugins/command/rst2html/__init__.py b/nikola/plugins/command/rst2html/__init__.py index 06afffd..68ea13d 100644 --- a/nikola/plugins/command/rst2html/__init__.py +++ b/nikola/plugins/command/rst2html/__init__.py @@ -36,7 +36,6 @@ from nikola.plugin_categories import Command class CommandRst2Html(Command): - """Compile reStructuredText to HTML, using Nikola architecture.""" name = "rst2html" @@ -65,7 +64,7 @@ class CommandRst2Html(Command): parser = lxml.html.HTMLParser(remove_blank_text=True) doc = lxml.html.document_fromstring(template_output, parser) html = b'<!DOCTYPE html>\n' + lxml.html.tostring(doc, encoding='utf8', method='html', pretty_print=True) - print(html) + print(html.decode('utf-8')) if error_level < 3: return 0 else: diff --git a/nikola/plugins/command/serve.plugin b/nikola/plugins/command/serve.plugin index aca71ec..a4a726f 100644 --- a/nikola/plugins/command/serve.plugin +++ b/nikola/plugins/command/serve.plugin @@ -5,7 +5,7 @@ module = serve [Documentation] author = Roberto Alsina version = 1.0 -website = http://getnikola.com +website = https://getnikola.com/ description = Start test server. [Nikola] diff --git a/nikola/plugins/command/serve.py b/nikola/plugins/command/serve.py index 0441c93..b647bb7 100644 --- a/nikola/plugins/command/serve.py +++ b/nikola/plugins/command/serve.py @@ -45,24 +45,23 @@ except ImportError: from nikola.plugin_categories import Command -from nikola.utils import get_logger, STDERR_HANDLER +from nikola.utils import dns_sd, get_logger, STDERR_HANDLER class IPv6Server(HTTPServer): - """An IPv6 HTTPServer.""" address_family = socket.AF_INET6 class CommandServe(Command): - """Start test server.""" name = "serve" doc_usage = "[options]" doc_purpose = "start the test webserver" logger = None + dns_sd = None cmd_options = ( { @@ -152,14 +151,16 @@ class CommandServe(Command): raise e else: try: + self.dns_sd = dns_sd(options['port'], (options['ipv6'] or '::' in options['address'])) httpd.serve_forever() except KeyboardInterrupt: self.logger.info("Server is shutting down.") + if self.dns_sd: + self.dns_sd.Reset() return 130 class OurHTTPRequestHandler(SimpleHTTPRequestHandler): - """A request handler, modified for Nikola.""" extensions_map = dict(SimpleHTTPRequestHandler.extensions_map) @@ -242,7 +243,10 @@ class OurHTTPRequestHandler(SimpleHTTPRequestHandler): f.seek(0) self.send_response(200) - self.send_header("Content-type", ctype) + if ctype.startswith('text/') or ctype.endswith('+xml'): + self.send_header("Content-Type", "{0}; charset=UTF-8".format(ctype)) + else: + self.send_header("Content-Type", ctype) if os.path.splitext(path)[1] == '.svgz': # Special handling for svgz to make it work nice with browsers. self.send_header("Content-Encoding", 'gzip') diff --git a/nikola/plugins/command/status.py b/nikola/plugins/command/status.py index 55e7f95..40f4f77 100644 --- a/nikola/plugins/command/status.py +++ b/nikola/plugins/command/status.py @@ -36,7 +36,6 @@ from nikola.plugin_categories import Command class CommandStatus(Command): - """Display site status.""" name = "status" diff --git a/nikola/plugins/command/version.plugin b/nikola/plugins/command/version.plugin index 4708bdb..d78b79b 100644 --- a/nikola/plugins/command/version.plugin +++ b/nikola/plugins/command/version.plugin @@ -5,7 +5,7 @@ module = version [Documentation] author = Roberto Alsina version = 1.0 -website = http://getnikola.com +website = https://getnikola.com/ description = Show nikola version [Nikola] diff --git a/nikola/plugins/command/version.py b/nikola/plugins/command/version.py index ad08f64..b83d622 100644 --- a/nikola/plugins/command/version.py +++ b/nikola/plugins/command/version.py @@ -38,7 +38,6 @@ URL = 'https://pypi.python.org/pypi?:action=doap&name=Nikola' class CommandVersion(Command): - """Print Nikola version.""" name = "version" diff --git a/nikola/plugins/compile/html.plugin b/nikola/plugins/compile/html.plugin index 53ade61..f95bdd5 100644 --- a/nikola/plugins/compile/html.plugin +++ b/nikola/plugins/compile/html.plugin @@ -5,7 +5,7 @@ module = html [Documentation] author = Roberto Alsina version = 1.0 -website = http://getnikola.com +website = https://getnikola.com/ description = Compile HTML into HTML (just copy) [Nikola] diff --git a/nikola/plugins/compile/html.py b/nikola/plugins/compile/html.py index 5f8b244..6ff5de8 100644 --- a/nikola/plugins/compile/html.py +++ b/nikola/plugins/compile/html.py @@ -36,7 +36,6 @@ from nikola.utils import makedirs, write_metadata class CompileHtml(PageCompiler): - """Compile HTML into HTML.""" name = "html" diff --git a/nikola/plugins/compile/ipynb.py b/nikola/plugins/compile/ipynb.py index a9dedde..1023b31 100644 --- a/nikola/plugins/compile/ipynb.py +++ b/nikola/plugins/compile/ipynb.py @@ -53,7 +53,6 @@ from nikola.utils import makedirs, req_missing, get_logger, STDERR_HANDLER class CompileIPynb(PageCompiler): - """Compile IPynb into HTML.""" name = "ipynb" diff --git a/nikola/plugins/compile/markdown.plugin b/nikola/plugins/compile/markdown.plugin index f7d11b1..2607413 100644 --- a/nikola/plugins/compile/markdown.plugin +++ b/nikola/plugins/compile/markdown.plugin @@ -5,7 +5,7 @@ module = markdown [Documentation] author = Roberto Alsina version = 1.0 -website = http://getnikola.com +website = https://getnikola.com/ description = Compile Markdown into HTML [Nikola] diff --git a/nikola/plugins/compile/markdown/__init__.py b/nikola/plugins/compile/markdown/__init__.py index c1425a1..93438a3 100644 --- a/nikola/plugins/compile/markdown/__init__.py +++ b/nikola/plugins/compile/markdown/__init__.py @@ -44,7 +44,6 @@ from nikola.utils import makedirs, req_missing, write_metadata class CompileMarkdown(PageCompiler): - """Compile Markdown into HTML.""" name = "markdown" diff --git a/nikola/plugins/compile/markdown/mdx_gist.plugin b/nikola/plugins/compile/markdown/mdx_gist.plugin index 7fe676c..85b5450 100644 --- a/nikola/plugins/compile/markdown/mdx_gist.plugin +++ b/nikola/plugins/compile/markdown/mdx_gist.plugin @@ -9,6 +9,6 @@ plugincategory = CompilerExtension [Documentation] author = Roberto Alsina version = 0.1 -website = http://getnikola.com +website = https://getnikola.com/ description = Extension for embedding gists diff --git a/nikola/plugins/compile/markdown/mdx_gist.py b/nikola/plugins/compile/markdown/mdx_gist.py index f439fa2..a930de5 100644 --- a/nikola/plugins/compile/markdown/mdx_gist.py +++ b/nikola/plugins/compile/markdown/mdx_gist.py @@ -22,7 +22,7 @@ # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Warning: URL formats of "raw" gists are undocummented and subject to change. -# See also: http://developer.github.com/v3/gists/ +# See also: https://developer.github.com/v3/gists/ # # Inspired by "[Python] reStructuredText GitHub Gist directive" # (https://gist.github.com/brianhsu/1407759), public domain by Brian Hsu @@ -217,7 +217,6 @@ GIST_RST_RE = r'(?m)^\.\.\s*gist::\s*(?P<gist_id>[^\]\s]+)(?:\s*(?P<filename>.+? class GistFetchException(Exception): - """Raised when attempt to fetch content of a Gist from github.com fails.""" def __init__(self, url, status_code): @@ -228,7 +227,6 @@ class GistFetchException(Exception): class GistPattern(Pattern): - """InlinePattern for footnote markers in a document's body text.""" def __init__(self, pattern, configs): @@ -290,7 +288,6 @@ class GistPattern(Pattern): class GistExtension(MarkdownExtension, Extension): - """Gist extension for Markdown.""" def __init__(self, configs={}): diff --git a/nikola/plugins/compile/markdown/mdx_nikola.plugin b/nikola/plugins/compile/markdown/mdx_nikola.plugin index 12e4fb6..3c5c638 100644 --- a/nikola/plugins/compile/markdown/mdx_nikola.plugin +++ b/nikola/plugins/compile/markdown/mdx_nikola.plugin @@ -9,6 +9,6 @@ plugincategory = CompilerExtension [Documentation] author = Roberto Alsina version = 0.1 -website = http://getnikola.com +website = https://getnikola.com/ description = Nikola-specific Markdown extensions diff --git a/nikola/plugins/compile/markdown/mdx_nikola.py b/nikola/plugins/compile/markdown/mdx_nikola.py index 54cc18c..7984121 100644 --- a/nikola/plugins/compile/markdown/mdx_nikola.py +++ b/nikola/plugins/compile/markdown/mdx_nikola.py @@ -24,25 +24,31 @@ # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -"""Markdown Extension for Nikola-specific post-processing.""" +"""Markdown Extension for Nikola. + +- Specific post-processing. +- Strikethrough inline patterns. +""" from __future__ import unicode_literals import re try: from markdown.postprocessors import Postprocessor + from markdown.inlinepatterns import SimpleTagPattern from markdown.extensions import Extension except ImportError: # No need to catch this, if you try to use this without Markdown, # the markdown compiler will fail first - Postprocessor = Extension = object + Postprocessor = SimpleTagPattern = Extension = object from nikola.plugin_categories import MarkdownExtension + CODERE = re.compile('<div class="codehilite"><pre>(.*?)</pre></div>', flags=re.MULTILINE | re.DOTALL) +STRIKE_RE = r"(~{2})(.+?)(~{2})" # ~~strike~~ class NikolaPostProcessor(Postprocessor): - """Nikola-specific post-processing for Markdown.""" def run(self, text): @@ -57,13 +63,22 @@ class NikolaPostProcessor(Postprocessor): class NikolaExtension(MarkdownExtension, Extension): + """Nikola Markdown extensions.""" - """Extension for injecting the postprocessor.""" - - def extendMarkdown(self, md, md_globals): + def _add_nikola_post_processor(self, md): """Extend Markdown with the postprocessor.""" pp = NikolaPostProcessor() md.postprocessors.add('nikola_post_processor', pp, '_end') + + def _add_strikethrough_inline_pattern(self, md): + """Support PHP-Markdown style strikethrough, for example: ``~~strike~~``.""" + pattern = SimpleTagPattern(STRIKE_RE, 'del') + md.inlinePatterns.add('strikethrough', pattern, '_end') + + def extendMarkdown(self, md, md_globals): + """Extend markdown to Nikola flavours.""" + self._add_nikola_post_processor(md) + self._add_strikethrough_inline_pattern(md) md.registerExtension(self) diff --git a/nikola/plugins/compile/markdown/mdx_podcast.plugin b/nikola/plugins/compile/markdown/mdx_podcast.plugin index c92a8a0..c4ee7e9 100644 --- a/nikola/plugins/compile/markdown/mdx_podcast.plugin +++ b/nikola/plugins/compile/markdown/mdx_podcast.plugin @@ -9,6 +9,6 @@ plugincategory = CompilerExtension [Documentation] author = Roberto Alsina version = 0.1 -website = http://getnikola.com +website = https://getnikola.com/ description = Markdown extensions for embedding podcasts and other audio files diff --git a/nikola/plugins/compile/markdown/mdx_podcast.py b/nikola/plugins/compile/markdown/mdx_podcast.py index 61afdbf..0f68e40 100644 --- a/nikola/plugins/compile/markdown/mdx_podcast.py +++ b/nikola/plugins/compile/markdown/mdx_podcast.py @@ -30,10 +30,10 @@ Extension to Python Markdown for Embedded Audio. Basic Example: >>> import markdown ->>> text = "[podcast]http://archive.org/download/Rebeldes_Stereotipos/rs20120609_1.mp3[/podcast]" +>>> text = "[podcast]https://archive.org/download/Rebeldes_Stereotipos/rs20120609_1.mp3[/podcast]" >>> html = markdown.markdown(text, [PodcastExtension()]) >>> print(html) -<p><audio controls=""><source src="http://archive.org/download/Rebeldes_Stereotipos/rs20120609_1.mp3" type="audio/mpeg"></source></audio></p> +<p><audio controls=""><source src="https://archive.org/download/Rebeldes_Stereotipos/rs20120609_1.mp3" type="audio/mpeg"></source></audio></p> """ from __future__ import print_function, unicode_literals @@ -51,7 +51,6 @@ PODCAST_RE = r'\[podcast\](?P<url>.+)\[/podcast\]' class PodcastPattern(Pattern): - """InlinePattern for footnote markers in a document's body text.""" def __init__(self, pattern, configs): @@ -70,7 +69,6 @@ class PodcastPattern(Pattern): class PodcastExtension(MarkdownExtension, Extension): - """"Podcast extension for Markdown.""" def __init__(self, configs={}): diff --git a/nikola/plugins/compile/pandoc.plugin b/nikola/plugins/compile/pandoc.plugin index 3ff3668..2a69095 100644 --- a/nikola/plugins/compile/pandoc.plugin +++ b/nikola/plugins/compile/pandoc.plugin @@ -5,7 +5,7 @@ module = pandoc [Documentation] author = Roberto Alsina version = 1.0 -website = http://getnikola.com +website = https://getnikola.com/ description = Compile markups into HTML using pandoc [Nikola] diff --git a/nikola/plugins/compile/pandoc.py b/nikola/plugins/compile/pandoc.py index 3030626..85e84fc 100644 --- a/nikola/plugins/compile/pandoc.py +++ b/nikola/plugins/compile/pandoc.py @@ -40,7 +40,6 @@ from nikola.utils import req_missing, makedirs, write_metadata class CompilePandoc(PageCompiler): - """Compile markups into HTML using pandoc.""" name = "pandoc" diff --git a/nikola/plugins/compile/php.plugin b/nikola/plugins/compile/php.plugin index 151c022..f4fb0c1 100644 --- a/nikola/plugins/compile/php.plugin +++ b/nikola/plugins/compile/php.plugin @@ -5,7 +5,7 @@ module = php [Documentation] author = Roberto Alsina version = 1.0 -website = http://getnikola.com +website = https://getnikola.com/ description = Compile PHP into HTML (just copy and name the file .php) [Nikola] diff --git a/nikola/plugins/compile/php.py b/nikola/plugins/compile/php.py index 28f4923..a60e31a 100644 --- a/nikola/plugins/compile/php.py +++ b/nikola/plugins/compile/php.py @@ -37,7 +37,6 @@ from hashlib import md5 class CompilePhp(PageCompiler): - """Compile PHP into PHP.""" name = "php" diff --git a/nikola/plugins/compile/rest.plugin b/nikola/plugins/compile/rest.plugin index cf842c7..4d9041a 100644 --- a/nikola/plugins/compile/rest.plugin +++ b/nikola/plugins/compile/rest.plugin @@ -5,7 +5,7 @@ module = rest [Documentation] author = Roberto Alsina version = 1.0 -website = http://getnikola.com +website = https://getnikola.com/ description = Compile reSt into HTML [Nikola] diff --git a/nikola/plugins/compile/rest/__init__.py b/nikola/plugins/compile/rest/__init__.py index b99e872..4b04958 100644 --- a/nikola/plugins/compile/rest/__init__.py +++ b/nikola/plugins/compile/rest/__init__.py @@ -42,7 +42,6 @@ from nikola.utils import unicode_str, get_logger, makedirs, write_metadata, STDE class CompileRest(PageCompiler): - """Compile reStructuredText into HTML.""" name = "rest" @@ -172,7 +171,6 @@ def get_observer(settings): class NikolaReader(docutils.readers.standalone.Reader): - """Nikola-specific docutils reader.""" def __init__(self, *args, **kwargs): @@ -196,7 +194,7 @@ def add_node(node, visit_function=None, depart_function=None): """Register a Docutils node class. This function is completely optional. It is a same concept as - `Sphinx add_node function <http://sphinx-doc.org/ext/appapi.html#sphinx.application.Sphinx.add_node>`_. + `Sphinx add_node function <http://sphinx-doc.org/extdev/appapi.html#sphinx.application.Sphinx.add_node>`_. For example:: diff --git a/nikola/plugins/compile/rest/chart.plugin b/nikola/plugins/compile/rest/chart.plugin index 438abe4..0a7896f 100644 --- a/nikola/plugins/compile/rest/chart.plugin +++ b/nikola/plugins/compile/rest/chart.plugin @@ -9,6 +9,6 @@ plugincategory = CompilerExtension [Documentation] author = Roberto Alsina version = 0.1 -website = http://getnikola.com +website = https://getnikola.com/ description = Chart directive based in PyGal diff --git a/nikola/plugins/compile/rest/chart.py b/nikola/plugins/compile/rest/chart.py index 88fdff3..ed8a250 100644 --- a/nikola/plugins/compile/rest/chart.py +++ b/nikola/plugins/compile/rest/chart.py @@ -43,7 +43,6 @@ _site = None class Plugin(RestExtension): - """Plugin for chart role.""" name = "rest_chart" @@ -57,7 +56,6 @@ class Plugin(RestExtension): class Chart(Directive): - """reStructuredText extension for inserting charts as SVG. Usage: @@ -150,15 +148,15 @@ class Chart(Directive): style = getattr(pygal.style, style_name) for k, v in self.options.items(): options[k] = literal_eval(v) - - chart = getattr(pygal, self.arguments[0])(style=style) + chart = pygal + for o in self.arguments[0].split('.'): + chart = getattr(chart, o) + chart = chart(style=style) + if _site and _site.invariant: + chart.no_prefix = True chart.config(**options) for line in self.content: label, series = literal_eval('({0})'.format(line)) chart.add(label, series) data = chart.render().decode('utf8') - if _site and _site.invariant: - import re - data = re.sub('id="chart-[a-f0-9\-]+"', 'id="chart-foobar"', data) - data = re.sub('#chart-[a-f0-9\-]+', '#chart-foobar', data) return [nodes.raw('', data, format='html')] diff --git a/nikola/plugins/compile/rest/doc.plugin b/nikola/plugins/compile/rest/doc.plugin index facdd03..e447eb2 100644 --- a/nikola/plugins/compile/rest/doc.plugin +++ b/nikola/plugins/compile/rest/doc.plugin @@ -9,6 +9,6 @@ plugincategory = CompilerExtension [Documentation] author = Manuel Kaufmann version = 0.1 -website = http://getnikola.com +website = https://getnikola.com/ description = Role to link another page / post from the blog diff --git a/nikola/plugins/compile/rest/doc.py b/nikola/plugins/compile/rest/doc.py index 99cce81..578f012 100644 --- a/nikola/plugins/compile/rest/doc.py +++ b/nikola/plugins/compile/rest/doc.py @@ -34,7 +34,6 @@ from nikola.plugin_categories import RestExtension class Plugin(RestExtension): - """Plugin for doc role.""" name = 'rest_doc' diff --git a/nikola/plugins/compile/rest/gist.plugin b/nikola/plugins/compile/rest/gist.plugin index 9fa2e82..763c1d2 100644 --- a/nikola/plugins/compile/rest/gist.plugin +++ b/nikola/plugins/compile/rest/gist.plugin @@ -9,6 +9,6 @@ plugincategory = CompilerExtension [Documentation] author = Roberto Alsina version = 0.1 -website = http://getnikola.com +website = https://getnikola.com/ description = Gist directive diff --git a/nikola/plugins/compile/rest/gist.py b/nikola/plugins/compile/rest/gist.py index 736ee37..e40c3b2 100644 --- a/nikola/plugins/compile/rest/gist.py +++ b/nikola/plugins/compile/rest/gist.py @@ -11,7 +11,6 @@ from nikola.plugin_categories import RestExtension class Plugin(RestExtension): - """Plugin for gist directive.""" name = "rest_gist" @@ -24,7 +23,6 @@ class Plugin(RestExtension): class GitHubGist(Directive): - """Embed GitHub Gist. Usage: diff --git a/nikola/plugins/compile/rest/listing.plugin b/nikola/plugins/compile/rest/listing.plugin index 85c780f..3ebb296 100644 --- a/nikola/plugins/compile/rest/listing.plugin +++ b/nikola/plugins/compile/rest/listing.plugin @@ -9,6 +9,6 @@ plugincategory = CompilerExtension [Documentation] author = Roberto Alsina version = 0.1 -website = http://getnikola.com +website = https://getnikola.com/ description = Extension for source listings diff --git a/nikola/plugins/compile/rest/listing.py b/nikola/plugins/compile/rest/listing.py index 4871bf3..07f1686 100644 --- a/nikola/plugins/compile/rest/listing.py +++ b/nikola/plugins/compile/rest/listing.py @@ -55,7 +55,6 @@ from nikola.plugin_categories import RestExtension # A sanitized version of docutils.parsers.rst.directives.body.CodeBlock. class CodeBlock(Directive): - """Parse and mark up content of a code block.""" optional_arguments = 1 @@ -126,7 +125,6 @@ docutils.parsers.rst.directives.misc.CodeBlock = CodeBlock class Plugin(RestExtension): - """Plugin for listing directive.""" name = "rest_listing" @@ -152,7 +150,6 @@ listing_spec['linenos'] = directives.unchanged class Listing(Include): - """Create a highlighted block of code from a file in listings/. Usage: @@ -171,7 +168,12 @@ class Listing(Include): """Run listing directive.""" _fname = self.arguments.pop(0) fname = _fname.replace('/', os.sep) - lang = self.arguments.pop(0) + try: + lang = self.arguments.pop(0) + self.options['code'] = lang + except IndexError: + self.options['literal'] = True + if len(self.folders) == 1: listings_folder = next(iter(self.folders.keys())) if fname.startswith(listings_folder): @@ -181,7 +183,6 @@ class Listing(Include): else: fpath = os.path.join(fname) # must be new syntax: specify folder name self.arguments.insert(0, fpath) - self.options['code'] = lang if 'linenos' in self.options: self.options['number-lines'] = self.options['linenos'] with io.open(fpath, 'r+', encoding='utf8') as fileobject: diff --git a/nikola/plugins/compile/rest/media.plugin b/nikola/plugins/compile/rest/media.plugin index 9803c8f..8dfb19c 100644 --- a/nikola/plugins/compile/rest/media.plugin +++ b/nikola/plugins/compile/rest/media.plugin @@ -9,6 +9,6 @@ plugincategory = CompilerExtension [Documentation] author = Roberto Alsina version = 0.1 -website = http://getnikola.com +website = https://getnikola.com/ description = Directive to support oembed via micawber diff --git a/nikola/plugins/compile/rest/media.py b/nikola/plugins/compile/rest/media.py index 345e331..d075e44 100644 --- a/nikola/plugins/compile/rest/media.py +++ b/nikola/plugins/compile/rest/media.py @@ -40,7 +40,6 @@ from nikola.utils import req_missing class Plugin(RestExtension): - """Plugin for reST media directive.""" name = "rest_media" @@ -53,7 +52,6 @@ class Plugin(RestExtension): class Media(Directive): - """reST extension for inserting any sort of media using micawber.""" has_content = False diff --git a/nikola/plugins/compile/rest/post_list.plugin b/nikola/plugins/compile/rest/post_list.plugin index 48969bf..1802f2b 100644 --- a/nikola/plugins/compile/rest/post_list.plugin +++ b/nikola/plugins/compile/rest/post_list.plugin @@ -9,6 +9,6 @@ plugincategory = CompilerExtension [Documentation] author = Udo Spallek version = 0.1 -website = http://getnikola.com +website = https://getnikola.com/ description = Includes a list of posts with tag and slide based filters. diff --git a/nikola/plugins/compile/rest/post_list.py b/nikola/plugins/compile/rest/post_list.py index a22ee85..df9376b 100644 --- a/nikola/plugins/compile/rest/post_list.py +++ b/nikola/plugins/compile/rest/post_list.py @@ -43,7 +43,6 @@ from nikola.plugin_categories import RestExtension class Plugin(RestExtension): - """Plugin for reST post-list directive.""" name = "rest_post_list" @@ -57,7 +56,6 @@ class Plugin(RestExtension): class PostList(Directive): - """Provide a reStructuredText directive to create a list of posts. Post List @@ -204,8 +202,10 @@ class PostList(Directive): template_data = { 'lang': lang, 'posts': posts, - 'date_format': self.site.GLOBAL_CONTEXT.get('date_format'), + # Need to provide str, not TranslatableSetting (Issue #2104) + 'date_format': self.site.GLOBAL_CONTEXT.get('date_format')[lang], 'post_list_id': post_list_id, + 'messages': self.site.MESSAGES, } output = self.site.template_system.render_template( template, None, template_data) diff --git a/nikola/plugins/compile/rest/slides.plugin b/nikola/plugins/compile/rest/slides.plugin index 5c05b89..389da39 100644 --- a/nikola/plugins/compile/rest/slides.plugin +++ b/nikola/plugins/compile/rest/slides.plugin @@ -9,6 +9,6 @@ plugincategory = CompilerExtension [Documentation] author = Roberto Alsina version = 0.1 -website = http://getnikola.com +website = https://getnikola.com/ description = Slides directive diff --git a/nikola/plugins/compile/rest/slides.py b/nikola/plugins/compile/rest/slides.py index 2522e55..eb2cc97 100644 --- a/nikola/plugins/compile/rest/slides.py +++ b/nikola/plugins/compile/rest/slides.py @@ -37,7 +37,6 @@ from nikola.plugin_categories import RestExtension class Plugin(RestExtension): - """Plugin for reST slides directive.""" name = "rest_slides" @@ -51,7 +50,6 @@ class Plugin(RestExtension): class Slides(Directive): - """reST extension for inserting slideshows.""" has_content = True diff --git a/nikola/plugins/compile/rest/soundcloud.plugin b/nikola/plugins/compile/rest/soundcloud.plugin index 75469e4..4e36ea4 100644 --- a/nikola/plugins/compile/rest/soundcloud.plugin +++ b/nikola/plugins/compile/rest/soundcloud.plugin @@ -9,6 +9,6 @@ plugincategory = CompilerExtension [Documentation] author = Roberto Alsina version = 0.1 -website = http://getnikola.com +website = https://getnikola.com/ description = Soundcloud directive diff --git a/nikola/plugins/compile/rest/soundcloud.py b/nikola/plugins/compile/rest/soundcloud.py index 30134a9..2577ff1 100644 --- a/nikola/plugins/compile/rest/soundcloud.py +++ b/nikola/plugins/compile/rest/soundcloud.py @@ -10,7 +10,6 @@ from nikola.plugin_categories import RestExtension class Plugin(RestExtension): - """Plugin for soundclound directive.""" name = "rest_soundcloud" @@ -31,7 +30,6 @@ src="https://w.soundcloud.com/player/?url=http://api.soundcloud.com/{preslug}/"" class SoundCloud(Directive): - """reST extension for inserting SoundCloud embedded music. Usage: @@ -70,7 +68,6 @@ class SoundCloud(Directive): class SoundCloudPlaylist(SoundCloud): - """reST directive for SoundCloud playlists.""" preslug = "playlists" diff --git a/nikola/plugins/compile/rest/thumbnail.plugin b/nikola/plugins/compile/rest/thumbnail.plugin index 0084310..3324c31 100644 --- a/nikola/plugins/compile/rest/thumbnail.plugin +++ b/nikola/plugins/compile/rest/thumbnail.plugin @@ -9,6 +9,6 @@ plugincategory = CompilerExtension [Documentation] author = Pelle Nilsson version = 0.1 -website = http://getnikola.com +website = https://getnikola.com/ description = reST directive to facilitate enlargeable images with thumbnails diff --git a/nikola/plugins/compile/rest/thumbnail.py b/nikola/plugins/compile/rest/thumbnail.py index 1fae06c..c24134a 100644 --- a/nikola/plugins/compile/rest/thumbnail.py +++ b/nikola/plugins/compile/rest/thumbnail.py @@ -35,7 +35,6 @@ from nikola.plugin_categories import RestExtension class Plugin(RestExtension): - """Plugin for thumbnail directive.""" name = "rest_thumbnail" @@ -48,7 +47,6 @@ class Plugin(RestExtension): class Thumbnail(Figure): - """Thumbnail directive for reST.""" def align(argument): @@ -70,8 +68,12 @@ class Thumbnail(Figure): def run(self): """Run the thumbnail directive.""" uri = directives.uri(self.arguments[0]) + if uri.endswith('.svg'): + # the ? at the end makes docutil output an <img> instead of an object for the svg, which colorbox requires + self.arguments[0] = '.thumbnail'.join(os.path.splitext(uri)) + '?' + else: + self.arguments[0] = '.thumbnail'.join(os.path.splitext(uri)) self.options['target'] = uri - self.arguments[0] = '.thumbnail'.join(os.path.splitext(uri)) if self.content: (node,) = Figure.run(self) else: diff --git a/nikola/plugins/compile/rest/vimeo.py b/nikola/plugins/compile/rest/vimeo.py index c694a87..29ce5c1 100644 --- a/nikola/plugins/compile/rest/vimeo.py +++ b/nikola/plugins/compile/rest/vimeo.py @@ -37,7 +37,6 @@ from nikola.plugin_categories import RestExtension class Plugin(RestExtension): - """Plugin for vimeo reST directive.""" name = "rest_vimeo" @@ -60,7 +59,6 @@ VIDEO_DEFAULT_WIDTH = 281 class Vimeo(Directive): - """reST extension for inserting vimeo embedded videos. Usage: diff --git a/nikola/plugins/compile/rest/youtube.py b/nikola/plugins/compile/rest/youtube.py index 6c5c211..b3b84b0 100644 --- a/nikola/plugins/compile/rest/youtube.py +++ b/nikola/plugins/compile/rest/youtube.py @@ -34,7 +34,6 @@ from nikola.plugin_categories import RestExtension class Plugin(RestExtension): - """Plugin for the youtube directive.""" name = "rest_youtube" @@ -54,7 +53,6 @@ src="//www.youtube.com/embed/{yid}?rel=0&hd=1&wmode=transparent" class Youtube(Directive): - """reST extension for inserting youtube embedded videos. Usage: diff --git a/nikola/plugins/misc/scan_posts.plugin b/nikola/plugins/misc/scan_posts.plugin index 6d2351f..f4af811 100644 --- a/nikola/plugins/misc/scan_posts.plugin +++ b/nikola/plugins/misc/scan_posts.plugin @@ -5,6 +5,6 @@ Module = scan_posts [Documentation] Author = Roberto Alsina Version = 1.0 -Website = http://getnikola.com +Website = https://getnikola.com/ Description = Scan posts and create timeline diff --git a/nikola/plugins/misc/scan_posts.py b/nikola/plugins/misc/scan_posts.py index 1f4f995..9db4533 100644 --- a/nikola/plugins/misc/scan_posts.py +++ b/nikola/plugins/misc/scan_posts.py @@ -37,7 +37,6 @@ from nikola.post import Post class ScanPosts(PostScanner): - """Scan posts in the site.""" name = "scan_posts" diff --git a/nikola/plugins/task/archive.plugin b/nikola/plugins/task/archive.plugin index 25f1195..eb079da 100644 --- a/nikola/plugins/task/archive.plugin +++ b/nikola/plugins/task/archive.plugin @@ -5,7 +5,7 @@ module = archive [Documentation] author = Roberto Alsina version = 1.0 -website = http://getnikola.com +website = https://getnikola.com/ description = Generates the blog's archive pages. [Nikola] diff --git a/nikola/plugins/task/archive.py b/nikola/plugins/task/archive.py index 126aed4..3cdd33b 100644 --- a/nikola/plugins/task/archive.py +++ b/nikola/plugins/task/archive.py @@ -37,7 +37,6 @@ from nikola.utils import config_changed, adjust_name_for_index_path, adjust_name class Archive(Task): - """Render the post archives.""" name = "render_archive" @@ -53,7 +52,7 @@ class Archive(Task): """Prepare an archive task.""" # name: used to build permalink and destination # posts, items: posts or items; only one of them should be used, - # the other be None + # the other should be None # template_name: name of the template to use # title: the (translated) title for the generated page # deps_translatable: dependencies (None if not added) @@ -175,10 +174,10 @@ class Archive(Task): if not kw["create_monthly_archive"] or kw["create_full_archives"]: yield self._generate_posts_task(kw, year, lang, posts, title, deps_translatable) else: - months = set([(m.split('/')[1], self.site.link("archive", m, lang)) for m in self.site.posts_per_month.keys() if m.startswith(str(year))]) + months = set([(m.split('/')[1], self.site.link("archive", m, lang), len(self.site.posts_per_month[m])) for m in self.site.posts_per_month.keys() if m.startswith(str(year))]) months = sorted(list(months)) months.reverse() - items = [[nikola.utils.LocaleBorg().get_month_name(int(month), lang), link] for month, link in months] + items = [[nikola.utils.LocaleBorg().get_month_name(int(month), lang), link, count] for month, link, count in months] yield self._prepare_task(kw, year, lang, None, items, "list.tmpl", title, deps_translatable) if not kw["create_monthly_archive"] and not kw["create_full_archives"] and not kw["create_daily_archive"]: @@ -219,11 +218,16 @@ class Archive(Task): years.sort(reverse=True) kw['years'] = years for lang in kw["translations"]: - items = [(y, self.site.link("archive", y, lang)) for y in years] + items = [(y, self.site.link("archive", y, lang), len(self.site.posts_per_year[y])) for y in years] yield self._prepare_task(kw, None, lang, None, items, "list.tmpl", kw["messages"][lang]["Archive"]) def archive_path(self, name, lang, is_feed=False): - """Return archive paths.""" + """Link to archive path, name is the year. + + Example: + + link://archive/2013 => /archives/2013/index.html + """ if is_feed: extension = ".atom" archive_file = os.path.splitext(self.site.config['ARCHIVE_FILENAME'])[0] + extension @@ -241,5 +245,10 @@ class Archive(Task): archive_file] if _f] def archive_atom_path(self, name, lang): - """Return Atom archive paths.""" + """Link to atom archive path, name is the year. + + Example: + + link://archive_atom/2013 => /archives/2013/index.atom + """ return self.archive_path(name, lang, is_feed=True) diff --git a/nikola/plugins/task/authors.plugin b/nikola/plugins/task/authors.plugin new file mode 100644 index 0000000..3fc4ef2 --- /dev/null +++ b/nikola/plugins/task/authors.plugin @@ -0,0 +1,10 @@ +[Core] +Name = render_authors +Module = authors + +[Documentation] +Author = Juanjo Conti +Version = 0.1 +Website = http://getnikola.com +Description = Render the author pages and feeds. + diff --git a/nikola/plugins/task/authors.py b/nikola/plugins/task/authors.py new file mode 100644 index 0000000..081d21d --- /dev/null +++ b/nikola/plugins/task/authors.py @@ -0,0 +1,316 @@ +# -*- coding: utf-8 -*- + +# Copyright © 2015 Juanjo Conti. + +# Permission is hereby granted, free of charge, to any +# person obtaining a copy of this software and associated +# documentation files (the "Software"), to deal in the +# Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the +# Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice +# shall be included in all copies or substantial portions of +# the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +"""Render the author pages and feeds.""" + +from __future__ import unicode_literals +import os +import natsort +try: + from urlparse import urljoin +except ImportError: + from urllib.parse import urljoin # NOQA +from collections import defaultdict + +from nikola.plugin_categories import Task +from nikola import utils + + +class RenderAuthors(Task): + """Render the author pages and feeds.""" + + name = "render_authors" + posts_per_author = None + + def set_site(self, site): + """Set Nikola site.""" + if site.config["ENABLE_AUTHOR_PAGES"]: + site.register_path_handler('author_index', self.author_index_path) + site.register_path_handler('author', self.author_path) + site.register_path_handler('author_atom', self.author_atom_path) + site.register_path_handler('author_rss', self.author_rss_path) + return super(RenderAuthors, self).set_site(site) + + def gen_tasks(self): + """Render the author pages and feeds.""" + kw = { + "translations": self.site.config["TRANSLATIONS"], + "blog_title": self.site.config["BLOG_TITLE"], + "site_url": self.site.config["SITE_URL"], + "base_url": self.site.config["BASE_URL"], + "messages": self.site.MESSAGES, + "output_folder": self.site.config['OUTPUT_FOLDER'], + "filters": self.site.config['FILTERS'], + 'author_path': self.site.config['AUTHOR_PATH'], + "author_pages_are_indexes": self.site.config['AUTHOR_PAGES_ARE_INDEXES'], + "generate_rss": self.site.config['GENERATE_RSS'], + "feed_teasers": self.site.config["FEED_TEASERS"], + "feed_plain": self.site.config["FEED_PLAIN"], + "feed_link_append_query": self.site.config["FEED_LINKS_APPEND_QUERY"], + "show_untranslated_posts": self.site.config['SHOW_UNTRANSLATED_POSTS'], + "feed_length": self.site.config['FEED_LENGTH'], + "tzinfo": self.site.tzinfo, + "pretty_urls": self.site.config['PRETTY_URLS'], + "strip_indexes": self.site.config['STRIP_INDEXES'], + "index_file": self.site.config['INDEX_FILE'], + } + + yield self.group_task() + self.site.scan_posts() + + generate_author_pages = self.site.config["ENABLE_AUTHOR_PAGES"] and len(self._posts_per_author()) > 1 + self.site.GLOBAL_CONTEXT["author_pages_generated"] = generate_author_pages + if generate_author_pages: + yield self.list_authors_page(kw) + + if not self._posts_per_author(): # this may be self.site.posts_per_author + return + + author_list = list(self._posts_per_author().items()) + + def render_lists(author, posts): + """Render author pages as RSS files and lists/indexes.""" + post_list = sorted(posts, key=lambda a: a.date) + post_list.reverse() + for lang in kw["translations"]: + if kw["show_untranslated_posts"]: + filtered_posts = post_list + else: + filtered_posts = [x for x in post_list if x.is_translation_available(lang)] + if kw["generate_rss"]: + yield self.author_rss(author, lang, filtered_posts, kw) + # Render HTML + if kw['author_pages_are_indexes']: + yield self.author_page_as_index(author, lang, filtered_posts, kw) + else: + yield self.author_page_as_list(author, lang, filtered_posts, kw) + + for author, posts in author_list: + for task in render_lists(author, posts): + yield task + + def _create_authors_page(self, kw): + """Create a global "all authors" page for each language.""" + template_name = "authors.tmpl" + kw = kw.copy() + for lang in kw["translations"]: + authors = natsort.natsorted([author for author in self._posts_per_author().keys()], + alg=natsort.ns.F | natsort.ns.IC) + has_authors = (authors != []) + kw['authors'] = authors + output_name = os.path.join( + kw['output_folder'], self.site.path('author_index', None, lang)) + context = {} + if has_authors: + context["title"] = kw["messages"][lang]["Authors"] + context["items"] = [(author, self.site.link("author", author, lang)) for author + in authors] + context["description"] = context["title"] + else: + context["items"] = None + context["permalink"] = self.site.link("author_index", None, lang) + context["pagekind"] = ["list", "authors_page"] + task = self.site.generic_post_list_renderer( + lang, + [], + output_name, + template_name, + kw['filters'], + context, + ) + task['uptodate'] = task['uptodate'] + [utils.config_changed(kw, 'nikola.plugins.task.authors:page')] + task['basename'] = str(self.name) + yield task + + def list_authors_page(self, kw): + """Create a global "all authors" page for each language.""" + yield self._create_authors_page(kw) + + def _get_title(self, author): + return author + + def _get_description(self, author, lang): + descriptions = self.site.config['AUTHOR_PAGES_DESCRIPTIONS'] + return descriptions[lang][author] if lang in descriptions and author in descriptions[lang] else None + + def author_page_as_index(self, author, lang, post_list, kw): + """Render a sort of index page collection using only this author's posts.""" + kind = "author" + + def page_link(i, displayed_i, num_pages, force_addition, extension=None): + feed = "_atom" if extension == ".atom" else "" + return utils.adjust_name_for_index_link(self.site.link(kind + feed, author, lang), i, displayed_i, lang, self.site, force_addition, extension) + + def page_path(i, displayed_i, num_pages, force_addition, extension=None): + feed = "_atom" if extension == ".atom" else "" + return utils.adjust_name_for_index_path(self.site.path(kind + feed, author, lang), i, displayed_i, lang, self.site, force_addition, extension) + + context_source = {} + title = self._get_title(author) + if kw["generate_rss"]: + # On a author page, the feeds include the author's feeds + rss_link = ("""<link rel="alternate" type="application/rss+xml" """ + """title="RSS for author """ + """{0} ({1})" href="{2}">""".format( + title, lang, self.site.link(kind + "_rss", author, lang))) + context_source['rss_link'] = rss_link + context_source["author"] = title + indexes_title = kw["messages"][lang]["Posts by %s"] % title + context_source["description"] = self._get_description(author, lang) + context_source["pagekind"] = ["index", "author_page"] + template_name = "authorindex.tmpl" + + yield self.site.generic_index_renderer(lang, post_list, indexes_title, template_name, context_source, kw, str(self.name), page_link, page_path) + + def author_page_as_list(self, author, lang, post_list, kw): + """Render a single flat link list with this author's posts.""" + kind = "author" + template_name = "author.tmpl" + output_name = os.path.join(kw['output_folder'], self.site.path( + kind, author, lang)) + context = {} + context["lang"] = lang + title = self._get_title(author) + context["author"] = title + context["title"] = kw["messages"][lang]["Posts by %s"] % title + context["posts"] = post_list + context["permalink"] = self.site.link(kind, author, lang) + context["kind"] = kind + context["description"] = self._get_description(author, lang) + context["pagekind"] = ["list", "author_page"] + task = self.site.generic_post_list_renderer( + lang, + post_list, + output_name, + template_name, + kw['filters'], + context, + ) + task['uptodate'] = task['uptodate'] + [utils.config_changed(kw, 'nikola.plugins.task.authors:list')] + task['basename'] = str(self.name) + yield task + + def author_rss(self, author, lang, posts, kw): + """Create a RSS feed for a single author in a given language.""" + kind = "author" + # Render RSS + output_name = os.path.normpath( + os.path.join(kw['output_folder'], + self.site.path(kind + "_rss", author, lang))) + feed_url = urljoin(self.site.config['BASE_URL'], self.site.link(kind + "_rss", author, lang).lstrip('/')) + deps = [] + deps_uptodate = [] + post_list = sorted(posts, key=lambda a: a.date) + post_list.reverse() + for post in post_list: + deps += post.deps(lang) + deps_uptodate += post.deps_uptodate(lang) + task = { + 'basename': str(self.name), + 'name': output_name, + 'file_dep': deps, + 'targets': [output_name], + 'actions': [(utils.generic_rss_renderer, + (lang, "{0} ({1})".format(kw["blog_title"](lang), self._get_title(author)), + kw["site_url"], None, post_list, + output_name, kw["feed_teasers"], kw["feed_plain"], kw['feed_length'], + feed_url, None, kw["feed_link_append_query"]))], + 'clean': True, + 'uptodate': [utils.config_changed(kw, 'nikola.plugins.task.authors:rss')] + deps_uptodate, + 'task_dep': ['render_posts'], + } + return utils.apply_filters(task, kw['filters']) + + def slugify_author_name(self, name): + """Slugify an author name.""" + if self.site.config['SLUG_AUTHOR_PATH']: + name = utils.slugify(name) + return name + + def author_index_path(self, name, lang): + """Link to the author's index. + + Example: + + link://authors/ => /authors/index.html + """ + return [_f for _f in [self.site.config['TRANSLATIONS'][lang], + self.site.config['AUTHOR_PATH'], + self.site.config['INDEX_FILE']] if _f] + + def author_path(self, name, lang): + """Link to an author's page. + + Example: + + link://author/joe => /authors/joe.html + """ + if self.site.config['PRETTY_URLS']: + return [_f for _f in [ + self.site.config['TRANSLATIONS'][lang], + self.site.config['AUTHOR_PATH'], + self.slugify_author_name(name), + self.site.config['INDEX_FILE']] if _f] + else: + return [_f for _f in [ + self.site.config['TRANSLATIONS'][lang], + self.site.config['AUTHOR_PATH'], + self.slugify_author_name(name) + ".html"] if _f] + + def author_atom_path(self, name, lang): + """Link to an author's Atom feed. + + Example: + + link://author_atom/joe => /authors/joe.atom + """ + return [_f for _f in [self.site.config['TRANSLATIONS'][lang], + self.site.config['AUTHOR_PATH'], self.slugify_author_name(name) + ".atom"] if + _f] + + def author_rss_path(self, name, lang): + """Link to an author's RSS feed. + + Example: + + link://author_rss/joe => /authors/joe.rss + """ + return [_f for _f in [self.site.config['TRANSLATIONS'][lang], + self.site.config['AUTHOR_PATH'], self.slugify_author_name(name) + ".xml"] if + _f] + + def _add_extension(self, path, extension): + path[-1] += extension + return path + + def _posts_per_author(self): + """Return a dict of posts per author.""" + if self.posts_per_author is None: + self.posts_per_author = defaultdict(list) + for post in self.site.timeline: + if post.is_post: + self.posts_per_author[post.author()].append(post) + return self.posts_per_author diff --git a/nikola/plugins/task/bundles.plugin b/nikola/plugins/task/bundles.plugin index ca997d0..b5bf6e4 100644 --- a/nikola/plugins/task/bundles.plugin +++ b/nikola/plugins/task/bundles.plugin @@ -5,7 +5,7 @@ module = bundles [Documentation] author = Roberto Alsina version = 1.0 -website = http://getnikola.com +website = https://getnikola.com/ description = Theme bundles using WebAssets [Nikola] diff --git a/nikola/plugins/task/bundles.py b/nikola/plugins/task/bundles.py index b9c57b9..e709133 100644 --- a/nikola/plugins/task/bundles.py +++ b/nikola/plugins/task/bundles.py @@ -40,7 +40,6 @@ from nikola import utils class BuildBundles(LateTask): - """Bundle assets using WebAssets.""" name = "create_bundles" @@ -52,6 +51,7 @@ class BuildBundles(LateTask): utils.req_missing(['webassets'], 'USE_BUNDLES', optional=True) self.logger.warn('Setting USE_BUNDLES to False.') site.config['USE_BUNDLES'] = False + site._GLOBAL_CONTEXT['use_bundles'] = False super(BuildBundles, self).set_site(site) def gen_tasks(self): @@ -100,7 +100,11 @@ class BuildBundles(LateTask): files.append(os.path.join(dname, fname)) file_dep = [os.path.join(kw['output_folder'], fname) for fname in files if - utils.get_asset_path(fname, self.site.THEMES, self.site.config['FILES_FOLDERS']) or fname == os.path.join('assets', 'css', 'code.css')] + utils.get_asset_path( + fname, + self.site.THEMES, + self.site.config['FILES_FOLDERS'], + output_dir=kw['output_folder']) or fname == os.path.join('assets', 'css', 'code.css')] # code.css will be generated by us if it does not exist in # FILES_FOLDERS or theme assets. It is guaranteed that the # generation will happen before this task. diff --git a/nikola/plugins/task/copy_assets.plugin b/nikola/plugins/task/copy_assets.plugin index c182150..ddd38df 100644 --- a/nikola/plugins/task/copy_assets.plugin +++ b/nikola/plugins/task/copy_assets.plugin @@ -5,7 +5,7 @@ module = copy_assets [Documentation] author = Roberto Alsina version = 1.0 -website = http://getnikola.com +website = https://getnikola.com/ description = Copy theme assets into output. [Nikola] diff --git a/nikola/plugins/task/copy_assets.py b/nikola/plugins/task/copy_assets.py index 58521d4..2cab71a 100644 --- a/nikola/plugins/task/copy_assets.py +++ b/nikola/plugins/task/copy_assets.py @@ -36,7 +36,6 @@ from nikola import utils class CopyAssets(Task): - """Copy theme assets into output.""" name = "copy_assets" @@ -61,10 +60,7 @@ class CopyAssets(Task): code_css_path = os.path.join(kw['output_folder'], 'assets', 'css', 'code.css') code_css_input = utils.get_asset_path('assets/css/code.css', themes=kw['themes'], - files_folders=kw['files_folders']) - - kw["code.css_input"] = code_css_input - + files_folders=kw['files_folders'], output_dir=None) yield self.group_task() for theme_name in kw['themes']: @@ -77,7 +73,9 @@ class CopyAssets(Task): task['uptodate'] = [utils.config_changed(kw, 'nikola.plugins.task.copy_assets')] task['basename'] = self.name if code_css_input: - task['file_dep'] = [code_css_input] + if 'file_dep' not in task: + task['file_dep'] = [] + task['file_dep'].append(code_css_input) yield utils.apply_filters(task, kw['filters']) # Check whether or not there is a code.css file around. diff --git a/nikola/plugins/task/copy_files.plugin b/nikola/plugins/task/copy_files.plugin index ce8f5d0..e4bb1cf 100644 --- a/nikola/plugins/task/copy_files.plugin +++ b/nikola/plugins/task/copy_files.plugin @@ -5,7 +5,7 @@ module = copy_files [Documentation] author = Roberto Alsina version = 1.0 -website = http://getnikola.com +website = https://getnikola.com/ description = Copy static files into the output. [Nikola] diff --git a/nikola/plugins/task/copy_files.py b/nikola/plugins/task/copy_files.py index 1232248..0488011 100644 --- a/nikola/plugins/task/copy_files.py +++ b/nikola/plugins/task/copy_files.py @@ -33,7 +33,6 @@ from nikola import utils class CopyFiles(Task): - """Copy static files into the output folder.""" name = "copy_files" diff --git a/nikola/plugins/task/galleries.plugin b/nikola/plugins/task/galleries.plugin index 9d3fa28..2064e68 100644 --- a/nikola/plugins/task/galleries.plugin +++ b/nikola/plugins/task/galleries.plugin @@ -5,7 +5,7 @@ module = galleries [Documentation] author = Roberto Alsina version = 1.0 -website = http://getnikola.com +website = https://getnikola.com/ description = Create image galleries automatically. [Nikola] diff --git a/nikola/plugins/task/galleries.py b/nikola/plugins/task/galleries.py index c0df4a4..d3f1db7 100644 --- a/nikola/plugins/task/galleries.py +++ b/nikola/plugins/task/galleries.py @@ -57,7 +57,6 @@ _image_size_cache = {} class Galleries(Task, ImageProcessor): - """Render image galleries.""" name = 'render_galleries' @@ -122,20 +121,45 @@ class Galleries(Task, ImageProcessor): sys.exit(1) def gallery_path(self, name, lang): - """Return a gallery path.""" + """Link to an image gallery's path. + + It will try to find a gallery with that name if it's not ambiguous + or with that path. For example: + + link://gallery/london => /galleries/trips/london/index.html + + link://gallery/trips/london => /galleries/trips/london/index.html + """ gallery_path = self._find_gallery_path(name) return [_f for _f in [self.site.config['TRANSLATIONS'][lang]] + gallery_path.split(os.sep) + [self.site.config['INDEX_FILE']] if _f] def gallery_global_path(self, name, lang): - """Return the global gallery path, which contains images.""" + """Link to the global gallery path, which contains all the images in galleries. + + There is only one copy of an image on multilingual blogs, in the site root. + + link://gallery_global/london => /galleries/trips/london/index.html + + link://gallery_global/trips/london => /galleries/trips/london/index.html + + (a ``gallery`` link could lead to eg. /en/galleries/trips/london/index.html) + """ gallery_path = self._find_gallery_path(name) return [_f for _f in gallery_path.split(os.sep) + [self.site.config['INDEX_FILE']] if _f] def gallery_rss_path(self, name, lang): - """Return path to the RSS file for a gallery.""" + """Link to an image gallery's RSS feed. + + It will try to find a gallery with that name if it's not ambiguous + or with that path. For example: + + link://gallery_rss/london => /galleries/trips/london/rss.xml + + link://gallery_rss/trips/london => /galleries/trips/london/rss.xml + """ gallery_path = self._find_gallery_path(name) return [_f for _f in [self.site.config['TRANSLATIONS'][lang]] + gallery_path.split(os.sep) + @@ -538,9 +562,12 @@ class Galleries(Task, ImageProcessor): for img, thumb, title in zip(img_list, thumbs, img_titles): w, h = _image_size_cache.get(thumb, (None, None)) if w is None: - im = Image.open(thumb) - w, h = im.size - _image_size_cache[thumb] = w, h + if os.path.splitext(thumb)[1] in ['.svg', '.svgz']: + w, h = 200, 200 + else: + im = Image.open(thumb) + w, h = im.size + _image_size_cache[thumb] = w, h # Thumbs are files in output, we need URLs photo_array.append({ 'url': url_from_path(img), @@ -587,7 +614,7 @@ class Galleries(Task, ImageProcessor): description='', lastBuildDate=datetime.datetime.utcnow(), items=items, - generator='http://getnikola.com/', + generator='https://getnikola.com/', language=lang ) diff --git a/nikola/plugins/task/gzip.plugin b/nikola/plugins/task/gzip.plugin index 7834d22..d3a34ee 100644 --- a/nikola/plugins/task/gzip.plugin +++ b/nikola/plugins/task/gzip.plugin @@ -5,7 +5,7 @@ module = gzip [Documentation] author = Roberto Alsina version = 1.0 -website = http://getnikola.com +website = https://getnikola.com/ description = Create gzipped copies of files [Nikola] diff --git a/nikola/plugins/task/gzip.py b/nikola/plugins/task/gzip.py index cf16f63..aaa213d 100644 --- a/nikola/plugins/task/gzip.py +++ b/nikola/plugins/task/gzip.py @@ -35,7 +35,6 @@ from nikola.plugin_categories import TaskMultiplier class GzipFiles(TaskMultiplier): - """If appropiate, create tasks to create gzipped versions of files.""" name = "gzip" diff --git a/nikola/plugins/task/indexes.plugin b/nikola/plugins/task/indexes.plugin index d9b0e5f..553b5ad 100644 --- a/nikola/plugins/task/indexes.plugin +++ b/nikola/plugins/task/indexes.plugin @@ -5,7 +5,7 @@ module = indexes [Documentation] author = Roberto Alsina version = 1.0 -website = http://getnikola.com +website = https://getnikola.com/ description = Generates the blog's index pages. [Nikola] diff --git a/nikola/plugins/task/indexes.py b/nikola/plugins/task/indexes.py index c02818e..2ab97fa 100644 --- a/nikola/plugins/task/indexes.py +++ b/nikola/plugins/task/indexes.py @@ -29,23 +29,45 @@ from __future__ import unicode_literals from collections import defaultdict import os +try: + from urlparse import urljoin +except ImportError: + from urllib.parse import urljoin # NOQA from nikola.plugin_categories import Task from nikola import utils class Indexes(Task): - """Render the blog indexes.""" name = "render_indexes" def set_site(self, site): """Set Nikola site.""" + self.number_of_pages = dict() + self.number_of_pages_section = {lang: dict() for lang in site.config['TRANSLATIONS']} site.register_path_handler('index', self.index_path) site.register_path_handler('index_atom', self.index_atom_path) + site.register_path_handler('section_index', self.index_section_path) + site.register_path_handler('section_index_atom', self.index_section_atom_path) return super(Indexes, self).set_site(site) + def _get_filtered_posts(self, lang, show_untranslated_posts): + """Return a filtered list of all posts for the given language. + + If show_untranslated_posts is True, will only include posts which + are translated to the given language. Otherwise, returns all posts. + """ + if show_untranslated_posts: + return self.site.posts + else: + return [x for x in self.site.posts if x.is_translation_available(lang)] + + def _compute_number_of_pages(self, filtered_posts, posts_count): + """Given a list of posts and the maximal number of posts per page, computes the number of pages needed.""" + return min(1, (len(filtered_posts) + posts_count - 1) // posts_count) + def gen_tasks(self): """Render the blog indexes.""" self.site.scan_posts() @@ -56,16 +78,16 @@ class Indexes(Task): "messages": self.site.MESSAGES, "output_folder": self.site.config['OUTPUT_FOLDER'], "filters": self.site.config['FILTERS'], + "index_file": self.site.config['INDEX_FILE'], "show_untranslated_posts": self.site.config['SHOW_UNTRANSLATED_POSTS'], "index_display_post_count": self.site.config['INDEX_DISPLAY_POST_COUNT'], "indexes_title": self.site.config['INDEXES_TITLE'], + "strip_indexes": self.site.config['STRIP_INDEXES'], "blog_title": self.site.config["BLOG_TITLE"], "generate_atom": self.site.config["GENERATE_ATOM"], } template_name = "index.tmpl" - posts = self.site.posts - self.number_of_pages = dict() for lang in kw["translations"]: def page_link(i, displayed_i, num_pages, force_addition, extension=None): feed = "_atom" if extension == ".atom" else "" @@ -77,19 +99,75 @@ class Indexes(Task): return utils.adjust_name_for_index_path(self.site.path("index" + feed, None, lang), i, displayed_i, lang, self.site, force_addition, extension) - if kw["show_untranslated_posts"]: - filtered_posts = posts - else: - filtered_posts = [x for x in posts if x.is_translation_available(lang)] + filtered_posts = self._get_filtered_posts(lang, kw["show_untranslated_posts"]) indexes_title = kw['indexes_title'](lang) or kw['blog_title'](lang) - self.number_of_pages[lang] = (len(filtered_posts) + kw['index_display_post_count'] - 1) // kw['index_display_post_count'] + self.number_of_pages[lang] = self._compute_number_of_pages(filtered_posts, kw['index_display_post_count']) context = {} - context["pagekind"] = ["index"] + context["pagekind"] = ["main_index", "index"] yield self.site.generic_index_renderer(lang, filtered_posts, indexes_title, template_name, context, kw, 'render_indexes', page_link, page_path) + if self.site.config['POSTS_SECTIONS']: + + kw["posts_section_are_indexes"] = self.site.config['POSTS_SECTION_ARE_INDEXES'] + index_len = len(kw['index_file']) + + groups = defaultdict(list) + for p in filtered_posts: + groups[p.section_slug(lang)].append(p) + + # don't build sections when there is only one, aka. default setups + if not len(groups.items()) > 1: + continue + + for section_slug, post_list in groups.items(): + self.number_of_pages_section[lang][section_slug] = self._compute_number_of_pages(post_list, kw['index_display_post_count']) + + def cat_link(i, displayed_i, num_pages, force_addition, extension=None): + feed = "_atom" if extension == ".atom" else "" + return utils.adjust_name_for_index_link(self.site.link("section_index" + feed, section_slug, lang), i, displayed_i, + lang, self.site, force_addition, extension) + + def cat_path(i, displayed_i, num_pages, force_addition, extension=None): + feed = "_atom" if extension == ".atom" else "" + return utils.adjust_name_for_index_path(self.site.path("section_index" + feed, section_slug, lang), i, displayed_i, + lang, self.site, force_addition, extension) + + context = {} + + short_destination = os.path.join(section_slug, kw['index_file']) + link = short_destination.replace('\\', '/') + if kw['strip_indexes'] and link[-(1 + index_len):] == '/' + kw['index_file']: + link = link[:-index_len] + context["permalink"] = link + context["pagekind"] = ["section_page"] + context["description"] = self.site.config['POSTS_SECTION_DESCRIPTIONS'](lang)[section_slug] if section_slug in self.site.config['POSTS_SECTION_DESCRIPTIONS'](lang) else "" + + if kw["posts_section_are_indexes"]: + context["pagekind"].append("index") + kw["posts_section_title"] = self.site.config['POSTS_SECTION_TITLE'](lang) + + section_title = None + if type(kw["posts_section_title"]) is dict: + if section_slug in kw["posts_section_title"]: + section_title = kw["posts_section_title"][section_slug] + elif type(kw["posts_section_title"]) is str: + section_title = kw["posts_section_title"] + if not section_title: + section_title = post_list[0].section_name(lang) + section_title = section_title.format(name=post_list[0].section_name(lang)) + + task = self.site.generic_index_renderer(lang, post_list, section_title, "sectionindex.tmpl", context, kw, self.name, cat_link, cat_path) + else: + context["pagekind"].append("list") + output_name = os.path.join(kw['output_folder'], section_slug, kw['index_file']) + task = self.site.generic_post_list_renderer(lang, post_list, output_name, "list.tmpl", kw['filters'], context) + task['uptodate'] = [utils.config_changed(kw, 'nikola.plugins.task.indexes')] + task['basename'] = self.name + yield task + if not self.site.config["STORY_INDEX"]: return kw = { @@ -134,7 +212,8 @@ class Indexes(Task): should_render = False else: context["items"].append((post.title(lang), - post.permalink(lang))) + post.permalink(lang), + None)) if should_render: task = self.site.generic_post_list_renderer(lang, post_list, @@ -147,22 +226,75 @@ class Indexes(Task): yield task def index_path(self, name, lang, is_feed=False): - """Return path to an index.""" + """Link to a numbered index. + + Example: + + link://index/3 => /index-3.html + """ extension = None if is_feed: extension = ".atom" index_file = os.path.splitext(self.site.config['INDEX_FILE'])[0] + extension else: index_file = self.site.config['INDEX_FILE'] + if lang in self.number_of_pages: + number_of_pages = self.number_of_pages[lang] + else: + number_of_pages = self._compute_number_of_pages(self._get_filtered_posts(lang, self.site.config['SHOW_UNTRANSLATED_POSTS']), self.site.config['INDEX_DISPLAY_POST_COUNT']) + self.number_of_pages[lang] = number_of_pages return utils.adjust_name_for_index_path_list([_f for _f in [self.site.config['TRANSLATIONS'][lang], self.site.config['INDEX_PATH'], index_file] if _f], name, - utils.get_displayed_page_number(name, self.number_of_pages[lang], self.site), + utils.get_displayed_page_number(name, number_of_pages, self.site), + lang, + self.site, + extension=extension) + + def index_section_path(self, name, lang, is_feed=False): + """Link to the index for a section. + + Example: + + link://section_index/cars => /cars/index.html + """ + extension = None + + if is_feed: + extension = ".atom" + index_file = os.path.splitext(self.site.config['INDEX_FILE'])[0] + extension + else: + index_file = self.site.config['INDEX_FILE'] + if name in self.number_of_pages_section[lang]: + number_of_pages = self.number_of_pages_section[lang][name] + else: + posts = [post for post in self._get_filtered_posts(lang, self.site.config['SHOW_UNTRANSLATED_POSTS']) if post.section_slug(lang) == name] + number_of_pages = self._compute_number_of_pages(posts, self.site.config['INDEX_DISPLAY_POST_COUNT']) + self.number_of_pages_section[lang][name] = number_of_pages + return utils.adjust_name_for_index_path_list([_f for _f in [self.site.config['TRANSLATIONS'][lang], + name, + index_file] if _f], + None, + utils.get_displayed_page_number(None, number_of_pages, self.site), lang, self.site, extension=extension) def index_atom_path(self, name, lang): - """Return path to an Atom index.""" + """Link to a numbered Atom index. + + Example: + + link://index_atom/3 => /index-3.atom + """ return self.index_path(name, lang, is_feed=True) + + def index_section_atom_path(self, name, lang): + """Link to the Atom index for a section. + + Example: + + link://section_index_atom/cars => /cars/index.atom + """ + return self.index_section_path(name, lang, is_feed=True) diff --git a/nikola/plugins/task/listings.plugin b/nikola/plugins/task/listings.plugin index 435234b..8fc2e2d 100644 --- a/nikola/plugins/task/listings.plugin +++ b/nikola/plugins/task/listings.plugin @@ -5,7 +5,7 @@ module = listings [Documentation] author = Roberto Alsina version = 1.0 -website = http://getnikola.com +website = https://getnikola.com/ description = Render code listings into output [Nikola] diff --git a/nikola/plugins/task/listings.py b/nikola/plugins/task/listings.py index 5f79724..891f361 100644 --- a/nikola/plugins/task/listings.py +++ b/nikola/plugins/task/listings.py @@ -28,6 +28,7 @@ from __future__ import unicode_literals, print_function +from collections import defaultdict import sys import os import lxml.html @@ -41,16 +42,13 @@ from nikola import utils class Listings(Task): - """Render code listings.""" name = "render_listings" def register_output_name(self, input_folder, rel_name, rel_output_name): """Register proper and improper file mappings.""" - if rel_name not in self.improper_input_file_mapping: - self.improper_input_file_mapping[rel_name] = [] - self.improper_input_file_mapping[rel_name].append(rel_output_name) + self.improper_input_file_mapping[rel_name].add(rel_output_name) self.proper_input_file_mapping[os.path.join(input_folder, rel_name)] = rel_output_name self.proper_input_file_mapping[rel_output_name] = rel_output_name @@ -85,7 +83,7 @@ class Listings(Task): # a list is needed. This is needed for compatibility to previous Nikola # versions, where there was no need to specify the input directory name # when asking for a link via site.link('listing', ...). - self.improper_input_file_mapping = {} + self.improper_input_file_mapping = defaultdict(set) # proper_input_file_mapping maps relative input file (relative to CWD) # to a generated output file. Since we don't allow an input directory @@ -255,7 +253,16 @@ class Listings(Task): }, self.kw["filters"]) def listing_path(self, namep, lang): - """Return path to a listing.""" + """A link to a listing. + + It will try to use the file name if it's not ambiguous, or the file path. + + Example: + + link://listing/hello.py => /listings/tutorial/hello.py.html + + link://listing/tutorial/hello.py => /listings/tutorial/hello.py.html + """ namep = namep.replace('/', os.sep) nameh = namep + '.html' for name in (namep, nameh): @@ -271,7 +278,7 @@ class Listings(Task): sys.exit(1) if len(self.site.config['LISTINGS_FOLDERS']) > 1: utils.LOGGER.notice("Using listings names in site.link() without input directory prefix while configuration's LISTINGS_FOLDERS has more than one entry.") - name = self.improper_input_file_mapping[name][0] + name = list(self.improper_input_file_mapping[name])[0] break else: utils.LOGGER.error("Unknown listing name {0}!".format(namep)) diff --git a/nikola/plugins/task/pages.plugin b/nikola/plugins/task/pages.plugin index 023d41b..1bdc7f4 100644 --- a/nikola/plugins/task/pages.plugin +++ b/nikola/plugins/task/pages.plugin @@ -5,7 +5,7 @@ module = pages [Documentation] author = Roberto Alsina version = 1.0 -website = http://getnikola.com +website = https://getnikola.com/ description = Create pages in the output. [Nikola] diff --git a/nikola/plugins/task/pages.py b/nikola/plugins/task/pages.py index e6a8a82..8d41035 100644 --- a/nikola/plugins/task/pages.py +++ b/nikola/plugins/task/pages.py @@ -32,7 +32,6 @@ from nikola.utils import config_changed class RenderPages(Task): - """Render pages into output.""" name = "render_pages" diff --git a/nikola/plugins/task/posts.plugin b/nikola/plugins/task/posts.plugin index 79b7c51..c9578bc 100644 --- a/nikola/plugins/task/posts.plugin +++ b/nikola/plugins/task/posts.plugin @@ -5,7 +5,7 @@ module = posts [Documentation] author = Roberto Alsina version = 1.0 -website = http://getnikola.com +website = https://getnikola.com/ description = Create HTML fragments out of posts. [Nikola] diff --git a/nikola/plugins/task/posts.py b/nikola/plugins/task/posts.py index a3a8375..8735beb 100644 --- a/nikola/plugins/task/posts.py +++ b/nikola/plugins/task/posts.py @@ -44,7 +44,6 @@ def update_deps(post, lang, task): class RenderPosts(Task): - """Build HTML fragments from metadata and text.""" name = "render_posts" @@ -77,6 +76,8 @@ class RenderPosts(Task): deps_dict = copy(kw) deps_dict.pop('timeline') for post in kw['timeline']: + if not post.is_translation_available(lang) and not self.site.config['SHOW_UNTRANSLATED_POSTS']: + continue # Extra config dependencies picked from config for p in post.fragment_deps(lang): if p.startswith('####MAGIC####CONFIG:'): @@ -114,7 +115,7 @@ class RenderPosts(Task): pass else: flist.append(f) - yield utils.apply_filters(task, {os.path.splitext(dest): flist}) + yield utils.apply_filters(task, {os.path.splitext(dest)[-1]: flist}) def dependence_on_timeline(self, post, lang): """Check if a post depends on the timeline.""" diff --git a/nikola/plugins/task/py3_switch.plugin b/nikola/plugins/task/py3_switch.plugin new file mode 100644 index 0000000..b0014e1 --- /dev/null +++ b/nikola/plugins/task/py3_switch.plugin @@ -0,0 +1,13 @@ +[Core] +name = py3_switch +module = py3_switch + +[Documentation] +author = Roberto Alsina +version = 1.0 +website = https://getnikola.com/ +description = Beg the user to switch to Python 3 + +[Nikola] +plugincategory = Task + diff --git a/nikola/plugins/task/py3_switch.py b/nikola/plugins/task/py3_switch.py new file mode 100644 index 0000000..930c593 --- /dev/null +++ b/nikola/plugins/task/py3_switch.py @@ -0,0 +1,103 @@ +# -*- coding: utf-8 -*- + +# Copyright © 2012-2015 Roberto Alsina and others. + +# Permission is hereby granted, free of charge, to any +# person obtaining a copy of this software and associated +# documentation files (the "Software"), to deal in the +# Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the +# Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice +# shall be included in all copies or substantial portions of +# the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +"""Beg the user to switch to python 3.""" + +import datetime +import os +import random +import sys + +import doit.tools + +from nikola.utils import get_logger, STDERR_HANDLER +from nikola.plugin_categories import LateTask + +PY2_AND_NO_PY3_WARNING = """Nikola is going to deprecate Python 2 support in 2016. Your current +version will continue to work, but please consider upgrading to Python 3. + +Please check http://bit.ly/1FKEsiX for details. +""" +PY2_WARNING = """Nikola is going to deprecate Python 2 support in 2016. You already have Python 3 +available in your system. Why not switch? + +Please check http://bit.ly/1FKEsiX for details. +""" +PY2_BARBS = [ + "Python 2 has been deprecated for years. Stop clinging to your long gone youth and switch to Python3.", + "Python 2 is the safety blanket of languages. Be a big kid and switch to Python 3", + "Python 2 is old and busted. Python 3 is the new hotness.", + "Nice unicode you have there, would be a shame something happened to it.. switch to python 3!.", + "Don’t get in the way of progress! Upgrade to Python 3 and save a developer’s mind today!", + "Winners don't use Python 2 -- Signed: The FBI", + "Python 2? What year is it?", + "I just wanna tell you how I'm feeling\n" + "Gotta make you understand\n" + "Never gonna give you up [But Python 2 has to go]", + "The year 2009 called, and they want their Python 2.7 back.", +] + + +LOGGER = get_logger('Nikola', STDERR_HANDLER) + + +def has_python_3(): + """Check if python 3 is available.""" + if 'win' in sys.platform: + py_bin = 'py.exe' + else: + py_bin = 'python3' + for path in os.environ["PATH"].split(os.pathsep): + if os.access(os.path.join(path, py_bin), os.X_OK): + return True + return False + + +class Py3Switch(LateTask): + """Beg the user to switch to python 3.""" + + name = "_switch to py3" + + def gen_tasks(self): + """Beg the user to switch to python 3.""" + def give_warning(): + if sys.version_info[0] == 3: + return + if has_python_3(): + LOGGER.warn(random.choice(PY2_BARBS)) + LOGGER.warn(PY2_WARNING) + else: + LOGGER.warn(PY2_AND_NO_PY3_WARNING) + + task = { + 'basename': self.name, + 'name': 'please!', + 'actions': [give_warning], + 'clean': True, + 'uptodate': [doit.tools.timeout(datetime.timedelta(days=3))] + } + + return task diff --git a/nikola/plugins/task/redirect.plugin b/nikola/plugins/task/redirect.plugin index c3137b9..c5a3042 100644 --- a/nikola/plugins/task/redirect.plugin +++ b/nikola/plugins/task/redirect.plugin @@ -5,7 +5,7 @@ module = redirect [Documentation] author = Roberto Alsina version = 1.0 -website = http://getnikola.com +website = https://getnikola.com/ description = Create redirect pages. [Nikola] diff --git a/nikola/plugins/task/redirect.py b/nikola/plugins/task/redirect.py index 8530f5e..2d4eba4 100644 --- a/nikola/plugins/task/redirect.py +++ b/nikola/plugins/task/redirect.py @@ -35,7 +35,6 @@ from nikola import utils class Redirect(Task): - """Generate redirections.""" name = "redirect" diff --git a/nikola/plugins/task/robots.plugin b/nikola/plugins/task/robots.plugin index 72ce31f..7ae56c6 100644 --- a/nikola/plugins/task/robots.plugin +++ b/nikola/plugins/task/robots.plugin @@ -5,7 +5,7 @@ module = robots [Documentation] author = Daniel Aleksandersen version = 1.0 -website = http://getnikola.com +website = https://getnikola.com/ description = Generate /robots.txt exclusion file and promote sitemap. [Nikola] diff --git a/nikola/plugins/task/robots.py b/nikola/plugins/task/robots.py index 65254b6..7c7f5df 100644 --- a/nikola/plugins/task/robots.py +++ b/nikola/plugins/task/robots.py @@ -39,7 +39,6 @@ from nikola import utils class RobotsFile(LateTask): - """Generate a robots.txt file.""" name = "robots_file" @@ -64,14 +63,15 @@ class RobotsFile(LateTask): with io.open(robots_path, 'w+', encoding='utf8') as outf: outf.write("Sitemap: {0}\n\n".format(sitemapindex_url)) + outf.write("User-Agent: *\n") if kw["robots_exclusions"]: - outf.write("User-Agent: *\n") for loc in kw["robots_exclusions"]: outf.write("Disallow: {0}\n".format(loc)) + outf.write("Host: {0}\n".format(urlparse(kw["base_url"]).netloc)) yield self.group_task() - if not utils.get_asset_path("robots.txt", [], files_folders=kw["files_folders"]): + if not utils.get_asset_path("robots.txt", [], files_folders=kw["files_folders"], output_dir=False): yield utils.apply_filters({ "basename": self.name, "name": robots_path, @@ -82,6 +82,6 @@ class RobotsFile(LateTask): "task_dep": ["sitemap"] }, kw["filters"]) elif kw["robots_exclusions"]: - utils.LOGGER.warn('Did not generate robots.txt as one already exists in FILES_FOLDERS. ROBOTS_EXCLUSIONS will not have any affect on the copied fie.') + utils.LOGGER.warn('Did not generate robots.txt as one already exists in FILES_FOLDERS. ROBOTS_EXCLUSIONS will not have any affect on the copied file.') else: utils.LOGGER.debug('Did not generate robots.txt as one already exists in FILES_FOLDERS.') diff --git a/nikola/plugins/task/rss.plugin b/nikola/plugins/task/rss.plugin index cf9b7a7..4dd8aba 100644 --- a/nikola/plugins/task/rss.plugin +++ b/nikola/plugins/task/rss.plugin @@ -5,7 +5,7 @@ module = rss [Documentation] author = Roberto Alsina version = 1.0 -website = http://getnikola.com +website = https://getnikola.com/ description = Generate RSS feeds. [Nikola] diff --git a/nikola/plugins/task/rss.py b/nikola/plugins/task/rss.py index 9020a06..be57f5c 100644 --- a/nikola/plugins/task/rss.py +++ b/nikola/plugins/task/rss.py @@ -38,7 +38,6 @@ from nikola.plugin_categories import Task class GenerateRSS(Task): - """Generate RSS feeds.""" name = "generate_rss" @@ -58,13 +57,14 @@ class GenerateRSS(Task): "base_url": self.site.config["BASE_URL"], "blog_description": self.site.config["BLOG_DESCRIPTION"], "output_folder": self.site.config["OUTPUT_FOLDER"], - "rss_teasers": self.site.config["RSS_TEASERS"], - "rss_plain": self.site.config["RSS_PLAIN"], + "feed_teasers": self.site.config["FEED_TEASERS"], + "feed_plain": self.site.config["FEED_PLAIN"], "show_untranslated_posts": self.site.config['SHOW_UNTRANSLATED_POSTS'], "feed_length": self.site.config['FEED_LENGTH'], + "feed_previewimage": self.site.config["FEED_PREVIEWIMAGE"], "tzinfo": self.site.tzinfo, - "rss_read_more_link": self.site.config["RSS_READ_MORE_LINK"], - "rss_links_append_query": self.site.config["RSS_LINKS_APPEND_QUERY"], + "feed_read_more_link": self.site.config["FEED_READ_MORE_LINK"], + "feed_links_append_query": self.site.config["FEED_LINKS_APPEND_QUERY"], } self.site.scan_posts() # Check for any changes in the state of use_in_feeds for any post. @@ -96,8 +96,8 @@ class GenerateRSS(Task): 'actions': [(utils.generic_rss_renderer, (lang, kw["blog_title"](lang), kw["site_url"], kw["blog_description"](lang), posts, output_name, - kw["rss_teasers"], kw["rss_plain"], kw['feed_length'], feed_url, - None, kw["rss_links_append_query"]))], + kw["feed_teasers"], kw["feed_plain"], kw['feed_length'], feed_url, + None, kw["feed_links_append_query"]))], 'task_dep': ['render_posts'], 'clean': True, @@ -106,6 +106,11 @@ class GenerateRSS(Task): yield utils.apply_filters(task, kw['filters']) def rss_path(self, name, lang): - """Return RSS path.""" + """A link to the RSS feed path. + + Example: + + link://rss => /blog/rss.xml + """ return [_f for _f in [self.site.config['TRANSLATIONS'][lang], self.site.config['RSS_PATH'], 'rss.xml'] if _f] diff --git a/nikola/plugins/task/scale_images.plugin b/nikola/plugins/task/scale_images.plugin index d906b8c..3edd0c6 100644 --- a/nikola/plugins/task/scale_images.plugin +++ b/nikola/plugins/task/scale_images.plugin @@ -5,7 +5,7 @@ module = scale_images [Documentation] author = Pelle Nilsson version = 1.0 -website = http://getnikola.com +website = https://getnikola.com/ description = Create down-scaled images and thumbnails. [Nikola] diff --git a/nikola/plugins/task/scale_images.py b/nikola/plugins/task/scale_images.py index 22ed2ab..e55dc6c 100644 --- a/nikola/plugins/task/scale_images.py +++ b/nikola/plugins/task/scale_images.py @@ -34,7 +34,6 @@ from nikola import utils class ScaleImage(Task, ImageProcessor): - """Resize images and create thumbnails for them.""" name = "scale_images" diff --git a/nikola/plugins/task/sitemap.plugin b/nikola/plugins/task/sitemap.plugin index e3c991f..83e72c4 100644 --- a/nikola/plugins/task/sitemap.plugin +++ b/nikola/plugins/task/sitemap.plugin @@ -5,7 +5,7 @@ module = sitemap [Documentation] author = Roberto Alsina version = 1.0 -website = http://getnikola.com +website = https://getnikola.com/ description = Generate google sitemap. [Nikola] diff --git a/nikola/plugins/task/sitemap/__init__.py b/nikola/plugins/task/sitemap/__init__.py index fd781d6..90acdd3 100644 --- a/nikola/plugins/task/sitemap/__init__.py +++ b/nikola/plugins/task/sitemap/__init__.py @@ -31,6 +31,7 @@ import io import datetime import dateutil.tz import os +import sys try: from urlparse import urljoin, urlparse import robotparser as robotparser @@ -39,7 +40,7 @@ except ImportError: import urllib.robotparser as robotparser # NOQA from nikola.plugin_categories import LateTask -from nikola.utils import config_changed, apply_filters +from nikola.utils import apply_filters, config_changed, encodelink urlset_header = """<?xml version="1.0" encoding="UTF-8"?> @@ -106,7 +107,6 @@ def get_base_path(base): class Sitemap(LateTask): - """Generate a sitemap.""" name = "sitemap" @@ -146,7 +146,10 @@ class Sitemap(LateTask): continue # Totally empty, not on sitemap path = os.path.relpath(root, output) # ignore the current directory. - path = (path.replace(os.sep, '/') + '/').replace('./', '') + if path == '.': + path = '' + else: + path = path.replace(os.sep, '/') + '/' lastmod = self.get_lastmod(root) loc = urljoin(base_url, base_path + path) if kw['index_file'] in files and kw['strip_indexes']: # ignore folders when not stripping urls @@ -157,10 +160,10 @@ class Sitemap(LateTask): if post: for lang in kw['translations']: alt_url = post.permalink(lang=lang, absolute=True) - if loc == alt_url: + if encodelink(loc) == alt_url: continue alternates.append(alternates_format.format(lang, alt_url)) - urlset[loc] = loc_format.format(loc, lastmod, ''.join(alternates)) + urlset[loc] = loc_format.format(encodelink(loc), lastmod, ''.join(alternates)) for fname in files: if kw['strip_indexes'] and fname == kw['index_file']: continue # We already mapped the folder @@ -200,7 +203,7 @@ class Sitemap(LateTask): path = path.replace(os.sep, '/') lastmod = self.get_lastmod(real_path) loc = urljoin(base_url, base_path + path) - sitemapindex[loc] = sitemap_format.format(loc, lastmod) + sitemapindex[loc] = sitemap_format.format(encodelink(loc), lastmod) continue else: continue # ignores all XML files except those presumed to be RSS @@ -214,18 +217,22 @@ class Sitemap(LateTask): if post: for lang in kw['translations']: alt_url = post.permalink(lang=lang, absolute=True) - if loc == alt_url: + if encodelink(loc) == alt_url: continue alternates.append(alternates_format.format(lang, alt_url)) - urlset[loc] = loc_format.format(loc, lastmod, '\n'.join(alternates)) + urlset[loc] = loc_format.format(encodelink(loc), lastmod, '\n'.join(alternates)) def robot_fetch(path): """Check if robots can fetch a file.""" for rule in kw["robots_exclusions"]: robot = robotparser.RobotFileParser() robot.parse(["User-Agent: *", "Disallow: {0}".format(rule)]) - if not robot.can_fetch("*", '/' + path): - return False # not robot food + if sys.version_info[0] == 3: + if not robot.can_fetch("*", '/' + path): + return False # not robot food + else: + if not robot.can_fetch("*", ('/' + path).encode('utf-8')): + return False # not robot food return True def write_sitemap(): diff --git a/nikola/plugins/task/sources.plugin b/nikola/plugins/task/sources.plugin index d232c2b..66856f1 100644 --- a/nikola/plugins/task/sources.plugin +++ b/nikola/plugins/task/sources.plugin @@ -5,7 +5,7 @@ module = sources [Documentation] author = Roberto Alsina version = 1.0 -website = http://getnikola.com +website = https://getnikola.com/ description = Copy page sources into the output. [Nikola] diff --git a/nikola/plugins/task/sources.py b/nikola/plugins/task/sources.py index 87b4ae7..f782ad4 100644 --- a/nikola/plugins/task/sources.py +++ b/nikola/plugins/task/sources.py @@ -33,7 +33,6 @@ from nikola import utils class Sources(Task): - """Copy page sources into the output.""" name = "render_sources" diff --git a/nikola/plugins/task/tags.plugin b/nikola/plugins/task/tags.plugin index 283a16a..c3a5be3 100644 --- a/nikola/plugins/task/tags.plugin +++ b/nikola/plugins/task/tags.plugin @@ -5,7 +5,7 @@ module = tags [Documentation] author = Roberto Alsina version = 1.0 -website = http://getnikola.com +website = https://getnikola.com/ description = Render the tag pages and feeds. [Nikola] diff --git a/nikola/plugins/task/tags.py b/nikola/plugins/task/tags.py index 3186636..6d9d495 100644 --- a/nikola/plugins/task/tags.py +++ b/nikola/plugins/task/tags.py @@ -41,7 +41,6 @@ from nikola import utils class RenderTags(Task): - """Render the tag/category pages and feeds.""" name = "render_tags" @@ -74,9 +73,9 @@ class RenderTags(Task): 'category_prefix': self.site.config['CATEGORY_PREFIX'], "category_pages_are_indexes": self.site.config['CATEGORY_PAGES_ARE_INDEXES'], "generate_rss": self.site.config['GENERATE_RSS'], - "rss_teasers": self.site.config["RSS_TEASERS"], - "rss_plain": self.site.config["RSS_PLAIN"], - "rss_link_append_query": self.site.config["RSS_LINKS_APPEND_QUERY"], + "feed_teasers": self.site.config["FEED_TEASERS"], + "feed_plain": self.site.config["FEED_PLAIN"], + "feed_link_append_query": self.site.config["FEED_LINKS_APPEND_QUERY"], "show_untranslated_posts": self.site.config['SHOW_UNTRANSLATED_POSTS'], "feed_length": self.site.config['FEED_LENGTH'], "taglist_minimum_post_count": self.site.config['TAGLIST_MINIMUM_POSTS'], @@ -84,6 +83,10 @@ class RenderTags(Task): "pretty_urls": self.site.config['PRETTY_URLS'], "strip_indexes": self.site.config['STRIP_INDEXES'], "index_file": self.site.config['INDEX_FILE'], + "category_pages_descriptions": self.site.config['CATEGORY_PAGES_DESCRIPTIONS'], + "category_pages_titles": self.site.config['CATEGORY_PAGES_TITLES'], + "tag_pages_descriptions": self.site.config['TAG_PAGES_DESCRIPTIONS'], + "tag_pages_titles": self.site.config['TAG_PAGES_TITLES'], } self.site.scan_posts() @@ -168,7 +171,7 @@ class RenderTags(Task): """Write tag data into JSON file, for use in tag clouds.""" utils.makedirs(os.path.dirname(output_name)) with open(output_name, 'w+') as fd: - json.dump(data, fd) + json.dump(data, fd, sort_keys=True) if self.site.config['WRITE_TAG_CLOUD']: task = { @@ -199,7 +202,6 @@ class RenderTags(Task): kw['tags'] = tags output_name = os.path.join( kw['output_folder'], self.site.path('tag_index' if has_tags else 'category_index', None, lang)) - output_name = output_name context = {} if has_categories and has_tags: context["title"] = kw["messages"][lang]["Tags and Categories"] @@ -251,6 +253,10 @@ class RenderTags(Task): else: return tag + def _get_indexes_title(self, tag, is_category, lang, messages): + titles = self.site.config['CATEGORY_PAGES_TITLES'] if is_category else self.site.config['TAG_PAGES_TITLES'] + return titles[lang][tag] if lang in titles and tag in titles[lang] else messages[lang]["Posts about %s"] % tag + def _get_description(self, tag, is_category, lang): descriptions = self.site.config['CATEGORY_PAGES_DESCRIPTIONS'] if is_category else self.site.config['TAG_PAGES_DESCRIPTIONS'] return descriptions[lang][tag] if lang in descriptions and tag in descriptions[lang] else None @@ -276,7 +282,7 @@ class RenderTags(Task): if kw["generate_rss"]: # On a tag page, the feeds include the tag's feeds rss_link = ("""<link rel="alternate" type="application/rss+xml" """ - """type="application/rss+xml" title="RSS for tag """ + """title="RSS for tag """ """{0} ({1})" href="{2}">""".format( title, lang, self.site.link(kind + "_rss", tag, lang))) context_source['rss_link'] = rss_link @@ -284,7 +290,7 @@ class RenderTags(Task): context_source["category"] = tag context_source["category_path"] = self.site.parse_category_name(tag) context_source["tag"] = title - indexes_title = kw["messages"][lang]["Posts about %s"] % title + indexes_title = self._get_indexes_title(title, is_category, lang, kw["messages"]) context_source["description"] = self._get_description(tag, is_category, lang) if is_category: context_source["subcategories"] = self._get_subcategories(tag) @@ -306,7 +312,7 @@ class RenderTags(Task): context["category"] = tag context["category_path"] = self.site.parse_category_name(tag) context["tag"] = title - context["title"] = kw["messages"][lang]["Posts about %s"] % title + context["title"] = self._get_indexes_title(title, is_category, lang, kw["messages"]) context["posts"] = post_list context["permalink"] = self.site.link(kind, tag, lang) context["kind"] = kind @@ -326,6 +332,29 @@ class RenderTags(Task): task['basename'] = str(self.name) yield task + if self.site.config['GENERATE_ATOM']: + yield self.atom_feed_list(kind, tag, lang, post_list, context, kw) + + def atom_feed_list(self, kind, tag, lang, post_list, context, kw): + """Generate atom feeds for tag lists.""" + if kind == 'tag': + context['feedlink'] = self.site.abs_link(self.site.path('tag_atom', tag, lang)) + feed_path = os.path.join(kw['output_folder'], self.site.path('tag_atom', tag, lang)) + elif kind == 'category': + context['feedlink'] = self.site.abs_link(self.site.path('category_atom', tag, lang)) + feed_path = os.path.join(kw['output_folder'], self.site.path('category_atom', tag, lang)) + + task = { + 'basename': str(self.name), + 'name': feed_path, + 'targets': [feed_path], + 'actions': [(self.site.atom_feed_renderer, (lang, post_list, feed_path, kw['filters'], context))], + 'clean': True, + 'uptodate': [utils.config_changed(kw, 'nikola.plugins.task.tags:atom')], + 'task_dep': ['render_posts'], + } + return task + def tag_rss(self, tag, lang, posts, kw, is_category): """Create a RSS feed for a single tag in a given language.""" kind = "category" if is_category else "tag" @@ -349,8 +378,8 @@ class RenderTags(Task): 'actions': [(utils.generic_rss_renderer, (lang, "{0} ({1})".format(kw["blog_title"](lang), self._get_title(tag, is_category)), kw["site_url"], None, post_list, - output_name, kw["rss_teasers"], kw["rss_plain"], kw['feed_length'], - feed_url, None, kw["rss_link_append_query"]))], + output_name, kw["feed_teasers"], kw["feed_plain"], kw['feed_length'], + feed_url, None, kw["feed_link_append_query"]))], 'clean': True, 'uptodate': [utils.config_changed(kw, 'nikola.plugins.task.tags:rss')] + deps_uptodate, 'task_dep': ['render_posts'], @@ -364,41 +393,71 @@ class RenderTags(Task): return name def tag_index_path(self, name, lang): - """Return path to the tag index.""" - return [_f for _f in [self.site.config['TRANSLATIONS'][lang], - self.site.config['TAG_PATH'], - self.site.config['INDEX_FILE']] if _f] + """A link to the tag index. + + Example: + + link://tag_index => /tags/index.html + """ + if self.site.config['TAGS_INDEX_PATH'][lang]: + paths = [_f for _f in [self.site.config['TRANSLATIONS'][lang], + self.site.config['TAGS_INDEX_PATH'][lang]] if _f] + else: + paths = [_f for _f in [self.site.config['TRANSLATIONS'][lang], + self.site.config['TAG_PATH'][lang], + self.site.config['INDEX_FILE']] if _f] + return paths def category_index_path(self, name, lang): - """Return path to the category index.""" + """A link to the category index. + + Example: + + link://category_index => /categories/index.html + """ return [_f for _f in [self.site.config['TRANSLATIONS'][lang], - self.site.config['CATEGORY_PATH'], + self.site.config['CATEGORY_PATH'][lang], self.site.config['INDEX_FILE']] if _f] def tag_path(self, name, lang): - """Return path to a tag.""" + """A link to a tag's page. + + Example: + + link://tag/cats => /tags/cats.html + """ if self.site.config['PRETTY_URLS']: return [_f for _f in [ self.site.config['TRANSLATIONS'][lang], - self.site.config['TAG_PATH'], + self.site.config['TAG_PATH'][lang], self.slugify_tag_name(name), self.site.config['INDEX_FILE']] if _f] else: return [_f for _f in [ self.site.config['TRANSLATIONS'][lang], - self.site.config['TAG_PATH'], + self.site.config['TAG_PATH'][lang], self.slugify_tag_name(name) + ".html"] if _f] def tag_atom_path(self, name, lang): - """Return path to a tag Atom feed.""" + """A link to a tag's Atom feed. + + Example: + + link://tag_atom/cats => /tags/cats.atom + """ return [_f for _f in [self.site.config['TRANSLATIONS'][lang], - self.site.config['TAG_PATH'], self.slugify_tag_name(name) + ".atom"] if + self.site.config['TAG_PATH'][lang], self.slugify_tag_name(name) + ".atom"] if _f] def tag_rss_path(self, name, lang): - """Return path to a tag RSS feed.""" + """A link to a tag's RSS feed. + + Example: + + link://tag_rss/cats => /tags/cats.xml + """ return [_f for _f in [self.site.config['TRANSLATIONS'][lang], - self.site.config['TAG_PATH'], self.slugify_tag_name(name) + ".xml"] if + self.site.config['TAG_PATH'][lang], self.slugify_tag_name(name) + ".xml"] if _f] def slugify_category_name(self, name): @@ -417,24 +476,39 @@ class RenderTags(Task): return path def category_path(self, name, lang): - """Return path to a category.""" + """A link to a category. + + Example: + + link://category/dogs => /categories/dogs.html + """ if self.site.config['PRETTY_URLS']: return [_f for _f in [self.site.config['TRANSLATIONS'][lang], - self.site.config['CATEGORY_PATH']] if + self.site.config['CATEGORY_PATH'][lang]] if _f] + self.slugify_category_name(name) + [self.site.config['INDEX_FILE']] else: return [_f for _f in [self.site.config['TRANSLATIONS'][lang], - self.site.config['CATEGORY_PATH']] if + self.site.config['CATEGORY_PATH'][lang]] if _f] + self._add_extension(self.slugify_category_name(name), ".html") def category_atom_path(self, name, lang): - """Return path to a category Atom feed.""" + """A link to a category's Atom feed. + + Example: + + link://category_atom/dogs => /categories/dogs.atom + """ return [_f for _f in [self.site.config['TRANSLATIONS'][lang], - self.site.config['CATEGORY_PATH']] if + self.site.config['CATEGORY_PATH'][lang]] if _f] + self._add_extension(self.slugify_category_name(name), ".atom") def category_rss_path(self, name, lang): - """Return path to a category RSS feed.""" + """A link to a category's RSS feed. + + Example: + + link://category_rss/dogs => /categories/dogs.xml + """ return [_f for _f in [self.site.config['TRANSLATIONS'][lang], - self.site.config['CATEGORY_PATH']] if + self.site.config['CATEGORY_PATH'][lang]] if _f] + self._add_extension(self.slugify_category_name(name), ".xml") diff --git a/nikola/plugins/template/jinja.plugin b/nikola/plugins/template/jinja.plugin index cfe9fa8..78fd41b 100644 --- a/nikola/plugins/template/jinja.plugin +++ b/nikola/plugins/template/jinja.plugin @@ -5,7 +5,7 @@ module = jinja [Documentation] author = Roberto Alsina version = 1.0 -website = http://getnikola.com +website = https://getnikola.com/ description = Support for Jinja2 templates. [Nikola] diff --git a/nikola/plugins/template/jinja.py b/nikola/plugins/template/jinja.py index b02d75c..e7df102 100644 --- a/nikola/plugins/template/jinja.py +++ b/nikola/plugins/template/jinja.py @@ -42,7 +42,6 @@ from nikola.utils import makedirs, req_missing class JinjaTemplates(TemplateSystem): - """Support for Jinja2 templates.""" name = "jinja" diff --git a/nikola/plugins/template/mako.plugin b/nikola/plugins/template/mako.plugin index d256faf..308d291 100644 --- a/nikola/plugins/template/mako.plugin +++ b/nikola/plugins/template/mako.plugin @@ -5,7 +5,7 @@ module = mako [Documentation] author = Roberto Alsina version = 1.0 -website = http://getnikola.com +website = https://getnikola.com/ description = Support for Mako templates. [Nikola] diff --git a/nikola/plugins/template/mako.py b/nikola/plugins/template/mako.py index aed6596..6da21db 100644 --- a/nikola/plugins/template/mako.py +++ b/nikola/plugins/template/mako.py @@ -44,7 +44,6 @@ LOGGER = get_logger('mako', STDERR_HANDLER) class MakoTemplates(TemplateSystem): - """Support for Mako templates.""" name = "mako" diff --git a/nikola/post.py b/nikola/post.py index 7badfc6..f8039e0 100644 --- a/nikola/post.py +++ b/nikola/post.py @@ -56,7 +56,6 @@ from math import ceil # for tearDown with _reload we cannot use 'from import' to get forLocaleBorg import nikola.utils from .utils import ( - bytes_str, current_time, Functionary, LOGGER, @@ -77,7 +76,6 @@ _UPGRADE_METADATA_ADVERTISED = False class Post(object): - """Represent a blog post or site page.""" def __init__( @@ -130,7 +128,6 @@ class Post(object): self._template_name = template_name self.is_two_file = True self.newstylemeta = True - self.hyphenate = self.config['HYPHENATE'] self._reading_time = None self._remaining_reading_time = None self._paragraph_count = None @@ -232,6 +229,11 @@ class Post(object): # Register potential extra dependencies self.compiler.register_extra_dependencies(self) + def _get_hyphenate(self): + return bool(self.config['HYPHENATE'] or self.meta('hyphenate')) + + hyphenate = property(_get_hyphenate) + def __repr__(self): """Provide a representation of the post object.""" # Calculate a hash that represents most data about the post @@ -320,23 +322,12 @@ class Post(object): @property def template_name(self): """Return template name for this post.""" - return self.meta('template') or self._template_name + lang = nikola.utils.LocaleBorg().current_lang + return self.meta[lang]['template'] or self._template_name def formatted_date(self, date_format, date=None): """Return the formatted date as unicode.""" - date = date if date else self.date - - if date_format == 'webiso': - # Formatted after RFC 3339 (web ISO 8501 profile) with Zulu - # zone desgignator for times in UTC and no microsecond precision. - fmt_date = date.replace(microsecond=0).isoformat().replace('+00:00', 'Z') - else: - fmt_date = date.strftime(date_format) - - # Issue #383, this changes from py2 to py3 - if isinstance(fmt_date, bytes_str): - fmt_date = fmt_date.decode('utf8') - return fmt_date + return utils.LocaleBorg().formatted_date(date_format, date if date else self.date) def formatted_updated(self, date_format): """Return the updated date as unicode.""" @@ -447,11 +438,16 @@ class Post(object): if self.default_lang in self.translated_to: deps.append(self.base_path) deps.append(self.source_path) + if os.path.exists(self.metadata_path): + deps.append(self.metadata_path) if lang != self.default_lang: cand_1 = get_translation_candidate(self.config, self.source_path, lang) cand_2 = get_translation_candidate(self.config, self.base_path, lang) if os.path.exists(cand_1): deps.extend([cand_1, cand_2]) + cand_3 = get_translation_candidate(self.config, self.metadata_path, lang) + if os.path.exists(cand_3): + deps.append(cand_3) deps += self._get_dependencies(self._dependency_file_page[lang]) deps += self._get_dependencies(self._dependency_file_page[None]) return sorted(deps) @@ -558,13 +554,13 @@ class Post(object): return get_translation_candidate(self.config, self.base_path, sorted(self.translated_to)[0]) def text(self, lang=None, teaser_only=False, strip_html=False, show_read_more_link=True, - rss_read_more_link=False, rss_links_append_query=None): + feed_read_more_link=False, feed_links_append_query=None): """Read the post file for that language and return its contents. teaser_only=True breaks at the teaser marker and returns only the teaser. strip_html=True removes HTML tags show_read_more_link=False does not add the Read more... link - rss_read_more_link=True uses RSS_READ_MORE_LINK instead of INDEX_READ_MORE_LINK + feed_read_more_link=True uses FEED_READ_MORE_LINK instead of INDEX_READ_MORE_LINK lang=None uses the last used to set locale All links in the returned HTML will be relative. @@ -613,9 +609,9 @@ class Post(object): teaser_text = teaser_regexp.search(data).groups()[-1] else: teaser_text = self.messages[lang]["Read more"] - l = self.config['RSS_READ_MORE_LINK'](lang) if rss_read_more_link else self.config['INDEX_READ_MORE_LINK'](lang) + l = self.config['FEED_READ_MORE_LINK'](lang) if feed_read_more_link else self.config['INDEX_READ_MORE_LINK'](lang) teaser += l.format( - link=self.permalink(lang, query=rss_links_append_query), + link=self.permalink(lang, query=feed_links_append_query), read_more=teaser_text, min_remaining_read=self.messages[lang]["%d min remaining to read"] % (self.remaining_reading_time), reading_time=self.reading_time, @@ -715,10 +711,9 @@ class Post(object): def source_link(self, lang=None): """Return absolute link to the post's source.""" ext = self.source_ext(True) - return "/" + self.destination_path( - lang=lang, - extension=ext, - sep='/') + link = "/" + self.destination_path(lang=lang, extension=ext, sep='/') + link = utils.encodelink(link) + return link def destination_path(self, lang=None, extension='.html', sep=os.sep): """Destination path for this post, relative to output/. @@ -740,6 +735,61 @@ class Post(object): path = path[2:] return path + def section_color(self, lang=None): + """Return the color of the post's section.""" + slug = self.section_slug(lang) + if slug in self.config['POSTS_SECTION_COLORS'](lang): + return self.config['POSTS_SECTION_COLORS'](lang)[slug] + base = self.config['THEME_COLOR'] + return utils.colorize_str_from_base_color(slug, base) + + def section_link(self, lang=None): + """Return the link to the post's section (deprecated).""" + utils.LOGGER.warning("Post.section_link is deprecated. Please use " + + "site.link('section_index', post.section_slug()) instead.") + if lang is None: + lang = nikola.utils.LocaleBorg().current_lang + + slug = self.section_slug(lang) + t = os.path.normpath(self.translations[lang]) + if t == '.': + t = '' + link = '/' + '/'.join(i for i in (t, slug) if i) + '/' + if not self.pretty_urls: + link = urljoin(link, self.index_file) + link = utils.encodelink(link) + return link + + def section_name(self, lang=None): + """Return the name of the post's section.""" + slug = self.section_slug(lang) + if slug in self.config['POSTS_SECTION_NAME'](lang): + name = self.config['POSTS_SECTION_NAME'](lang)[slug] + else: + name = slug.replace('-', ' ').title() + return name + + def section_slug(self, lang=None): + """Return the slug for the post's section.""" + if lang is None: + lang = nikola.utils.LocaleBorg().current_lang + + if not self.config['POSTS_SECTION_FROM_META']: + dest = self.destination_path(lang) + if dest[-(1 + len(self.index_file)):] == os.sep + self.index_file: + dest = dest[:-(1 + len(self.index_file))] + dirname = os.path.dirname(dest) + slug = dest.split(os.sep) + if not slug or dirname == '.': + slug = self.messages[lang]["Uncategorized"] + elif lang == slug[0]: + slug = slug[1] + else: + slug = slug[0] + else: + slug = self.meta[lang]['section'].split(',')[0] if 'section' in self.meta[lang] else self.messages[lang]["Uncategorized"] + return utils.slugify(slug) + def permalink(self, lang=None, absolute=False, extension='.html', query=None): """Return permalink for a post.""" if lang is None: @@ -764,6 +814,7 @@ class Post(object): link = link[:-index_len] if query: link = link + "?" + query + link = utils.encodelink(link) return link @property @@ -842,7 +893,7 @@ def get_metadata_from_file(source_path, config=None, lang=None): source_path = get_translation_candidate(config, source_path, lang) elif lang: source_path += '.' + lang - with io.open(source_path, "r", encoding="utf8") as meta_file: + with io.open(source_path, "r", encoding="utf-8-sig") as meta_file: meta_data = [x.strip() for x in meta_file.readlines()] return _get_metadata_from_file(meta_data) except (UnicodeDecodeError, UnicodeEncodeError): @@ -851,18 +902,37 @@ def get_metadata_from_file(source_path, config=None, lang=None): return {} +re_md_title = re.compile(r'^{0}([^{0}].*)'.format(re.escape('#'))) +# Assuming rst titles are going to be at least 4 chars long +# otherwise this detects things like ''' wich breaks other markups. +re_rst_title = re.compile(r'^([{0}]{{4,}})'.format(re.escape( + string.punctuation))) + + +def _get_title_from_contents(meta_data): + """Extract title from file contents, LAST RESOURCE.""" + piece = meta_data[:] + title = None + for i, line in enumerate(piece): + if re_rst_title.findall(line) and i > 0: + title = meta_data[i - 1].strip() + break + if (re_rst_title.findall(line) and i >= 0 and + re_rst_title.findall(meta_data[i + 2])): + title = meta_data[i + 1].strip() + break + if re_md_title.findall(line): + title = re_md_title.findall(line)[0] + break + return title + + def _get_metadata_from_file(meta_data): """Extract metadata from a post's source file.""" meta = {} if not meta_data: return meta - re_md_title = re.compile(r'^{0}([^{0}].*)'.format(re.escape('#'))) - # Assuming rst titles are going to be at least 4 chars long - # otherwise this detects things like ''' wich breaks other markups. - re_rst_title = re.compile(r'^([{0}]{{4,}})'.format(re.escape( - string.punctuation))) - # Skip up to one empty line at the beginning (for txt2tags) if not meta_data[0]: meta_data = meta_data[1:] @@ -879,18 +949,9 @@ def _get_metadata_from_file(meta_data): # If we have no title, try to get it from document if 'title' not in meta: - piece = meta_data[:] - for i, line in enumerate(piece): - if re_rst_title.findall(line) and i > 0: - meta['title'] = meta_data[i - 1].strip() - break - if (re_rst_title.findall(line) and i >= 0 and - re_rst_title.findall(meta_data[i + 2])): - meta['title'] = meta_data[i + 1].strip() - break - if re_md_title.findall(line): - meta['title'] = re_md_title.findall(line)[0] - break + t = _get_title_from_contents(meta_data) + if t is not None: + meta['title'] = t return meta @@ -1015,7 +1076,11 @@ def hyphenate(dom, _lang): """Hyphenate a post.""" # circular import prevention from .nikola import LEGAL_VALUES - lang = LEGAL_VALUES['PYPHEN_LOCALES'].get(_lang, pyphen.language_fallback(_lang)) + lang = None + if pyphen is not None: + lang = LEGAL_VALUES['PYPHEN_LOCALES'].get(_lang, pyphen.language_fallback(_lang)) + else: + utils.req_missing(['pyphen'], 'hyphenate texts', optional=True) if pyphen is not None and lang is not None: # If pyphen does exist, we tell the user when configuring the site. # If it does not support a language, we ignore it quietly. diff --git a/nikola/utils.py b/nikola/utils.py index 3a268ff..3359514 100644 --- a/nikola/utils.py +++ b/nikola/utils.py @@ -31,6 +31,7 @@ import calendar import datetime import dateutil.tz import hashlib +import husl import io import locale import logging @@ -39,20 +40,30 @@ import os import re import json import shutil +import socket import subprocess import sys import dateutil.parser import dateutil.tz import logbook +try: + from urllib import quote as urlquote + from urllib import unquote as urlunquote + from urlparse import urlparse, urlunparse +except ImportError: + from urllib.parse import quote as urlquote # NOQA + from urllib.parse import unquote as urlunquote # NOQA + from urllib.parse import urlparse, urlunparse # NOQA import warnings import PyRSS2Gen as rss -from collections import defaultdict, Callable +from collections import defaultdict, Callable, OrderedDict from logbook.compat import redirect_logging from logbook.more import ExceptionHandler, ColorizedStderrHandler from pygments.formatters import HtmlFormatter from zipfile import ZipFile as zipf from doit import tools from unidecode import unidecode +from unicodedata import normalize as unicodenormalize from pkg_resources import resource_filename from doit.cmdparse import CmdParse @@ -94,7 +105,6 @@ class ApplicationWarning(Exception): class ColorfulStderrHandler(ColorizedStderrHandler): - """Stream handler with colors.""" _colorful = False @@ -116,7 +126,7 @@ def get_logger(name, handlers): STDERR_HANDLER = [ColorfulStderrHandler( - level=logbook.INFO if not DEBUG else logbook.DEBUG, + level=logbook.NOTICE if not DEBUG else logbook.DEBUG, format_string=u'[{record.time:%Y-%m-%dT%H:%M:%SZ}] {record.level_name}: {record.channel}: {record.message}' )] @@ -228,7 +238,6 @@ def makedirs(path): class Functionary(defaultdict): - """Class that looks like a function, but is a defaultdict.""" def __init__(self, default, default_lang): @@ -244,7 +253,6 @@ class Functionary(defaultdict): class TranslatableSetting(object): - """A setting that can be translated. You can access it via: SETTING(lang). You can omit lang, in which @@ -423,15 +431,20 @@ class TranslatableSetting(object): def __eq__(self, other): """Test whether two TranslatableSettings are equal.""" - return self.values == other.values + try: + return self.values == other.values + except AttributeError: + return self(self.default_lang) == other def __ne__(self, other): """Test whether two TranslatableSettings are inequal.""" - return self.values != other.values + try: + return self.values != other.values + except AttributeError: + return self(self.default_lang) != other class TemplateHookRegistry(object): - r"""A registry for template hooks. Usage: @@ -497,7 +510,6 @@ class TemplateHookRegistry(object): class CustomEncoder(json.JSONEncoder): - """Custom JSON encoder.""" def default(self, obj): @@ -513,7 +525,6 @@ class CustomEncoder(json.JSONEncoder): class config_changed(tools.config_changed): - """A copy of doit's config_changed, using pickle instead of serializing manually.""" def __init__(self, config, identifier=None): @@ -610,7 +621,6 @@ language_incomplete_warned = [] class LanguageNotFoundError(Exception): - """An exception thrown if language is not found.""" def __init__(self, lang, orig): @@ -724,7 +734,7 @@ def remove_file(source): elif os.path.isfile(source) or os.path.islink(source): os.remove(source) -# slugify is copied from +# slugify is adopted from # http://code.activestate.com/recipes/ # 577257-slugify-make-a-string-usable-in-a-url-or-filename/ _slugify_strip_re = re.compile(r'[^+\w\s-]') @@ -782,11 +792,23 @@ def unslugify(value, discard_numbers=True): return value +def encodelink(iri): + """Given an encoded or unencoded link string, return an encoded string suitable for use as a link in HTML and XML.""" + iri = unicodenormalize('NFC', iri) + link = OrderedDict(urlparse(iri)._asdict()) + link['path'] = urlquote(urlunquote(link['path']).encode('utf-8')) + try: + link['netloc'] = link['netloc'].encode('utf-8').decode('idna').encode('idna').decode('utf-8') + except UnicodeDecodeError: + link['netloc'] = link['netloc'].encode('idna').decode('utf-8') + encoded_link = urlunparse(link.values()) + return encoded_link + # A very slightly safer version of zip.extractall that works on # python < 2.6 -class UnsafeZipException(Exception): +class UnsafeZipException(Exception): """Exception for unsafe zip files.""" pass @@ -945,12 +967,14 @@ def get_crumbs(path, is_file=False, index_folder=None): return list(reversed(_crumbs)) -def get_asset_path(path, themes, files_folders={'files': ''}, _themes_dir='themes'): +def get_asset_path(path, themes, files_folders={'files': ''}, _themes_dir='themes', output_dir='output'): """Return the "real", absolute path to the asset. By default, it checks which theme provides the asset. If the asset is not provided by a theme, then it will be checked for in the FILES_FOLDERS. + If it's not provided by either, it will be chacked in output, where + it may have been created by another plugin. >>> print(get_asset_path('assets/css/rst.css', ['bootstrap3', 'base'])) /.../nikola/data/themes/base/assets/css/rst.css @@ -961,9 +985,12 @@ def get_asset_path(path, themes, files_folders={'files': ''}, _themes_dir='theme >>> print(get_asset_path('nikola.py', ['bootstrap3', 'base'], {'nikola': ''})) /.../nikola/nikola.py - >>> print(get_asset_path('nikola/nikola.py', ['bootstrap3', 'base'], {'nikola':'nikola'})) + >>> print(get_asset_path('nikola.py', ['bootstrap3', 'base'], {'nikola': 'nikola'})) None + >>> print(get_asset_path('nikola/nikola.py', ['bootstrap3', 'base'], {'nikola': 'nikola'})) + /.../nikola/nikola.py + """ for theme_name in themes: candidate = os.path.join( @@ -973,7 +1000,14 @@ def get_asset_path(path, themes, files_folders={'files': ''}, _themes_dir='theme if os.path.isfile(candidate): return candidate for src, rel_dst in files_folders.items(): - candidate = os.path.abspath(os.path.join(src, path)) + relpath = os.path.normpath(os.path.relpath(path, rel_dst)) + if not relpath.startswith('..' + os.path.sep): + candidate = os.path.abspath(os.path.join(src, relpath)) + if os.path.isfile(candidate): + return candidate + + if output_dir: + candidate = os.path.join(output_dir, path) if os.path.isfile(candidate): return candidate @@ -982,7 +1016,6 @@ def get_asset_path(path, themes, files_folders={'files': ''}, _themes_dir='theme class LocaleBorgUninitializedException(Exception): - """Exception for unitialized LocaleBorg.""" def __init__(self): @@ -991,7 +1024,6 @@ class LocaleBorgUninitializedException(Exception): class LocaleBorg(object): - """Provide locale related services and autoritative current_lang. current_lang is the last lang for which the locale was set @@ -1040,6 +1072,8 @@ class LocaleBorg(object): assert initial_lang is not None and initial_lang in locales cls.reset() cls.locales = locales + cls.month_name_handlers = [] + cls.formatted_date_handlers = [] # needed to decode some localized output in py2x encodings = {} @@ -1049,25 +1083,71 @@ class LocaleBorg(object): encodings[lang] = encoding cls.encodings = encodings - cls.__shared_state['current_lang'] = initial_lang + cls.__initial_lang = initial_lang cls.initialized = True + def __get_shared_state(self): + if not self.initialized: + raise LocaleBorgUninitializedException() + shared_state = getattr(self.__thread_local, 'shared_state', None) + if shared_state is None: + shared_state = {'current_lang': self.__initial_lang} + self.__thread_local.shared_state = shared_state + return shared_state + @classmethod def reset(cls): """Reset LocaleBorg. Used in testing to prevent leaking state between tests. """ + import threading + cls.__thread_local = threading.local() + cls.__thread_lock = threading.Lock() + cls.locales = {} cls.encodings = {} - cls.__shared_state = {'current_lang': None} cls.initialized = False + cls.month_name_handlers = [] + cls.formatted_date_handlers = [] + cls.thread_local = None + cls.thread_lock = None + + @classmethod + def add_handler(cls, month_name_handler=None, formatted_date_handler=None): + """Allow to add month name and formatted date handlers. + + If month_name_handler is not None, it is expected to be a callable + which accepts (month_no, lang) and returns either a string or None. + + If formatted_date_handler is not None, it is expected to be a callable + which accepts (date_format, date, lang) and returns either a string or + None. + + A handler is expected to either return the correct result for the given + language and data, or return None to indicate it is not able to do the + job. In that case, the next handler is asked, and finally the default + implementation is used. + """ + if month_name_handler is not None: + cls.month_name_handlers.append(month_name_handler) + if formatted_date_handler is not None: + cls.formatted_date_handlers.append(formatted_date_handler) def __init__(self): """Initialize.""" if not self.initialized: raise LocaleBorgUninitializedException() - self.__dict__ = self.__shared_state + + @property + def current_lang(self): + """Return the current language.""" + return self.__get_shared_state()['current_lang'] + + def __set_locale(self, lang): + """Set the locale for language lang without updating current_lang.""" + locale_n = self.locales[lang] + locale.setlocale(locale.LC_ALL, locale_n) def set_locale(self, lang): """Set the locale for language lang, returns an empty string. @@ -1076,36 +1156,70 @@ class LocaleBorg(object): in windows that cannot be guaranted. In either case, the locale encoding is available in cls.encodings[lang] """ - # intentional non try-except: templates must ask locales with a lang, - # let the code explode here and not hide the point of failure - # Also, not guarded with an if lang==current_lang because calendar may - # put that out of sync - locale_n = self.locales[lang] - self.__shared_state['current_lang'] = lang - locale.setlocale(locale.LC_ALL, locale_n) - return '' + with self.__thread_lock: + # intentional non try-except: templates must ask locales with a lang, + # let the code explode here and not hide the point of failure + # Also, not guarded with an if lang==current_lang because calendar may + # put that out of sync + self.__set_locale(lang) + self.__get_shared_state()['current_lang'] = lang + return '' def get_month_name(self, month_no, lang): """Return localized month name in an unicode string.""" - if sys.version_info[0] == 3: # Python 3 - with calendar.different_locale(self.locales[lang]): - s = calendar.month_name[month_no] - # for py3 s is unicode - else: # Python 2 - with calendar.TimeEncoding(self.locales[lang]): - s = calendar.month_name[month_no] - enc = self.encodings[lang] - if not enc: - enc = 'UTF-8' - - s = s.decode(enc) - # paranoid about calendar ending in the wrong locale (windows) - self.set_locale(self.current_lang) - return s + # For thread-safety + with self.__thread_lock: + for handler in self.month_name_handlers: + res = handler(month_no, lang) + if res is not None: + return res + if sys.version_info[0] == 3: # Python 3 + with calendar.different_locale(self.locales[lang]): + s = calendar.month_name[month_no] + # for py3 s is unicode + else: # Python 2 + with calendar.TimeEncoding(self.locales[lang]): + s = calendar.month_name[month_no] + enc = self.encodings[lang] + if not enc: + enc = 'UTF-8' + + s = s.decode(enc) + # paranoid about calendar ending in the wrong locale (windows) + self.__set_locale(self.current_lang) + return s + def formatted_date(self, date_format, date): + """Return the formatted date as unicode.""" + with self.__thread_lock: + current_lang = self.current_lang + # For thread-safety + self.__set_locale(current_lang) + fmt_date = None + # Get a string out of a TranslatableSetting + if isinstance(date_format, TranslatableSetting): + date_format = date_format(current_lang) + # First check handlers + for handler in self.formatted_date_handlers: + fmt_date = handler(date_format, date, current_lang) + if fmt_date is not None: + break + # If no handler was able to format the date, ask Python + if fmt_date is None: + if date_format == 'webiso': + # Formatted after RFC 3339 (web ISO 8501 profile) with Zulu + # zone desgignator for times in UTC and no microsecond precision. + fmt_date = date.replace(microsecond=0).isoformat().replace('+00:00', 'Z') + else: + fmt_date = date.strftime(date_format) -class ExtendedRSS2(rss.RSS2): + # Issue #383, this changes from py2 to py3 + if isinstance(fmt_date, bytes_str): + fmt_date = fmt_date.decode('utf8') + return fmt_date + +class ExtendedRSS2(rss.RSS2): """Extended RSS class.""" xsl_stylesheet_href = None @@ -1129,7 +1243,6 @@ class ExtendedRSS2(rss.RSS2): class ExtendedItem(rss.RSSItem): - """Extended RSS item.""" def __init__(self, **kw): @@ -1326,7 +1439,6 @@ def ask_yesno(query, default=None): class CommandWrapper(object): - """Converts commands into functions.""" def __init__(self, cmd, commands_object): @@ -1342,7 +1454,6 @@ class CommandWrapper(object): class Commands(object): - """Nikola Commands. Sample usage: @@ -1433,7 +1544,6 @@ def options2docstring(name, options): class NikolaPygmentsHTML(HtmlFormatter): - """A Nikola-specific modification of Pygments' HtmlFormatter.""" def __init__(self, anchor_ref, classes=None, linenos='table', linenostart=1): @@ -1540,7 +1650,6 @@ def create_redirect(src, dst): class TreeNode(object): - """A tree node.""" indent_levels = None # use for formatting comments as tree @@ -1673,6 +1782,73 @@ def join_hierarchical_category_path(category_path): return '/'.join([escape(p) for p in category_path]) +def colorize_str_from_base_color(string, base_color): + """Find a perceptual similar color from a base color based on the hash of a string. + + Make up to 16 attempts (number of bytes returned by hashing) at picking a + hue for our color at least 27 deg removed from the base color, leaving + lightness and saturation untouched using HUSL colorspace. + """ + def hash_str(string, pos): + return hashlib.md5(string.encode('utf-8')).digest()[pos] + + def degreediff(dega, degb): + return min(abs(dega - degb), abs((degb - dega) + 360)) + + def husl_similar_from_base(string, base_color): + h, s, l = husl.hex_to_husl(base_color) + old_h = h + idx = 0 + while degreediff(old_h, h) < 27 and idx < 16: + h = 360.0 * (float(hash_str(string, idx)) / 255) + idx += 1 + return husl.husl_to_hex(h, s, l) + + return husl_similar_from_base(string, base_color) + + +def color_hsl_adjust_hex(hexstr, adjust_h=None, adjust_s=None, adjust_l=None): + """Adjust a hex color using HSL arguments, adjustments in percentages 1.0 to -1.0. Returns a hex color.""" + h, s, l = husl.hex_to_husl(hexstr) + + if adjust_h: + h = h + (adjust_h * 360.0) + + if adjust_s: + s = s + (adjust_s * 100.0) + + if adjust_l: + l = l + (adjust_l * 100.0) + + return husl.husl_to_hex(h, s, l) + + +def dns_sd(port, inet6): + """Optimistically publish a HTTP service to the local network over DNS-SD. + + Works only on Linux/FreeBSD. Requires the `avahi` and `dbus` modules (symlinks in virtualenvs) + """ + try: + import avahi + import dbus + inet = avahi.PROTO_INET6 if inet6 else avahi.PROTO_INET + name = "{0}'s Nikola Server on {1}".format(os.getlogin(), socket.gethostname()) + bus = dbus.SystemBus() + bus_server = dbus.Interface(bus.get_object(avahi.DBUS_NAME, + avahi.DBUS_PATH_SERVER), + avahi.DBUS_INTERFACE_SERVER) + bus_group = dbus.Interface(bus.get_object(avahi.DBUS_NAME, + bus_server.EntryGroupNew()), + avahi.DBUS_INTERFACE_ENTRY_GROUP) + bus_group.AddService(avahi.IF_UNSPEC, inet, dbus.UInt32(0), + name, '_http._tcp', '', '', + dbus.UInt16(port), '') + bus_group.Commit() + return bus_group # remember to bus_group.Reset() to unpublish + except Exception: + return None + + # Stolen from textwrap in Python 3.4.3. def indent(text, prefix, predicate=None): """Add 'prefix' to the beginning of selected lines in 'text'. |
