Exemple #1
0
 def __init__(self, settings=None):
     """
     Note that the _first_ one created will have its config used.
     """
     self.settings = settings
     self.inline = Inline()
     self.list_counter = 1
Exemple #2
0
    def __init__(self, parts, outline, id_prefix):
        """
        Create bibliography entries, and the structure for recording citations
        as they occur (These will be back-linked in the bibliography's
        citations list). The unique entry_id will be a slug of author/date,
        with an added Letters() counter for uniqueness.

                        / SortedDict
        self.entries = {entry_id: entry_text}

                                       / SortedDict
        self.citations = {entry_id: {part_number: [indexes]}}
        """
        assert isinstance(outline, Outline)
        assert isinstance(parts, dict)

        self.outline = outline
        self.entries = collate_bibliography(parts)

        self.id_prefix = id_prefix

        self.citations = {}
        self.counters = {}

        self.inline = Inline()
Exemple #3
0
def alert(content, dump_vars=None):
    """
    Show a visual error notice.
    """
    inline = Inline()
    pattern = "<div class=\"alert alert-warning\" role=\"alert\">%s</div>"
    html = pattern % inline.process(content)
    if not dump_vars:
        dump_vars = []
    html += "\n\n".join([pformat(var) for var in dump_vars])
    return html
Exemple #4
0
def test_dashes():
    test = Inline()
    assert '0&ndash;9' == test.process("0-9")
    assert 'x &ndash; y' == test.process("x -- y")
    assert 'x&mdash;y' == test.process("x---y")
    assert '&ndash; x &ndash; y &ndash;' == test.process("-- x -- y --")
    assert '&ndash;x&ndash;y&ndash;' == test.process("--x--y--")
    assert '&mdash;x&mdash;y&mdash;' == test.process("--- x --- y ---")
    assert '&mdash;x&mdash;y&mdash;' == test.process("---x---y---")
Exemple #5
0
def test_happy_path():
    test = Inline()
    out = '<strong>To boldly</strong> go where <u>none</u> have gone <em>before.</em>'
    assert out == test.process(
        "*[To boldly] go where _[none] have gone /[before.]")
    out = '<ins>To boldly</ins> go where <del>none</del> ' + \
          'have gone <kbd>before</kbd>.'
    assert out == test.process(
        '+[To boldly] go where -[none] have gone `[before].')
    out = '<sup>To boldly</sup> go where <sub>none</sub> ' + \
          'have gone before.'
    assert out == test.process(
        "'[To boldly] go where ,[none] have gone before.")
Exemple #6
0
    def list_block_recursor(char, items, settings, depth=1):
        """
        Recursion handler for list_block().

        $ NUMBERING = ... is the next item to count from.
        $ CONTINUE = ... is the count to use if NUMBERING = continue
        """
        inline = Inline()
        list_tag = 'ol' if char == '#' else 'ul'
        properties = ['']
        if list_tag == 'ol' and depth == 1:
            numbering = settings.get('NUMBERING', '')
            if numbering.isdigit():
                properties += ['start="%s"' % numbering]
                settings.set('CONTINUE', int(numbering))
                settings.set('NUMBERING', '')
            elif numbering == 'continue':
                properties += ['start="%s"' % settings.get('CONTINUE')]
                settings.set('NUMBERING', '')
            else:
                settings.set('CONTINUE', 1)
        if char == '_':
            properties += ['class="checkboxes"']

        # XHTML (for ebooks) requires that a child list appears inside the
        # preceeding <li> rather than inside it's own <li>. So we'll accumulate
        # display_items distinct from structural items, and join these into an
        # HTML list at the end.
        open_tag = "<%s%s>" % (list_tag, ' '.join(properties))
        display_items = []
        for item in items:
            if isinstance(item, list):
                # There should always be a preceding element when we reach a
                # sublist. Append to that:
                if len(display_items) > 0:
                    last = len(display_items) - 1
                    sub_list = list_block_recursor(char, item, settings,
                                                   depth + 1)
                    display_items[last] += sub_list
            else:
                display_items += [inline.process(item)]
                if list_tag == 'ol' and depth == 1:
                    settings.set('CONTINUE', settings.get('CONTINUE') + 1)
        close_tag = "</%s>" % list_tag
        lines = [open_tag]
        for item in display_items:
            lines.append("<li>" + item + "</li>")
        lines.append(close_tag)
        return "\n".join(lines)
