Просмотр исходного кода

Merge pull request #309 from barrysteyn/render_math

Render math update
Justin Mayer лет назад: 10
Родитель
Сommit
11f3f465c0

+ 50 - 110
render_math/Readme.md

@@ -1,26 +1,14 @@
 Math Render Plugin For Pelican
 Math Render Plugin For Pelican
 ==============================
 ==============================
 This plugin gives pelican the ability to render mathematics. It accomplishes
 This plugin gives pelican the ability to render mathematics. It accomplishes
-this by using the [MathJax](http://www.mathjax.org/) javascript engine. Both
-[LaTex](http://en.wikipedia.org/wiki/LaTeX) and [MathML](http://en.wikipedia.org/wiki/MathML) 
-can be rendered within the content.
+this by using the [MathJax](http://www.mathjax.org/) javascript engine.
 
 
-The plugin also ensures that pelican and recognized math "play" nicely together, by
-ensuring [Typogrify](https://github.com/mintchaos/typogrify) does not alter math content
-and summaries that get cut off are repaired.
+The plugin also ensures that Typogrify and recognized math "play" nicely together, by
+ensuring [Typogrify](https://github.com/mintchaos/typogrify) does not alter math content.
+It requires at a minimum Pelican version *3.5* and Typogrify version *2.0.7* to work.
+If these versions are not available, Typogrify will be disabled for the entire site.
 
 
-Recognized math in the context of this plugin is either LaTex or MathML as described below.
-
-### LaTex
-Anything between `$`...`$` (inline math) and `$$`..`$$` (displayed math) will be recognized as
-LaTex. In addition, anything the `\begin` and `\end` LaTex macros will also be 
-recognized as LaTex. For example, `\begin{equation}`...`\end{equation}` would be used to 
-render math equations with numbering.
-
-Within recognized LaTex as described above, any supported LaTex macro can be used.
-
-### MathML
-Anything between `<math>` and `</math>` tags will be recognized as MathML
+Both Markdown and reStructuredText is supported.
 
 
 Installation
 Installation
 ------------
 ------------
@@ -37,45 +25,16 @@ In the past, using [Typgogrify](https://github.com/mintchaos/typogrify) would al
 in math that could not be rendered by MathJax. The only option was to ensure
 in math that could not be rendered by MathJax. The only option was to ensure
 that Typogrify was disabled in the settings.
 that Typogrify was disabled in the settings.
 
 
-The problem has been recitified in this plugin, but it requires [Typogrify version 2.04](https://pypi.python.org/pypi/typogrify)
-(or higher). If this version of Typogrify is not present, the plugin will inform that an incorrect
-version of Typogrify is not present and disable Typogrify for the entire site
+The problem has been recitified in this plugin, but it requires [Typogrify version 2.0.7](https://pypi.python.org/pypi/typogrify)
+(or higher) and Pelican version 3.5 or higher. If these versions are not present, the plugin will disable
+Typogrify for the entire site
 
 
 Usage
 Usage
 -----
 -----
-### Backward Compatibility
-This plugin is backward compatible in the sense that it
-will render previous setups correctly. This is because those
-settings and metadata information is ignored by this version. Therefore
-you can remove them to neaten up your site
-
 ### Templates
 ### Templates
 No alteration is needed to a template for this plugin to work. Just install
 No alteration is needed to a template for this plugin to work. Just install
 the plugin and start writing your Math. 
 the plugin and start writing your Math. 
 
 
-If on the other hand, you are template designer and want total control
-over the MathJax JavaScript, you can set the `auto_insert` setting to 
-`False` which will cause no MathJax JavaScript to be added to the content.
-
-If you choose this option, you should really know what you are doing. Therefore
-only do this if you are designing your template. There is no real advantage to
-to letting template logic handle the insertion of the MathJax JavaScript other
-than it being slightly neater.
-
-By setting `auto_insert` to `False`, metadata with `key` value of `mathjax`
-will be present in all pages and articles where MathJax should be present.
-The template designer can detect this and then use the `MATHJAXSCRIPT` setting
-which will contain the user specified MathJax script to insert into the content.
-
-For example, this code could be used:
-```
-{% if not MATH['auto_insert'] %}
-    {% if page and page.mathjax or article and article.mathjax %}
-        {{ MATHJAXSCRIPT }}
-    {% endif %}
-{% endif %}
-```
-
 ### Settings
 ### Settings
 Certain MathJax rendering options can be set. These options 
 Certain MathJax rendering options can be set. These options 
 are in a dictionary variable called `MATH` in the pelican
 are in a dictionary variable called `MATH` in the pelican
@@ -83,18 +42,6 @@ settings file.
 
 
 The dictionary can be set with the following keys:
 The dictionary can be set with the following keys:
 
 
- * `auto_insert`: controls whether plugin should automatically insert
-MathJax JavaScript in content that has Math. It is only recommended
-to set this to False if you are a template designer and you want
-extra control over where the MathJax JavaScript is renderd. **Default Value**:
-True
- * `wrap_latex`: controls the tags that LaTex math is wrapped with inside the resulting
-html. For example, setting `wrap_latex` to `mathjax` would wrap all LaTex math inside
-`<mathjax>...</mathjax>` tags. If typogrify is set to True, then math needs
-to be wrapped in tags and `wrap_latex` will therefore default to `mathjax` if not
-set. `wrap_latex` cannot be set to `'math'` because this tag is reserved for 
-mathml notation. **Default Value**: None unless Typogrify is enabled in which case, 
-it defaults to `mathjax`
  * `align`: controls how displayed math will be aligned. Can be set to either
  * `align`: controls how displayed math will be aligned. Can be set to either
 `left`, `right` or `center`. **Default Value**: `center`.
 `left`, `right` or `center`. **Default Value**: `center`.
  * `indent`: if `align` not set to `center`, then this controls the indent
  * `indent`: if `align` not set to `center`, then this controls the indent
@@ -107,67 +54,60 @@ sequences. **Default Value**: True
 rendering LaTex. If set to `Tex`, then the TeX code is used as the preview 
 rendering LaTex. If set to `Tex`, then the TeX code is used as the preview 
 (which will be visible until it is processed by MathJax). **Default Value**: `Tex`
 (which will be visible until it is processed by MathJax). **Default Value**: `Tex`
  * `color`: controls the color of the mathjax rendered font. **Default Value**: `black`
  * `color`: controls the color of the mathjax rendered font. **Default Value**: `black`
- * `ssl`: specifies if ssl should be used to load MathJax engine. Can be set to one
-of three things
-  * `auto`: **Default Value** will automatically determine what protodol to use 
-based on current protocol of the site. 
-  * `force`: will force ssl to be used.
-  * `off`: will ensure that ssl is not used
 
 
 For example, in settings.py, the following would make math render in blue and
 For example, in settings.py, the following would make math render in blue and
 displaymath align to the left:
 displaymath align to the left:
 
 
     MATH = {'color':'blue','align':left}
     MATH = {'color':'blue','align':left}
 
 
-LaTex Examples
---------------
-###Inline
-LaTex between `$`..`$`, for example, `$`x^2`$`, will be rendered inline
-with respect to the current html block.
+#### Resulting HTML
+Inlined math is wrapped in `span` tags, while displayed math is wrapped in `div` tags.
+These tags will have a class attribute that is set to `math` which 
+can be used by template designers to alter the display of the math.
+
+Markdown
+--------
+This plugin implements a custom extension for markdown resulting in math
+being a "first class citizen" for Pelican. 
+
+###Inlined Math
+Math between `$`..`$`, for example, `$`x^2`$`, will be rendered inline
+with respect to the current html block. Note: To use inline math, there
+must *not* be any whitespace before the ending `$`. So for example:
+
+ * **Relevant inline math**: `$e=mc^2$`
+ * **Will not render as inline math**: `$40 vs $50`
 
 
 ###Displayed Math
 ###Displayed Math
-LaTex between `$$`..`$$`, for example, `$$`x^2`$$`, will be rendered centered in a
+Math between `$$`..`$$`, for example, `$$`x^2`$$`, will be rendered centered in a
 new paragraph.
 new paragraph.
 
 
-###Equations
-LaTex between `\begin` and `\end`, for example, `begin{equation}` x^2 `\end{equation}`,
-will be rendered centered in a new paragraph with a right justified equation number
-at the top of the paragraph. This equation number can be referenced in the document.
+###Latex Macros
+Latex macros are supported, and are automatically treated like displayed math 
+(i.e. it is wrapped in `div` tag). For example, `begin{equation}` x^2 `\end{equation}`,
+will be rendered in its own block with a right justified equation number
+at the top of the block. This equation number can be referenced in the document.
 To do this, use a `label` inside of the equation format and then refer to that label
 To do this, use a `label` inside of the equation format and then refer to that label
 using `ref`. For example: `begin{equation}` `\label{eq}` X^2 `\end{equation}`. Now
 using `ref`. For example: `begin{equation}` `\label{eq}` X^2 `\end{equation}`. Now
 refer to that equation number by `$`\ref{eq}`$`.
 refer to that equation number by `$`\ref{eq}`$`.
 
 
-MathML Examples
----------------
-The following will render the Quadratic formula:
+reStructuredText
+----------------
+If there is math detected in reStructuredText document, the plugin will automatically
+set the [math_output](http://docutils.sourceforge.net/docs/user/config.html#math-output) configuration setting to `MathJax`.
+
+###Inlined Math
+Inlined math needs to use the [math role](http://docutils.sourceforge.net/docs/ref/rst/roles.html#math):
+
+```
+The area of a circle is :math:`A_\text{c} = (\pi/4) d^2`.
+```
+
+###Displayed Math
+Displayed math uses the [math block](http://docutils.sourceforge.net/docs/ref/rst/directives.html#math):
+
 ```
 ```
-<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> 
-  <mrow>
-    <mi>x</mi>
-    <mo>=</mo>
-    <mfrac>
-      <mrow>
-        <mo>&#x2212;</mo>
-        <mi>b</mi>
-        <mo>&#xB1;</mo>
-        <msqrt>
-          <mrow>
-            <msup>
-              <mi>b</mi>
-              <mn>2</mn>
-            </msup>
-            <mo>&#x2212;</mo>
-            <mn>4</mn>
-            <mi>a</mi>
-            <mi>c</mi>
-          </mrow>
-        </msqrt>
-      </mrow>
-      <mrow>
-        <mn>2</mn>
-        <mi>a</mi>
-      </mrow>
-    </mfrac>
-  </mrow>
-</math>
+.. math::
+
+  α_t(i) = P(O_1, O_2, … O_t, q_t = S_i λ)
 ```
 ```

+ 113 - 306
render_math/math.py

@@ -2,8 +2,17 @@
 """
 """
 Math Render Plugin for Pelican
 Math Render Plugin for Pelican
 ==============================
 ==============================
-This plugin allows your site to render Math. It supports both LaTeX and MathML
-using the MathJax JavaScript engine.
+This plugin allows your site to render Math. It uses
+the MathJax JavaScript engine.
+
+For markdown, the plugin works by creating a Markdown 
+extension which is used during the markdown compilation stage. 
+Math therefore gets treated like a "first class citizen" in Pelican
+
+For reStructuredText, the plugin instructs the rst engine
+to output Mathjax for for math.
+
+The mathjax script is automatically inserted into the HTML.
 
 
 Typogrify Compatibility
 Typogrify Compatibility
 -----------------------
 -----------------------
@@ -19,176 +28,15 @@ See README for more details.
 """
 """
 
 
 import os
 import os
-import re
+import sys
 
 
 from pelican import signals
 from pelican import signals
-from pelican import contents
-
-
-# Global Variables
-_TYPOGRIFY = None  # if Typogrify is enabled, this is set to the typogrify.filter function
-_WRAP_LATEX = None  # the tag to wrap LaTeX math in (needed to play nicely with Typogrify or for template designers)
-_MATH_REGEX = re.compile(r'(\$\$|\$|\\begin\{(.+?)\}|<(math)(?:\s.*?)?>).*?(\1|\\end\{\2\}|</\3>)', re.DOTALL | re.IGNORECASE)  # used to detect math
-_MATH_SUMMARY_REGEX = None  # used to match math in summary
-_MATH_INCOMPLETE_TAG_REGEX = None  # used to match math that has been cut off in summary
-_MATHJAX_SETTINGS = {}  # settings that can be specified by the user, used to control mathjax script settings
-with open (os.path.dirname(os.path.realpath(__file__))+'/mathjax_script.txt', 'r') as mathjax_script:  # Read the mathjax javascript from file
-    _MATHJAX_SCRIPT=mathjax_script.read()
-
-
-# Python standard library for binary search, namely bisect is cool but I need
-# specific business logic to evaluate my search predicate, so I am using my
-# own version
-def binary_search(match_tuple, ignore_within):
-    """Determines if t is within tupleList. Using the fact that tupleList is
-    ordered, binary search can be performed which is O(logn)
-    """
-
-    ignore = False
-    if ignore_within == []:
-        return False
-
-    lo = 0
-    hi = len(ignore_within)-1
-
-    # Find first value in array where predicate is False
-    # predicate function: tupleList[mid][0] < t[index]
-    while lo < hi:
-        mid = lo + (hi-lo+1)//2
-        if ignore_within[mid][0] < match_tuple[0]:
-            lo = mid
-        else:
-            hi = mid-1
-
-    if lo >= 0 and lo <= len(ignore_within)-1:
-        ignore = (ignore_within[lo][0] <= match_tuple[0] and ignore_within[lo][1] >= match_tuple[1])
-
-    return ignore
-
-
-def ignore_content(content):
-    """Creates a list of match span tuples for which content should be ignored
-    e.g. <pre> and <code> tags
-    """
-    ignore_within = []
-
-    # used to detect all <pre> and <code> tags. NOTE: Alter this regex should
-    # additional tags need to be ignored
-    ignore_regex = re.compile(r'<(pre|code)(?:\s.*?)?>.*?</(\1)>', re.DOTALL | re.IGNORECASE)
-
-    for match in ignore_regex.finditer(content):
-        ignore_within.append(match.span())
-
-    return ignore_within
-
-
-def wrap_math(content, ignore_within):
-    """Wraps math in user specified tags.
-
-    This is needed for Typogrify to play nicely with math but it can also be
-    styled by template providers
-    """
-
-    wrap_math.found_math = False
-
-    def math_tag_wrap(match):
-        """function for use in re.sub"""
-
-        # determine if the tags are within <pre> and <code> blocks
-        ignore = binary_search(match.span(1), ignore_within) or binary_search(match.span(4), ignore_within)
-
-        if ignore or match.group(3) == 'math':
-            if match.group(3) == 'math':
-                # Will detect mml, but not wrap anything around it
-                wrap_math.found_math = True
-
-            return match.group(0)
-        else:
-            wrap_math.found_math = True
-            return '<%s>%s</%s>' % (_WRAP_LATEX, match.group(0), _WRAP_LATEX)
-
-    return (_MATH_REGEX.sub(math_tag_wrap, content), wrap_math.found_math)
-
-
-def process_summary(instance, ignore_within):
-    """Summaries need special care. If Latex is cut off, it must be restored.
-
-    In addition, the mathjax script must be included if necessary thereby
-    making it independent to the template
-    """
-
-    process_summary.altered_summary = False
-    insert_mathjax = False
-    end_tag = '</%s>' % _WRAP_LATEX if _WRAP_LATEX is not None else ''
-
-    # use content's _get_summary method to obtain summary
-    summary = instance._get_summary()
-
-    # Determine if there is any math in the summary which are not within the
-    # ignore_within tags
-    math_item = None
-    for math_item in _MATH_SUMMARY_REGEX.finditer(summary):
-        ignore = binary_search(math_item.span(2), ignore_within)
-        if '...' not in math_item.group(5):
-            ignore = ignore or binary_search(math_item.span(5), ignore_within)
-        else:
-            ignore = ignore or binary_search(math_item.span(6), ignore_within)
-
-        if ignore:
-            math_item = None # In <code> or <pre> tags, so ignore
-        else:
-            insert_mathjax = True
-
-    # Repair the math if it was cut off math_item will be the final math
-    # code  matched that is not within <pre> or <code> tags
-    if math_item and '...' in math_item.group(5):
-        if math_item.group(3) is not None:
-            end = r'\end{%s}' % math_item.group(3)
-        elif math_item.group(4) is not None:
-            end = r'</math>'
-        elif math_item.group(2) is not None:
-            end = math_item.group(2)
-
-        search_regex = r'%s(%s.*?%s)' % (re.escape(instance._content[0:math_item.start(1)]), re.escape(math_item.group(1)), re.escape(end))
-        math_match = re.search(search_regex, instance._content, re.DOTALL | re.IGNORECASE)
-
-        if math_match:
-            new_summary = summary.replace(math_item.group(0), math_match.group(1)+'%s ...' % end_tag)
-
-            if new_summary != summary:
-                if _MATHJAX_SETTINGS['auto_insert']:
-                    return new_summary+_MATHJAX_SCRIPT.format(**_MATHJAX_SETTINGS)
-                else:
-                    instance.mathjax = True
-                    return new_summary
-
-    def incomplete_end_latex_tag(match):
-        """function for use in re.sub"""
-        if binary_search(match.span(3), ignore_within):
-            return match.group(0)
-
-        process_summary.altered_summary = True
-        return match.group(1) + match.group(4)
-
-    # check for partial math tags at end. These must be removed
-    summary = _MATH_INCOMPLETE_TAG_REGEX.sub(incomplete_end_latex_tag, summary)
-
-    if process_summary.altered_summary or insert_mathjax:
-        if insert_mathjax:
-            if _MATHJAX_SETTINGS['auto_insert']:
-                summary+= _MATHJAX_SCRIPT.format(**_MATHJAX_SETTINGS)
-            else:
-                instance.mathjax = True
-
-        return summary
+from . pelican_mathjax_markdown_extension import PelicanMathJaxExtension
 
 
-    return None  # Making it explicit that summary was not altered
-
-
-def process_settings(settings):
+def process_settings(pelicanobj):
     """Sets user specified MathJax settings (see README for more details)"""
     """Sets user specified MathJax settings (see README for more details)"""
 
 
-    global _MATHJAX_SETTINGS
+    mathjax_settings = {}
 
 
     # NOTE TO FUTURE DEVELOPERS: Look at the README and what is happening in
     # NOTE TO FUTURE DEVELOPERS: Look at the README and what is happening in
     # this function if any additional changes to the mathjax settings need to
     # this function if any additional changes to the mathjax settings need to
@@ -196,184 +44,143 @@ def process_settings(settings):
     # will be used for
     # will be used for
 
 
     # Default settings
     # Default settings
-    _MATHJAX_SETTINGS['align'] = 'center'  # controls alignment of of displayed equations (values can be: left, right, center)
-    _MATHJAX_SETTINGS['indent'] = '0em'  # if above is not set to 'center', then this setting acts as an indent
-    _MATHJAX_SETTINGS['show_menu'] = 'true'  # controls whether to attach mathjax contextual menu
-    _MATHJAX_SETTINGS['process_escapes'] = 'true'  # controls whether escapes are processed
-    _MATHJAX_SETTINGS['latex_preview'] = 'TeX'  # controls what user sees while waiting for LaTex to render
-    _MATHJAX_SETTINGS['color'] = 'black'  # controls color math is rendered in
-
-    # Source for MathJax: default (below) is to automatically determine what protocol to use
-    _MATHJAX_SETTINGS['source'] = """'https:' == document.location.protocol
-                ? 'https://c328740.ssl.cf1.rackcdn.com/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML'
-                : 'http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML'"""
-
-    # This next setting controls whether the mathjax script should be automatically
-    # inserted into the content. The mathjax script will not be inserted into
-    # the content if no math is detected. For summaries that are present in the
-    # index listings, mathjax script will also be automatically inserted.
-    # Setting this value to false means the template must be altered if this
-    # plugin is to work, and so it is only recommended for the template
-    # designer who wants maximum control.
-    _MATHJAX_SETTINGS['auto_insert'] = True  # controls whether mathjax script is automatically inserted into the content
+    mathjax_settings['align'] = 'center'  # controls alignment of of displayed equations (values can be: left, right, center)
+    mathjax_settings['indent'] = '0em'  # if above is not set to 'center', then this setting acts as an indent
+    mathjax_settings['show_menu'] = 'true'  # controls whether to attach mathjax contextual menu
+    mathjax_settings['process_escapes'] = 'true'  # controls whether escapes are processed
+    mathjax_settings['latex_preview'] = 'TeX'  # controls what user sees while waiting for LaTex to render
+    mathjax_settings['color'] = 'black'  # controls color math is rendered in
+
+    # Source for MathJax: Works boths for http and https (see http://docs.mathjax.org/en/latest/start.html#secure-access-to-the-cdn)
+    mathjax_settings['source'] = "'//cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML'"
+    
+    # Get the user specified settings
+    try:
+        settings = pelicanobj.settings['MATH_JAX']
+    except:
+        settings = None
 
 
+    # If no settings have been specified, then return the defaults
     if not isinstance(settings, dict):
     if not isinstance(settings, dict):
-        return
+        return mathjax_settings
 
 
     # The following mathjax settings can be set via the settings dictionary
     # The following mathjax settings can be set via the settings dictionary
-    # Iterate over dictionary in a way that is compatible with both version 2
-    # and 3 of python
     for key, value in ((key, settings[key]) for key in settings):
     for key, value in ((key, settings[key]) for key in settings):
-        if key == 'auto_insert' and isinstance(value, bool):
-            _MATHJAX_SETTINGS[key] = value
-
+        # Iterate over dictionary in a way that is compatible with both version 2
+        # and 3 of python
         if key == 'align' and isinstance(value, str):
         if key == 'align' and isinstance(value, str):
             if value == 'left' or value == 'right' or value == 'center':
             if value == 'left' or value == 'right' or value == 'center':
-                _MATHJAX_SETTINGS[key] = value
+                mathjax_settings[key] = value
             else:
             else:
-                _MATHJAX_SETTINGS[key] = 'center'
+                mathjax_settings[key] = 'center'
 
 
         if key == 'indent':
         if key == 'indent':
-            _MATHJAX_SETTINGS[key] = value
+            mathjax_settings[key] = value
 
 
         if key == 'show_menu' and isinstance(value, bool):
         if key == 'show_menu' and isinstance(value, bool):
-            _MATHJAX_SETTINGS[key] = 'true' if value else 'false'
+            mathjax_settings[key] = 'true' if value else 'false'
 
 
         if key == 'process_escapes' and isinstance(value, bool):
         if key == 'process_escapes' and isinstance(value, bool):
-            _MATHJAX_SETTINGS[key] = 'true' if value else 'false'
+            mathjax_settings[key] = 'true' if value else 'false'
 
 
         if key == 'latex_preview' and isinstance(value, str):
         if key == 'latex_preview' and isinstance(value, str):
-            _MATHJAX_SETTINGS[key] = value
+            mathjax_settings[key] = value
 
 
         if key == 'color' and isinstance(value, str):
         if key == 'color' and isinstance(value, str):
-            _MATHJAX_SETTINGS[key] = value
+            mathjax_settings[key] = value
 
 
-        if key == 'ssl' and isinstance(value, str):
-            if value == 'off':
-                _MATHJAX_SETTINGS['source'] = "'http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML'"
+    return mathjax_settings
 
 
-            if value == 'force':
-                _MATHJAX_SETTINGS['source'] = "'https://c328740.ssl.cf1.rackcdn.com/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML'"
+def configure_typogrify(pelicanobj, mathjax_settings):
+    """Instructs Typogrify to ignore math tags - which allows Typogfrify
+    to play nicely with math related content"""
+    
+    # If Typogrify is not being used, then just exit
+    if not pelicanobj.settings.get('TYPOGRIFY', False):
+        return
 
 
+    try:
+        import typogrify
+        from distutils.version import LooseVersion
+        
+        if LooseVersion(typogrify.__version__) < LooseVersion('2.0.7'):
+            raise TypeError('Incorrect version of Typogrify')
 
 
-def process_content(instance):
-    """Processes content, with logic to ensure that Typogrify does not clash
-    with math.
+        from typogrify.filters import typogrify
 
 
-    In addition, mathjax script is inserted at the end of the content thereby
-    making it independent of the template
-    """
+        # At this point, we are happy to use Typogrify, meaning
+        # it is installed and it is a recent enough version
+        # that can be used to ignore all math
+        # Instantiate markdown extension and append it to the current extensions
+        pelicanobj.settings['TYPOGRIFY_IGNORE_TAGS'].extend(['.math', 'script'])  # ignore math class and script
 
 
-    if not instance._content:
-        return
+    except (ImportError, TypeError, KeyError) as e:
+        pelicanobj.settings['TYPOGRIFY'] = False  # disable Typogrify
 
 
-    ignore_within = ignore_content(instance._content)
-
-    if _WRAP_LATEX:
-        instance._content, math = wrap_math(instance._content, ignore_within)
-    else:
-        math = True if _MATH_REGEX.search(instance._content) else False
-
-    # The user initially set Typogrify to be True, but since it would clash
-    # with math, we set it to False. This means that the default reader will
-    # not call Typogrify, so it is called here, where we are able to control
-    # logic for it ignore math if necessary
-    if _TYPOGRIFY:
-        # Tell Typogrify to ignore the tags that math has been wrapped in
-        # also, Typogrify must always ignore mml (math) tags
-        ignore_tags = [_WRAP_LATEX,'math'] if _WRAP_LATEX else ['math']
-
-        # Exact copy of the logic as found in the default reader
-        instance._content = _TYPOGRIFY(instance._content, ignore_tags)
-        instance.metadata['title'] = _TYPOGRIFY(instance.metadata['title'], ignore_tags)
-
-    if math:
-        if _MATHJAX_SETTINGS['auto_insert']:
-            # Mathjax script added to content automatically. Now it
-            # does not need to be explicitly added to the template
-            instance._content += _MATHJAX_SCRIPT.format(**_MATHJAX_SETTINGS)
-        else:
-            # Place the burden on ensuring mathjax script is available to
-            # browser on the template designer (see README for more details)
-            instance.mathjax = True
-
-        # The summary needs special care because math math cannot just be cut
-        # off
-        summary = process_summary(instance, ignore_within)
-        if summary is not None:
-            instance._summary = summary
+        if isinstance(e, ImportError):
+            print("\nTypogrify is not installed, so it is being ignored.\nIf you want to use it, please install via: pip install typogrify\n")
 
 
+        if isinstance(e, TypeError):
+            print("\nA more recent version of Typogrify is needed for the render_math module.\nPlease upgrade Typogrify to the latest version (anything equal or above version 2.0.7 is okay).\nTypogrify will be turned off due to this reason.\n")
 
 
-def pelican_init(pelicanobj):
-    """Intialializes certain global variables and sets typogogrify setting to
-    False should it be set to True.
-    """
+        if isinstance(e, KeyError):
+            print("\nA more recent version of Pelican is needed for Typogrify to work with render_math.\nPlease upgrade Pelican to the latest version or clone it directly from the master GitHub branch\nTypogrify will be turned off due to this reason\n")
+
+def process_mathjax_script(mathjax_settings):
+    """Load the mathjax script template from file, and render with the settings"""
 
 
-    global _TYPOGRIFY
-    global _WRAP_LATEX
-    global _MATH_SUMMARY_REGEX
-    global _MATH_INCOMPLETE_TAG_REGEX
+    # Read the mathjax javascript template from file
+    with open (os.path.dirname(os.path.realpath(__file__))+'/mathjax_script_template', 'r') as mathjax_script_template:
+        mathjax_template = mathjax_script_template.read()
 
 
+    return mathjax_template.format(**mathjax_settings)
+
+def mathjax_for_markdown(pelicanobj, mathjax_settings):
+    """Instantiates a customized markdown extension for handling mathjax
+    related content"""
+
+    # Create the configuration for the markdown template
+    config = {}
+    config['mathjax_script'] = [process_mathjax_script(mathjax_settings),'Mathjax JavaScript script']
+    config['math_tag_class'] = ['math', 'The class of the tag in which mathematics is wrapped']
+    
+    # Instantiate markdown extension and append it to the current extensions
     try:
     try:
-        settings = pelicanobj.settings['MATH']
+        pelicanobj.settings['MD_EXTENSIONS'].append(PelicanMathJaxExtension(config))
     except:
     except:
-        settings = None
+        print("\nError - the pelican mathjax markdown extension was not configured, so mathjax will not be work.\nThe error message was as follows - [%s]" % sys.exc_info()[0])
 
 
-    process_settings(settings)
+def mathjax_for_rst(pelicanobj, mathjax_settings):
+    pelicanobj.settings['DOCUTILS_SETTINGS'] = {'math_output': 'MathJax'}
+    rst_add_mathjax.mathjax_script = process_mathjax_script(mathjax_settings)
 
 
-    # Allows MathJax script to be accessed from template should it be needed
-    pelicanobj.settings['MATHJAXSCRIPT'] = _MATHJAX_SCRIPT.format(**_MATHJAX_SETTINGS)
+def pelican_init(pelicanobj):
+    """Loads the mathjax script according to the settings. Instantiate the Python
+    markdown extension, passing in the mathjax script as config parameter
+    """
 
 
-    # If Typogrify set to True, then we need to handle it manually so it does
-    # not conflict with LaTeX
-    try:
-        if pelicanobj.settings['TYPOGRIFY'] is True:
-            pelicanobj.settings['TYPOGRIFY'] = False
-            try:
-                from typogrify.filters import typogrify
-
-                # Determine if this is the correct version of Typogrify to use
-                import inspect
-                typogrify_args = inspect.getargspec(typogrify).args
-                if len(typogrify_args) < 2 or 'ignore_tags' not in typogrify_args:
-                    raise TypeError('Incorrect version of Typogrify')
-
-                # At this point, we are happy to use Typogrify, meaning
-                # it is installed and it is a recent enough version
-                # that can be used to ignore all math
-                _TYPOGRIFY = typogrify
-                _WRAP_LATEX = 'mathjax' # default to wrap mathjax content inside of
-            except ImportError:
-                print("\nTypogrify is not installed, so it is being ignored.\nIf you want to use it, please install via: pip install typogrify\n")
-            except TypeError:
-                print("\nA more recent version of Typogrify is needed for the render_math module.\nPlease upgrade Typogrify to the latest version (anything above version 2.04 is okay).\nTypogrify will be turned off due to this reason.\n")
-    except KeyError:
-        pass
-
-    # Set _WRAP_LATEX to the settings tag if defined. The idea behind this is
-    # to give template designers control over how math would be rendered
-    try:
-        if pelicanobj.settings['MATH']['wrap_latex']:
-            _WRAP_LATEX = pelicanobj.settings['MATH']['wrap_latex']
-    except (KeyError, TypeError):
-        pass
+    # Process settings
+    mathjax_settings = process_settings(pelicanobj)
 
 
-    # regular expressions that depend on _WRAP_LATEX are set here
-    tag_start= r'<%s>' % _WRAP_LATEX if not _WRAP_LATEX is None else ''
-    tag_end = r'</%s>' % _WRAP_LATEX if not _WRAP_LATEX is None else ''
-    math_summary_regex = r'((\$\$|\$|\\begin\{(.+?)\}|<(math)(?:\s.*?)?>).+?)(\2|\\end\{\3\}|</\4>|\s?\.\.\.)(%s|</\4>)?' % tag_end
+    # Configure Typogrify
+    configure_typogrify(pelicanobj, mathjax_settings)
 
 
-    # NOTE: The logic in _get_summary will handle <math> correctly because it
-    # is perceived as an html tag. Therefore we are only interested in handling
-    # non mml (i.e. LaTex)
-    incomplete_end_latex_tag = r'(.*)(%s)(\\\S*?|\$)\s*?(\s?\.\.\.)(%s)?$' % (tag_start, tag_end)
+    # Configure Mathjax For Markdown
+    mathjax_for_markdown(pelicanobj, mathjax_settings)
 
 
-    _MATH_SUMMARY_REGEX = re.compile(math_summary_regex, re.DOTALL | re.IGNORECASE)
-    _MATH_INCOMPLETE_TAG_REGEX = re.compile(incomplete_end_latex_tag, re.DOTALL | re.IGNORECASE)
+    # Configure Mathjax For RST
+    mathjax_for_rst(pelicanobj, mathjax_settings)
 
 
+def rst_add_mathjax(instance):
+    _, ext = os.path.splitext(os.path.basename(instance.source_path))
+    if ext != '.rst':
+        return
+
+    # If math class is present in text, add the javascript
+    if 'class="math"' in instance._content:
+        instance._content += "<script type='text/javascript'>%s</script>" % rst_add_mathjax.mathjax_script
 
 
 def register():
 def register():
     """Plugin registration"""
     """Plugin registration"""
-
     signals.initialized.connect(pelican_init)
     signals.initialized.connect(pelican_init)
-    signals.content_object_init.connect(process_content)
+    signals.content_object_init.connect(rst_add_mathjax)

+ 0 - 28
render_math/mathjax_script.txt

@@ -1,28 +0,0 @@
-<script type= "text/javascript">
-    if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {{
-        var mathjaxscript = document.createElement('script');
-        mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
-        mathjaxscript.type = 'text/javascript';
-        mathjaxscript.src = {source};
-        mathjaxscript[(window.opera ? "innerHTML" : "text")] =
-            "MathJax.Hub.Config({{" +
-            "    config: ['MMLorHTML.js']," +
-            "    TeX: {{ extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: {{ autoNumber: 'AMS' }} }}," +
-            "    jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
-            "    extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
-            "    displayAlign: '{align}'," +
-            "    displayIndent: '{indent}'," +
-            "    showMathMenu: {show_menu}," +
-            "    tex2jax: {{ " +
-            "        inlineMath: [ ['$','$'] ], " +
-            "        displayMath: [ ['$$','$$'] ]," +
-            "        processEscapes: {process_escapes}," +
-            "        preview: '{latex_preview}'," +
-            "    }}, " +
-            "    'HTML-CSS': {{ " +
-            "        styles: {{ '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {{color: '{color} ! important'}} }}" +
-            "    }} " +
-            "}}); ";
-        (document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
-    }}
-</script>

+ 26 - 0
render_math/mathjax_script_template

@@ -0,0 +1,26 @@
+if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {{
+    var mathjaxscript = document.createElement('script');
+    mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
+    mathjaxscript.type = 'text/javascript';
+    mathjaxscript.src = {source};
+    mathjaxscript[(window.opera ? "innerHTML" : "text")] =
+        "MathJax.Hub.Config({{" +
+        "    config: ['MMLorHTML.js']," +
+        "    TeX: {{ extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: {{ autoNumber: 'AMS' }} }}," +
+        "    jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
+        "    extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
+        "    displayAlign: '{align}'," +
+        "    displayIndent: '{indent}'," +
+        "    showMathMenu: {show_menu}," +
+        "    tex2jax: {{ " +
+        "        inlineMath: [ ['\\\\(','\\\\)'] ], " +
+        "        displayMath: [ ['$$','$$'] ]," +
+        "        processEscapes: {process_escapes}," +
+        "        preview: '{latex_preview}'," +
+        "    }}, " +
+        "    'HTML-CSS': {{ " +
+        "        styles: {{ '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {{color: '{color} ! important'}} }}" +
+        "    }} " +
+        "}}); ";
+    (document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
+}}

+ 79 - 0
render_math/pelican_mathjax_markdown_extension.py

@@ -0,0 +1,79 @@
+# -*- coding: utf-8 -*-
+"""
+Pelican Mathjax Markdown Extension
+==================================
+An extension for the Python Markdown module that enables
+the Pelican python blog to process mathjax. This extension
+gives Pelican the ability to use Mathjax as a "first class
+citizen" of the blog
+"""
+
+import markdown
+
+from markdown.util import etree
+from markdown.util import AtomicString
+
+class PelicanMathJaxPattern(markdown.inlinepatterns.Pattern):
+    """Pattern for matching mathjax"""
+
+    def __init__(self, pelican_mathjax_extension, tag, pattern):
+        super(PelicanMathJaxPattern,self).__init__(pattern)
+        self.math_tag_class = pelican_mathjax_extension.getConfig('math_tag_class')
+        self.pelican_mathjax_extension = pelican_mathjax_extension
+        self.tag = tag
+
+    def handleMatch(self, m):
+        node = markdown.util.etree.Element(self.tag)
+        node.set('class', self.math_tag_class)
+
+        prefix = '\\(' if m.group('prefix') == '$' else m.group('prefix')
+        suffix = '\\)' if m.group('suffix') == '$' else m.group('suffix')
+        node.text = markdown.util.AtomicString(prefix + m.group('math') + suffix)
+
+        # If mathjax was successfully matched, then JavaScript needs to be added
+        # for rendering. The boolean below indicates this
+        self.pelican_mathjax_extension.mathjax_needed = True
+        return node
+
+class PelicanMathJaxTreeProcessor(markdown.treeprocessors.Treeprocessor):
+    """Tree Processor for adding Mathjax JavaScript to the blog"""
+
+    def __init__(self, pelican_mathjax_extension):
+        self.pelican_mathjax_extension = pelican_mathjax_extension
+
+    def run(self, root):
+
+        # If no mathjax was present, then exit
+        if (not self.pelican_mathjax_extension.mathjax_needed):
+            return
+
+        # Add the mathjax script to the html document
+        mathjax_script = etree.Element('script')
+        mathjax_script.set('type','text/javascript')
+        mathjax_script.text = AtomicString(self.pelican_mathjax_extension.getConfig('mathjax_script'))
+        root.append(mathjax_script)
+
+        # Reset the boolean switch to false so that script is only added
+        # to other pages if needed
+        self.pelican_mathjax_extension.mathjax_needed = False
+
+class PelicanMathJaxExtension(markdown.Extension):
+    """A markdown extension enabling mathjax processing in Markdown for Pelican"""
+    def __init__(self, config):
+        super(PelicanMathJaxExtension,self).__init__(config)
+
+        # Regex to detect mathjax
+        self.mathjax_inline_regex = r'(?P<prefix>\$)(?P<math>.+?)(?P<suffix>(?<!\s)\2)'
+        self.mathjax_display_regex = r'(?P<prefix>(?:\$\$)|\\begin\{(.+?)\})(?P<math>.+?)(?P<suffix>\2|\\end\{\3\})'
+        self.mathjax_needed = False
+
+    def extendMarkdown(self, md, md_globals):
+        # Process mathjax before escapes are processed since escape processing will
+        # intefer with mathjax. The order in which the displayed and inlined math
+        # is registered below matters
+        md.inlinePatterns.add('mathjax_displayed', PelicanMathJaxPattern(self, 'div', self.mathjax_display_regex), '<escape')
+        md.inlinePatterns.add('mathjax_inlined', PelicanMathJaxPattern(self, 'span', self.mathjax_inline_regex), '<escape')
+
+        # If necessary, add the JavaScript Mathjax library to the document. This must
+        # be last in the ordered dict (hence it is given the position '_end')
+        md.treeprocessors.add('mathjax', PelicanMathJaxTreeProcessor(self), '_end')