Exemple #7
0
def tag(tag_name, text, class_name=''):
    """
    Regular tag+class convenience function
    """
    inline = Inline()
    if text == "":
        html = "&nbsp;"
    else:
        html = inline.process(text)
    if class_name != '':
        tpl = "<%s class=\"%s\">%s</%s>"
        return tpl % (tag_name, class_name, html, tag_name)
    else:
        tpl = "<%s>%s</%s>"
        return tpl % (tag_name, html, tag_name)
Exemple #8
0
def get_cell_formatting(align, cell):
    """
    For table_block: align is one of lrc$
    """
    inline = Inline()
    if align == 'r':
        return ("class=\"text-right\"", inline.process(cell))
    elif align == 'c':
        return ("class=\"text-center\"", inline.process(cell))
    elif align == '$':
        try:
            # @todo: Allow locale to be set with settings.
            value = locale.currency(float(cell), grouping=True)
        except ValueError:
            value = cell
        return ("class=\"text-right\"", value)
    else:
        return ("class=\"text-left\"", inline.process(cell))
Exemple #9
0
    def split_author(self, author):
        """
        UNUSED FOR NOW. v.0.1.0 is SINGLE_USER.

        Split into authors and lines.

        $ AUTHOR = Author / Affiliation
                 + Author2 / Affiliation2

        No author should return an empty list.
        """
        inline = Inline()
        if author.strip() == '':
            return []
        else:
            return [[
                inline.process(line.strip()) for line in block.split(' / ')
            ] for block in author.split(' + ')]
Exemple #10
0
    def __init__(self, parts, outline, id_prefix):
        """
        Matches ^[link] and

        ^ That link's corresponding footnote.

        Constructs: {part_numbering, {count: (link_text, footnote_text)}}
        """
        assert isinstance(outline, Outline)
        self.outline = outline

        assert isinstance(parts, dict)
        self.links, self.footnotes = self.collate_footnotes(parts)
        self.backlinks = SortedDict()

        self.id_prefix = id_prefix

        self.inline = Inline()
        self.counters = {}
Exemple #11
0
def gloss_block(text, settings):
    """
    Build HTML from text.
    """
    inline = Inline()
    char = text[0]  # '/', as used here
    gloss = []
    num = None
    for _, line in split_to_array(text, char):
        parts = line.split(" %s " % char)
        if len(parts) == 1:
            num = parts.pop(0)
        else:
            source_html = inline.process(parts.pop(0))
            translations_html = [inline.process(part) for part in parts]
            if num is not None:
                gloss += [[(str(num), ''), (source_html, translations_html)]]
                num = None
            else:
                gloss += [[(source_html, translations_html)]]

    env = Environment(autoescape=True)
    tpl = env.from_string(
        trim("""
        <div class="gloss">
        {% for translation_group in gloss %}
            <div class="phrase-group">
                {% for source_html, translations in translation_group %}
                <div class="phrase">
                    <div class="source">{{ source_html|safe }}</div>
                    {% for translation_html in translations %}
                    <div class="translation">{{ translation_html|safe }}</div>
                    {% endfor %}
                </div>
                {% endfor %}
            </div>
        {% endfor %}
        </div>
    """))

    return tpl.render(gloss=gloss)
Exemple #12
0
def test_typography():
    test = Inline()
    assert "x&mdash;x &ndash; x &copy; &trade;" == \
        test.typography("x --- x -- x (C) (TM)")
    assert "6&times;9 &hellip; 9&frac12; 9&frac14; 9&frac34;" == \
        test.typography("6x9 ... 9(1/2) 9(1/4) 9(3/4)")
    assert "“That’s ‘OK’,” I sez." == \
        test.typography("\"That's 'OK',\" I sez.")
    assert '&#8195;&#8195;&#8195;&#8195;' == \
        test.typography("(4EM)")
Exemple #13
0
class Footnotes(object):
    """
    Manage erm... the footnotes.
    """
    def __init__(self, parts, outline, id_prefix):
        """
        Matches ^[link] and

        ^ That link's corresponding footnote.

        Constructs: {part_numbering, {count: (link_text, footnote_text)}}
        """
        assert isinstance(outline, Outline)
        self.outline = outline

        assert isinstance(parts, dict)
        self.links, self.footnotes = self.collate_footnotes(parts)
        self.backlinks = SortedDict()

        self.id_prefix = id_prefix

        self.inline = Inline()
        self.counters = {}

    def collate_footnotes(self, parts):
        """
        Make lists of the links and footnotes in each section. Add
        error notices to the outline indicating mismatches.
        """
        links = {}
        notes = {}
        for slug, text in parts.items():
            links[slug] = match_links(text)
            notes[slug] = match_footnotes(text)
            num_links = len(links[slug])
            num_notes = len(notes[slug])
            if num_notes > num_links:
                message = "More <kbd>^[link]s</kbd> than footnotes! (+%d)"
                diff = num_notes - num_links
                self.outline.error(slug, "", message % diff)
            if num_links > num_notes:
                message = "More footnotes than <kbd>^[link]s</kbd>! (+%d)"
                diff = num_links - num_notes
                self.outline.error(slug, "", message % diff)
        return (links, notes)

    def get_count(self, part_slug):
        """
        Return the footnote count character for this part:
        """
        if part_slug not in self.counters:
            self.counters[part_slug] = Numbers()
        return next(self.counters[part_slug])

    def footnote(self, prefix, pattern, part_slug, count):
        """
        Take ^[some link] and return a link to its Index line.
        Also store the back-link for when the index is generated.
        If the reference if a link, make the inline text that link.
        """
        numbering = self.outline.find_numbering(part_slug)
        number = get_number(numbering)
        nav_id = get_nav_id(numbering, count)

        link_markup, punctuation = split_pattern(pattern)

        footnote_text = ''
        if part_slug in self.footnotes:
            if count in self.footnotes[part_slug]:
                footnote_text = self.footnotes[part_slug][count]

        if validators.url(footnote_text):
            ref_text = footnote_text
            # print('URL', ref_text)
        else:
            ref_text = self.inline.process(footnote_text)
            # print('NOT URL', footnote_text, ref_text)

        _ = '<a class="%s" id="%s_footnote_%s" href="#%s_link_%s">%s</a>'
        ref_link = _ % ('web-marker', self.id_prefix, nav_id, self.id_prefix,
                        nav_id, count)

        if number not in self.backlinks:
            self.backlinks[number] = {}

        self.backlinks[number][count] = (ref_link, ref_text)

        if validators.url(footnote_text):

            link = trim("""
                <a class="web-link" title="%s" href="%s"
                   target="_blank">%s</a>%s<a class="web-marker"
                   id="%s_link_%s" href="#%s_footnote_%s"><sup>%s</sup></a>
                """) % (strip_markup(footnote_text), footnote_text,
                        link_markup, punctuation, self.id_prefix, nav_id,
                        self.id_prefix, nav_id, count)

        else:

            link = trim("""
                <a class="web-link" title="%s" id="%s_link_%s"
                    href="#%s_footnote_%s">%s%s<sup>%s</sup></a>
                """) % (strip_markup(footnote_text), self.id_prefix,
                        nav_id, self.id_prefix, nav_id,
                        self.inline.process(link_markup), punctuation, count)

        return link

    def sort(self):
        """
        Put the footnotes into a settled numerical order.

        Take {section_number: {count: entry}}
        Yield (section_number, [(count, entry), ...])
        """
        for section_number in sorted(self.backlinks):
            section = self.backlinks[section_number]
            sortable = {int(_): section[_] for _ in section}  # numerical
            new_section = [(_, sortable[_]) for _ in sorted(sortable)]
            yield (section_number, new_section)

    def html(self):
        """
        Render a lit of footnotes for the entire document.

        TODO: This has been obsoleted by self.html_parts() now; retaining here
        as a reminder to consider a SETTING that allows either per-section or
        end-of-document footnotes.
        """
        assert isinstance(self.backlinks, dict)
        for _ in list(self.backlinks.items()):
            assert isinstance(_, tuple)

        env = Environment(autoescape=True)
        tpl = env.from_string("""
            <section id="{{ id_prefix }}-footnotes">
            {% if backlinks|length < 2 %}
                {% for number, entries in backlinks %}
                    {% for count, (ref_link, ref_text) in entries %}
                <div class="footnote-item">
                    <sup>{{ ref_link|safe }}</sup> {{ ref_text|safe }}
                </div>
                    {% endfor %}
                {% endfor %}
            {% else %}
                <h1><a href="#{{ id_prefix }}-footnotes">Footnotes</a></h1>
                {% for number, entries in backlinks %}
                <div class="no-widows">
                    <div class="footnote-title"><b>{{ number }}</b></div>
                {% for count, (ref_link, ref_text) in entries %}
                    <div class="footnote-item">
                        <sup>{{ ref_link|safe }}</sup> {{ ref_text|safe }}
                    </div>
                {% endfor %}
                </div>
                {% endfor %}
            {% endif %}
            </section>
            """)

        if len(self.backlinks) == 0:
            return ""
        else:
            return tpl.render(backlinks=list(self.sort()),
                              id_prefix=self.id_prefix)

    def html_parts(self) -> list:
        """
        Retrieve the HTML for each section, in a dictionary
        """
        parts = {}
        backlinks = list(self.sort())
        for number, entries in backlinks:
            numbering = number.split('.')
            slug = self.outline.find_slug(numbering)
            parts[slug] = ""
            for count, (ref_link, ref_text) in entries:
                pattern = '<div class="footnote-item"><sup>%s</sup> %s</div>'
                parts[slug] += pattern % (ref_link, ref_text)
        return parts
Exemple #14
0
def user_page(user_slug):
    """
    Show <user_slug>/fixtures/author + user documents.
    """
    header_buttons = [login_or_logout_button()]
    login = get_login()
    if login and login['username'] == user_slug:
        header_buttons += [
            new_article_button(user_slug),
        ]

    header_buttons += [
        edit_button(user_slug, 'fixtures', 'author'),
    ]

    if not login:
        header_buttons += [subscribe_button(), rss_button(user_slug)]

    footer_buttons = []
    if config['ARTICLE_WIKI_CREDIT'] == 'YES':
        footer_buttons += [source_button()]
    footer_buttons += [help_button()]
    if has_authority_for_user(user_slug):
        footer_buttons += [export_archive_button(user_slug)]

    slugs = data.userDocumentSet_list(user_slug)
    changes_list = data.userDocumentLastChanged_list(user_slug)

    if not has_authority_for_user(user_slug):
        # Show only those that have been published
        changes_list, __ = split_published(changes_list)

    article_slugs = [_ for _ in slugs if _ not in ['fixtures', 'templates']]
    article_keys = [
        data.userDocumentMetadata_key(user_slug, _) for _ in article_slugs
    ]
    article_list = sorted(data.get_hashes(article_keys),
                          key=lambda _: _.get('title', ''))

    published_articles, unpublished_articles = split_published(article_list)
    if not has_authority_for_user(user_slug):
        unpublished_articles = []

    settings = Settings({
        'config:host': domain_name(bottle.request),
        'config:user': user_slug,
        'config:document': 'fixtures',
    })
    wiki = Wiki(settings)
    document = data.userDocument_get(user_slug, 'fixtures')
    if not document:
        msg = "User '{:s}' not found."
        bottle.abort(HTTP_NOT_FOUND, msg.format(user_slug))
    if 'author' in document:
        text = document['author']
    else:
        text = trim("""
            Author Page

            (Author information to be added here...)
            """)

    blocks = BlockList(clean_text(text))
    page_title, page_summary = blocks.pop_titles()
    content_html = wiki.process(None,
                                None, {'index': blocks.text()},
                                fragment=True,
                                preview=True)
    inline = Inline()

    return views.get_template('user.html').render(
        config=config,
        user=user_slug,
        page_title="{:s} - {:s}".format(page_title, page_summary),
        title_html=inline.process(page_title),
        summary_html=inline.process(page_summary),
        header_buttons=header_buttons,
        footer_buttons=footer_buttons,
        changes_list=changes_list,
        published_articles=published_articles,
        unpublished_articles=unpublished_articles,
        content_html=content_html,
        pluralize=pluralize  # <-- hack function injection
    )
Exemple #15
0
    def make_index(self, text):
        """
        Front matter preceding index text.
        Multi-author blocks are disabled for now; v.0.1.0 is SINGLE_USER.
        """
        env = Environment(autoescape=True)
        tpl = env.from_string(
            trim("""
        <header>

            {% if title_html != "" %}
            <hgroup>
                <h1 class="balance-text">{{ title_html|safe }}</h1>
            {% if summary != "" %}
                <h2 class="balance-text">{{ summary_html|safe }}</h2>
            {% endif %}
            </hgroup>
            {% endif %}

            {% if author != "" or email != "" %}
            <div class="author-list">
                <address>
                {% if author != "" %}
                    <div>{{ author }}</div>
                {% endif %}
                {% if email != "" %}
                    <div><a href="mailto:{{ email }}">{{ email }}</a></div>
                {% endif %}
                </address>
            </div>
            {% endif %}

            {% if facebook != "" or twitter != "" %}
            <p class="space space-between">
                {% if facebook != "" %}
                <a href="https://facebook.com/{{ facebook }}" target="_blank">
                    FB: {{ facebook }}
                </a>
                {% endif %}
                {% if twitter != "" %}
                <a href="https://twitter.com/{{ twitter }}" target="_blank">
                    TW: {{ twitter }}
                </a>
                {% endif %}
            </p>
            {% endif %}

            {% if date %}
            <p class="space" rel="date">
                {% if parsed_date != None %}
                <time pubdate datetime="{{ parsed_date }}">{{ date }}</time>
                {% else %}
                {{ date }}
                {% endif %}
            </p>
            {% endif %}

        </header>
        <section class="depth-0">
            {{ content_html|safe }}
        </section>
        """))

        inline = Inline()
        content, _ = split_bibliography(text)
        blocks = BlockList(content)
        title, summary = blocks.pop_titles()
        content_html = blocks.html(['0'],
                                   'index',
                                   self.settings,
                                   fragment=True)

        date_string = inline.process(self.settings.get('DATE', ''))
        try:
            dt = parse(date_string)
            date_yyyymmdd = dt.date().isoformat()
        except ValueError:
            date_yyyymmdd = None

        # author = self.split_author()
        return tpl.render(
            title_html=inline.process(title),
            summary_html=inline.process(summary),
            author=self.settings.get('AUTHOR', ''),
            email=self.settings.get('EMAIL', ''),
            facebook=self.settings.get('FACEBOOK', ''),
            twitter=self.settings.get('TWITTER', ''),
            date=date_string,
            parsed_date=date_yyyymmdd,
            edit_link=self.settings.get_base_uri('edit') + '/index',
            content_html=content_html,
        )
Exemple #16
0
    def html(self, edit_base_uri):
        """
        Generate a table of contents.
        """
        env = Environment(autoescape=True)
        tpl = env.from_string(
            trim("""
            {% if outline|length > 0 %}
            <h2>Table of Contents</h2>
            <table class="table table-of-contents table-condensed">
                <tbody>
                    {% for numbering, name, slug, title, word_count, subtotal in outline %}
                    <tr>
                        {% if slug == 'index' %}
                        <td></td>
                        <td class="word-count" colspan="{{ (max_depth * 2) - 1 }}">
                            Index.
                        </td>
                        <td class="word-count">{{ word_count }}</td>
                        {% else %}

                            {% for i in range(numbering|length - 1) %}
                        <td></td>
                            {% endfor %}

                        <td class="text-right">
                            <b>{{ numbering | join('.') }}</b>.
                        </td>

                        {% if subtotal == "0" %}
                        <td colspan="{{ (max_depth - numbering|length) * 2 + 1 }}">
                        {% else %}
                        <td colspan="{{ (max_depth - numbering|length) * 2 }}">
                        {% endif %}

                            {% if word_count == "0" %}
                            <a href="{{ edit_base_uri }}/{{ slug }}?title={{ title|urlencode }}" class="unmarked">
                                <i>{{ title|safe }}</i>
                            </a>
                            {% else %}
                            <a href="#{{ name }}" class="unmarked">
                                {{ title|safe }}
                            </a>
                            {% endif %}
                        </td>

                        <td class="word-count">
                            {% if word_count == "0" %}&mdash;{% else %}{{ word_count }}{% endif %}
                        </td>

                            {% if numbering|length > 1 or subtotal != "0" %}
                                {% if subtotal != "0" %}
                        <td class="word-count">
                            \\&nbsp;<b>{{ subtotal }}</b>
                        </td>
                                {% endif %}
                                {% for i in range(numbering|length - 2) %}
                        <td>
                        </td>
                                {% endfor %}
                            {% endif %}
                        {% endif %}
                    </tr>
                    {% endfor %}
                    <tr>
                        <td></td>
                        <td class="word-count" colspan="{{ (max_depth * 2) - 1 }}">
                            Total words.
                        </td>
                        <td class="word-count"><b>{{ total_word_count }}</b></td>
                    </tr>
                </tbody>
            </table>
            {% endif %}
            """))
        inline = Inline()
        max_depth = max([len(nums) for (nums, _, _, _, _) in self.elements])
        formatted = [(
            numbering,
            anchor_name(numbering, slug),
            slug,
            inline.process(title),
            '{:,d}'.format(word_count),
            '{:,d}'.format(subtotal),
        ) for (numbering, slug, title, total_slug, word_count,
               subtotal) in totalize(self.elements)]
        return tpl.render(outline=formatted,
                          max_depth=max_depth,
                          edit_base_uri=edit_base_uri,
                          total_word_count='{:,d}'.format(
                              self.total_word_count()))
Exemple #17
0
class Html(object):
    """
    Provide all repeatable higher-level HTML structures.
    """
    def __init__(self, settings=None):
        """
        Note that the _first_ one created will have its config used.
        """
        self.settings = settings
        self.inline = Inline()
        self.list_counter = 1

    def markup(self, text):
        """
        Apply inline processing only.
        """
        return self.inline.process(text)

    # ---------------
    # BUILDING BLOCKS
    # ---------------

    def strip_markup(self, content):
        """
        Convert to HTML and strip tags.
        """
        return clean(self.markup(content),
                     tags=[],
                     strip=True,
                     strip_comments=True)

    # ----------------------------------------------------
    # Blocks used in CharacterBlock
    # ----------------------------------------------------

    def nav_buttons(self):
        """
        Make the buttons list.
        """
        edit_uri = self.settings.get_base_uri('edit', 'index')
        return """
            <div class="no-preview no-print">
                <div class="text-center big-space">
                  <a class="btn btn-default btn-sm" target="_blank" href="%s">
                      <i class="fa fa-pencil"></i> Edit
                  </a>
                </div>
            </div>
            """ % (edit_uri)

    def UNUSED____make_subheader(self, numbering, title):
        """
        call(['1', '2', '1'], 'Title')
        """
        slug = slugify(title)
        path = self.settings.get_base_uri('edit') + '/' + slug
        header_tag = 'h' + (len(numbering) + 1)  # <-- h2, h3, etc
        number = '.'.join([str(_) for _ in numbering])
        if len(number) > 0:
            prefix = number + ' &nbsp; '
            prefix_slug = number + '-'
        else:
            prefix = ''
            prefix_slug = ''
        return """
            <h%s class="show-parent balance-text" id="%s">
                <div class="no-preview no-print">
                    <div class="float-right">
                      <a class="button" target="_blank" href="%s">
                          <i class="fa fa-pencil"></i> Edit
                      </a>
                    </div>
                </div>
                <a href="#%s">%s</a>
            </h%s>
            """ % (header_tag, prefix_slug + slug, path, prefix_slug + slug,
                   prefix + self.markup(title), header_tag)

    def generate_grid(self, data):
        """
        For now, a simple table.
        """
        html = "<table class=\"table table-condensed\">"
        html += "<tbody>"
        for row in data:
            html += "<tr>"
            for cell in row:
                html += "<td>%s</td>" % self.markup(cell)
            html += "</tr>"
        html += "</tbody>"
        html += "</table>"
        return html
Exemple #18
0
class Bibliography(object):
    """
    Parts are a dictionary of strings; one called 'biblio' is entirely
    comprised of bibliography lines; the others have optional endmatter
    separated by a line of underscores, which is also entirely comprised of
    bibliography lines.

    @todo: Allow an option of '-' for bibliography lines in end matter, so
    they wrap?  Or '~'?
    """
    def __init__(self, parts, outline, id_prefix):
        """
        Create bibliography entries, and the structure for recording citations
        as they occur (These will be back-linked in the bibliography's
        citations list). The unique entry_id will be a slug of author/date,
        with an added Letters() counter for uniqueness.

                        / SortedDict
        self.entries = {entry_id: entry_text}

                                       / SortedDict
        self.citations = {entry_id: {part_number: [indexes]}}
        """
        assert isinstance(outline, Outline)
        assert isinstance(parts, dict)

        self.outline = outline
        self.entries = collate_bibliography(parts)

        self.id_prefix = id_prefix

        self.citations = {}
        self.counters = {}

        self.inline = Inline()

    def __iter__(self):
        """
        Make it easy to loop through the bibliographies.
        """
        return iter(self.entries)

    def match(self, citation, default=None):
        """
        Return the (unique) label for the first bibliography item matching all
        words in the citation. Note that citation is assumed to be the first
        part of ((Citation, Note)) before the comma, so we're not matching page
        numbers or comments.
        """
        terms = get_words(strip_markup(citation))
        for label in self.entries:
            entry = self.entries[label]
            lowercase_entry = label.lower() + ' ' + entry.lower()
            if all([_ in lowercase_entry for _ in terms]):
                return label
        return default

    def get_count(self, part_slug):
        """
        Return the footnote counter for this part:
        """
        if part_slug not in self.counters:
            if part_slug == 'index':
                self.counters[part_slug] = Symbols()
            else:
                self.counters[part_slug] = Letters()

        return next(self.counters[part_slug])

    def citation(self, citation, note, punctuation, label, numbering, count):
        """
        Take ~[Citation 2000, p.34] and return a link to its bibiography
        entry. Also store the event for summarising in the bibiography.

        @todo http://lispp.blogspot.com/2014/01/ibid-and-op-cit.html
        ibid., op. cit., loc. cit.
        """
        number = get_number(numbering)
        nav_id = get_nav_id(label, numbering, count)

        if label not in self.citations:
            self.citations[label] = {}
        if number not in self.citations[label]:
            self.citations[label][number] = []

        # Use a '-' leader to suppress brackets.
        if citation[0] == '-':
            use_citation = citation[1:]
            bracket_open, bracket_close = '', ''
        else:
            use_citation = citation
            bracket_open, bracket_close = '(', ')'

        citation_markup = self.inline.process(use_citation)

        if note.strip() != "":
            note_markup = self.inline.process(note)
            link = "<a id=\"%s_%s\" href=\"#ref_%s_%s\">%s%s, %s%s%s" \
                   "<sup>%s</sup></a>" % \
                   (self.id_prefix, nav_id, self.id_prefix, nav_id,
                    bracket_open, citation_markup, note_markup, bracket_close,
                    punctuation, count)
        else:
            link = "<a id=\"%s_%s\" href=\"#ref_%s_%s\">%s%s%s%s" \
                   "<sup>%s</sup></a>" % \
                   (self.id_prefix, nav_id, self.id_prefix, nav_id,
                    bracket_open, citation_markup, bracket_close,
                    punctuation, count)

        ref_link = "<a id=\"ref_%s_%s\" href=\"#%s_%s\">%s</a>" % \
                   (self.id_prefix, nav_id, self.id_prefix, nav_id, count)
        self.citations[label][number] += [ref_link]

        return link

    def html(self):
        """
        From a list of bibliography lines.
        """
        assert isinstance(self.entries, dict)

        env = Environment(autoescape=True)
        tpl = env.from_string("""
            {% if entries|length > 0 %}
            <section id="{{ id_prefix }}-bibliography" class="bibliography">
            {% if not single_page %}
                <h1>
                    <a href="#{{ id_prefix }}-bibliography">Bibliography</a>
                </h1>
            {% endif %}
            {% for label in entries %}
                <div class="indent-hanging">
                    {{ label }}&nbsp; {{ entries[label] }}
                    {% if label in citations %}
                        <span class="wiki-no-select">
                        {% for number in citations[label] %}
                            {% if not single_page %}
                            <b>{{ number }}.</b>
                            {% endif %}
                            ({{
                                citations[label][number] | join(', ') | safe
                            }}).
                        {% endfor %}
                        </span>
                    {% endif %}
                </div>
            {% endfor %}
            {#
            <pre>{{ entries | tojson }}</pre>
            <pre>{{ citations | tojson }}</pre>
            #}
            </section>
            {% endif %}
            """)

        return tpl.render(single_page=self.outline.single_page(),
                          entries=self.entries,
                          citations=self.citations,
                          id_prefix=self.id_prefix)
Exemple #19
0
def test_brackets():
    test = Inline()
    assert '<strong>bold face</strong>' == test.brackets('*', "bold face")
    assert '<em><strong><u>bold-underline-italics</u></strong></em>' == \
        test.brackets('*_/', "bold-underline-italics")
Exemple #20
0
def test_apostrophes():
    test = Inline()
    assert '‘X’' == test.process("'X'")
    assert 'X’s' == test.process("X's")
    assert '(‘x)' == test.process("('x)")