Sync to trunk
11
COPYRIGHT
@ -41,6 +41,12 @@ License: Apache 2.0
|
||||
The full text of the Apache 2.0 license is available at:
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Files: resources/viewer/mathjax/*
|
||||
Copyright: Unknown
|
||||
License: Apache 2.0
|
||||
The full text of the Apache 2.0 license is available at:
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Files: /src/cherrypy/*
|
||||
Copyright: Copyright (c) 2004-2007, CherryPy Team (team@cherrypy.org)
|
||||
Copyright: Copyright (C) 2005, Tiago Cogumbreiro <cogumbreiro@users.sf.net>
|
||||
@ -396,8 +402,9 @@ License: other
|
||||
|
||||
Liberation Fonts
|
||||
-----------------
|
||||
calibre includes a copy of the liberation fonts, available from
|
||||
https://calibre-ebook.com/downloads/liberation-fonts
|
||||
calibre includes a copy of the liberation fonts in TTF format, licensed under
|
||||
the SIL Open Font License, Version 1.1, and available from
|
||||
https://fedorahosted.org/liberation-fonts/
|
||||
|
||||
BSD License (for all the BSD licensed code indicated above)
|
||||
-----------------------------------------------------------
|
||||
|
243
Changelog.yaml
@ -19,6 +19,249 @@
|
||||
# new recipes:
|
||||
# - title:
|
||||
|
||||
- version: 0.8.65
|
||||
date: 2012-08-17
|
||||
|
||||
new features:
|
||||
- title: "A new wireless device driver. This allows connecting wirelessly to a device running a 'smart' calibre client"
|
||||
description: "The wireless connection functions just as if the device was plugged into the computer by USB cable. Currently, Android devices are supported. See https://play.google.com/store/apps/details?id=com.multipie.calibreandroid"
|
||||
type: major
|
||||
|
||||
- title: "MOBI Output: Add an option to control the type of MOBI file produced, to the MOBI Output conversion options. You can now generate an old MOBI6, a new KF8 or a joint MOBI6/KF8 file. By default, MOBI6 files are generated. This replaces the previous use of a tweak."
|
||||
|
||||
- title: "E-book viewer: Make paged mode the default. You can go back to the old flow mode by clicking the button with the yellow scroll in the top right corner of the viewer."
|
||||
|
||||
- title: "Driver for COBY kYROS MID7042 and Samsung Galaxy Ace S5839i"
|
||||
|
||||
bug fixes:
|
||||
- title: "Update version of poppler bundled with calibre to fix reading covers from some PDF files"
|
||||
|
||||
- title: "Get Books: Fix clicking of results from Diesel books when there is only a single result not working"
|
||||
|
||||
- title: "Improve detection of system language on first run of calibre"
|
||||
tickets: [1036354]
|
||||
|
||||
- title: "When finding the next series index and the last series index is a fractional number, use the next largest integer, instead of just adding 1"
|
||||
|
||||
- title: "Fix exception when saving a search/replace when no saved search/replace had been opened previously in the bulk search/replace dialog"
|
||||
tickets: [1036464]
|
||||
|
||||
- title: "Fix restore database not restoring entries for the original_* formats"
|
||||
|
||||
- title: "Fix first run wizard not allowing empty email sending settings"
|
||||
tickets: [1036358]
|
||||
|
||||
- title: "Do not error out when setting the cover for a book that has no folders in the library."
|
||||
tickets: [1035935]
|
||||
|
||||
- title: "Conversion pipeline: Ignore unparseable values in the color attribute of font tags, instead of erroring out on them."
|
||||
tickets: [1035633]
|
||||
|
||||
- title: "Catalogs: Fix regression that broke creation of catalogs while a device is connected"
|
||||
|
||||
- title: "Fix --with-library=/whatever not working for calibredb list"
|
||||
|
||||
improved recipes:
|
||||
- Slashdot
|
||||
- Various Canadian newspapers
|
||||
- Business Spectator
|
||||
|
||||
- version: 0.8.64
|
||||
date: 2012-08-09
|
||||
|
||||
new features:
|
||||
- title: "E-book viewer: Allow viewing images in the book in a separate pop-up window by right clicking on the image. Useful if you want to keep some image, like a map to the side while reading the book."
|
||||
|
||||
- title: "Catalogs: Allow generation of catalogs in AZW3 format. Also add more powerful configuration options to exclude books and set prefixes. See http://www.mobileread.com/forums/showthread.php?t=187298 for details."
|
||||
|
||||
- title: "Generate a PDF version of the User Manual"
|
||||
|
||||
bug fixes:
|
||||
- title: "News download: Fix broken handling of nesting for HTML 5 tags when parsing with BeautifulSoup"
|
||||
|
||||
- title: "EPUB: Handle files in the EPUB that have semi-colons in their file names. This means in particular using URL escaping when creating the NCX as ADE cannot handle unescaped semi-colons in the NCX."
|
||||
tickets: [1033665]
|
||||
|
||||
- title: "Conversion pipeline: Ignore unparseable CSS instead of erroring out on it."
|
||||
tickets: [1034074]
|
||||
|
||||
- title: "When setting up a column coloring rule based on the languages column, allow entry of localized language names instead of only ISO codes"
|
||||
|
||||
- title: "Catalogs: Generate cover for mobi/azw3 catalogs"
|
||||
|
||||
- title: "Update the last modified column record of a book, whenever a format is added to the book."
|
||||
|
||||
- title: "E-book viewer: Fix line scrolling stops at breaks option not working in paged mode"
|
||||
tickets: [1033430]
|
||||
|
||||
- title: "MOBI Output: Fix ToC at start option having no effect when converting some input documents that have an out-of-spine ToC."
|
||||
tickets: [1033656]
|
||||
|
||||
- title: "Catalog Generation: When generating EPUB/MOBI catalogs add more flexible rules for excluding books. Also add rules to customize the prefix characters used."
|
||||
|
||||
- title: "Make setting published date using metadata search/replace more robust."
|
||||
|
||||
- title: "Tag Browser: Flatten the display of sub-groups when sort by is not set to 'name'."
|
||||
tickets: [1032746]
|
||||
|
||||
- title: "Fix isbn:false not matching if other identifiers are attached to the book."
|
||||
|
||||
improved recipes:
|
||||
- The New Republic
|
||||
- ZDNet
|
||||
- Metro UK
|
||||
- FHM UK
|
||||
|
||||
new recipes:
|
||||
- title: eKundelek.pl
|
||||
author: Artur Stachecki
|
||||
|
||||
- title: Sueddeutsche Mobil
|
||||
author: Andreas Zeiser
|
||||
|
||||
- version: 0.8.63
|
||||
date: 2012-08-02
|
||||
|
||||
new features:
|
||||
- title: "E-book viewer: Allow quick saving and loading of viewer settings as 'themes'."
|
||||
tickets: [1024611]
|
||||
|
||||
- title: "Ebook-viewer: Add a restore defaults button to the viewer preferences dialog"
|
||||
|
||||
- title: "E-book viewer: Add simple settings for text and background colors"
|
||||
|
||||
- title: "Add an entry to save to disk when right clicking a format in the book details panel"
|
||||
|
||||
- title: "ODT metadata: Read first image as the metadata cover from ODT files. Also allow ODT authors to set custom properties for extended metadata."
|
||||
|
||||
- title: "E-book viewer and PDF Output: Resize images that are longer than the page to fit onto a single page"
|
||||
|
||||
bug fixes:
|
||||
- title: "KF8 Output: Fix bug where some calibre generated KF8 files would cause the Amazon KF8 viewer on the Touch to go to into an infinite loop when using the next page function"
|
||||
tickets: [1026421]
|
||||
|
||||
- title: "News download: Add support for <img> tags that link to SVG images."
|
||||
tickets: [1031553]
|
||||
|
||||
- title: "Update podofo to 0.9.1 in all binary builds, to fix corruption of some PDFs when updating metadata."
|
||||
tickets: [1031086]
|
||||
|
||||
- title: "Catalog generation: Handle authors whose last name is a number."
|
||||
|
||||
- title: "KF8 Input: Handle html entities in the NCX toc entries correctly"
|
||||
|
||||
- title: "Fix a calibre crash that affected some windows installs"
|
||||
tickets: [1030234]
|
||||
|
||||
- title: "MOBI Output: Normalize unicode strings before writing to file, to workaround lack of support for non-normal unicode in Amazon's MOBI renderer."
|
||||
tickets: [1029825]
|
||||
|
||||
- title: "EPUB Input: Handle files that have duplicate entries in the spine"
|
||||
|
||||
- title: "Fix regression in Kobo driver that caused the on device column to not be updated after deleting books"
|
||||
|
||||
new recipes:
|
||||
- title: Dziennik Polski
|
||||
author: Gregorz Maj
|
||||
|
||||
- title: High Country Blogs
|
||||
author: Armin Geller
|
||||
|
||||
- title: Philosophy Now
|
||||
author: Rick Shang
|
||||
|
||||
- version: 0.8.62
|
||||
date: 2012-07-27
|
||||
|
||||
new features:
|
||||
- title: "Book details panel: Allow right clicking on a format to delete it."
|
||||
|
||||
- title: "When errors occur in lots of background jobs, add an option to the error message to temporarily suppress subsequent error messages."
|
||||
tickets: [886904]
|
||||
|
||||
- title: "E-book viewer full screen mode: Allow clicking in the left and right page margins to turn pages."
|
||||
tickets: [1024819]
|
||||
|
||||
- title: "Drivers for various Android devices"
|
||||
tickets: [1028690,1027431]
|
||||
|
||||
- title: "Advanced search dialog: When starting on the title/author/etc. tab, restore the previously used search kind as well."
|
||||
tickets: [1029745]
|
||||
|
||||
- title: "When presenting the calibre must be restarted warning after installing a new plugin, add a restart now button so that the user can conveniently restart calibre. Currently only works when going vie Preferences->Plugins->Get new plugins"
|
||||
|
||||
bug fixes:
|
||||
- title: "Fix main window layout state being saved incorrectly if calibre is killed without a proper shutdown"
|
||||
|
||||
- title: "Fix boolean and date searching in non english calibre installs."
|
||||
|
||||
- title: "Conversion: Ignore invalid chapter detection and level n ToC expressions instead of erroring out"
|
||||
|
||||
improved recipes:
|
||||
- Psychology Today
|
||||
- The Smithsonian
|
||||
- The New Republic
|
||||
- Various updated Polish news sources
|
||||
- The Sun
|
||||
- San Francisco Bay Guardian
|
||||
- AnandTech
|
||||
- Smashing Magazine
|
||||
|
||||
new recipes:
|
||||
- title: Linux Journal and Conowego.pl
|
||||
author: fenuks
|
||||
|
||||
- title: A list apart and .net magazine
|
||||
author: Marc Busque
|
||||
|
||||
- version: 0.8.61
|
||||
date: 2012-07-20
|
||||
|
||||
new features:
|
||||
- title: "E-book viewer: Add a paged mode that splits up the text into pages, like in a paper book instead of presenting it as a single column. To activate click the button with the yellow scroll icon in the top right corner."
|
||||
type: major
|
||||
description: "In paged mode, the ebook viewer no longer cuts off the last line of text at the bottom of the screen, and it respects CSS page-break directives. You can also set page margins and control the number of pages displayed on screen by clicking the Preferences button in the viewer and going to 'Text layout in paged mode'."
|
||||
|
||||
- title: "Digitally sign the calibre OS X and windows builds"
|
||||
|
||||
- title: "Get Books: Add Mills and Boon UK"
|
||||
|
||||
- title: "Various minor improvements to the Bulk metadata edit dialog"
|
||||
tickets: [1025825, 1025838, 1025628]
|
||||
|
||||
- title: "Fix various regression in the auto-complete functionality for authors/series/tags etc introduced in 0.8.60"
|
||||
|
||||
- title: "Drivers for various new Android devices"
|
||||
tickets: [1024934]
|
||||
|
||||
- title: "MOBI: Add support for the new language EXTH header field in MOBI files generated by kindlegen 2.5"
|
||||
|
||||
bug fixes:
|
||||
- title: "KF8 Output: Fix calibre produced KF8 files not showing the 'Use publisher font' option on the Kindle Touch when they have embedded fonts"
|
||||
|
||||
- title: "Txt/fb2/rtf/pml/rb output: Fix non-visibile element's tail text (which should be visible) is being ignored when it shouldn't."
|
||||
tickets: [1026541]
|
||||
|
||||
- title: "Book details panel: When displaying a link to amazon, use a country specific name like amazon.fr instead of using amazon.com for all countries"
|
||||
|
||||
- title: "Conversion: When splitting on page breaks, ignore page-breaks with values of auto and inherit. "
|
||||
tickets: [1018875]
|
||||
|
||||
- title: "Metadata jacket: Specify foreground in addition to the background color for the title banner so that it remain readable if the user tries to monkey with the CSS in the viewer."
|
||||
|
||||
- title: "PDF Output: Fix rendering of cover as first age of PDF (ignore margins so that the image covers the entire page)"
|
||||
|
||||
- title: "Linux binaries: Bundle libglib to avoid incompatibilities with glib on various distros."
|
||||
tickets: [1022019]
|
||||
|
||||
- title: "Fix find_identical_books() choking on books with too many authors"
|
||||
|
||||
|
||||
improved recipes:
|
||||
- Toronto Star
|
||||
- American Prospect
|
||||
- faz.net
|
||||
|
||||
- version: 0.8.60
|
||||
date: 2012-07-13
|
||||
|
||||
|
@ -60,7 +60,7 @@ htmlhelp:
|
||||
|
||||
latex:
|
||||
mkdir -p .build/latex .build/doctrees
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) .build/latex
|
||||
$(SPHINXBUILD) -b mylatex $(ALLSPHINXOPTS) .build/latex
|
||||
@echo
|
||||
@echo "Build finished; the LaTeX files are in .build/latex."
|
||||
@echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
|
||||
|
@ -14,10 +14,10 @@
|
||||
import sys, os
|
||||
|
||||
# If your extensions are in another directory, add it here.
|
||||
sys.path.append(os.path.abspath('../src'))
|
||||
sys.path.append(os.path.abspath('.'))
|
||||
__appname__ = os.environ.get('__appname__', 'calibre')
|
||||
__version__ = os.environ.get('__version__', '0.0.0')
|
||||
import init_calibre
|
||||
init_calibre
|
||||
from calibre.constants import __appname__, __version__
|
||||
import custom
|
||||
custom
|
||||
# General configuration
|
||||
@ -154,7 +154,8 @@ latex_font_size = '10pt'
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, document class [howto/manual]).
|
||||
#latex_documents = []
|
||||
latex_documents = [('index', 'calibre.tex', 'calibre User Manual',
|
||||
'Kovid Goyal', 'manual', False)]
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#latex_preamble = ''
|
||||
@ -164,3 +165,11 @@ latex_font_size = '10pt'
|
||||
|
||||
# If false, no module index is generated.
|
||||
#latex_use_modindex = True
|
||||
|
||||
latex_logo = 'resources/logo.png'
|
||||
latex_show_pagerefs = True
|
||||
latex_show_urls = 'footnote'
|
||||
latex_elements = {
|
||||
'papersize':'letterpaper',
|
||||
'fontenc':r'\usepackage[T2A,T1]{fontenc}'
|
||||
}
|
||||
|
@ -710,3 +710,35 @@ EPUB from the ZIP file are::
|
||||
|
||||
Note that because this file explores the potential of EPUB, most of the advanced formatting is not going to work on readers less capable than |app|'s built-in EPUB viewer.
|
||||
|
||||
|
||||
Convert ODT documents
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|app| can directly convert ODT (OpenDocument Text) files. You should use styles to format your document and minimize the use of direct formatting.
|
||||
When inserting images into your document you need to anchor them to the paragraph, images anchored to a page will all end up in the front of the conversion.
|
||||
|
||||
To enable automatic detection of chapters, you need to mark them with the build-in styles called 'Heading 1', 'Heading 2', ..., 'Heading 6' ('Heading 1' equates to the HTML tag <h1>, 'Heading 2' to <h2> etc). When you convert in |app| you can enter which style you used into the 'Detect chapters at' box. Example:
|
||||
|
||||
* If you mark Chapters with style 'Heading 2', you have to set the 'Detect chapters at' box to ``//h:h2``
|
||||
* For a nested TOC with Sections marked with 'Heading 2' and the Chapters marked with 'Heading 3' you need to enter ``//h:h2|//h:h3``. On the Convert - TOC page set the 'Level 1 TOC' box to ``//h:h2`` and the 'Level 2 TOC' box to ``//h:h3``.
|
||||
|
||||
Well-known document properties (Title, Keywords, Description, Creator) are recognized and |app| will use the first image (not to small, and with good aspect-ratio) as the cover image.
|
||||
|
||||
There is also an advanced property conversion mode, which is activated by setting the custom property ``opf.metadata`` ('Yes or No' type) to Yes in your ODT document (File->Properties->Custom Properties).
|
||||
If this property is detected by |app|, the following custom properties are recognized (``opf.authors`` overrides document creator)::
|
||||
|
||||
opf.titlesort
|
||||
opf.authors
|
||||
opf.authorsort
|
||||
opf.publisher
|
||||
opf.pubdate
|
||||
opf.isbn
|
||||
opf.language
|
||||
opf.series
|
||||
opf.seriesindex
|
||||
|
||||
In addition to this, you can specify the picture to use as the cover by naming it ``opf.cover`` (right click, Picture->Options->Name) in the ODT. If no picture with this name is found, the 'smart' method is used.
|
||||
As the cover detection might result in double covers in certain output formats, the process will remove the paragraph (only if the only content is the cover!) from the document. But this works only with the named picture!
|
||||
|
||||
To disable cover detection you can set the custom property ``opf.nocover`` ('Yes or No' type) to Yes in advanced mode.
|
||||
|
||||
|
@ -14,6 +14,7 @@ from sphinx.util.console import bold
|
||||
sys.path.append(os.path.abspath('../../../'))
|
||||
from calibre.linux import entry_points
|
||||
from epub import EPUBHelpBuilder
|
||||
from latex import LaTeXHelpBuilder
|
||||
|
||||
def substitute(app, doctree):
|
||||
pass
|
||||
@ -251,6 +252,7 @@ def template_docs(app):
|
||||
def setup(app):
|
||||
app.add_config_value('kovid_epub_cover', None, False)
|
||||
app.add_builder(EPUBHelpBuilder)
|
||||
app.add_builder(LaTeXHelpBuilder)
|
||||
app.connect('doctree-read', substitute)
|
||||
app.connect('builder-inited', generate_docs)
|
||||
app.connect('build-finished', finished)
|
||||
|
61
manual/develop.rst
Executable file → Normal file
@ -6,9 +6,9 @@ Setting up a |app| development environment
|
||||
===========================================
|
||||
|
||||
|app| is completely open source, licensed under the `GNU GPL v3 <http://www.gnu.org/copyleft/gpl.html>`_.
|
||||
This means that you are free to download and modify the program to your heart's content. In this section,
|
||||
you will learn how to get a |app| development environment set up on the operating system of your choice.
|
||||
|app| is written primarily in `Python <http://www.python.org>`_ with some C/C++ code for speed and system interfacing.
|
||||
This means that you are free to download and modify the program to your heart's content. In this section,
|
||||
you will learn how to get a |app| development environment set up on the operating system of your choice.
|
||||
|app| is written primarily in `Python <http://www.python.org>`_ with some C/C++ code for speed and system interfacing.
|
||||
Note that |app| is not compatible with Python 3 and requires at least Python 2.7.
|
||||
|
||||
.. contents:: Contents
|
||||
@ -20,14 +20,14 @@ Design philosophy
|
||||
|
||||
|app| has its roots in the Unix world, which means that its design is highly modular.
|
||||
The modules interact with each other via well defined interfaces. This makes adding new features and fixing
|
||||
bugs in |app| very easy, resulting in a frenetic pace of development. Because of its roots, |app| has a
|
||||
bugs in |app| very easy, resulting in a frenetic pace of development. Because of its roots, |app| has a
|
||||
comprehensive command line interface for all its functions, documented in :ref:`cli`.
|
||||
|
||||
The modular design of |app| is expressed via ``Plugins``. There is a :ref:`tutorial <customize>` on writing |app| plugins.
|
||||
For example, adding support for a new device to |app| typically involves writing less than a 100 lines of code in the form of
|
||||
a device driver plugin. You can browse the
|
||||
`built-in drivers <http://bazaar.launchpad.net/%7Ekovid/calibre/trunk/files/head%3A/src/calibre/devices/>`_. Similarly, adding support
|
||||
for new conversion formats involves writing input/output format plugins. Another example of the modular design is the :ref:`recipe system <news>` for
|
||||
a device driver plugin. You can browse the
|
||||
`built-in drivers <http://bazaar.launchpad.net/%7Ekovid/calibre/trunk/files/head%3A/src/calibre/devices/>`_. Similarly, adding support
|
||||
for new conversion formats involves writing input/output format plugins. Another example of the modular design is the :ref:`recipe system <news>` for
|
||||
fetching news. For more examples of plugins designed to add features to |app|, see the `plugin index <http://www.mobileread.com/forums/showthread.php?p=1362767#post1362767>`_.
|
||||
|
||||
Code layout
|
||||
@ -91,15 +91,15 @@ this, make your changes, then run::
|
||||
This will create a :file:`my-changes` file in the current directory,
|
||||
simply attach that to a ticket on the |app| `bug tracker <https://bugs.launchpad.net/calibre>`_.
|
||||
|
||||
If you plan to do a lot of development on |app|, then the best method is to create a
|
||||
If you plan to do a lot of development on |app|, then the best method is to create a
|
||||
`Launchpad <http://launchpad.net>`_ account. Once you have an account, you can use it to register
|
||||
your bzr branch created by the `bzr branch` command above. First run the
|
||||
following command to tell bzr about your launchpad account::
|
||||
|
||||
bzr launchpad-login your_launchpad_username
|
||||
|
||||
Now, you have to setup SSH access to Launchpad. First create an SSH public/private keypair. Then upload
|
||||
the public key to Launchpad by going to your Launchpad account page. Instructions for setting up the
|
||||
Now, you have to setup SSH access to Launchpad. First create an SSH public/private keypair. Then upload
|
||||
the public key to Launchpad by going to your Launchpad account page. Instructions for setting up the
|
||||
private key in bzr are at http://bazaar-vcs.org/Bzr_and_SSH. Now you can upload your branch to the |app|
|
||||
project in Launchpad by following the instructions at https://help.launchpad.net/Code/UploadingABranch.
|
||||
Whenever you commit changes to your branch with the command::
|
||||
@ -108,7 +108,7 @@ Whenever you commit changes to your branch with the command::
|
||||
|
||||
Kovid can merge it directly from your branch into the main |app| source tree. You should also keep an eye on the |app|
|
||||
`development forum <http://www.mobileread.com/forums/forumdisplay.php?f=240>`. Before making major changes, you should
|
||||
discuss them in the forum or contact Kovid directly (his email address is all over the source code).
|
||||
discuss them in the forum or contact Kovid directly (his email address is all over the source code).
|
||||
|
||||
Windows development environment
|
||||
---------------------------------
|
||||
@ -118,12 +118,12 @@ the previously checked out |app| code directory. For example::
|
||||
|
||||
cd C:\Users\kovid\work\calibre
|
||||
|
||||
calibre is the directory that contains the src and resources sub-directories.
|
||||
calibre is the directory that contains the src and resources sub-directories.
|
||||
|
||||
The next step is to set the environment variable ``CALIBRE_DEVELOP_FROM`` to the absolute path of the src directory.
|
||||
So, following the example above, it would be ``C:\Users\kovid\work\calibre\src``. `Here is a short
|
||||
guide <http://docs.python.org/using/windows.html#excursus-setting-environment-variables>`_ to setting environment
|
||||
variables on Windows.
|
||||
variables on Windows.
|
||||
|
||||
Once you have set the environment variable, open a new command prompt and check that it was correctly set by using
|
||||
the command::
|
||||
@ -134,7 +134,7 @@ Setting this environment variable means that |app| will now load all its Python
|
||||
|
||||
That's it! You are now ready to start hacking on the |app| code. For example, open the file :file:`src\\calibre\\__init__.py`
|
||||
in your favorite editor and add the line::
|
||||
|
||||
|
||||
print ("Hello, world!")
|
||||
|
||||
near the top of the file. Now run the command :command:`calibredb`. The very first line of output should be ``Hello, world!``.
|
||||
@ -149,24 +149,25 @@ the previously checked out |app| code directory, for example::
|
||||
|
||||
calibre is the directory that contains the src and resources sub-directories. Ensure you have installed the |app| commandline tools via :guilabel:`Preferences->Advanced->Miscellaneous` in the |app| GUI.
|
||||
|
||||
The next step is to set the environment variable ``CALIBRE_DEVELOP_FROM`` to the absolute path of the src directory.
|
||||
So, following the example above, it would be ``/Users/kovid/work/calibre/src``. Apple
|
||||
`documentation <http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPRuntimeConfig/Articles/EnvironmentVars.html#//apple_ref/doc/uid/20002093-BCIJIJBH>`_
|
||||
on how to set environment variables.
|
||||
The next step is to create a bash script that will set the environment variable ``CALIBRE_DEVELOP_FROM`` to the absolute path of the src directory when running calibre in debug mode.
|
||||
|
||||
Once you have set the environment variable, open a new Terminal and check that it was correctly set by using
|
||||
the command::
|
||||
Create a plain text file::
|
||||
|
||||
echo $CALIBRE_DEVELOP_FROM
|
||||
#!/bin/sh
|
||||
export CALIBRE_DEVELOP_FROM="/Users/kovid/work/calibre/src"
|
||||
calibre-debug -g
|
||||
|
||||
Setting this environment variable means that |app| will now load all its Python code from the specified location.
|
||||
Save this file as ``/usr/bin/calibre-develop``, then set its permissions so that it can be executed::
|
||||
|
||||
That's it! You are now ready to start hacking on the |app| code. For example, open the file :file:`src/calibre/__init__.py`
|
||||
in your favorite editor and add the line::
|
||||
|
||||
print ("Hello, world!")
|
||||
chmod +x /usr/bin/calibre-develop
|
||||
|
||||
near the top of the file. Now run the command :command:`calibredb`. The very first line of output should be ``Hello, world!``.
|
||||
Once you have done this, run::
|
||||
|
||||
calibre-develop
|
||||
|
||||
You should see some diagnostic information in the Terminal window as calibre
|
||||
starts up, and you should see an asterisk after the version number in the GUI
|
||||
window, indicating that you are running from source.
|
||||
|
||||
Linux development environment
|
||||
------------------------------
|
||||
@ -181,11 +182,11 @@ Install the |app| using the binary installer. Then open a terminal and change to
|
||||
|
||||
cd /home/kovid/work/calibre
|
||||
|
||||
calibre is the directory that contains the src and resources sub-directories.
|
||||
calibre is the directory that contains the src and resources sub-directories.
|
||||
|
||||
The next step is to set the environment variable ``CALIBRE_DEVELOP_FROM`` to the absolute path of the src directory.
|
||||
So, following the example above, it would be ``/home/kovid/work/calibre/src``. How to set environment variables depends on
|
||||
your Linux distribution and what shell you are using.
|
||||
your Linux distribution and what shell you are using.
|
||||
|
||||
Once you have set the environment variable, open a new terminal and check that it was correctly set by using
|
||||
the command::
|
||||
@ -196,7 +197,7 @@ Setting this environment variable means that |app| will now load all its Python
|
||||
|
||||
That's it! You are now ready to start hacking on the |app| code. For example, open the file :file:`src/calibre/__init__.py`
|
||||
in your favorite editor and add the line::
|
||||
|
||||
|
||||
print ("Hello, world!")
|
||||
|
||||
near the top of the file. Now run the command :command:`calibredb`. The very first line of output should be ``Hello, world!``.
|
||||
|
@ -548,7 +548,7 @@ Calibre has several keyboard shortcuts to save you time and mouse movement. Thes
|
||||
- Toggle jobs list
|
||||
* - :kbd:`Alt+Shift+B`
|
||||
- Toggle Cover Browser
|
||||
* - :kbd:`Alt+Shift+B`
|
||||
* - :kbd:`Alt+Shift+D`
|
||||
- Toggle Book Details panel
|
||||
* - :kbd:`Alt+Shift+T`
|
||||
- Toggle Tag Browser
|
||||
|
BIN
manual/images/lorentz.png
Normal file
After Width: | Height: | Size: 7.1 KiB |
@ -17,7 +17,7 @@ To get started with more advanced usage, you should read about the :ref:`Graphic
|
||||
|
||||
.. only:: online
|
||||
|
||||
**An ebook version of this user manual is available in** `EPUB format <calibre.epub>`_ and `AZW3 (Kindle Fire) format <calibre.azw3>`_.
|
||||
**An ebook version of this user manual is available in** `EPUB format <calibre.epub>`_, `AZW3 (Kindle Fire) format <calibre.azw3>`_ and `PDF format <calibre.pdf>`_.
|
||||
|
||||
Sections
|
||||
------------
|
||||
|
25
manual/latex.py
Normal file
@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from __future__ import with_statement
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import os
|
||||
|
||||
|
||||
from sphinx.builders.latex import LaTeXBuilder
|
||||
|
||||
class LaTeXHelpBuilder(LaTeXBuilder):
|
||||
name = 'mylatex'
|
||||
|
||||
def finish(self):
|
||||
LaTeXBuilder.finish(self)
|
||||
self.info('Fixing Cyrillic characters...')
|
||||
tex = os.path.join(self.outdir, 'calibre.tex')
|
||||
with open(tex, 'r+b') as f:
|
||||
raw = f.read().replace(b'Михаил Горбачёв',
|
||||
br'{\fontencoding{T2A}\selectfont Михаил Горбачёв}')
|
||||
f.seek(0)
|
||||
f.write(raw)
|
102
manual/mathjax.html
Normal file
@ -0,0 +1,102 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!-- Copyright (c) 2012 Design Science, Inc. -->
|
||||
<head>
|
||||
<title>Math Test Page</title>
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
|
||||
|
||||
<!-- This script tag is needed to make calibre's ebook-viewer recpgnize that this file needs math typesetting -->
|
||||
<script type="text/x-mathjax-config">
|
||||
// This line adds numbers to all equations automatically, unless explicitly suppressed.
|
||||
MathJax.Hub.Config({ TeX: { equationNumbers: {autoNumber: "all"} } });
|
||||
</script>
|
||||
|
||||
<style>
|
||||
h1 {text-align:center}
|
||||
h2 {
|
||||
font-weight: bold;
|
||||
background-color: #DDDDDD;
|
||||
padding: .2em .5em;
|
||||
margin-top: 1.5em;
|
||||
border-top: 3px solid #666666;
|
||||
border-bottom: 2px solid #999999;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>Sample Equations</h1>
|
||||
|
||||
<h2>The Lorenz Equations</h2>
|
||||
|
||||
<p>
|
||||
\begin{align}
|
||||
\dot{x} & = \sigma(y-x) \label{lorenz}\\
|
||||
\dot{y} & = \rho x - y - xz \\
|
||||
\dot{z} & = -\beta z + xy
|
||||
\end{align}
|
||||
</p>
|
||||
|
||||
<h2>The Cauchy-Schwarz Inequality</h2>
|
||||
|
||||
<p>\[
|
||||
\left( \sum_{k=1}^n a_k b_k \right)^{\!\!2} \leq
|
||||
\left( \sum_{k=1}^n a_k^2 \right) \left( \sum_{k=1}^n b_k^2 \right)
|
||||
\]</p>
|
||||
|
||||
<h2>A Cross Product Formula</h2>
|
||||
|
||||
<p>\[
|
||||
\mathbf{V}_1 \times \mathbf{V}_2 =
|
||||
\begin{vmatrix}
|
||||
\mathbf{i} & \mathbf{j} & \mathbf{k} \\
|
||||
\frac{\partial X}{\partial u} & \frac{\partial Y}{\partial u} & 0 \\
|
||||
\frac{\partial X}{\partial v} & \frac{\partial Y}{\partial v} & 0 \\
|
||||
\end{vmatrix}
|
||||
\]</p>
|
||||
|
||||
<h2>The probability of getting \(k\) heads when flipping \(n\) coins is:</h2>
|
||||
|
||||
<p>\[P(E) = {n \choose k} p^k (1-p)^{ n-k} \]</p>
|
||||
|
||||
<h2>An Identity of Ramanujan</h2>
|
||||
|
||||
<p>\[
|
||||
\frac{1}{(\sqrt{\phi \sqrt{5}}-\phi) e^{\frac25 \pi}} =
|
||||
1+\frac{e^{-2\pi}} {1+\frac{e^{-4\pi}} {1+\frac{e^{-6\pi}}
|
||||
{1+\frac{e^{-8\pi}} {1+\ldots} } } }
|
||||
\]</p>
|
||||
|
||||
<h2>A Rogers-Ramanujan Identity</h2>
|
||||
|
||||
<p>\[
|
||||
1 + \frac{q^2}{(1-q)}+\frac{q^6}{(1-q)(1-q^2)}+\cdots =
|
||||
\prod_{j=0}^{\infty}\frac{1}{(1-q^{5j+2})(1-q^{5j+3})},
|
||||
\quad\quad \text{for $|q|<1$}.
|
||||
\]</p>
|
||||
|
||||
<h2>Maxwell's Equations</h2>
|
||||
|
||||
<p>
|
||||
\begin{align}
|
||||
\nabla \times \vec{\mathbf{B}} -\, \frac1c\, \frac{\partial\vec{\mathbf{E}}}{\partial t} & = \frac{4\pi}{c}\vec{\mathbf{j}} \\
|
||||
\nabla \cdot \vec{\mathbf{E}} & = 4 \pi \rho \\
|
||||
\nabla \times \vec{\mathbf{E}}\, +\, \frac1c\, \frac{\partial\vec{\mathbf{B}}}{\partial t} & = \vec{\mathbf{0}} \\
|
||||
\nabla \cdot \vec{\mathbf{B}} & = 0
|
||||
\end{align}
|
||||
</p>
|
||||
|
||||
<h2>In-line Mathematics</h2>
|
||||
|
||||
<p>While display equations look good for a page of samples, the
|
||||
ability to mix math and text in a paragraph is also important. This
|
||||
expression \(\sqrt{3x-1}+(1+x)^2\) is an example of an inline equation. As
|
||||
you see, equations can be used this way as well, without unduly
|
||||
disturbing the spacing between lines.</p>
|
||||
|
||||
<h2>References to equations</h2>
|
||||
|
||||
<p>Here is a reference to the Lorenz Equations (\ref{lorenz}). Clicking on the equation number will take you back to the equation.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -30,7 +30,7 @@ Lets pick a couple of feeds that look interesting:
|
||||
#. Business Travel: http://feeds.portfolio.com/portfolio/businesstravel
|
||||
#. Tech Observer: http://feeds.portfolio.com/portfolio/thetechobserver
|
||||
|
||||
I got the URLs by clicking the little orange RSS icon next to each feed name. To make |app| download the feeds and convert them into an ebook, you should click the :guilabel:`Fetch news` button and then the :guilabel:`Add a custom news source` menu item. A dialog similar to that shown below should open up.
|
||||
I got the URLs by clicking the little orange RSS icon next to each feed name. To make |app| download the feeds and convert them into an ebook, you should right click the :guilabel:`Fetch news` button and then the :guilabel:`Add a custom news source` menu item. A dialog similar to that shown below should open up.
|
||||
|
||||
.. image:: images/custom_news.png
|
||||
:align: center
|
||||
|
BIN
manual/resources/mathjax.epub
Normal file
@ -18,4 +18,5 @@ Here you will find tutorials to get you started using |app|'s more advanced feat
|
||||
regexp
|
||||
server
|
||||
creating_plugins
|
||||
typesetting_math
|
||||
|
||||
|
70
manual/typesetting_math.rst
Normal file
@ -0,0 +1,70 @@
|
||||
|
||||
.. include:: global.rst
|
||||
|
||||
.. _typesetting_math:
|
||||
|
||||
|
||||
Typesetting Math in ebooks
|
||||
============================
|
||||
|
||||
The |app| ebook viewer has the ability to display math embedded in ebooks (ePub
|
||||
and HTML files). You can typeset the math directly with TeX or MathML or
|
||||
AsciiMath. The |app| viewer uses the excellent `MathJax
|
||||
<http://www.mathjax.org>`_ library to do this. This is a brief tutorial on
|
||||
creating ebooks with math in them that work well with the |app| viewer.
|
||||
|
||||
.. note::
|
||||
This only applies to calibre version 0.8.66 and newer
|
||||
|
||||
A simple HTML file with mathematics
|
||||
-------------------------------------
|
||||
|
||||
You can write mathematics inline inside a simple HTML file and the |app| viewer
|
||||
will render it into properly typeset mathematics. In the example below, we use
|
||||
TeX notation for mathematics. You will see that you can use normal TeX
|
||||
commands, with the small caveat that ampersands and less than and greater than
|
||||
signs have to be written as & < and > respectively.
|
||||
|
||||
The first step is to tell |app| that this will contains maths. You do this by
|
||||
adding the following snippet of code to the <head> section of the HTML file::
|
||||
|
||||
<script type="text/x-mathjax-config"></script>
|
||||
|
||||
That's it, now you can type mathematics just as you would in a .tex file. For
|
||||
example, here are Lorentz's equations::
|
||||
|
||||
<h2>The Lorenz Equations</h2>
|
||||
|
||||
<p>
|
||||
\begin{align}
|
||||
\dot{x} & = \sigma(y-x) \\
|
||||
\dot{y} & = \rho x - y - xz \\
|
||||
\dot{z} & = -\beta z + xy
|
||||
\end{align}
|
||||
</p>
|
||||
|
||||
This snippet looks like the following screen shot in the |app| viewer.
|
||||
|
||||
.. figure:: images/lorentz.png
|
||||
:align: center
|
||||
|
||||
:guilabel:`The Lorentz Equations`
|
||||
|
||||
The complete HTML file, with more equations and inline mathematics is
|
||||
reproduced below. You can convert this HTML file to EPUB in |app| to end up
|
||||
with an ebook you can distribute easily to other people.
|
||||
|
||||
.. only:: online
|
||||
|
||||
Here is the generated EPUB file: `mathjax.epub <_static/mathjax.epub>`_.
|
||||
|
||||
.. literalinclude:: mathjax.html
|
||||
:language: html
|
||||
|
||||
More information
|
||||
-----------------
|
||||
|
||||
Since the |app| viewer uses the MathJax library to render mathematics, the best
|
||||
place to find out more about math in ebooks and get help is the `MathJax
|
||||
website <http://www.mathjax.org>`_.
|
||||
|
@ -21,8 +21,12 @@ class anan(BasicNewsRecipe):
|
||||
remove_javascript = True
|
||||
encoding = 'utf-8'
|
||||
|
||||
remove_tags=[dict(name='a', attrs={'style':'width:110px; margin-top:0px;text-align:center;'}),
|
||||
dict(name='a', attrs={'style':'width:110px; margin-top:0px; margin-right:20px;text-align:center;'})]
|
||||
remove_tags=[
|
||||
dict(name='a', attrs={'style':'width:110px; margin-top:0px;text-align:center;'}),
|
||||
dict(name='a', attrs={'style':'width:110px; margin-top:0px; margin-right:20px;text-align:center;'}),
|
||||
{'attrs':{'class':['article_links', 'header', 'body_right']}},
|
||||
{'id':['crumbs']},
|
||||
]
|
||||
|
||||
feeds = [ ('Anandtech', 'http://www.anandtech.com/rss/')]
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
import re
|
||||
class Benchmark_pl(BasicNewsRecipe):
|
||||
class BenchmarkPl(BasicNewsRecipe):
|
||||
title = u'Benchmark.pl'
|
||||
__author__ = 'fenuks'
|
||||
description = u'benchmark.pl -IT site'
|
||||
@ -14,7 +14,7 @@ class Benchmark_pl(BasicNewsRecipe):
|
||||
preprocess_regexps = [(re.compile(ur'<h3><span style="font-size: small;"> Zobacz poprzednie <a href="http://www.benchmark.pl/news/zestawienie/grupa_id/135">Opinie dnia:</a></span>.*</body>', re.DOTALL|re.IGNORECASE), lambda match: '</body>'), (re.compile(ur'Więcej o .*?</ul>', re.DOTALL|re.IGNORECASE), lambda match: '')]
|
||||
keep_only_tags=[dict(name='div', attrs={'class':['m_zwykly', 'gallery']})]
|
||||
remove_tags_after=dict(name='div', attrs={'class':'body'})
|
||||
remove_tags=[dict(name='div', attrs={'class':['kategoria', 'socialize', 'thumb', 'panelOcenaObserwowane', 'categoryNextToSocializeGallery']}), dict(name='table', attrs={'background':'http://www.benchmark.pl/uploads/backend_img/a/fotki_newsy/opinie_dnia/bg.png'}), dict(name='table', attrs={'width':'210', 'cellspacing':'1', 'cellpadding':'4', 'border':'0', 'align':'right'})]
|
||||
remove_tags=[dict(name='div', attrs={'class':['kategoria', 'socialize', 'thumb', 'panelOcenaObserwowane', 'categoryNextToSocializeGallery', 'breadcrumb']}), dict(name='table', attrs={'background':'http://www.benchmark.pl/uploads/backend_img/a/fotki_newsy/opinie_dnia/bg.png'}), dict(name='table', attrs={'width':'210', 'cellspacing':'1', 'cellpadding':'4', 'border':'0', 'align':'right'})]
|
||||
INDEX= 'http://www.benchmark.pl'
|
||||
feeds = [(u'Aktualności', u'http://www.benchmark.pl/rss/aktualnosci-pliki.xml'),
|
||||
(u'Testy i recenzje', u'http://www.benchmark.pl/rss/testy-recenzje-minirecenzje.xml')]
|
||||
|
82
recipes/brecha.recipe
Normal file
@ -0,0 +1,82 @@
|
||||
from __future__ import (unicode_literals, division, absolute_import,
|
||||
print_function)
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2012, Darko Miletic <darko.miletic at gmail.com>'
|
||||
'''
|
||||
www.brecha.com.uy
|
||||
'''
|
||||
|
||||
import urllib
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class Brecha(BasicNewsRecipe):
|
||||
title = 'Brecha Digital'
|
||||
__author__ = 'Darko Miletic'
|
||||
description = 'Brecha , Cultura ,Sociales , Separatas, Lupas, Vueltas de Montevideo y toda la infomacion que caracteriza a este semanario'
|
||||
publisher = 'Brecha'
|
||||
category = 'brecha, digital, prensa, uruguay, semanario, sociedad, politica, cultura'
|
||||
oldest_article = 7
|
||||
max_articles_per_feed = 200
|
||||
no_stylesheets = True
|
||||
encoding = 'utf8'
|
||||
use_embedded_content = False
|
||||
language = 'es_UY'
|
||||
remove_empty_feeds = True
|
||||
publication_type = 'magazine'
|
||||
auto_cleanup = True
|
||||
needs_subscription = 'optional'
|
||||
masthead_url = 'http://www.brecha.com.uy/templates/ja_nex/themes/orange/images/logo.png'
|
||||
extra_css = """
|
||||
body{font-family: Arial,Helvetica,sans-serif }
|
||||
img{margin-bottom: 0.4em; display:block}
|
||||
"""
|
||||
|
||||
conversion_options = {
|
||||
'comment' : description
|
||||
, 'tags' : category
|
||||
, 'publisher' : publisher
|
||||
, 'language' : language
|
||||
}
|
||||
|
||||
|
||||
def get_browser(self):
|
||||
br = BasicNewsRecipe.get_browser()
|
||||
br.open('http://www.brecha.com.uy/index.php/acceder-miembros')
|
||||
if self.username is not None and self.password is not None:
|
||||
data = urllib.urlencode({ 'task':'login'
|
||||
,'view':'register'
|
||||
,'username':self.username
|
||||
,'password':self.password
|
||||
})
|
||||
br.open('http://www.brecha.com.uy/index.php/index.php?option=com_osemsc&controller=register',data)
|
||||
return br
|
||||
|
||||
remove_tags = [
|
||||
dict(name=['meta','link']),
|
||||
dict(name='div', attrs={'id':'js_ja'}),
|
||||
dict(name='ul', attrs={'class':'actions'})
|
||||
]
|
||||
remove_attributes=['lang', 'border']
|
||||
|
||||
feeds = [
|
||||
(u'Politica' , u'http://www.brecha.com.uy/index.php/politica-uruguaya?format=feed&type=rss'),
|
||||
(u'Mundo' , u'http://www.brecha.com.uy/index.php/mundo?format=feed&type=rss'),
|
||||
(u'Mapamundi', u'http://www.brecha.com.uy/index.php/mundo/mapamundi?format=feed&type=rss'),
|
||||
(u'Cultura' , u'http://www.brecha.com.uy/index.php/cultura?format=feed&type=rss'),
|
||||
(u'Vueltas de Montevideo', u'http://www.brecha.com.uy/index.php/cultura/vueltas-de-montevideo?format=feed&type=rss'),
|
||||
(u'Secos y Mojados', u'http://www.brecha.com.uy/index.php/cultura/secos-y-mojados?format=feed&type=rss'),
|
||||
(u'Literarias', u'http://www.brecha.com.uy/index.php/cultura/literarias?format=feed&type=rss'),
|
||||
(u'Sociedad', u'http://www.brecha.com.uy/index.php/sociedad?format=feed&type=rss'),
|
||||
(u'Especiales', u'http://www.brecha.com.uy/index.php/especiales?format=feed&type=rss'),
|
||||
(u'Contratapa', u'http://www.brecha.com.uy/index.php/contratapa?format=feed&type=rss')
|
||||
]
|
||||
|
||||
def print_version(self, url):
|
||||
return url + '?tmpl=component&print=1&layout=default&page='
|
||||
|
||||
def get_cover_url(self):
|
||||
soup = self.index_to_soup('http://www.brecha.com.uy/index.php')
|
||||
for image in soup.findAll('img', alt=True):
|
||||
if image['alt'].startswith('Tapa '):
|
||||
return 'http://www.brecha.com.uy' + urllib.quote(image['src'])
|
||||
return None
|
@ -16,6 +16,7 @@ class BusinessSpectator(BasicNewsRecipe):
|
||||
oldest_article = 2
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets = True
|
||||
auto_cleanup = True
|
||||
#delay = 1
|
||||
use_embedded_content = False
|
||||
encoding = 'utf8'
|
||||
@ -32,11 +33,11 @@ class BusinessSpectator(BasicNewsRecipe):
|
||||
,'linearize_tables': False
|
||||
}
|
||||
|
||||
keep_only_tags = [dict(id='storyHeader'), dict(id='body-html')]
|
||||
#keep_only_tags = [dict(id='storyHeader'), dict(id='body-html')]
|
||||
|
||||
remove_tags = [dict(attrs={'class':'hql'})]
|
||||
#remove_tags = [dict(attrs={'class':'hql'})]
|
||||
|
||||
remove_attributes = ['width','height','style']
|
||||
#remove_attributes = ['width','height','style']
|
||||
|
||||
feeds = [
|
||||
('Top Stories', 'http://www.businessspectator.com.au/top-stories.rss'),
|
||||
@ -46,3 +47,4 @@ class BusinessSpectator(BasicNewsRecipe):
|
||||
('Daily Dossier', 'http://www.businessspectator.com.au/bs.nsf/RSS?readform&type=kgb&cat=dossier'),
|
||||
('Australia', 'http://www.businessspectator.com.au/bs.nsf/RSS?readform&type=region&cat=australia'),
|
||||
]
|
||||
|
||||
|
@ -1,35 +1,314 @@
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class CalgaryHerald(BasicNewsRecipe):
|
||||
title = u'Calgary Herald'
|
||||
oldest_article = 3
|
||||
max_articles_per_feed = 100
|
||||
|
||||
feeds = [
|
||||
(u'News', u'http://rss.canada.com/get/?F233'),
|
||||
(u'Calgary', u'http://www.calgaryherald.com/scripts/sp6query.aspx?catalog=cahr&tags=keyword|calgary&output=rss?link=http%3a%2f%2fwww.calgaryherald'),
|
||||
(u'Alberta', u'http://www.calgaryherald.com/scripts/Sp6Query.aspx?catalog=CAHR&tags=Keyword|Alberta&output=rss?link=http%3A%2F%2Fwww.calgaryherald.com%2Fnews%2Falberta%2Findex.html'),
|
||||
(u'Politics', u'http://rss.canada.com/get/?F7551'),
|
||||
(u'National', u'http://rss.canada.com/get/?F7552'),
|
||||
(u'World', u'http://rss.canada.com/get/?F7553'),
|
||||
]
|
||||
__author__ = 'rty'
|
||||
pubisher = 'Calgary Herald'
|
||||
description = 'Calgary, Alberta, Canada'
|
||||
category = 'News, Calgary, Alberta, Canada'
|
||||
|
||||
|
||||
remove_javascript = True
|
||||
use_embedded_content = False
|
||||
no_stylesheets = True
|
||||
language = 'en_CA'
|
||||
encoding = 'utf-8'
|
||||
conversion_options = {'linearize_tables':True}
|
||||
##masthead_url = 'http://www.calgaryherald.com/index.html'
|
||||
keep_only_tags = [
|
||||
dict(name='div', attrs={'id':'storyheader'}),
|
||||
dict(name='div', attrs={'id':'storycontent'})
|
||||
|
||||
]
|
||||
remove_tags_after = {'class':"story_tool_hr"}
|
||||
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
__license__ = 'GPL v3'
|
||||
|
||||
'''
|
||||
www.canada.com
|
||||
'''
|
||||
import re
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
from calibre.ebooks.BeautifulSoup import Tag, BeautifulStoneSoup
|
||||
|
||||
|
||||
class CanWestPaper(BasicNewsRecipe):
|
||||
|
||||
postmedia_index_pages = [
|
||||
(u'Headlines',u'/index.html'),
|
||||
(u'Ottawa & Area',u'/news/ottawa/index.html'),
|
||||
(u'Vancouver',u'/news/vancouver/index.html'),
|
||||
(u'Calgary',u'/news/calgary/index.html'),
|
||||
(u'Edmonton',u'/news/edmonton/index.html'),
|
||||
(u'Montreal',u'/news/montreal/index.html'),
|
||||
(u'Fraser Valley',u'/news/fraser-valley/index.html'),
|
||||
(u'British Columbia',u'/news/bc/index.html'),
|
||||
(u'Alberta',u'/news/alberta/index.html'),
|
||||
(u'Canada',u'/news/canada/index.html'),
|
||||
(u'National',u'/news/national/index.html'),
|
||||
(u'Politics',u'/news/politics/index.html'),
|
||||
(u'Insight',u'/news/insight/index.html'),
|
||||
(u'Special Reports',u'/news/specialreports/index.html'),
|
||||
(u'Gangs',u'/news/gangs/index.html'),
|
||||
(u'Education',u'/news/education/index.html'),
|
||||
(u'Health',u'/news/health/index.html'),
|
||||
(u'Environment',u'/news/environment/index.html'),
|
||||
(u'World',u'/news/world/index.html'),
|
||||
(u'Police Blotter',u'/news/crime-and-justice/index.html'),
|
||||
(u'Crime',u'/news/blotter/index.html'),
|
||||
(u'Around Town',u'/news/topic.html?t=keyword&q=Around+Town'),
|
||||
(u'Diplomatica',u'/news/diplomatica/index.html'),
|
||||
(u'Opinion',u'/opinion/index.html'),
|
||||
(u'Columnists',u'/columnists/index.html'),
|
||||
(u'Editorials',u'/opinion/editorials/index.html'),
|
||||
(u'Letters',u'/opinion/letters/index.html'),
|
||||
(u'Business',u'/business/index.html'),
|
||||
(u'Sports',u'/sports/index.html'),
|
||||
(u'Arts',u'/entertainment/index.html'),
|
||||
(u'Life',u'/life/index.html'),
|
||||
(u'Technology',u'/technology/index.html'),
|
||||
(u'Travel',u'/travel/index.html'),
|
||||
(u'Health',u'/health/index.html')
|
||||
]
|
||||
|
||||
|
||||
# un-comment the following six lines for the Vancouver Province
|
||||
## title = u'Vancouver Province'
|
||||
## url_prefix = 'http://www.theprovince.com'
|
||||
## description = u'News from Vancouver, BC'
|
||||
## std_logo_url = 'http://www.theprovince.com/images/logo_theprovince.jpg'
|
||||
## logo_url = 'vplogo.jpg'
|
||||
## fp_tag = 'CAN_TP'
|
||||
|
||||
# un-comment the following six lines for the Vancouver Sun
|
||||
## title = u'Vancouver Sun'
|
||||
## url_prefix = 'http://www.vancouversun.com'
|
||||
## description = u'News from Vancouver, BC'
|
||||
## std_logo_url = 'http://www.vancouversun.com/images/logo_vancouversun.jpg'
|
||||
## logo_url = 'vslogo.jpg'
|
||||
## fp_tag = 'CAN_VS'
|
||||
|
||||
# un-comment the following six lines for the Calgary Herald
|
||||
title = u'Calgary Herald'
|
||||
url_prefix = 'http://www.calgaryherald.com'
|
||||
description = u'News from Calgary, AB'
|
||||
std_logo_url = 'http://www.calgaryherald.com/images/logo_calgaryherald.jpg'
|
||||
logo_url = 'chlogo.jpg'
|
||||
fp_tag = 'CAN_CH'
|
||||
|
||||
# un-comment the following six lines for the Edmonton Journal
|
||||
## title = u'Edmonton Journal'
|
||||
## url_prefix = 'http://www.edmontonjournal.com'
|
||||
## description = u'News from Edmonton, AB'
|
||||
## std_logo_url = 'http://www.edmontonjournal.com/images/logo_edmontonjournal.jpg'
|
||||
## logo_url = 'ejlogo.jpg'
|
||||
## fp_tag = 'CAN_EJ'
|
||||
|
||||
# un-comment the following six lines for the Ottawa Citizen
|
||||
## title = u'Ottawa Citizen'
|
||||
## url_prefix = 'http://www.ottawacitizen.com'
|
||||
## description = u'News from Ottawa, ON'
|
||||
## std_logo_url = 'http://www.ottawacitizen.com/images/logo_ottawacitizen.jpg'
|
||||
## logo_url = 'oclogo.jpg'
|
||||
## fp_tag = 'CAN_OC'
|
||||
|
||||
# un-comment the following six lines for the Montreal Gazette
|
||||
## title = u'Montreal Gazette'
|
||||
## url_prefix = 'http://www.montrealgazette.com'
|
||||
## description = u'News from Montreal, QC'
|
||||
## std_logo_url = 'http://www.montrealgazette.com/images/logo_montrealgazette.jpg'
|
||||
## logo_url = 'mglogo.jpg'
|
||||
## fp_tag = 'CAN_MG'
|
||||
|
||||
Kindle_Fire=False
|
||||
masthead_url = std_logo_url
|
||||
|
||||
url_list = []
|
||||
language = 'en_CA'
|
||||
__author__ = 'Nick Redding'
|
||||
no_stylesheets = True
|
||||
timefmt = ' [%b %d]'
|
||||
encoding = 'utf-8'
|
||||
extra_css = '''
|
||||
.timestamp { font-size:xx-small; display: block; }
|
||||
#storyheader { font-size: medium; }
|
||||
#storyheader h1 { font-size: x-large; }
|
||||
#storyheader h2 { font-size: small; font-style: italic; }
|
||||
.byline { font-size:xx-small; }
|
||||
#photocaption { font-size: xx-small; font-style: italic; font-weight: normal; }
|
||||
.photocaption { font-size: xx-small; font-style: italic; font-weight: normal; }
|
||||
#photocredit { font-size: xx-small; font-weight: normal; }'''
|
||||
|
||||
keep_only_tags = [dict(name='div', attrs={'id':'storyheader'}),dict(name='div', attrs={'id':'storycontent'}),dict(name='div', attrs={'id':'storyphoto'})]
|
||||
|
||||
remove_tags = [{'class':'comments'},
|
||||
dict(name='div', attrs={'class':'navbar'}),dict(name='div', attrs={'class':'morelinks'}),
|
||||
dict(name='h2', attrs={'id':'photocredit'}),
|
||||
dict(name='div', attrs={'class':'viewmore'}),dict(name='li', attrs={'class':'email'}),
|
||||
dict(name='div', attrs={'class':'story_tool_hr'}),dict(name='div', attrs={'class':'clear'}),
|
||||
dict(name='div', attrs={'class':'story_tool'}),dict(name='div', attrs={'class':'copyright'}),
|
||||
dict(name='div', attrs={'class':'rule_grey_solid'}),
|
||||
dict(name='li', attrs={'class':'print'}),dict(name='li', attrs={'class':'share'}),dict(name='ul', attrs={'class':'bullet'})]
|
||||
|
||||
|
||||
def get_cover_url(self):
|
||||
from datetime import timedelta, date
|
||||
cover = 'http://webmedia.newseum.org/newseum-multimedia/dfp/jpg'+str(date.today().day)+'/lg/'+self.fp_tag+'.jpg'
|
||||
br = BasicNewsRecipe.get_browser()
|
||||
daysback=1
|
||||
try:
|
||||
br.open(cover)
|
||||
except:
|
||||
while daysback<7:
|
||||
cover = 'http://webmedia.newseum.org/newseum-multimedia/dfp/jpg'+str((date.today() - timedelta(days=daysback)).day)+'/lg/'+self.fp_tag+'.jpg'
|
||||
br = BasicNewsRecipe.get_browser()
|
||||
try:
|
||||
br.open(cover)
|
||||
except:
|
||||
daysback = daysback+1
|
||||
continue
|
||||
break
|
||||
if daysback==7:
|
||||
self.log("\nCover unavailable")
|
||||
cover = None
|
||||
return cover
|
||||
|
||||
def prepare_masthead_image(self, path_to_image, out_path):
|
||||
if self.Kindle_Fire:
|
||||
from calibre.utils.magick import Image, create_canvas
|
||||
img = Image()
|
||||
img.open(path_to_image)
|
||||
width, height = img.size
|
||||
img2 = create_canvas(width, height)
|
||||
img2.compose(img)
|
||||
img2.save(out_path)
|
||||
else:
|
||||
BasicNewsRecipe.prepare_masthead_image(self, path_to_image, out_path)
|
||||
|
||||
def fixChars(self,string):
|
||||
# Replace lsquo (\x91)
|
||||
fixed = re.sub("\x91","‘",string)
|
||||
# Replace rsquo (\x92)
|
||||
fixed = re.sub("\x92","’",fixed)
|
||||
# Replace ldquo (\x93)
|
||||
fixed = re.sub("\x93","“",fixed)
|
||||
# Replace rdquo (\x94)
|
||||
fixed = re.sub("\x94","”",fixed)
|
||||
# Replace ndash (\x96)
|
||||
fixed = re.sub("\x96","–",fixed)
|
||||
# Replace mdash (\x97)
|
||||
fixed = re.sub("\x97","—",fixed)
|
||||
fixed = re.sub("’","’",fixed)
|
||||
return fixed
|
||||
|
||||
def massageNCXText(self, description):
|
||||
# Kindle TOC descriptions won't render certain characters
|
||||
if description:
|
||||
massaged = unicode(BeautifulStoneSoup(description, convertEntities=BeautifulStoneSoup.HTML_ENTITIES))
|
||||
# Replace '&' with '&'
|
||||
massaged = re.sub("&","&", massaged)
|
||||
return self.fixChars(massaged)
|
||||
else:
|
||||
return description
|
||||
|
||||
def populate_article_metadata(self, article, soup, first):
|
||||
if first:
|
||||
picdiv = soup.find('body').find('img')
|
||||
if picdiv is not None:
|
||||
self.add_toc_thumbnail(article,re.sub(r'links\\link\d+\\','',picdiv['src']))
|
||||
xtitle = article.text_summary.strip()
|
||||
if len(xtitle) == 0:
|
||||
desc = soup.find('meta',attrs={'property':'og:description'})
|
||||
if desc is not None:
|
||||
article.summary = article.text_summary = desc['content']
|
||||
|
||||
def strip_anchors(self,soup):
|
||||
paras = soup.findAll(True)
|
||||
for para in paras:
|
||||
aTags = para.findAll('a')
|
||||
for a in aTags:
|
||||
if a.img is None:
|
||||
a.replaceWith(a.renderContents().decode('cp1252','replace'))
|
||||
return soup
|
||||
|
||||
|
||||
def preprocess_html(self,soup):
|
||||
#delete empty id attributes--they screw up the TOC for unknown reasons
|
||||
divtags = soup.findAll('div',attrs={'id':''})
|
||||
if divtags:
|
||||
for div in divtags:
|
||||
del(div['id'])
|
||||
|
||||
pgall = soup.find('div',attrs={'id':'storyphoto'})
|
||||
if pgall is not None: # photo gallery perhaps
|
||||
if (soup.find('div',attrs={'id':'storycontent'}) is None):
|
||||
allpics = Tag(soup,'div')
|
||||
first_img = pgall.find('div','storyimage')
|
||||
if first_img is not None:
|
||||
first_img.extract()
|
||||
tlist = pgall.find('div',attrs={'id':'relatedthumbs'})
|
||||
if tlist is not None:
|
||||
for atag in tlist.findAll('a'):
|
||||
img = Tag(soup,'img')
|
||||
srcpre, sep, srcpost = atag.img['src'].partition('?')
|
||||
img['src'] = srcpre
|
||||
pdesc = Tag(soup,'p')
|
||||
pdesc.insert(0,atag.img['alt'])
|
||||
pdesc['class']='photocaption'
|
||||
div = Tag(soup,'div')
|
||||
div.insert(0,pdesc)
|
||||
div.insert(0,img)
|
||||
allpics.append(div)
|
||||
pgall.replaceWith(allpics)
|
||||
|
||||
for pg in soup.findAll('div',attrs={'id':'storyphoto'}):
|
||||
pg.extract()
|
||||
return self.strip_anchors(soup)
|
||||
|
||||
|
||||
|
||||
def parse_index(self):
|
||||
|
||||
articles = {}
|
||||
ans = []
|
||||
|
||||
|
||||
def handle_article(adiv,key):
|
||||
h1tag = adiv.h1
|
||||
if h1tag is not None:
|
||||
atag = h1tag.a
|
||||
if atag is not None:
|
||||
url = atag['href']
|
||||
if atag['href'].startswith('http'):
|
||||
return
|
||||
elif atag['href'].startswith('/'):
|
||||
url = self.url_prefix+atag['href']
|
||||
else:
|
||||
url = self.url_prefix+'/'+atag['href']
|
||||
if url in self.url_list:
|
||||
return
|
||||
self.url_list.append(url)
|
||||
title = self.tag_to_string(atag,False)
|
||||
if 'VIDEO' in title.upper():
|
||||
return
|
||||
if 'GALLERY' in title.upper():
|
||||
return
|
||||
if 'PHOTOS' in title.upper():
|
||||
return
|
||||
dtag = adiv.find('div','content')
|
||||
description=''
|
||||
print("URL "+url)
|
||||
print("TITLE "+title)
|
||||
if dtag is not None:
|
||||
stag = dtag.span
|
||||
if stag is not None:
|
||||
if stag['class'] != 'timestamp':
|
||||
description = self.tag_to_string(stag,False)
|
||||
else:
|
||||
description = self.tag_to_string(dtag,False)
|
||||
print("DESCRIPTION: "+description)
|
||||
if not articles.has_key(key):
|
||||
articles[key] = []
|
||||
articles[key].append(dict(title=title,url=url,date='',description=description,author='',content=''))
|
||||
|
||||
def parse_web_index(key, keyurl):
|
||||
try:
|
||||
soup = self.index_to_soup(self.url_prefix+keyurl)
|
||||
except:
|
||||
return
|
||||
ans.append(key)
|
||||
mainsoup = soup.find('div','bodywrapper')
|
||||
footer = mainsoup.find(attrs={'id':'footerfeature'})
|
||||
if footer is not None:
|
||||
footer.extract()
|
||||
print("Section: "+key)
|
||||
for wdiv in mainsoup.findAll('div',attrs={'id':re.compile('^HorizontalFeatureSlider_1_Story')}):
|
||||
handle_article(wdiv,key)
|
||||
wdiv.extract()
|
||||
for wdiv in mainsoup.findAll(attrs={'id':['featurewidget','textfeature','textlinks_timestamp']}):
|
||||
for adiv in wdiv.findAll('div','featurecontent'):
|
||||
handle_article(adiv,key)
|
||||
|
||||
for (k,url) in self.postmedia_index_pages:
|
||||
parse_web_index(k,url)
|
||||
ans = [(key, articles[key]) for key in ans if articles.has_key(key)]
|
||||
return ans
|
||||
|
||||
|
38
recipes/conowego_pl.recipe
Executable file
@ -0,0 +1,38 @@
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
from calibre.ebooks.BeautifulSoup import BeautifulSoup
|
||||
class CoNowegoPl(BasicNewsRecipe):
|
||||
title = u'conowego.pl'
|
||||
__author__ = 'fenuks'
|
||||
description = u'Nowy wortal technologiczny oraz gazeta internetowa. Testy najnowszych produktów, fachowe porady i recenzje. U nas znajdziesz wszystko o elektronice użytkowej !'
|
||||
cover_url = 'http://www.conowego.pl/fileadmin/templates/main/images/logo_top.png'
|
||||
category = 'IT, news'
|
||||
language = 'pl'
|
||||
oldest_article = 7
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets = True
|
||||
remove_empty_feeds = True
|
||||
use_embedded_content = False
|
||||
keep_only_tags = [dict(name='div', attrs={'class':'news_list single_view'})]
|
||||
remove_tags = [dict(name='div', attrs={'class':['ni_bottom', 'ni_rank', 'ni_date']})]
|
||||
feeds = [(u'Aktualno\u015bci', u'http://www.conowego.pl/rss/aktualnosci-5/?type=100'), (u'Gaming', u'http://www.conowego.pl/rss/gaming-6/?type=100'), (u'Porady', u'http://www.conowego.pl/rss/porady-3/?type=100'), (u'Testy', u'http://www.conowego.pl/rss/testy-2/?type=100')]
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
for i in soup.findAll('img'):
|
||||
i.parent.insert(0, BeautifulSoup('<br />'))
|
||||
i.insert(len(i), BeautifulSoup('<br />'))
|
||||
self.append_page(soup, soup.body)
|
||||
return soup
|
||||
|
||||
|
||||
def append_page(self, soup, appendtag):
|
||||
tag = appendtag.find('div', attrs={'class':'pages'})
|
||||
if tag:
|
||||
nexturls=tag.findAll('a')
|
||||
for nexturl in nexturls[:-1]:
|
||||
soup2 = self.index_to_soup('http://www.conowego.pl/' + nexturl['href'])
|
||||
pagetext = soup2.find(attrs={'class':'ni_content'})
|
||||
pos = len(appendtag.contents)
|
||||
appendtag.insert(pos, pagetext)
|
||||
|
||||
for r in appendtag.findAll(attrs={'class':['pages', 'paginationWrap']}):
|
||||
r.extract()
|
32
recipes/dot_net.recipe
Normal file
@ -0,0 +1,32 @@
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
import re
|
||||
|
||||
class NetMagazineRecipe (BasicNewsRecipe):
|
||||
__author__ = u'Marc Busqué <marc@lamarciana.com>'
|
||||
__url__ = 'http://www.lamarciana.com'
|
||||
__version__ = '1.0'
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = u'2012, Marc Busqué <marc@lamarciana.com>'
|
||||
title = u'.net magazine'
|
||||
description = u'net is the world’s best-selling magazine for web designers and developers, featuring tutorials from leading agencies, interviews with the web’s biggest names, and agenda-setting features on the hottest issues affecting the internet today.'
|
||||
language = 'en'
|
||||
tags = 'web development, software'
|
||||
oldest_article = 7
|
||||
remove_empty_feeds = True
|
||||
no_stylesheets = True
|
||||
cover_url = u'http://media.netmagazine.futurecdn.net/sites/all/themes/netmag/logo.png'
|
||||
keep_only_tags = [
|
||||
dict(name='article', attrs={'class': re.compile('^node.*$', re.IGNORECASE)})
|
||||
]
|
||||
remove_tags = [
|
||||
dict(name='span', attrs={'class': 'comment-count'}),
|
||||
dict(name='div', attrs={'class': 'item-list share-links'}),
|
||||
dict(name='footer'),
|
||||
]
|
||||
remove_attributes = ['border', 'cellspacing', 'align', 'cellpadding', 'colspan', 'valign', 'vspace', 'hspace', 'alt', 'width', 'height', 'style']
|
||||
extra_css = 'img {max-width: 100%; display: block; margin: auto;} .captioned-image div {text-align: center; font-style: italic;}'
|
||||
|
||||
feeds = [
|
||||
(u'.net', u'http://feeds.feedburner.com/net/topstories'),
|
||||
]
|
132
recipes/dziennik_polski.recipe
Normal file
@ -0,0 +1,132 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
__license__='GPL v3'
|
||||
__author__='grzegorz.maj@dziennik.krakow.pl>'
|
||||
|
||||
'''
|
||||
http://dziennikpolski24.pl
|
||||
Author: grzegorz.maj@dziennik.krakow.pl
|
||||
'''
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class DziennikPolski24(BasicNewsRecipe):
|
||||
|
||||
title=u'Dziennik Polski'
|
||||
publisher=u'Grupa Polskapresse'
|
||||
|
||||
__author__='grzegorz.maj'
|
||||
description=u'Wiadomości z wydania Dziennika Polskiego'
|
||||
oldest_article=1
|
||||
max_articles_per_feed=50
|
||||
needs_subscription=True
|
||||
|
||||
remove_javascript=True
|
||||
no_stylesheets=True
|
||||
use_embedded_content=False
|
||||
remove_empty_feeds=True
|
||||
extra_css='.date{margin-top: 4em;} .logo_author{margin-left:0.5em;}'
|
||||
|
||||
publication_type='newspaper'
|
||||
cover_url='http://www.dziennikpolski24.pl/_p/images/logoDP24-b.gif'
|
||||
INDEX='http://dziennikpolski24.pl/'
|
||||
|
||||
encoding='utf-8'
|
||||
language='pl'
|
||||
|
||||
keep_only_tags=[
|
||||
|
||||
dict(name = 'div', attrs = {'class':['toolbar']})
|
||||
, dict(name = 'h1')
|
||||
, dict(name = 'h2', attrs = {'class':['teaser']})
|
||||
, dict(name = 'div', attrs = {'class':['picture']})
|
||||
, dict(name = 'div', attrs = {'id':['showContent']})
|
||||
, dict(name = 'div', attrs = {'class':['paging']})
|
||||
, dict(name = 'div', attrs = {'class':['wykupTresc']})
|
||||
]
|
||||
|
||||
remove_tags=[
|
||||
|
||||
]
|
||||
|
||||
feeds=[
|
||||
(u'Kraj', u'http://www.dziennikpolski24.pl/rss/feed/1151')
|
||||
, (u'Świat', u'http://www.dziennikpolski24.pl/rss/feed/1153')
|
||||
, (u'Gospodarka', u'http://www.dziennikpolski24.pl/rss/feed/1154')
|
||||
, (u'Małopolska', u'http://www.dziennikpolski24.pl/rss/feed/1155')
|
||||
, (u'Kultura', u'http://www.dziennikpolski24.pl/rss/feed/1156')
|
||||
, (u'Opinie', u'http://www.dziennikpolski24.pl/rss/feed/1158')
|
||||
, (u'Kronika Nowohucka', u'http://www.dziennikpolski24.pl/rss/feed/1656')
|
||||
, (u'Na bieżąco', u'http://www.dziennikpolski24.pl/rss/feed/1543')
|
||||
, (u'Londyn 2012', u'http://www.dziennikpolski24.pl/rss/feed/2545')
|
||||
, (u'Piłka nożna', u'http://www.dziennikpolski24.pl/rss/feed/2196')
|
||||
, (u'Siatkówka', u'http://www.dziennikpolski24.pl/rss/feed/2197')
|
||||
, (u'Koszykówka', u'http://www.dziennikpolski24.pl/rss/feed/2198')
|
||||
, (u'Tenis', u'http://www.dziennikpolski24.pl/rss/feed/2199')
|
||||
, (u'Formuła 1', u'http://www.dziennikpolski24.pl/rss/feed/2203')
|
||||
, (u'Lekkoatletyka', u'http://www.dziennikpolski24.pl/rss/feed/2204')
|
||||
, (u'Żużel', u'http://www.dziennikpolski24.pl/rss/feed/2200')
|
||||
, (u'Sporty motorowe', u'http://www.dziennikpolski24.pl/rss/feed/2206')
|
||||
, (u'Publicystyka sportowa', u'http://www.dziennikpolski24.pl/rss/feed/2201')
|
||||
, (u'Kolarstwo', u'http://www.dziennikpolski24.pl/rss/feed/2205')
|
||||
, (u'Inne', u'http://www.dziennikpolski24.pl/rss/feed/2202')
|
||||
, (u'Miasto Kraków', u'http://www.dziennikpolski24.pl/rss/feed/1784')
|
||||
, (u'Region nowosądecki', u'http://www.dziennikpolski24.pl/rss/feed/1795')
|
||||
, (u'Region Małopolski Zachodniej', u'http://www.dziennikpolski24.pl/rss/feed/1793')
|
||||
, (u'Region tarnowski', u'http://www.dziennikpolski24.pl/rss/feed/1797')
|
||||
, (u'Region podhalański', u'http://www.dziennikpolski24.pl/rss/feed/1789')
|
||||
, (u'Region olkuski', u'http://www.dziennikpolski24.pl/rss/feed/1670')
|
||||
, (u'Region miechowski', u'http://www.dziennikpolski24.pl/rss/feed/1806')
|
||||
, (u'Region podkrakowski', u'http://www.dziennikpolski24.pl/rss/feed/1787')
|
||||
, (u'Region proszowicki', u'http://www.dziennikpolski24.pl/rss/feed/1804')
|
||||
, (u'Region wielicki', u'http://www.dziennikpolski24.pl/rss/feed/1802')
|
||||
, (u'Region podbeskidzki', u'http://www.dziennikpolski24.pl/rss/feed/1791')
|
||||
, (u'Region myślenicki', u'http://www.dziennikpolski24.pl/rss/feed/1800')
|
||||
, (u'Autosalon', u'http://www.dziennikpolski24.pl/rss/feed/1294')
|
||||
, (u'Kariera', u'http://www.dziennikpolski24.pl/rss/feed/1289')
|
||||
, (u'Przegląd nieruchomości', u'http://www.dziennikpolski24.pl/rss/feed/1281')
|
||||
, (u'Magnes', u'http://www.dziennikpolski24.pl/rss/feed/1283')
|
||||
, (u'Magazyn Piątek', u'http://www.dziennikpolski24.pl/rss/feed/1293')
|
||||
, (u'Pejzaż rodzinny', u'http://www.dziennikpolski24.pl/rss/feed/1274')
|
||||
, (u'Podróże', u'http://www.dziennikpolski24.pl/rss/feed/1275')
|
||||
, (u'Konsument', u'http://www.dziennikpolski24.pl/rss/feed/1288')
|
||||
]
|
||||
|
||||
def append_page(self, soup, appendtag):
|
||||
loop=False
|
||||
tag=soup.find('div', attrs = {'class':'paging'})
|
||||
if tag:
|
||||
loop=True
|
||||
li_nks=tag.findAll('li')
|
||||
appendtag.find('div', attrs = {'class':'paging'}).extract()
|
||||
if appendtag.find('ul', attrs = {'class':'menuf'}):
|
||||
appendtag.find('ul', attrs = {'class':'menuf'}).extract()
|
||||
while loop:
|
||||
loop=False
|
||||
for li_nk in li_nks:
|
||||
link_tag=li_nk.contents[0].contents[0].string
|
||||
if u'następna' in link_tag:
|
||||
soup2=self.index_to_soup(self.INDEX+li_nk.contents[0]['href'])
|
||||
if soup2.find('div', attrs = {'id':'showContent'}):
|
||||
pagetext=soup2.find('div', attrs = {'id':'showContent'})
|
||||
pos=len(appendtag.contents)
|
||||
appendtag.insert(pos, pagetext)
|
||||
if soup2.find('div', attrs = {'class':'rightbar'}):
|
||||
pagecont=soup2.find('div', attrs = {'class':'rightbar'})
|
||||
tag=pagecont.find('div', attrs = {'class':'paging'})
|
||||
li_nks=tag.findAll('li')
|
||||
loop=True
|
||||
|
||||
def get_browser(self):
|
||||
br=BasicNewsRecipe.get_browser()
|
||||
if self.username is not None and self.password is not None:
|
||||
br.open('http://www.dziennikpolski24.pl/pl/moje-konto/950606-loguj.html')
|
||||
br.select_form(nr = 1)
|
||||
br["user_login[login]"]=self.username
|
||||
br['user_login[pass]']=self.password
|
||||
br.submit()
|
||||
return br
|
||||
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
self.append_page(soup, soup.body)
|
||||
return soup
|
||||
|
@ -1,105 +1,136 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
|
||||
'''
|
||||
www.canada.com
|
||||
'''
|
||||
|
||||
import re
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
from calibre.ebooks.BeautifulSoup import BeautifulStoneSoup
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
from calibre.ebooks.BeautifulSoup import BeautifulStoneSoup, Tag
|
||||
|
||||
|
||||
class CanWestPaper(BasicNewsRecipe):
|
||||
|
||||
# un-comment the following four lines for the Victoria Times Colonist
|
||||
## title = u'Victoria Times Colonist'
|
||||
## url_prefix = 'http://www.timescolonist.com'
|
||||
## description = u'News from Victoria, BC'
|
||||
## fp_tag = 'CAN_TC'
|
||||
postmedia_index_pages = [
|
||||
(u'Headlines',u'/index.html'),
|
||||
(u'Ottawa & Area',u'/news/ottawa/index.html'),
|
||||
(u'Vancouver',u'/news/vancouver/index.html'),
|
||||
(u'Calgary',u'/news/calgary/index.html'),
|
||||
(u'Edmonton',u'/news/edmonton/index.html'),
|
||||
(u'Montreal',u'/news/montreal/index.html'),
|
||||
(u'Fraser Valley',u'/news/fraser-valley/index.html'),
|
||||
(u'British Columbia',u'/news/bc/index.html'),
|
||||
(u'Alberta',u'/news/alberta/index.html'),
|
||||
(u'Canada',u'/news/canada/index.html'),
|
||||
(u'National',u'/news/national/index.html'),
|
||||
(u'Politics',u'/news/politics/index.html'),
|
||||
(u'Insight',u'/news/insight/index.html'),
|
||||
(u'Special Reports',u'/news/specialreports/index.html'),
|
||||
(u'Gangs',u'/news/gangs/index.html'),
|
||||
(u'Education',u'/news/education/index.html'),
|
||||
(u'Health',u'/news/health/index.html'),
|
||||
(u'Environment',u'/news/environment/index.html'),
|
||||
(u'World',u'/news/world/index.html'),
|
||||
(u'Police Blotter',u'/news/crime-and-justice/index.html'),
|
||||
(u'Crime',u'/news/blotter/index.html'),
|
||||
(u'Around Town',u'/news/topic.html?t=keyword&q=Around+Town'),
|
||||
(u'Diplomatica',u'/news/diplomatica/index.html'),
|
||||
(u'Opinion',u'/opinion/index.html'),
|
||||
(u'Columnists',u'/columnists/index.html'),
|
||||
(u'Editorials',u'/opinion/editorials/index.html'),
|
||||
(u'Letters',u'/opinion/letters/index.html'),
|
||||
(u'Business',u'/business/index.html'),
|
||||
(u'Sports',u'/sports/index.html'),
|
||||
(u'Arts',u'/entertainment/index.html'),
|
||||
(u'Life',u'/life/index.html'),
|
||||
(u'Technology',u'/technology/index.html'),
|
||||
(u'Travel',u'/travel/index.html'),
|
||||
(u'Health',u'/health/index.html')
|
||||
]
|
||||
|
||||
# un-comment the following four lines for the Vancouver Province
|
||||
|
||||
# un-comment the following six lines for the Vancouver Province
|
||||
## title = u'Vancouver Province'
|
||||
## url_prefix = 'http://www.theprovince.com'
|
||||
## description = u'News from Vancouver, BC'
|
||||
## fp_tag = 'CAN_VP'
|
||||
## std_logo_url = 'http://www.theprovince.com/images/logo_theprovince.jpg'
|
||||
## logo_url = 'vplogo.jpg'
|
||||
## fp_tag = 'CAN_TP'
|
||||
|
||||
# un-comment the following four lines for the Vancouver Sun
|
||||
# un-comment the following six lines for the Vancouver Sun
|
||||
## title = u'Vancouver Sun'
|
||||
## url_prefix = 'http://www.vancouversun.com'
|
||||
## description = u'News from Vancouver, BC'
|
||||
## std_logo_url = 'http://www.vancouversun.com/images/logo_vancouversun.jpg'
|
||||
## logo_url = 'vslogo.jpg'
|
||||
## fp_tag = 'CAN_VS'
|
||||
|
||||
# un-comment the following four lines for the Edmonton Journal
|
||||
title = u'Edmonton Journal'
|
||||
url_prefix = 'http://www.edmontonjournal.com'
|
||||
description = u'News from Edmonton, AB'
|
||||
fp_tag = 'CAN_EJ'
|
||||
|
||||
# un-comment the following four lines for the Calgary Herald
|
||||
# un-comment the following six lines for the Calgary Herald
|
||||
## title = u'Calgary Herald'
|
||||
## url_prefix = 'http://www.calgaryherald.com'
|
||||
## description = u'News from Calgary, AB'
|
||||
## std_logo_url = 'http://www.calgaryherald.com/images/logo_calgaryherald.jpg'
|
||||
## logo_url = 'chlogo.jpg'
|
||||
## fp_tag = 'CAN_CH'
|
||||
|
||||
# un-comment the following four lines for the Regina Leader-Post
|
||||
## title = u'Regina Leader-Post'
|
||||
## url_prefix = 'http://www.leaderpost.com'
|
||||
## description = u'News from Regina, SK'
|
||||
## fp_tag = ''
|
||||
# un-comment the following six lines for the Edmonton Journal
|
||||
title = u'Edmonton Journal'
|
||||
url_prefix = 'http://www.edmontonjournal.com'
|
||||
description = u'News from Edmonton, AB'
|
||||
std_logo_url = 'http://www.edmontonjournal.com/images/logo_edmontonjournal.jpg'
|
||||
logo_url = 'ejlogo.jpg'
|
||||
fp_tag = 'CAN_EJ'
|
||||
|
||||
# un-comment the following four lines for the Saskatoon Star-Phoenix
|
||||
## title = u'Saskatoon Star-Phoenix'
|
||||
## url_prefix = 'http://www.thestarphoenix.com'
|
||||
## description = u'News from Saskatoon, SK'
|
||||
## fp_tag = ''
|
||||
|
||||
# un-comment the following four lines for the Windsor Star
|
||||
## title = u'Windsor Star'
|
||||
## url_prefix = 'http://www.windsorstar.com'
|
||||
## description = u'News from Windsor, ON'
|
||||
## fp_tag = 'CAN_'
|
||||
|
||||
# un-comment the following four lines for the Ottawa Citizen
|
||||
# un-comment the following six lines for the Ottawa Citizen
|
||||
## title = u'Ottawa Citizen'
|
||||
## url_prefix = 'http://www.ottawacitizen.com'
|
||||
## description = u'News from Ottawa, ON'
|
||||
## std_logo_url = 'http://www.ottawacitizen.com/images/logo_ottawacitizen.jpg'
|
||||
## logo_url = 'oclogo.jpg'
|
||||
## fp_tag = 'CAN_OC'
|
||||
|
||||
# un-comment the following four lines for the Montreal Gazette
|
||||
# un-comment the following six lines for the Montreal Gazette
|
||||
## title = u'Montreal Gazette'
|
||||
## url_prefix = 'http://www.montrealgazette.com'
|
||||
## description = u'News from Montreal, QC'
|
||||
## std_logo_url = 'http://www.montrealgazette.com/images/logo_montrealgazette.jpg'
|
||||
## logo_url = 'mglogo.jpg'
|
||||
## fp_tag = 'CAN_MG'
|
||||
|
||||
Kindle_Fire=False
|
||||
masthead_url = std_logo_url
|
||||
|
||||
url_list = []
|
||||
language = 'en_CA'
|
||||
__author__ = 'Nick Redding'
|
||||
no_stylesheets = True
|
||||
timefmt = ' [%b %d]'
|
||||
timefmt = ' [%b %d]'
|
||||
encoding = 'utf-8'
|
||||
extra_css = '''
|
||||
.timestamp { font-size:xx-small; display: block; }
|
||||
#storyheader { font-size: medium; }
|
||||
#storyheader h1 { font-size: x-large; }
|
||||
#storyheader h2 { font-size: large; font-style: italic; }
|
||||
#storyheader h2 { font-size: small; font-style: italic; }
|
||||
.byline { font-size:xx-small; }
|
||||
#photocaption { font-size: small; font-style: italic }
|
||||
#photocredit { font-size: xx-small; }'''
|
||||
keep_only_tags = [dict(name='div', attrs={'id':'storyheader'}),dict(name='div', attrs={'id':'storycontent'})]
|
||||
#photocaption { font-size: xx-small; font-style: italic; font-weight: normal; }
|
||||
.photocaption { font-size: xx-small; font-style: italic; font-weight: normal; }
|
||||
#photocredit { font-size: xx-small; font-weight: normal; }'''
|
||||
|
||||
keep_only_tags = [dict(name='div', attrs={'id':'storyheader'}),dict(name='div', attrs={'id':'storycontent'}),dict(name='div', attrs={'id':'storyphoto'})]
|
||||
|
||||
remove_tags = [{'class':'comments'},
|
||||
dict(name='div', attrs={'class':'navbar'}),dict(name='div', attrs={'class':'morelinks'}),
|
||||
dict(name='h2', attrs={'id':'photocredit'}),
|
||||
dict(name='div', attrs={'class':'viewmore'}),dict(name='li', attrs={'class':'email'}),
|
||||
dict(name='div', attrs={'class':'story_tool_hr'}),dict(name='div', attrs={'class':'clear'}),
|
||||
dict(name='div', attrs={'class':'story_tool'}),dict(name='div', attrs={'class':'copyright'}),
|
||||
dict(name='div', attrs={'class':'rule_grey_solid'}),
|
||||
dict(name='li', attrs={'class':'print'}),dict(name='li', attrs={'class':'share'}),dict(name='ul', attrs={'class':'bullet'})]
|
||||
|
||||
|
||||
def get_cover_url(self):
|
||||
from datetime import timedelta, date
|
||||
if self.fp_tag=='':
|
||||
return None
|
||||
cover = 'http://webmedia.newseum.org/newseum-multimedia/dfp/jpg'+str(date.today().day)+'/lg/'+self.fp_tag+'.jpg'
|
||||
br = BasicNewsRecipe.get_browser()
|
||||
daysback=1
|
||||
@ -120,6 +151,18 @@ class CanWestPaper(BasicNewsRecipe):
|
||||
cover = None
|
||||
return cover
|
||||
|
||||
def prepare_masthead_image(self, path_to_image, out_path):
|
||||
if self.Kindle_Fire:
|
||||
from calibre.utils.magick import Image, create_canvas
|
||||
img = Image()
|
||||
img.open(path_to_image)
|
||||
width, height = img.size
|
||||
img2 = create_canvas(width, height)
|
||||
img2.compose(img)
|
||||
img2.save(out_path)
|
||||
else:
|
||||
BasicNewsRecipe.prepare_masthead_image(self, path_to_image, out_path)
|
||||
|
||||
def fixChars(self,string):
|
||||
# Replace lsquo (\x91)
|
||||
fixed = re.sub("\x91","‘",string)
|
||||
@ -166,55 +209,106 @@ class CanWestPaper(BasicNewsRecipe):
|
||||
a.replaceWith(a.renderContents().decode('cp1252','replace'))
|
||||
return soup
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
|
||||
def preprocess_html(self,soup):
|
||||
#delete empty id attributes--they screw up the TOC for unknown reasons
|
||||
divtags = soup.findAll('div',attrs={'id':''})
|
||||
if divtags:
|
||||
for div in divtags:
|
||||
del(div['id'])
|
||||
|
||||
pgall = soup.find('div',attrs={'id':'storyphoto'})
|
||||
if pgall is not None: # photo gallery perhaps
|
||||
if (soup.find('div',attrs={'id':'storycontent'}) is None):
|
||||
allpics = Tag(soup,'div')
|
||||
first_img = pgall.find('div','storyimage')
|
||||
if first_img is not None:
|
||||
first_img.extract()
|
||||
tlist = pgall.find('div',attrs={'id':'relatedthumbs'})
|
||||
if tlist is not None:
|
||||
for atag in tlist.findAll('a'):
|
||||
img = Tag(soup,'img')
|
||||
srcpre, sep, srcpost = atag.img['src'].partition('?')
|
||||
img['src'] = srcpre
|
||||
pdesc = Tag(soup,'p')
|
||||
pdesc.insert(0,atag.img['alt'])
|
||||
pdesc['class']='photocaption'
|
||||
div = Tag(soup,'div')
|
||||
div.insert(0,pdesc)
|
||||
div.insert(0,img)
|
||||
allpics.append(div)
|
||||
pgall.replaceWith(allpics)
|
||||
|
||||
for pg in soup.findAll('div',attrs={'id':'storyphoto'}):
|
||||
pg.extract()
|
||||
return self.strip_anchors(soup)
|
||||
|
||||
|
||||
|
||||
def parse_index(self):
|
||||
soup = self.index_to_soup(self.url_prefix+'/news/todays-paper/index.html')
|
||||
|
||||
articles = {}
|
||||
key = 'News'
|
||||
ans = ['News']
|
||||
ans = []
|
||||
|
||||
# Find each instance of class="sectiontitle", class="featurecontent"
|
||||
for divtag in soup.findAll('div',attrs={'class' : ["section_title02","featurecontent"]}):
|
||||
#self.log(" div class = %s" % divtag['class'])
|
||||
if divtag['class'].startswith('section_title'):
|
||||
# div contains section title
|
||||
if not divtag.h3:
|
||||
continue
|
||||
key = self.tag_to_string(divtag.h3,False)
|
||||
ans.append(key)
|
||||
self.log("Section name %s" % key)
|
||||
continue
|
||||
# div contains article data
|
||||
h1tag = divtag.find('h1')
|
||||
if not h1tag:
|
||||
continue
|
||||
atag = h1tag.find('a',href=True)
|
||||
if not atag:
|
||||
continue
|
||||
url = self.url_prefix+'/news/todays-paper/'+atag['href']
|
||||
#self.log("Section %s" % key)
|
||||
#self.log("url %s" % url)
|
||||
title = self.tag_to_string(atag,False)
|
||||
#self.log("title %s" % title)
|
||||
pubdate = ''
|
||||
description = ''
|
||||
ptag = divtag.find('p');
|
||||
if ptag:
|
||||
description = self.tag_to_string(ptag,False)
|
||||
#self.log("description %s" % description)
|
||||
author = ''
|
||||
autag = divtag.find('h4')
|
||||
if autag:
|
||||
author = self.tag_to_string(autag,False)
|
||||
#self.log("author %s" % author)
|
||||
if not articles.has_key(key):
|
||||
articles[key] = []
|
||||
articles[key].append(dict(title=title,url=url,date=pubdate,description=description,author=author,content=''))
|
||||
|
||||
def handle_article(adiv,key):
|
||||
h1tag = adiv.h1
|
||||
if h1tag is not None:
|
||||
atag = h1tag.a
|
||||
if atag is not None:
|
||||
url = atag['href']
|
||||
if atag['href'].startswith('http'):
|
||||
return
|
||||
elif atag['href'].startswith('/'):
|
||||
url = self.url_prefix+atag['href']
|
||||
else:
|
||||
url = self.url_prefix+'/'+atag['href']
|
||||
if url in self.url_list:
|
||||
return
|
||||
self.url_list.append(url)
|
||||
title = self.tag_to_string(atag,False)
|
||||
if 'VIDEO' in title.upper():
|
||||
return
|
||||
if 'GALLERY' in title.upper():
|
||||
return
|
||||
if 'PHOTOS' in title.upper():
|
||||
return
|
||||
dtag = adiv.find('div','content')
|
||||
description=''
|
||||
print("URL "+url)
|
||||
print("TITLE "+title)
|
||||
if dtag is not None:
|
||||
stag = dtag.span
|
||||
if stag is not None:
|
||||
if stag['class'] != 'timestamp':
|
||||
description = self.tag_to_string(stag,False)
|
||||
else:
|
||||
description = self.tag_to_string(dtag,False)
|
||||
print("DESCRIPTION: "+description)
|
||||
if not articles.has_key(key):
|
||||
articles[key] = []
|
||||
articles[key].append(dict(title=title,url=url,date='',description=description,author='',content=''))
|
||||
|
||||
def parse_web_index(key, keyurl):
|
||||
try:
|
||||
soup = self.index_to_soup(self.url_prefix+keyurl)
|
||||
except:
|
||||
return
|
||||
ans.append(key)
|
||||
mainsoup = soup.find('div','bodywrapper')
|
||||
footer = mainsoup.find(attrs={'id':'footerfeature'})
|
||||
if footer is not None:
|
||||
footer.extract()
|
||||
print("Section: "+key)
|
||||
for wdiv in mainsoup.findAll('div',attrs={'id':re.compile('^HorizontalFeatureSlider_1_Story')}):
|
||||
handle_article(wdiv,key)
|
||||
wdiv.extract()
|
||||
for wdiv in mainsoup.findAll(attrs={'id':['featurewidget','textfeature','textlinks_timestamp']}):
|
||||
for adiv in wdiv.findAll('div','featurecontent'):
|
||||
handle_article(adiv,key)
|
||||
|
||||
for (k,url) in self.postmedia_index_pages:
|
||||
parse_web_index(k,url)
|
||||
ans = [(key, articles[key]) for key in ans if articles.has_key(key)]
|
||||
return ans
|
||||
|
||||
|
18
recipes/ekundelek_pl.recipe
Normal file
@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = u'2012, Artur Stachecki <artur.stachecki@gmail.com>'
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class swiatczytnikow(BasicNewsRecipe):
|
||||
title = u'eKundelek'
|
||||
description = u'Najsympatyczniejszy blog o e-czytnikach Kindle'
|
||||
language = 'pl'
|
||||
__author__ = u'Artur Stachecki'
|
||||
oldest_article = 7
|
||||
max_articles_per_feed = 100
|
||||
|
||||
remove_tags = [dict(name = 'div', attrs = {'class' : 'feedflare'})]
|
||||
|
||||
feeds = [(u'Wpisy', u'http://feeds.feedburner.com/Ekundelekpl?format=xml')]
|
@ -18,15 +18,15 @@ class AdvancedUserRecipe1325006965(BasicNewsRecipe):
|
||||
keep_only_tags = [
|
||||
dict(name='h1'),
|
||||
dict(name='img',attrs={'id' : 'ctl00_Body_imgMainImage'}),
|
||||
dict(name='div',attrs={'id' : ['articleLeft']}),
|
||||
dict(name='div',attrs={'class' : ['imagesCenterArticle','containerCenterArticle','articleBody']}),
|
||||
dict(name='div',attrs={'id' : ['profileLeft','articleLeft','profileRight','profileBody']}),
|
||||
dict(name='div',attrs={'class' : ['imagesCenterArticle','containerCenterArticle','articleBody',]}),
|
||||
|
||||
]
|
||||
|
||||
#remove_tags = [
|
||||
#dict(attrs={'class' : ['player']}),
|
||||
remove_tags = [
|
||||
dict(attrs={'id' : ['ctl00_Body_divSlideShow' ]}),
|
||||
|
||||
#]
|
||||
]
|
||||
feeds = [
|
||||
(u'Homepage 1',u'http://feed43.com/6655867614547036.xml'),
|
||||
(u'Homepage 2',u'http://feed43.com/4167731873103110.xml'),
|
||||
@ -34,7 +34,7 @@ class AdvancedUserRecipe1325006965(BasicNewsRecipe):
|
||||
(u'Homepage 4',u'http://feed43.com/6550421522527341.xml'),
|
||||
(u'Funny - The Very Best Of The Internet',u'http://feed43.com/4538510106331565.xml'),
|
||||
(u'Gaming',u'http://feed43.com/6537162612465672.xml'),
|
||||
(u'Girls',u'http://feed43.com/3674777224513254.xml'),
|
||||
(u'Girls',u'http://feed43.com/4574262733341068.xml'),# edit link http://feed43.com/feed.html?name=4574262733341068
|
||||
]
|
||||
|
||||
extra_css = '''
|
||||
|
@ -1,6 +1,7 @@
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class Filmweb_pl(BasicNewsRecipe):
|
||||
import re
|
||||
from calibre.ebooks.BeautifulSoup import BeautifulSoup
|
||||
class FilmWebPl(BasicNewsRecipe):
|
||||
title = u'FilmWeb'
|
||||
__author__ = 'fenuks'
|
||||
description = 'FilmWeb - biggest polish movie site'
|
||||
@ -12,8 +13,9 @@ class Filmweb_pl(BasicNewsRecipe):
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets= True
|
||||
remove_empty_feeds=True
|
||||
preprocess_regexps = [(re.compile(u'\(kliknij\,\ aby powiększyć\)', re.IGNORECASE), lambda m: ''), ]#(re.compile(ur' | ', re.IGNORECASE), lambda m: '')]
|
||||
extra_css = '.hdrBig {font-size:22px;} ul {list-style-type:none; padding: 0; margin: 0;}'
|
||||
remove_tags= [dict(name='div', attrs={'class':['recommendOthers']}), dict(name='ul', attrs={'class':'fontSizeSet'})]
|
||||
remove_tags= [dict(name='div', attrs={'class':['recommendOthers']}), dict(name='ul', attrs={'class':'fontSizeSet'}), dict(attrs={'class':'userSurname anno'})]
|
||||
keep_only_tags= [dict(name='h1', attrs={'class':['hdrBig', 'hdrEntity']}), dict(name='div', attrs={'class':['newsInfo', 'newsInfoSmall', 'reviewContent description']})]
|
||||
feeds = [(u'Wszystkie newsy', u'http://www.filmweb.pl/feed/news/latest'),
|
||||
(u'News / Filmy w produkcji', 'http://www.filmweb.pl/feed/news/category/filminproduction'),
|
||||
@ -31,18 +33,22 @@ class Filmweb_pl(BasicNewsRecipe):
|
||||
(u'News / Kino polskie', u'http://www.filmweb.pl/feed/news/category/polish.cinema'),
|
||||
(u'News / Telewizja', u'http://www.filmweb.pl/feed/news/category/tv'),
|
||||
(u'Recenzje redakcji', u'http://www.filmweb.pl/feed/reviews/latest'),
|
||||
(u'Recenzje użytkowników', u'http://www.filmweb.pl/feed/user-reviews/latest')]
|
||||
(u'Recenzje użytkowników', u'http://www.filmweb.pl/feed/user-reviews/latest')
|
||||
]
|
||||
|
||||
def skip_ad_pages(self, soup):
|
||||
def skip_ad_pages(self, soup):
|
||||
skip_tag = soup.find('a', attrs={'class':'welcomeScreenButton'})
|
||||
if skip_tag is not None:
|
||||
self.log.warn('skip_tag')
|
||||
self.log.warn(skip_tag)
|
||||
return self.index_to_soup(skip_tag['href'], raw=True)
|
||||
|
||||
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
for a in soup('a'):
|
||||
if a.has_key('href') and 'http://' not in a['href'] and 'https://' not in a['href']:
|
||||
a['href']=self.index + a['href']
|
||||
return soup
|
||||
for i in soup.findAll('a', attrs={'class':'fn'}):
|
||||
i.insert(len(i), BeautifulSoup('<br />'))
|
||||
for i in soup.findAll('sup'):
|
||||
if not i.string or i.string.startswith('(kliknij'):
|
||||
i.extract()
|
||||
return soup
|
||||
|
@ -1,6 +1,6 @@
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
|
||||
class Gry_online_pl(BasicNewsRecipe):
|
||||
class GryOnlinePl(BasicNewsRecipe):
|
||||
title = u'Gry-Online.pl'
|
||||
__author__ = 'fenuks'
|
||||
description = 'Gry-Online.pl - computer games'
|
||||
@ -21,17 +21,18 @@ class Gry_online_pl(BasicNewsRecipe):
|
||||
tag = appendtag.find('div', attrs={'class':'n5p'})
|
||||
if tag:
|
||||
nexturls=tag.findAll('a')
|
||||
for nexturl in nexturls[1:]:
|
||||
try:
|
||||
soup2 = self.index_to_soup('http://www.gry-online.pl/S020.asp'+ nexturl['href'])
|
||||
except:
|
||||
soup2 = self.index_to_soup('http://www.gry-online.pl/S022.asp'+ nexturl['href'])
|
||||
url_part = soup.find('link', attrs={'rel':'canonical'})['href']
|
||||
url_part = url_part[25:].rpartition('?')[0]
|
||||
for nexturl in nexturls[1:-1]:
|
||||
soup2 = self.index_to_soup('http://www.gry-online.pl/' + url_part + nexturl['href'])
|
||||
pagetext = soup2.find(attrs={'class':'gc660'})
|
||||
for r in pagetext.findAll(name='header'):
|
||||
r.extract()
|
||||
for r in pagetext.findAll(attrs={'itemprop':'description'}):
|
||||
r.extract()
|
||||
pos = len(appendtag.contents)
|
||||
appendtag.insert(pos, pagetext)
|
||||
for r in appendtag.findAll(attrs={'class':['n5p', 'add-info', 'twitter-share-button']}):
|
||||
for r in appendtag.findAll(attrs={'class':['n5p', 'add-info', 'twitter-share-button', 'lista lista3 lista-gry']}):
|
||||
r.extract()
|
||||
|
||||
|
||||
|
44
recipes/high_country_blogs.recipe
Normal file
@ -0,0 +1,44 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>, Armin Geller'
|
||||
|
||||
'''
|
||||
Fetch High Country News - Blogs
|
||||
'''
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
class HighCountryNewsBlogs(BasicNewsRecipe):
|
||||
|
||||
title = u'High Country News - Blogs'
|
||||
description = u'High Country News - Blogs (RSS Version)'
|
||||
__author__ = 'Armin Geller' # 2012-08-01
|
||||
publisher = 'High Country News'
|
||||
category = 'news, politics, Germany'
|
||||
timefmt = ' [%a, %d %b %Y]'
|
||||
language = 'en'
|
||||
encoding = 'UTF-8'
|
||||
publication_type = 'newspaper'
|
||||
oldest_article = 7
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets = True
|
||||
auto_cleanup = True
|
||||
remove_javascript = True
|
||||
use_embedded_content = False
|
||||
masthead_url = 'http://www.hcn.org/logo.jpg'
|
||||
cover_source = 'http://www.hcn.org'
|
||||
|
||||
def get_cover_url(self):
|
||||
cover_source_soup = self.index_to_soup(self.cover_source)
|
||||
preview_image_div = cover_source_soup.find(attrs={'class':' portaltype-Plone Site content--hcn template-homepage_view'})
|
||||
return preview_image_div.div.img['src']
|
||||
|
||||
feeds = [
|
||||
(u'From the Blogs', u'http://feeds.feedburner.com/hcn/FromTheBlogs?format=xml'),
|
||||
|
||||
(u'Heard around the West', u'http://feeds.feedburner.com/hcn/heard?format=xml'),
|
||||
(u'The GOAT Blog', u'http://feeds.feedburner.com/hcn/goat?format=xml'),
|
||||
(u'The Range', u'http://feeds.feedburner.com/hcn/range?format=xml'),
|
||||
]
|
||||
|
||||
def print_version(self, url):
|
||||
return url
|
||||
|
BIN
recipes/icons/conowego_pl.png
Normal file
After Width: | Height: | Size: 694 B |
BIN
recipes/icons/dziennik_polski.png
Normal file
After Width: | Height: | Size: 757 B |
BIN
recipes/icons/linux_journal.png
Normal file
After Width: | Height: | Size: 443 B |
36
recipes/linux_journal.recipe
Executable file
@ -0,0 +1,36 @@
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class LinuxJournal(BasicNewsRecipe):
|
||||
title = u'Linux Journal'
|
||||
__author__ = 'fenuks'
|
||||
description = u'The monthly magazine of the Linux community, promoting the use of Linux worldwide.'
|
||||
cover_url = 'http://www.linuxjournal.com/files/linuxjournal.com/ufiles/logo-lj.jpg'
|
||||
category = 'IT, Linux'
|
||||
language = 'en'
|
||||
oldest_article = 7
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
remove_empty_feeds = True
|
||||
keep_only_tags=[dict(id='content-inner')]
|
||||
remove_tags_after= dict(attrs={'class':'user-signature clear-block'})
|
||||
remove_tags=[dict(attrs={'class':['user-signature clear-block', 'breadcrumb', 'terms terms-inline']})]
|
||||
feeds = [(u'Front Page', u'http://feeds.feedburner.com/linuxjournalcom'), (u'News', u'http://feeds.feedburner.com/LinuxJournal-BreakingNews'), (u'Blogs', u'http://www.linuxjournal.com/blog/feed'), (u'Audio/Video', u'http://www.linuxjournal.com/taxonomy/term/28/0/feed'), (u'Community', u'http://www.linuxjournal.com/taxonomy/term/18/0/feed'), (u'Education', u'http://www.linuxjournal.com/taxonomy/term/25/0/feed'), (u'Embedded', u'http://www.linuxjournal.com/taxonomy/term/27/0/feed'), (u'Hardware', u'http://www.linuxjournal.com/taxonomy/term/23/0/feed'), (u'HOWTOs', u'http://www.linuxjournal.com/taxonomy/term/19/0/feed'), (u'International', u'http://www.linuxjournal.com/taxonomy/term/30/0/feed'), (u'Security', u'http://www.linuxjournal.com/taxonomy/term/31/0/feed'), (u'Software', u'http://www.linuxjournal.com/taxonomy/term/17/0/feed'), (u'Sysadmin', u'http://www.linuxjournal.com/taxonomy/term/21/0/feed'), (u'Webmaster', u'http://www.linuxjournal.com/taxonomy/term/24/0/feed')]
|
||||
|
||||
def append_page(self, soup, appendtag):
|
||||
next = appendtag.find('li', attrs={'class':'pager-next'})
|
||||
while next:
|
||||
nexturl = next.a['href']
|
||||
appendtag.find('div', attrs={'class':'links'}).extract()
|
||||
soup2 = self.index_to_soup('http://www.linuxjournal.com'+ nexturl)
|
||||
pagetext = soup2.find(attrs={'class':'node-inner'}).find(attrs={'class':'content'})
|
||||
next = appendtag.find('li', attrs={'class':'pager-next'})
|
||||
pos = len(appendtag.contents)
|
||||
appendtag.insert(pos, pagetext)
|
||||
tag = appendtag.find('div', attrs={'class':'links'})
|
||||
if tag:
|
||||
tag.extract()
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
self.append_page(soup, soup.body)
|
||||
return soup
|
33
recipes/list_apart.recipe
Normal file
@ -0,0 +1,33 @@
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class AListApart (BasicNewsRecipe):
|
||||
__author__ = u'Marc Busqué <marc@lamarciana.com>'
|
||||
__url__ = 'http://www.lamarciana.com'
|
||||
__version__ = '1.0'
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = u'2012, Marc Busqué <marc@lamarciana.com>'
|
||||
title = u'A List Apart'
|
||||
description = u'A List Apart Magazine (ISSN: 1534-0295) explores the design, development, and meaning of web content, with a special focus on web standards and best practices.'
|
||||
language = 'en'
|
||||
tags = 'web development, software'
|
||||
oldest_article = 120
|
||||
remove_empty_feeds = True
|
||||
no_stylesheets = True
|
||||
encoding = 'utf8'
|
||||
cover_url = u'http://alistapart.com/pix/alalogo.gif'
|
||||
keep_only_tags = [
|
||||
dict(name='div', attrs={'id': 'content'})
|
||||
]
|
||||
remove_tags = [
|
||||
dict(name='ul', attrs={'id': 'metastuff'}),
|
||||
dict(name='div', attrs={'class': 'discuss'}),
|
||||
dict(name='div', attrs={'class': 'discuss'}),
|
||||
dict(name='div', attrs={'id': 'learnmore'}),
|
||||
]
|
||||
remove_attributes = ['border', 'cellspacing', 'align', 'cellpadding', 'colspan', 'valign', 'vspace', 'hspace', 'alt', 'width', 'height']
|
||||
extra_css = u'img {max-width: 100%; display: block; margin: auto;} #authorbio img {float: left; margin-right: 2%;}'
|
||||
|
||||
feeds = [
|
||||
(u'A List Apart', u'http://www.alistapart.com/site/rss'),
|
||||
]
|
@ -1,31 +1,42 @@
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
class AdvancedUserRecipe1306097511(BasicNewsRecipe):
|
||||
title = u'Metro UK'
|
||||
description = 'News as provide by The Metro -UK'
|
||||
description = 'Author Dave Asbury : News as provide by The Metro -UK'
|
||||
#timefmt = ''
|
||||
__author__ = 'Dave Asbury'
|
||||
#last update 9/6/12
|
||||
#last update 4/8/12
|
||||
cover_url = 'http://profile.ak.fbcdn.net/hprofile-ak-snc4/276636_117118184990145_2132092232_n.jpg'
|
||||
#no_stylesheets = True
|
||||
no_stylesheets = True
|
||||
oldest_article = 1
|
||||
max_articles_per_feed = 10
|
||||
max_articles_per_feed = 12
|
||||
remove_empty_feeds = True
|
||||
remove_javascript = True
|
||||
auto_cleanup = True
|
||||
#auto_cleanup = True
|
||||
encoding = 'UTF-8'
|
||||
|
||||
cover_url ='http://profile.ak.fbcdn.net/hprofile-ak-snc4/157897_117118184990145_840702264_n.jpg'
|
||||
language = 'en_GB'
|
||||
masthead_url = 'http://e-edition.metro.co.uk/images/metro_logo.gif'
|
||||
extra_css = '''
|
||||
h1{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:1.6em;}
|
||||
h2{font-family:Arial,Helvetica,sans-serif; font-weight:normal;font-size:1.2em;}
|
||||
p{font-family:Arial,Helvetica,sans-serif;font-size:1.0em;}
|
||||
body{font-family:Helvetica,Arial,sans-serif;font-size:1.0em;}
|
||||
'''
|
||||
keep_only_tags = [
|
||||
|
||||
]
|
||||
#dict(name='h1'),
|
||||
#dict(name='h2'),
|
||||
#dict(name='div', attrs={'class' : ['row','article','img-cnt figure','clrd']})
|
||||
#dict(name='h3'),
|
||||
#dict(attrs={'class' : 'BText'}),
|
||||
]
|
||||
remove_tags = [
|
||||
|
||||
dict(name='span',attrs={'class' : 'share'}),
|
||||
dict(name='li'),
|
||||
dict(attrs={'class' : ['twitter-share-button','header-forms','hdr-lnks','close','art-rgt','fd-gr1-b clrd google-article','news m12 clrd clr-b p5t shareBtm','item-ds csl-3-img news','c-1of3 c-last','c-1of1','pd','item-ds csl-3-img sport']}),
|
||||
dict(attrs={'id' : ['','sky-left','sky-right','ftr-nav','and-ftr','notificationList','logo','miniLogo','comments-news','metro_extras']})
|
||||
]
|
||||
|
||||
remove_tags_before = dict(name='h1')
|
||||
#remove_tags_after = dict(attrs={'id':['topic-buttons']})
|
||||
|
||||
feeds = [
|
||||
(u'News', u'http://www.metro.co.uk/rss/news/'), (u'Money', u'http://www.metro.co.uk/rss/money/'), (u'Sport', u'http://www.metro.co.uk/rss/sport/'), (u'Film', u'http://www.metro.co.uk/rss/metrolife/film/'), (u'Music', u'http://www.metro.co.uk/rss/metrolife/music/'), (u'TV', u'http://www.metro.co.uk/rss/tv/'), (u'Showbiz', u'http://www.metro.co.uk/rss/showbiz/'), (u'Weird News', u'http://www.metro.co.uk/rss/weird/'), (u'Travel', u'http://www.metro.co.uk/rss/travel/'), (u'Lifestyle', u'http://www.metro.co.uk/rss/lifestyle/'), (u'Books', u'http://www.metro.co.uk/rss/lifestyle/books/'), (u'Food', u'http://www.metro.co.uk/rss/lifestyle/restaurants/')]
|
||||
extra_css = '''
|
||||
body{ text-align: justify; font-family:Arial,Helvetica,sans-serif; font-size:11px; font-size-adjust:none; font-stretch:normal; font-style:normal; font-variant:normal; font-weight:normal;}
|
||||
'''
|
||||
|
@ -1,48 +1,314 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
__license__ = 'GPL v3'
|
||||
|
||||
'''
|
||||
www.canada.com
|
||||
'''
|
||||
|
||||
import re
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
from calibre.ebooks.BeautifulSoup import BeautifulStoneSoup, Tag
|
||||
|
||||
|
||||
class CanWestPaper(BasicNewsRecipe):
|
||||
|
||||
# un-comment the following three lines for the Montreal Gazette
|
||||
postmedia_index_pages = [
|
||||
(u'Headlines',u'/index.html'),
|
||||
(u'Ottawa & Area',u'/news/ottawa/index.html'),
|
||||
(u'Vancouver',u'/news/vancouver/index.html'),
|
||||
(u'Calgary',u'/news/calgary/index.html'),
|
||||
(u'Edmonton',u'/news/edmonton/index.html'),
|
||||
(u'Montreal',u'/news/montreal/index.html'),
|
||||
(u'Fraser Valley',u'/news/fraser-valley/index.html'),
|
||||
(u'British Columbia',u'/news/bc/index.html'),
|
||||
(u'Alberta',u'/news/alberta/index.html'),
|
||||
(u'Canada',u'/news/canada/index.html'),
|
||||
(u'National',u'/news/national/index.html'),
|
||||
(u'Politics',u'/news/politics/index.html'),
|
||||
(u'Insight',u'/news/insight/index.html'),
|
||||
(u'Special Reports',u'/news/specialreports/index.html'),
|
||||
(u'Gangs',u'/news/gangs/index.html'),
|
||||
(u'Education',u'/news/education/index.html'),
|
||||
(u'Health',u'/news/health/index.html'),
|
||||
(u'Environment',u'/news/environment/index.html'),
|
||||
(u'World',u'/news/world/index.html'),
|
||||
(u'Police Blotter',u'/news/crime-and-justice/index.html'),
|
||||
(u'Crime',u'/news/blotter/index.html'),
|
||||
(u'Around Town',u'/news/topic.html?t=keyword&q=Around+Town'),
|
||||
(u'Diplomatica',u'/news/diplomatica/index.html'),
|
||||
(u'Opinion',u'/opinion/index.html'),
|
||||
(u'Columnists',u'/columnists/index.html'),
|
||||
(u'Editorials',u'/opinion/editorials/index.html'),
|
||||
(u'Letters',u'/opinion/letters/index.html'),
|
||||
(u'Business',u'/business/index.html'),
|
||||
(u'Sports',u'/sports/index.html'),
|
||||
(u'Arts',u'/entertainment/index.html'),
|
||||
(u'Life',u'/life/index.html'),
|
||||
(u'Technology',u'/technology/index.html'),
|
||||
(u'Travel',u'/travel/index.html'),
|
||||
(u'Health',u'/health/index.html')
|
||||
]
|
||||
|
||||
|
||||
# un-comment the following six lines for the Vancouver Province
|
||||
## title = u'Vancouver Province'
|
||||
## url_prefix = 'http://www.theprovince.com'
|
||||
## description = u'News from Vancouver, BC'
|
||||
## std_logo_url = 'http://www.theprovince.com/images/logo_theprovince.jpg'
|
||||
## logo_url = 'vplogo.jpg'
|
||||
## fp_tag = 'CAN_TP'
|
||||
|
||||
# un-comment the following six lines for the Vancouver Sun
|
||||
## title = u'Vancouver Sun'
|
||||
## url_prefix = 'http://www.vancouversun.com'
|
||||
## description = u'News from Vancouver, BC'
|
||||
## std_logo_url = 'http://www.vancouversun.com/images/logo_vancouversun.jpg'
|
||||
## logo_url = 'vslogo.jpg'
|
||||
## fp_tag = 'CAN_VS'
|
||||
|
||||
# un-comment the following six lines for the Calgary Herald
|
||||
## title = u'Calgary Herald'
|
||||
## url_prefix = 'http://www.calgaryherald.com'
|
||||
## description = u'News from Calgary, AB'
|
||||
## std_logo_url = 'http://www.calgaryherald.com/images/logo_calgaryherald.jpg'
|
||||
## logo_url = 'chlogo.jpg'
|
||||
## fp_tag = 'CAN_CH'
|
||||
|
||||
# un-comment the following six lines for the Edmonton Journal
|
||||
## title = u'Edmonton Journal'
|
||||
## url_prefix = 'http://www.edmontonjournal.com'
|
||||
## description = u'News from Edmonton, AB'
|
||||
## std_logo_url = 'http://www.edmontonjournal.com/images/logo_edmontonjournal.jpg'
|
||||
## logo_url = 'ejlogo.jpg'
|
||||
## fp_tag = 'CAN_EJ'
|
||||
|
||||
# un-comment the following six lines for the Ottawa Citizen
|
||||
## title = u'Ottawa Citizen'
|
||||
## url_prefix = 'http://www.ottawacitizen.com'
|
||||
## description = u'News from Ottawa, ON'
|
||||
## std_logo_url = 'http://www.ottawacitizen.com/images/logo_ottawacitizen.jpg'
|
||||
## logo_url = 'oclogo.jpg'
|
||||
## fp_tag = 'CAN_OC'
|
||||
|
||||
# un-comment the following six lines for the Montreal Gazette
|
||||
title = u'Montreal Gazette'
|
||||
url_prefix = 'http://www.montrealgazette.com'
|
||||
description = u'News from Montreal, QC'
|
||||
std_logo_url = 'http://www.montrealgazette.com/images/logo_montrealgazette.jpg'
|
||||
logo_url = 'mglogo.jpg'
|
||||
fp_tag = 'CAN_MG'
|
||||
|
||||
Kindle_Fire=False
|
||||
masthead_url = std_logo_url
|
||||
|
||||
url_list = []
|
||||
language = 'en_CA'
|
||||
__author__ = 'Nick Redding'
|
||||
no_stylesheets = True
|
||||
auto_cleanup = True
|
||||
auto_cleanup_keep = '//*[@id="imageBox"]'
|
||||
timefmt = ' [%b %d]'
|
||||
timefmt = ' [%b %d]'
|
||||
encoding = 'utf-8'
|
||||
extra_css = '''
|
||||
.timestamp { font-size:xx-small; display: block; }
|
||||
#storyheader { font-size: medium; }
|
||||
#storyheader h1 { font-size: x-large; }
|
||||
#storyheader h2 { font-size: large; font-style: italic; }
|
||||
#storyheader h2 { font-size: small; font-style: italic; }
|
||||
.byline { font-size:xx-small; }
|
||||
#photocaption { font-size: small; font-style: italic }
|
||||
#photocredit { font-size: xx-small; }'''
|
||||
|
||||
#photocaption { font-size: xx-small; font-style: italic; font-weight: normal; }
|
||||
.photocaption { font-size: xx-small; font-style: italic; font-weight: normal; }
|
||||
#photocredit { font-size: xx-small; font-weight: normal; }'''
|
||||
|
||||
keep_only_tags = [dict(name='div', attrs={'id':'storyheader'}),dict(name='div', attrs={'id':'storycontent'}),dict(name='div', attrs={'id':'storyphoto'})]
|
||||
|
||||
remove_tags = [{'class':'comments'},
|
||||
dict(name='div', attrs={'class':'navbar'}),dict(name='div', attrs={'class':'morelinks'}),
|
||||
dict(name='h2', attrs={'id':'photocredit'}),
|
||||
dict(name='div', attrs={'class':'viewmore'}),dict(name='li', attrs={'class':'email'}),
|
||||
dict(name='div', attrs={'class':'story_tool_hr'}),dict(name='div', attrs={'class':'clear'}),
|
||||
dict(name='div', attrs={'class':'story_tool'}),dict(name='div', attrs={'class':'copyright'}),
|
||||
dict(name='div', attrs={'class':'rule_grey_solid'}),
|
||||
dict(name='li', attrs={'class':'print'}),dict(name='li', attrs={'class':'share'}),dict(name='ul', attrs={'class':'bullet'})]
|
||||
|
||||
|
||||
feeds = [
|
||||
('News',
|
||||
'http://rss.canada.com/get/?F297'),
|
||||
('Sports',
|
||||
'http://rss.canada.com/get/?F299'),
|
||||
('Entertainment',
|
||||
'http://rss.canada.com/get/?F7366'),
|
||||
('Business',
|
||||
'http://rss.canada.com/get/?F6939'),
|
||||
]
|
||||
def get_cover_url(self):
|
||||
from datetime import timedelta, date
|
||||
cover = 'http://webmedia.newseum.org/newseum-multimedia/dfp/jpg'+str(date.today().day)+'/lg/'+self.fp_tag+'.jpg'
|
||||
br = BasicNewsRecipe.get_browser()
|
||||
daysback=1
|
||||
try:
|
||||
br.open(cover)
|
||||
except:
|
||||
while daysback<7:
|
||||
cover = 'http://webmedia.newseum.org/newseum-multimedia/dfp/jpg'+str((date.today() - timedelta(days=daysback)).day)+'/lg/'+self.fp_tag+'.jpg'
|
||||
br = BasicNewsRecipe.get_browser()
|
||||
try:
|
||||
br.open(cover)
|
||||
except:
|
||||
daysback = daysback+1
|
||||
continue
|
||||
break
|
||||
if daysback==7:
|
||||
self.log("\nCover unavailable")
|
||||
cover = None
|
||||
return cover
|
||||
|
||||
def prepare_masthead_image(self, path_to_image, out_path):
|
||||
if self.Kindle_Fire:
|
||||
from calibre.utils.magick import Image, create_canvas
|
||||
img = Image()
|
||||
img.open(path_to_image)
|
||||
width, height = img.size
|
||||
img2 = create_canvas(width, height)
|
||||
img2.compose(img)
|
||||
img2.save(out_path)
|
||||
else:
|
||||
BasicNewsRecipe.prepare_masthead_image(self, path_to_image, out_path)
|
||||
|
||||
def fixChars(self,string):
|
||||
# Replace lsquo (\x91)
|
||||
fixed = re.sub("\x91","‘",string)
|
||||
# Replace rsquo (\x92)
|
||||
fixed = re.sub("\x92","’",fixed)
|
||||
# Replace ldquo (\x93)
|
||||
fixed = re.sub("\x93","“",fixed)
|
||||
# Replace rdquo (\x94)
|
||||
fixed = re.sub("\x94","”",fixed)
|
||||
# Replace ndash (\x96)
|
||||
fixed = re.sub("\x96","–",fixed)
|
||||
# Replace mdash (\x97)
|
||||
fixed = re.sub("\x97","—",fixed)
|
||||
fixed = re.sub("’","’",fixed)
|
||||
return fixed
|
||||
|
||||
def massageNCXText(self, description):
|
||||
# Kindle TOC descriptions won't render certain characters
|
||||
if description:
|
||||
massaged = unicode(BeautifulStoneSoup(description, convertEntities=BeautifulStoneSoup.HTML_ENTITIES))
|
||||
# Replace '&' with '&'
|
||||
massaged = re.sub("&","&", massaged)
|
||||
return self.fixChars(massaged)
|
||||
else:
|
||||
return description
|
||||
|
||||
def populate_article_metadata(self, article, soup, first):
|
||||
if first:
|
||||
picdiv = soup.find('body').find('img')
|
||||
if picdiv is not None:
|
||||
self.add_toc_thumbnail(article,re.sub(r'links\\link\d+\\','',picdiv['src']))
|
||||
xtitle = article.text_summary.strip()
|
||||
if len(xtitle) == 0:
|
||||
desc = soup.find('meta',attrs={'property':'og:description'})
|
||||
if desc is not None:
|
||||
article.summary = article.text_summary = desc['content']
|
||||
|
||||
def strip_anchors(self,soup):
|
||||
paras = soup.findAll(True)
|
||||
for para in paras:
|
||||
aTags = para.findAll('a')
|
||||
for a in aTags:
|
||||
if a.img is None:
|
||||
a.replaceWith(a.renderContents().decode('cp1252','replace'))
|
||||
return soup
|
||||
|
||||
|
||||
|
||||
def preprocess_html(self,soup):
|
||||
#delete empty id attributes--they screw up the TOC for unknown reasons
|
||||
divtags = soup.findAll('div',attrs={'id':''})
|
||||
if divtags:
|
||||
for div in divtags:
|
||||
del(div['id'])
|
||||
|
||||
pgall = soup.find('div',attrs={'id':'storyphoto'})
|
||||
if pgall is not None: # photo gallery perhaps
|
||||
if (soup.find('div',attrs={'id':'storycontent'}) is None):
|
||||
allpics = Tag(soup,'div')
|
||||
first_img = pgall.find('div','storyimage')
|
||||
if first_img is not None:
|
||||
first_img.extract()
|
||||
tlist = pgall.find('div',attrs={'id':'relatedthumbs'})
|
||||
if tlist is not None:
|
||||
for atag in tlist.findAll('a'):
|
||||
img = Tag(soup,'img')
|
||||
srcpre, sep, srcpost = atag.img['src'].partition('?')
|
||||
img['src'] = srcpre
|
||||
pdesc = Tag(soup,'p')
|
||||
pdesc.insert(0,atag.img['alt'])
|
||||
pdesc['class']='photocaption'
|
||||
div = Tag(soup,'div')
|
||||
div.insert(0,pdesc)
|
||||
div.insert(0,img)
|
||||
allpics.append(div)
|
||||
pgall.replaceWith(allpics)
|
||||
|
||||
for pg in soup.findAll('div',attrs={'id':'storyphoto'}):
|
||||
pg.extract()
|
||||
return self.strip_anchors(soup)
|
||||
|
||||
|
||||
|
||||
def parse_index(self):
|
||||
|
||||
articles = {}
|
||||
ans = []
|
||||
|
||||
|
||||
def handle_article(adiv,key):
|
||||
h1tag = adiv.h1
|
||||
if h1tag is not None:
|
||||
atag = h1tag.a
|
||||
if atag is not None:
|
||||
url = atag['href']
|
||||
if atag['href'].startswith('http'):
|
||||
return
|
||||
elif atag['href'].startswith('/'):
|
||||
url = self.url_prefix+atag['href']
|
||||
else:
|
||||
url = self.url_prefix+'/'+atag['href']
|
||||
if url in self.url_list:
|
||||
return
|
||||
self.url_list.append(url)
|
||||
title = self.tag_to_string(atag,False)
|
||||
if 'VIDEO' in title.upper():
|
||||
return
|
||||
if 'GALLERY' in title.upper():
|
||||
return
|
||||
if 'PHOTOS' in title.upper():
|
||||
return
|
||||
dtag = adiv.find('div','content')
|
||||
description=''
|
||||
print("URL "+url)
|
||||
print("TITLE "+title)
|
||||
if dtag is not None:
|
||||
stag = dtag.span
|
||||
if stag is not None:
|
||||
if stag['class'] != 'timestamp':
|
||||
description = self.tag_to_string(stag,False)
|
||||
else:
|
||||
description = self.tag_to_string(dtag,False)
|
||||
print("DESCRIPTION: "+description)
|
||||
if not articles.has_key(key):
|
||||
articles[key] = []
|
||||
articles[key].append(dict(title=title,url=url,date='',description=description,author='',content=''))
|
||||
|
||||
def parse_web_index(key, keyurl):
|
||||
try:
|
||||
soup = self.index_to_soup(self.url_prefix+keyurl)
|
||||
except:
|
||||
return
|
||||
ans.append(key)
|
||||
mainsoup = soup.find('div','bodywrapper')
|
||||
footer = mainsoup.find(attrs={'id':'footerfeature'})
|
||||
if footer is not None:
|
||||
footer.extract()
|
||||
print("Section: "+key)
|
||||
for wdiv in mainsoup.findAll('div',attrs={'id':re.compile('^HorizontalFeatureSlider_1_Story')}):
|
||||
handle_article(wdiv,key)
|
||||
wdiv.extract()
|
||||
for wdiv in mainsoup.findAll(attrs={'id':['featurewidget','textfeature','textlinks_timestamp']}):
|
||||
for adiv in wdiv.findAll('div','featurecontent'):
|
||||
handle_article(adiv,key)
|
||||
|
||||
for (k,url) in self.postmedia_index_pages:
|
||||
parse_web_index(k,url)
|
||||
ans = [(key, articles[key]) for key in ans if articles.has_key(key)]
|
||||
return ans
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
import re
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class NaTemat(BasicNewsRecipe):
|
||||
@ -8,8 +9,9 @@ class NaTemat(BasicNewsRecipe):
|
||||
description = u'informacje, komentarze, opinie'
|
||||
category = 'news'
|
||||
language = 'pl'
|
||||
preprocess_regexps = [(re.compile(ur'Czytaj też\:.*?</a>', re.IGNORECASE), lambda m: ''), (re.compile(ur'Zobacz też\:.*?</a>', re.IGNORECASE), lambda m: ''), (re.compile(ur'Czytaj więcej\:.*?</a>', re.IGNORECASE), lambda m: ''), (re.compile(ur'Czytaj również\:.*?</a>', re.IGNORECASE), lambda m: '')]
|
||||
cover_url= 'http://blog.plona.pl/wp-content/uploads/2012/05/natemat.png'
|
||||
no_stylesheets = True
|
||||
keep_only_tags= [dict(id='main')]
|
||||
remove_tags= [dict(attrs={'class':['button', 'block-inside style_default', 'article-related']})]
|
||||
remove_tags= [dict(attrs={'class':['button', 'block-inside style_default', 'article-related', 'user-header', 'links']}), dict(name='img', attrs={'class':'indent'})]
|
||||
feeds = [(u'Artyku\u0142y', u'http://natemat.pl/rss/wszystkie')]
|
||||
|
@ -1,105 +1,136 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
|
||||
'''
|
||||
www.canada.com
|
||||
'''
|
||||
|
||||
import re
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
from calibre.ebooks.BeautifulSoup import BeautifulStoneSoup
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
from calibre.ebooks.BeautifulSoup import BeautifulStoneSoup, Tag
|
||||
|
||||
|
||||
class CanWestPaper(BasicNewsRecipe):
|
||||
|
||||
# un-comment the following four lines for the Victoria Times Colonist
|
||||
## title = u'Victoria Times Colonist'
|
||||
## url_prefix = 'http://www.timescolonist.com'
|
||||
## description = u'News from Victoria, BC'
|
||||
## fp_tag = 'CAN_TC'
|
||||
postmedia_index_pages = [
|
||||
(u'Headlines',u'/index.html'),
|
||||
(u'Ottawa & Area',u'/news/ottawa/index.html'),
|
||||
(u'Vancouver',u'/news/vancouver/index.html'),
|
||||
(u'Calgary',u'/news/calgary/index.html'),
|
||||
(u'Edmonton',u'/news/edmonton/index.html'),
|
||||
(u'Montreal',u'/news/montreal/index.html'),
|
||||
(u'Fraser Valley',u'/news/fraser-valley/index.html'),
|
||||
(u'British Columbia',u'/news/bc/index.html'),
|
||||
(u'Alberta',u'/news/alberta/index.html'),
|
||||
(u'Canada',u'/news/canada/index.html'),
|
||||
(u'National',u'/news/national/index.html'),
|
||||
(u'Politics',u'/news/politics/index.html'),
|
||||
(u'Insight',u'/news/insight/index.html'),
|
||||
(u'Special Reports',u'/news/specialreports/index.html'),
|
||||
(u'Gangs',u'/news/gangs/index.html'),
|
||||
(u'Education',u'/news/education/index.html'),
|
||||
(u'Health',u'/news/health/index.html'),
|
||||
(u'Environment',u'/news/environment/index.html'),
|
||||
(u'World',u'/news/world/index.html'),
|
||||
(u'Police Blotter',u'/news/crime-and-justice/index.html'),
|
||||
(u'Crime',u'/news/blotter/index.html'),
|
||||
(u'Around Town',u'/news/topic.html?t=keyword&q=Around+Town'),
|
||||
(u'Diplomatica',u'/news/diplomatica/index.html'),
|
||||
(u'Opinion',u'/opinion/index.html'),
|
||||
(u'Columnists',u'/columnists/index.html'),
|
||||
(u'Editorials',u'/opinion/editorials/index.html'),
|
||||
(u'Letters',u'/opinion/letters/index.html'),
|
||||
(u'Business',u'/business/index.html'),
|
||||
(u'Sports',u'/sports/index.html'),
|
||||
(u'Arts',u'/entertainment/index.html'),
|
||||
(u'Life',u'/life/index.html'),
|
||||
(u'Technology',u'/technology/index.html'),
|
||||
(u'Travel',u'/travel/index.html'),
|
||||
(u'Health',u'/health/index.html')
|
||||
]
|
||||
|
||||
# un-comment the following four lines for the Vancouver Province
|
||||
|
||||
# un-comment the following six lines for the Vancouver Province
|
||||
## title = u'Vancouver Province'
|
||||
## url_prefix = 'http://www.theprovince.com'
|
||||
## description = u'News from Vancouver, BC'
|
||||
## fp_tag = 'CAN_VP'
|
||||
## std_logo_url = 'http://www.theprovince.com/images/logo_theprovince.jpg'
|
||||
## logo_url = 'vplogo.jpg'
|
||||
## fp_tag = 'CAN_TP'
|
||||
|
||||
# un-comment the following four lines for the Vancouver Sun
|
||||
# un-comment the following six lines for the Vancouver Sun
|
||||
## title = u'Vancouver Sun'
|
||||
## url_prefix = 'http://www.vancouversun.com'
|
||||
## description = u'News from Vancouver, BC'
|
||||
## std_logo_url = 'http://www.vancouversun.com/images/logo_vancouversun.jpg'
|
||||
## logo_url = 'vslogo.jpg'
|
||||
## fp_tag = 'CAN_VS'
|
||||
|
||||
# un-comment the following four lines for the Edmonton Journal
|
||||
## title = u'Edmonton Journal'
|
||||
## url_prefix = 'http://www.edmontonjournal.com'
|
||||
## description = u'News from Edmonton, AB'
|
||||
## fp_tag = 'CAN_EJ'
|
||||
|
||||
# un-comment the following four lines for the Calgary Herald
|
||||
# un-comment the following six lines for the Calgary Herald
|
||||
## title = u'Calgary Herald'
|
||||
## url_prefix = 'http://www.calgaryherald.com'
|
||||
## description = u'News from Calgary, AB'
|
||||
## std_logo_url = 'http://www.calgaryherald.com/images/logo_calgaryherald.jpg'
|
||||
## logo_url = 'chlogo.jpg'
|
||||
## fp_tag = 'CAN_CH'
|
||||
|
||||
# un-comment the following four lines for the Regina Leader-Post
|
||||
## title = u'Regina Leader-Post'
|
||||
## url_prefix = 'http://www.leaderpost.com'
|
||||
## description = u'News from Regina, SK'
|
||||
## fp_tag = ''
|
||||
# un-comment the following six lines for the Edmonton Journal
|
||||
## title = u'Edmonton Journal'
|
||||
## url_prefix = 'http://www.edmontonjournal.com'
|
||||
## description = u'News from Edmonton, AB'
|
||||
## std_logo_url = 'http://www.edmontonjournal.com/images/logo_edmontonjournal.jpg'
|
||||
## logo_url = 'ejlogo.jpg'
|
||||
## fp_tag = 'CAN_EJ'
|
||||
|
||||
# un-comment the following four lines for the Saskatoon Star-Phoenix
|
||||
## title = u'Saskatoon Star-Phoenix'
|
||||
## url_prefix = 'http://www.thestarphoenix.com'
|
||||
## description = u'News from Saskatoon, SK'
|
||||
## fp_tag = ''
|
||||
|
||||
# un-comment the following four lines for the Windsor Star
|
||||
## title = u'Windsor Star'
|
||||
## url_prefix = 'http://www.windsorstar.com'
|
||||
## description = u'News from Windsor, ON'
|
||||
## fp_tag = 'CAN_'
|
||||
|
||||
# un-comment the following four lines for the Ottawa Citizen
|
||||
# un-comment the following six lines for the Ottawa Citizen
|
||||
title = u'Ottawa Citizen'
|
||||
url_prefix = 'http://www.ottawacitizen.com'
|
||||
description = u'News from Ottawa, ON'
|
||||
std_logo_url = 'http://www.ottawacitizen.com/images/logo_ottawacitizen.jpg'
|
||||
logo_url = 'oclogo.jpg'
|
||||
fp_tag = 'CAN_OC'
|
||||
|
||||
# un-comment the following four lines for the Montreal Gazette
|
||||
# un-comment the following six lines for the Montreal Gazette
|
||||
## title = u'Montreal Gazette'
|
||||
## url_prefix = 'http://www.montrealgazette.com'
|
||||
## description = u'News from Montreal, QC'
|
||||
## std_logo_url = 'http://www.montrealgazette.com/images/logo_montrealgazette.jpg'
|
||||
## logo_url = 'mglogo.jpg'
|
||||
## fp_tag = 'CAN_MG'
|
||||
|
||||
Kindle_Fire=False
|
||||
masthead_url = std_logo_url
|
||||
|
||||
url_list = []
|
||||
language = 'en_CA'
|
||||
__author__ = 'Nick Redding'
|
||||
no_stylesheets = True
|
||||
timefmt = ' [%b %d]'
|
||||
timefmt = ' [%b %d]'
|
||||
encoding = 'utf-8'
|
||||
extra_css = '''
|
||||
.timestamp { font-size:xx-small; display: block; }
|
||||
#storyheader { font-size: medium; }
|
||||
#storyheader h1 { font-size: x-large; }
|
||||
#storyheader h2 { font-size: large; font-style: italic; }
|
||||
#storyheader h2 { font-size: small; font-style: italic; }
|
||||
.byline { font-size:xx-small; }
|
||||
#photocaption { font-size: small; font-style: italic }
|
||||
#photocredit { font-size: xx-small; }'''
|
||||
keep_only_tags = [dict(name='div', attrs={'id':'storyheader'}),dict(name='div', attrs={'id':'storycontent'})]
|
||||
#photocaption { font-size: xx-small; font-style: italic; font-weight: normal; }
|
||||
.photocaption { font-size: xx-small; font-style: italic; font-weight: normal; }
|
||||
#photocredit { font-size: xx-small; font-weight: normal; }'''
|
||||
|
||||
keep_only_tags = [dict(name='div', attrs={'id':'storyheader'}),dict(name='div', attrs={'id':'storycontent'}),dict(name='div', attrs={'id':'storyphoto'})]
|
||||
|
||||
remove_tags = [{'class':'comments'},
|
||||
dict(name='div', attrs={'class':'navbar'}),dict(name='div', attrs={'class':'morelinks'}),
|
||||
dict(name='h2', attrs={'id':'photocredit'}),
|
||||
dict(name='div', attrs={'class':'viewmore'}),dict(name='li', attrs={'class':'email'}),
|
||||
dict(name='div', attrs={'class':'story_tool_hr'}),dict(name='div', attrs={'class':'clear'}),
|
||||
dict(name='div', attrs={'class':'story_tool'}),dict(name='div', attrs={'class':'copyright'}),
|
||||
dict(name='div', attrs={'class':'rule_grey_solid'}),
|
||||
dict(name='li', attrs={'class':'print'}),dict(name='li', attrs={'class':'share'}),dict(name='ul', attrs={'class':'bullet'})]
|
||||
|
||||
|
||||
def get_cover_url(self):
|
||||
from datetime import timedelta, date
|
||||
if self.fp_tag=='':
|
||||
return None
|
||||
cover = 'http://webmedia.newseum.org/newseum-multimedia/dfp/jpg'+str(date.today().day)+'/lg/'+self.fp_tag+'.jpg'
|
||||
br = BasicNewsRecipe.get_browser()
|
||||
daysback=1
|
||||
@ -120,6 +151,18 @@ class CanWestPaper(BasicNewsRecipe):
|
||||
cover = None
|
||||
return cover
|
||||
|
||||
def prepare_masthead_image(self, path_to_image, out_path):
|
||||
if self.Kindle_Fire:
|
||||
from calibre.utils.magick import Image, create_canvas
|
||||
img = Image()
|
||||
img.open(path_to_image)
|
||||
width, height = img.size
|
||||
img2 = create_canvas(width, height)
|
||||
img2.compose(img)
|
||||
img2.save(out_path)
|
||||
else:
|
||||
BasicNewsRecipe.prepare_masthead_image(self, path_to_image, out_path)
|
||||
|
||||
def fixChars(self,string):
|
||||
# Replace lsquo (\x91)
|
||||
fixed = re.sub("\x91","‘",string)
|
||||
@ -166,55 +209,106 @@ class CanWestPaper(BasicNewsRecipe):
|
||||
a.replaceWith(a.renderContents().decode('cp1252','replace'))
|
||||
return soup
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
|
||||
def preprocess_html(self,soup):
|
||||
#delete empty id attributes--they screw up the TOC for unknown reasons
|
||||
divtags = soup.findAll('div',attrs={'id':''})
|
||||
if divtags:
|
||||
for div in divtags:
|
||||
del(div['id'])
|
||||
|
||||
pgall = soup.find('div',attrs={'id':'storyphoto'})
|
||||
if pgall is not None: # photo gallery perhaps
|
||||
if (soup.find('div',attrs={'id':'storycontent'}) is None):
|
||||
allpics = Tag(soup,'div')
|
||||
first_img = pgall.find('div','storyimage')
|
||||
if first_img is not None:
|
||||
first_img.extract()
|
||||
tlist = pgall.find('div',attrs={'id':'relatedthumbs'})
|
||||
if tlist is not None:
|
||||
for atag in tlist.findAll('a'):
|
||||
img = Tag(soup,'img')
|
||||
srcpre, sep, srcpost = atag.img['src'].partition('?')
|
||||
img['src'] = srcpre
|
||||
pdesc = Tag(soup,'p')
|
||||
pdesc.insert(0,atag.img['alt'])
|
||||
pdesc['class']='photocaption'
|
||||
div = Tag(soup,'div')
|
||||
div.insert(0,pdesc)
|
||||
div.insert(0,img)
|
||||
allpics.append(div)
|
||||
pgall.replaceWith(allpics)
|
||||
|
||||
for pg in soup.findAll('div',attrs={'id':'storyphoto'}):
|
||||
pg.extract()
|
||||
return self.strip_anchors(soup)
|
||||
|
||||
|
||||
|
||||
def parse_index(self):
|
||||
soup = self.index_to_soup(self.url_prefix+'/news/todays-paper/index.html')
|
||||
|
||||
articles = {}
|
||||
key = 'News'
|
||||
ans = ['News']
|
||||
ans = []
|
||||
|
||||
# Find each instance of class="sectiontitle", class="featurecontent"
|
||||
for divtag in soup.findAll('div',attrs={'class' : ["section_title02","featurecontent"]}):
|
||||
#self.log(" div class = %s" % divtag['class'])
|
||||
if divtag['class'].startswith('section_title'):
|
||||
# div contains section title
|
||||
if not divtag.h3:
|
||||
continue
|
||||
key = self.tag_to_string(divtag.h3,False)
|
||||
ans.append(key)
|
||||
self.log("Section name %s" % key)
|
||||
continue
|
||||
# div contains article data
|
||||
h1tag = divtag.find('h1')
|
||||
if not h1tag:
|
||||
continue
|
||||
atag = h1tag.find('a',href=True)
|
||||
if not atag:
|
||||
continue
|
||||
url = self.url_prefix+'/news/todays-paper/'+atag['href']
|
||||
#self.log("Section %s" % key)
|
||||
#self.log("url %s" % url)
|
||||
title = self.tag_to_string(atag,False)
|
||||
#self.log("title %s" % title)
|
||||
pubdate = ''
|
||||
description = ''
|
||||
ptag = divtag.find('p');
|
||||
if ptag:
|
||||
description = self.tag_to_string(ptag,False)
|
||||
#self.log("description %s" % description)
|
||||
author = ''
|
||||
autag = divtag.find('h4')
|
||||
if autag:
|
||||
author = self.tag_to_string(autag,False)
|
||||
#self.log("author %s" % author)
|
||||
if not articles.has_key(key):
|
||||
articles[key] = []
|
||||
articles[key].append(dict(title=title,url=url,date=pubdate,description=description,author=author,content=''))
|
||||
|
||||
def handle_article(adiv,key):
|
||||
h1tag = adiv.h1
|
||||
if h1tag is not None:
|
||||
atag = h1tag.a
|
||||
if atag is not None:
|
||||
url = atag['href']
|
||||
if atag['href'].startswith('http'):
|
||||
return
|
||||
elif atag['href'].startswith('/'):
|
||||
url = self.url_prefix+atag['href']
|
||||
else:
|
||||
url = self.url_prefix+'/'+atag['href']
|
||||
if url in self.url_list:
|
||||
return
|
||||
self.url_list.append(url)
|
||||
title = self.tag_to_string(atag,False)
|
||||
if 'VIDEO' in title.upper():
|
||||
return
|
||||
if 'GALLERY' in title.upper():
|
||||
return
|
||||
if 'PHOTOS' in title.upper():
|
||||
return
|
||||
dtag = adiv.find('div','content')
|
||||
description=''
|
||||
print("URL "+url)
|
||||
print("TITLE "+title)
|
||||
if dtag is not None:
|
||||
stag = dtag.span
|
||||
if stag is not None:
|
||||
if stag['class'] != 'timestamp':
|
||||
description = self.tag_to_string(stag,False)
|
||||
else:
|
||||
description = self.tag_to_string(dtag,False)
|
||||
print("DESCRIPTION: "+description)
|
||||
if not articles.has_key(key):
|
||||
articles[key] = []
|
||||
articles[key].append(dict(title=title,url=url,date='',description=description,author='',content=''))
|
||||
|
||||
def parse_web_index(key, keyurl):
|
||||
try:
|
||||
soup = self.index_to_soup(self.url_prefix+keyurl)
|
||||
except:
|
||||
return
|
||||
ans.append(key)
|
||||
mainsoup = soup.find('div','bodywrapper')
|
||||
footer = mainsoup.find(attrs={'id':'footerfeature'})
|
||||
if footer is not None:
|
||||
footer.extract()
|
||||
print("Section: "+key)
|
||||
for wdiv in mainsoup.findAll('div',attrs={'id':re.compile('^HorizontalFeatureSlider_1_Story')}):
|
||||
handle_article(wdiv,key)
|
||||
wdiv.extract()
|
||||
for wdiv in mainsoup.findAll(attrs={'id':['featurewidget','textfeature','textlinks_timestamp']}):
|
||||
for adiv in wdiv.findAll('div','featurecontent'):
|
||||
handle_article(adiv,key)
|
||||
|
||||
for (k,url) in self.postmedia_index_pages:
|
||||
parse_web_index(k,url)
|
||||
ans = [(key, articles[key]) for key in ans if articles.has_key(key)]
|
||||
return ans
|
||||
|
||||
|
75
recipes/phillosophy_now.recipe
Normal file
@ -0,0 +1,75 @@
|
||||
import re
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
from collections import OrderedDict
|
||||
|
||||
class PhilosophyNow(BasicNewsRecipe):
|
||||
|
||||
title = 'Philosophy Now'
|
||||
__author__ = 'Rick Shang'
|
||||
|
||||
description = '''Philosophy Now is a lively magazine for everyone
|
||||
interested in ideas. It isn't afraid to tackle all the major questions of
|
||||
life, the universe and everything. Published every two months, it tries to
|
||||
corrupt innocent citizens by convincing them that philosophy can be
|
||||
exciting, worthwhile and comprehensible, and also to provide some enjoyable
|
||||
reading matter for those already ensnared by the muse, such as philosophy
|
||||
students and academics.'''
|
||||
language = 'en'
|
||||
category = 'news'
|
||||
encoding = 'UTF-8'
|
||||
|
||||
keep_only_tags = [dict(attrs={'id':'fullMainColumn'})]
|
||||
remove_tags = [dict(attrs={'class':'articleTools'})]
|
||||
no_javascript = True
|
||||
no_stylesheets = True
|
||||
needs_subscription = True
|
||||
|
||||
def get_browser(self):
|
||||
br = BasicNewsRecipe.get_browser()
|
||||
br.open('https://philosophynow.org/auth/login')
|
||||
br.select_form(nr = 1)
|
||||
br['username'] = self.username
|
||||
br['password'] = self.password
|
||||
br.submit()
|
||||
return br
|
||||
|
||||
def parse_index(self):
|
||||
#Go to the issue
|
||||
soup0 = self.index_to_soup('http://philosophynow.org/')
|
||||
issue = soup0.find('div',attrs={'id':'navColumn'})
|
||||
|
||||
#Find date & cover
|
||||
cover = issue.find('div', attrs={'id':'cover'})
|
||||
date = self.tag_to_string(cover.find('h3')).strip()
|
||||
self.timefmt = u' [%s]'%date
|
||||
img=cover.find('img',src=True)['src']
|
||||
self.cover_url = 'http://philosophynow.org' + re.sub('medium','large',img)
|
||||
issuenum = re.sub('/media/images/covers/medium/issue','',img)
|
||||
issuenum = re.sub('.jpg','',issuenum)
|
||||
|
||||
#Go to the main body
|
||||
current_issue_url = 'http://philosophynow.org/issues/' + issuenum
|
||||
soup = self.index_to_soup(current_issue_url)
|
||||
div = soup.find ('div', attrs={'class':'articlesColumn'})
|
||||
|
||||
feeds = OrderedDict()
|
||||
|
||||
for post in div.findAll('h3'):
|
||||
articles = []
|
||||
a=post.find('a',href=True)
|
||||
if a is not None:
|
||||
url="http://philosophynow.org" + a['href']
|
||||
title=self.tag_to_string(a).strip()
|
||||
s=post.findPrevious('h4')
|
||||
section_title = self.tag_to_string(s).strip()
|
||||
d=post.findNext('p')
|
||||
desc = self.tag_to_string(d).strip()
|
||||
articles.append({'title':title, 'url':url, 'description':desc, 'date':''})
|
||||
|
||||
if articles:
|
||||
if section_title not in feeds:
|
||||
feeds[section_title] = []
|
||||
feeds[section_title] += articles
|
||||
ans = [(key, val) for key, val in feeds.iteritems()]
|
||||
return ans
|
||||
|
@ -1,44 +1,79 @@
|
||||
import re
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
|
||||
from calibre.ptempfile import PersistentTemporaryFile
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class AdvancedUserRecipe1275708473(BasicNewsRecipe):
|
||||
title = u'Psychology Today'
|
||||
_author__ = 'rty'
|
||||
publisher = u'www.psychologytoday.com'
|
||||
category = u'Psychology'
|
||||
max_articles_per_feed = 100
|
||||
remove_javascript = True
|
||||
use_embedded_content = False
|
||||
no_stylesheets = True
|
||||
class PsychologyToday(BasicNewsRecipe):
|
||||
|
||||
title = 'Psychology Today'
|
||||
__author__ = 'Rick Shang'
|
||||
|
||||
description = 'This magazine takes information from the latest research in the field of psychology and makes it useful to people in their everyday lives. Its coverage encompasses self-improvement, relationships, the mind-body connection, health, family, the workplace and culture.'
|
||||
language = 'en'
|
||||
temp_files = []
|
||||
articles_are_obfuscated = True
|
||||
remove_tags = [
|
||||
dict(name='div', attrs={'class':['print-source_url','field-items','print-footer']}),
|
||||
dict(name='span', attrs={'class':'print-footnote'}),
|
||||
]
|
||||
remove_tags_before = dict(name='h1', attrs={'class':'print-title'})
|
||||
remove_tags_after = dict(name='div', attrs={'class':['field-items','print-footer']})
|
||||
category = 'news'
|
||||
encoding = 'UTF-8'
|
||||
keep_only_tags = [dict(attrs={'class':['print-title', 'print-submitted', 'print-content', 'print-footer', 'print-source_url', 'print-links']})]
|
||||
no_javascript = True
|
||||
no_stylesheets = True
|
||||
|
||||
feeds = [(u'Contents', u'http://www.psychologytoday.com/articles/index.rss')]
|
||||
|
||||
def get_article_url(self, article):
|
||||
return article.get('link', None)
|
||||
def parse_index(self):
|
||||
articles = []
|
||||
soup = self.index_to_soup('http://www.psychologytoday.com/magazine')
|
||||
|
||||
|
||||
#Go to the main body
|
||||
div = soup.find('div',attrs={'id':'content-content'})
|
||||
#Find cover & date
|
||||
cover_item = div.find('div', attrs={'class':'collections-header-image'})
|
||||
cover = cover_item.find('img',src=True)
|
||||
self.cover_url = cover['src']
|
||||
date = self.tag_to_string(cover['title'])
|
||||
self.timefmt = u' [%s]'%date
|
||||
|
||||
articles = []
|
||||
for post in div.findAll('div', attrs={'class':'collections-node-feature-info'}):
|
||||
title = self.tag_to_string(post.find('h2'))
|
||||
author_item=post.find('div', attrs={'class':'collection-node-byline'})
|
||||
author = re.sub(r'.*by\s',"",self.tag_to_string(author_item).strip())
|
||||
title = title + u' (%s)'%author
|
||||
article_page= self.index_to_soup('http://www.psychologytoday.com'+post.find('a', href=True)['href'])
|
||||
print_page=article_page.find('li', attrs={'class':'print_html first'})
|
||||
url='http://www.psychologytoday.com'+print_page.find('a',href=True)['href']
|
||||
desc = self.tag_to_string(post.find('div', attrs={'class':'collection-node-description'})).strip()
|
||||
self.log('Found article:', title)
|
||||
self.log('\t', url)
|
||||
self.log('\t', desc)
|
||||
articles.append({'title':title, 'url':url, 'date':'','description':desc})
|
||||
|
||||
for post in div.findAll('div', attrs={'class':'collections-node-thumbnail-info'}):
|
||||
title = self.tag_to_string(post.find('h2'))
|
||||
author_item=post.find('div', attrs={'class':'collection-node-byline'})
|
||||
article_page= self.index_to_soup('http://www.psychologytoday.com'+post.find('a', href=True)['href'])
|
||||
print_page=article_page.find('li', attrs={'class':'print_html first'})
|
||||
description = post.find('div', attrs={'class':'collection-node-description'})
|
||||
author = re.sub(r'.*by\s',"",self.tag_to_string(description.nextSibling).strip())
|
||||
desc = self.tag_to_string(description).strip()
|
||||
url='http://www.psychologytoday.com'+print_page.find('a',href=True)['href']
|
||||
title = title + u' (%s)'%author
|
||||
self.log('Found article:', title)
|
||||
self.log('\t', url)
|
||||
self.log('\t', desc)
|
||||
articles.append({'title':title, 'url':url, 'date':'','description':desc})
|
||||
|
||||
for post in div.findAll('li', attrs={'class':['collection-item-list-odd','collection-item-list-even']}):
|
||||
title = self.tag_to_string(post.find('h2'))
|
||||
author_item=post.find('div', attrs={'class':'collection-node-byline'})
|
||||
author = re.sub(r'.*by\s',"",self.tag_to_string(author_item).strip())
|
||||
title = title + u' (%s)'%author
|
||||
article_page= self.index_to_soup('http://www.psychologytoday.com'+post.find('a', href=True)['href'])
|
||||
print_page=article_page.find('li', attrs={'class':'print_html first'})
|
||||
url='http://www.psychologytoday.com'+print_page.find('a',href=True)['href']
|
||||
desc = self.tag_to_string(post.find('div', attrs={'class':'collection-node-description'})).strip()
|
||||
self.log('Found article:', title)
|
||||
self.log('\t', url)
|
||||
self.log('\t', desc)
|
||||
articles.append({'title':title, 'url':url, 'date':'','description':desc})
|
||||
|
||||
return [('Current Issue', articles)]
|
||||
|
||||
def get_obfuscated_article(self, url):
|
||||
br = self.get_browser()
|
||||
br.open(url)
|
||||
response = br.follow_link(url_regex = r'/print/[0-9]+', nr = 0)
|
||||
html = response.read()
|
||||
self.temp_files.append(PersistentTemporaryFile('_fa.html'))
|
||||
self.temp_files[-1].write(html)
|
||||
self.temp_files[-1].close()
|
||||
return self.temp_files[-1].name
|
||||
|
||||
def get_cover_url(self):
|
||||
index = 'http://www.psychologytoday.com/magazine/'
|
||||
soup = self.index_to_soup(index)
|
||||
for image in soup.findAll('img',{ "class" : "imagefield imagefield-field_magazine_cover" }):
|
||||
return image['src'] + '.jpg'
|
||||
return None
|
||||
|
@ -1,25 +1,35 @@
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class SanFranciscoBayGuardian(BasicNewsRecipe):
|
||||
title = u'San Francisco Bay Guardian'
|
||||
language = 'en'
|
||||
__author__ = 'Krittika Goyal'
|
||||
title = u'San Francisco Bay Guardian'
|
||||
language = 'en'
|
||||
__author__ = 'Krittika Goyal'
|
||||
oldest_article = 31 #days
|
||||
max_articles_per_feed = 25
|
||||
#encoding = 'latin1'
|
||||
|
||||
no_stylesheets = True
|
||||
#remove_tags_before = dict(name='div', attrs={'id':'story_header'})
|
||||
#remove_tags_after = dict(name='div', attrs={'id':'shirttail'})
|
||||
remove_tags = [
|
||||
dict(name='iframe'),
|
||||
dict(name='iframe'),
|
||||
#dict(name='div', attrs={'class':'related-articles'}),
|
||||
#dict(name='div', attrs={'id':['story_tools', 'toolbox', 'shirttail', 'comment_widget']}),
|
||||
#dict(name='ul', attrs={'class':'article-tools'}),
|
||||
#dict(name='ul', attrs={'id':'story_tabs'}),
|
||||
]
|
||||
|
||||
|
||||
feeds = [
|
||||
('sfbg', 'http://www.sfbg.com/rss.xml'),
|
||||
('politics', 'http://www.sfbg.com/politics/rss.xml'),
|
||||
('blogs', 'http://www.sfbg.com/blog/rss.xml'),
|
||||
('pixel_vision', 'http://www.sfbg.com/pixel_vision/rss.xml'),
|
||||
('bruce', 'http://www.sfbg.com/bruce/rss.xml'),
|
||||
]
|
||||
|
||||
|
||||
|
||||
#def preprocess_html(self, soup):
|
||||
#story = soup.find(name='div', attrs={'id':'story_body'})
|
||||
#td = heading.findParent(name='td')
|
||||
#td.extract()
|
||||
#soup = BeautifulSoup('<html><head><title>t</title></head><body></body></html>')
|
||||
#body = soup.find(name='body')
|
||||
#body.insert(0, story)
|
||||
#return soup
|
||||
|
@ -19,23 +19,12 @@ class Slashdot(BasicNewsRecipe):
|
||||
|
||||
__author__ = 'floweros edited by Huan T'
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
keep_only_tags = [
|
||||
dict(name='div',attrs={'id':'article'}),
|
||||
dict(name='div',attrs={'class':['postBody' 'details']}),
|
||||
dict(name='footer',attrs={'class':['clearfix meta article-foot']}),
|
||||
dict(name='article',attrs={'class':['fhitem fhitem-story article usermode thumbs grid_24']}),
|
||||
dict(name='dl',attrs={'class':'relatedPosts'}),
|
||||
dict(name='h2',attrs={'class':'story'}),
|
||||
dict(name='span',attrs={'class':'comments'}),
|
||||
]
|
||||
|
||||
|
||||
remove_tags = [
|
||||
dict(name='aside',attrs={'id':'slashboxes'}),
|
||||
dict(name='div',attrs={'class':'paginate'}),
|
||||
dict(name='section',attrs={'id':'comments'}),
|
||||
dict(name='span',attrs={'class':'topic'}),
|
||||
]
|
||||
dict(name='div',attrs={'class':'story'}),
|
||||
dict(name='div',attrs={'class':'body'}),
|
||||
dict(name='ul',attrs={'id':'commentlisting'}),
|
||||
]
|
||||
|
||||
feeds = [
|
||||
(u'Slashdot',
|
||||
|
@ -1,50 +1,24 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>'
|
||||
'''
|
||||
www.smashingmagazine.com
|
||||
'''
|
||||
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class SmashingMagazine(BasicNewsRecipe):
|
||||
title = 'Smashing Magazine'
|
||||
__author__ = 'Darko Miletic'
|
||||
description = 'We smash you with the information that will make your life easier, really'
|
||||
oldest_article = 20
|
||||
language = 'en'
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
publisher = 'Smashing Magazine'
|
||||
category = 'news, web, IT, css, javascript, html'
|
||||
encoding = 'utf-8'
|
||||
class SmashingMagazine (BasicNewsRecipe):
|
||||
__author__ = u'Marc Busqué <marc@lamarciana.com>'
|
||||
__url__ = 'http://www.lamarciana.com'
|
||||
__version__ = '1.0.1'
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = u'2012, Marc Busqué <marc@lamarciana.com>'
|
||||
title = u'Smashing Magazine'
|
||||
description = u'Founded in September 2006, Smashing Magazine delivers useful and innovative information to Web designers and developers. Our aim is to inform our readers about the latest trends and techniques in Web development. We try to persuade you not with the quantity but with the quality of the information we present. Smashing Magazine is and always has been independent.'
|
||||
language = 'en'
|
||||
tags = 'web development, software'
|
||||
oldest_article = 7
|
||||
remove_empty_feeds = True
|
||||
no_stylesheets = True
|
||||
encoding = 'utf8'
|
||||
cover_url = u'http://media.smashingmagazine.com/themes/smashingv4/images/logo.png'
|
||||
remove_attributes = ['border', 'cellspacing', 'align', 'cellpadding', 'colspan', 'valign', 'vspace', 'hspace', 'alt', 'width', 'height', 'style']
|
||||
extra_css = u'body div table:first-child {display: none;} img {max-width: 100%; display: block; margin: auto;}'
|
||||
|
||||
conversion_options = {
|
||||
'comments' : description
|
||||
,'tags' : category
|
||||
,'publisher' : publisher
|
||||
}
|
||||
|
||||
keep_only_tags = [dict(name='div', attrs={'id':'leftcolumn'})]
|
||||
remove_tags_after = dict(name='ul',attrs={'class':'social'})
|
||||
remove_tags = [
|
||||
dict(name=['link','object'])
|
||||
,dict(name='h1',attrs={'class':'logo'})
|
||||
,dict(name='div',attrs={'id':'booklogosec'})
|
||||
,dict(attrs={'src':'http://media2.smashingmagazine.com/wp-content/uploads/images/the-smashing-book/smbook6.gif'})
|
||||
]
|
||||
|
||||
feeds = [(u'Articles', u'http://rss1.smashingmagazine.com/feed/')]
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
for iter in soup.findAll('div',attrs={'class':'leftframe'}):
|
||||
it = iter.find('h1')
|
||||
if it == None:
|
||||
iter.extract()
|
||||
for item in soup.findAll('img'):
|
||||
oldParent = item.parent
|
||||
if oldParent.name == 'a':
|
||||
oldParent.name = 'div'
|
||||
return soup
|
||||
feeds = [
|
||||
(u'Smashing Magazine', u'http://rss1.smashingmagazine.com/feed/'),
|
||||
]
|
||||
|
@ -1,61 +1,67 @@
|
||||
import re
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
from calibre.ebooks.BeautifulSoup import BeautifulSoup
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
from collections import OrderedDict
|
||||
|
||||
class SmithsonianMagazine(BasicNewsRecipe):
|
||||
title = u'Smithsonian Magazine'
|
||||
language = 'en'
|
||||
__author__ = 'Krittika Goyal and TerminalVeracity'
|
||||
oldest_article = 31#days
|
||||
max_articles_per_feed = 50
|
||||
use_embedded_content = False
|
||||
recursions = 1
|
||||
cover_url = 'http://sphotos.xx.fbcdn.net/hphotos-snc7/431147_10150602715983253_764313347_n.jpg'
|
||||
match_regexps = ['&page=[2-9]$']
|
||||
preprocess_regexps = [
|
||||
(re.compile(r'for more of Smithsonian\'s coverage on history, science and nature.', re.DOTALL), lambda m: '')
|
||||
]
|
||||
extra_css = """
|
||||
h1{font-size: large; margin: .2em 0}
|
||||
h2{font-size: medium; margin: .2em 0}
|
||||
h3{font-size: medium; margin: .2em 0}
|
||||
#byLine{margin: .2em 0}
|
||||
.articleImageCaptionwide{font-style: italic}
|
||||
.wp-caption-text{font-style: italic}
|
||||
img{display: block}
|
||||
"""
|
||||
class Smithsonian(BasicNewsRecipe):
|
||||
|
||||
title = 'Smithsonian Magazine'
|
||||
__author__ = 'Rick Shang'
|
||||
|
||||
remove_stylesheets = True
|
||||
remove_tags_after = dict(name='div', attrs={'class':['post','articlePaginationWrapper']})
|
||||
remove_tags = [
|
||||
dict(name='iframe'),
|
||||
dict(name='div', attrs={'class':['article_sidebar_border','viewMorePhotos','addtoany_share_save_container','meta','social','OUTBRAIN','related-articles-inpage']}),
|
||||
dict(name='div', attrs={'id':['article_sidebar_border', 'most-popular_large', 'most-popular-body_large','comment_section','article-related']}),
|
||||
dict(name='ul', attrs={'class':'cat-breadcrumb col three last'}),
|
||||
dict(name='h4', attrs={'id':'related-topics'}),
|
||||
dict(name='table'),
|
||||
dict(name='a', attrs={'href':['/subArticleBottomWeb','/subArticleTopWeb','/subArticleTopMag','/subArticleBottomMag']}),
|
||||
dict(name='a', attrs={'name':'comments_shaded'}),
|
||||
]
|
||||
description = 'This magazine chronicles the arts, environment, sciences and popular culture of the times. It is edited for modern, well-rounded individuals with diverse, general interests. With your order, you become a National Associate Member of the Smithsonian. Membership benefits include your subscription to Smithsonian magazine, a personalized membership card, discounts from the Smithsonian catalog, and more.'
|
||||
language = 'en'
|
||||
category = 'news'
|
||||
encoding = 'UTF-8'
|
||||
keep_only_tags = [dict(attrs={'id':['articleTitle', 'subHead', 'byLine', 'articleImage', 'article-text']})]
|
||||
remove_tags = [dict(attrs={'class':['related-articles-inpage', 'viewMorePhotos']})]
|
||||
no_javascript = True
|
||||
no_stylesheets = True
|
||||
|
||||
def parse_index(self):
|
||||
#Go to the issue
|
||||
soup0 = self.index_to_soup('http://www.smithsonianmag.com/issue/archive/')
|
||||
div = soup0.find('div',attrs={'id':'archives'})
|
||||
issue = div.find('ul',attrs={'class':'clear-both'})
|
||||
current_issue_url = issue.find('a', href=True)['href']
|
||||
soup = self.index_to_soup(current_issue_url)
|
||||
|
||||
feeds = [
|
||||
('History and Archeology',
|
||||
'http://feeds.feedburner.com/smithsonianmag/history-archaeology'),
|
||||
('People and Places',
|
||||
'http://feeds.feedburner.com/smithsonianmag/people-places'),
|
||||
('Science and Nature',
|
||||
'http://feeds.feedburner.com/smithsonianmag/science-nature'),
|
||||
('Arts and Culture',
|
||||
'http://feeds.feedburner.com/smithsonianmag/arts-culture'),
|
||||
('Travel',
|
||||
'http://feeds.feedburner.com/smithsonianmag/travel'),
|
||||
]
|
||||
#Go to the main body
|
||||
div = soup.find ('div', attrs={'id':'content-inset'})
|
||||
|
||||
#Find date
|
||||
date = re.sub('.*\:\W*', "", self.tag_to_string(div.find('h2')).strip())
|
||||
self.timefmt = u' [%s]'%date
|
||||
|
||||
#Find cover
|
||||
self.cover_url = div.find('img',src=True)['src']
|
||||
|
||||
feeds = OrderedDict()
|
||||
section_title = ''
|
||||
subsection_title = ''
|
||||
for post in div.findAll('div', attrs={'class':['plainModule', 'departments plainModule']}):
|
||||
articles = []
|
||||
prefix = ''
|
||||
h3=post.find('h3')
|
||||
if h3 is not None:
|
||||
section_title = self.tag_to_string(h3)
|
||||
else:
|
||||
subsection=post.find('p',attrs={'class':'article-cat'})
|
||||
link=post.find('a',href=True)
|
||||
url=link['href']+'?c=y&story=fullstory'
|
||||
if subsection is not None:
|
||||
subsection_title = self.tag_to_string(subsection)
|
||||
prefix = (subsection_title+': ')
|
||||
description=self.tag_to_string(post('p', limit=2)[1]).strip()
|
||||
else:
|
||||
description=self.tag_to_string(post.find('p')).strip()
|
||||
desc=re.sub('\sBy\s.*', '', description, re.DOTALL)
|
||||
author=re.sub('.*By\s', '', description, re.DOTALL)
|
||||
title=prefix + self.tag_to_string(link).strip()+ u' (%s)'%author
|
||||
articles.append({'title':title, 'url':url, 'description':desc, 'date':''})
|
||||
|
||||
if articles:
|
||||
if section_title not in feeds:
|
||||
feeds[section_title] = []
|
||||
feeds[section_title] += articles
|
||||
ans = [(key, val) for key, val in feeds.iteritems()]
|
||||
return ans
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
story = soup.find(name='div', attrs={'id':'article-body'})
|
||||
soup = BeautifulSoup('<html><head><title>t</title></head><body></body></html>')
|
||||
body = soup.find(name='body')
|
||||
body.insert(0, story)
|
||||
return soup
|
||||
|
117
recipes/sueddeutsche_mobil.recipe
Normal file
@ -0,0 +1,117 @@
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2012, Andreas Zeiser <andreas.zeiser@web.de>'
|
||||
'''
|
||||
szmobil.sueddeutsche.de/
|
||||
'''
|
||||
|
||||
from calibre import strftime
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
import re
|
||||
|
||||
class SZmobil(BasicNewsRecipe):
|
||||
title = u'Süddeutsche Zeitung mobil'
|
||||
__author__ = u'Andreas Zeiser'
|
||||
description = u'Nachrichten aus Deutschland. Zugriff auf kostenpflichtiges Abo SZ mobil.'
|
||||
publisher = u'Sueddeutsche Zeitung'
|
||||
language = u'de'
|
||||
publication_type = u'newspaper'
|
||||
category = u'news, politics, Germany'
|
||||
|
||||
no_stylesheets = True
|
||||
oldest_article = 2
|
||||
encoding = 'iso-8859-1'
|
||||
needs_subscription = True
|
||||
remove_empty_feeds = True
|
||||
delay = 1
|
||||
cover_source = 'http://www.sueddeutsche.de/verlag'
|
||||
|
||||
timefmt = ' [%a, %d %b, %Y]'
|
||||
|
||||
root_url ='http://szmobil.sueddeutsche.de/'
|
||||
keep_only_tags = [dict(name='div', attrs={'class':'article'})]
|
||||
|
||||
def get_cover_url(self):
|
||||
src = self.index_to_soup(self.cover_source)
|
||||
image_url = src.find(attrs={'class':'preview-image'})
|
||||
return image_url.div.img['src']
|
||||
|
||||
def get_browser(self):
|
||||
browser = BasicNewsRecipe.get_browser(self)
|
||||
|
||||
# Login via fetching of Streiflicht -> Fill out login request
|
||||
url = self.root_url + 'show.php?id=streif'
|
||||
browser.open(url)
|
||||
|
||||
browser.select_form(nr=0) # to select the first form
|
||||
browser['username'] = self.username
|
||||
browser['password'] = self.password
|
||||
browser.submit()
|
||||
|
||||
return browser
|
||||
|
||||
def parse_index(self):
|
||||
# find all sections
|
||||
src = self.index_to_soup('http://szmobil.sueddeutsche.de')
|
||||
feeds = []
|
||||
for itt in src.findAll('a',href=True):
|
||||
if itt['href'].startswith('show.php?section'):
|
||||
feeds.append( (itt.string[0:-2],itt['href']) )
|
||||
|
||||
all_articles = []
|
||||
for feed in feeds:
|
||||
feed_url = self.root_url + feed[1]
|
||||
feed_title = feed[0]
|
||||
|
||||
self.report_progress(0, ('Fetching feed')+' %s...'%(feed_title if feed_title else feed_url))
|
||||
|
||||
src = self.index_to_soup(feed_url)
|
||||
articles = []
|
||||
shorttitles = dict()
|
||||
for itt in src.findAll('a', href=True):
|
||||
if itt['href'].startswith('show.php?id='):
|
||||
article_url = itt['href']
|
||||
article_id = int(re.search("id=(\d*)&etag=", itt['href']).group(1))
|
||||
|
||||
# first check if link is a special article in section "Meinungsseite"
|
||||
if itt.find('strong')!= None:
|
||||
article_name = itt.strong.string
|
||||
article_shorttitle = itt.contents[1]
|
||||
|
||||
articles.append( (article_name, article_url, article_id) )
|
||||
shorttitles[article_id] = article_shorttitle
|
||||
continue
|
||||
|
||||
|
||||
# candidate for a general article
|
||||
if itt.string == None:
|
||||
article_name = ''
|
||||
else:
|
||||
article_name = itt.string
|
||||
|
||||
if (article_name[0:10] == " mehr"):
|
||||
# just another link ("mehr") to an article
|
||||
continue
|
||||
|
||||
if itt.has_key('id'):
|
||||
shorttitles[article_id] = article_name
|
||||
else:
|
||||
articles.append( (article_name, article_url, article_id) )
|
||||
|
||||
feed_articles = []
|
||||
for article_name, article_url, article_id in articles:
|
||||
url = self.root_url + article_url
|
||||
title = article_name
|
||||
pubdate = strftime('%a, %d %b')
|
||||
description = ''
|
||||
if shorttitles.has_key(article_id):
|
||||
description = shorttitles[article_id]
|
||||
# we do not want the flag ("Impressum")
|
||||
if "HERAUSGEGEBEN VOM" in description:
|
||||
continue
|
||||
d = dict(title=title, url=url, date=pubdate, description=description, content='')
|
||||
feed_articles.append(d)
|
||||
all_articles.append( (feed_title, feed_articles) )
|
||||
|
||||
return all_articles
|
||||
|
@ -1,45 +1,68 @@
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
import re
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
from collections import OrderedDict
|
||||
|
||||
class The_New_Republic(BasicNewsRecipe):
|
||||
title = 'The New Republic'
|
||||
__author__ = 'cix3'
|
||||
class TNR(BasicNewsRecipe):
|
||||
|
||||
title = 'The New Republic'
|
||||
__author__ = 'Rick Shang'
|
||||
|
||||
description = 'The New Republic is a journal of opinion with an emphasis on politics and domestic and international affairs. It carries feature articles by staff and contributing editors. The second half of each issue is devoted to book and the arts, theater, motion pictures, music and art.'
|
||||
language = 'en'
|
||||
description = 'Intelligent, stimulating and rigorous examination of American politics, foreign policy and culture'
|
||||
timefmt = ' [%b %d, %Y]'
|
||||
|
||||
oldest_article = 7
|
||||
max_articles_per_feed = 100
|
||||
category = 'news'
|
||||
encoding = 'UTF-8'
|
||||
remove_tags = [dict(attrs={'class':['print-logo','print-site_name','print-hr']})]
|
||||
no_javascript = True
|
||||
no_stylesheets = True
|
||||
|
||||
remove_tags = [
|
||||
dict(name='div', attrs={'class':['print-logo', 'print-site_name', 'img-left', 'print-source_url']}),
|
||||
dict(name='hr', attrs={'class':'print-hr'}), dict(name='img')
|
||||
]
|
||||
|
||||
feeds = [
|
||||
('Politics', 'http://www.tnr.com/rss/articles/Politics'),
|
||||
('Books and Arts', 'http://www.tnr.com/rss/articles/Books-and-Arts'),
|
||||
('Economy', 'http://www.tnr.com/rss/articles/Economy'),
|
||||
('Environment and Energy', 'http://www.tnr.com/rss/articles/Environment-%2526-Energy'),
|
||||
('Health Care', 'http://www.tnr.com/rss/articles/Health-Care'),
|
||||
('Metro Policy', 'http://www.tnr.com/rss/articles/Metro-Policy'),
|
||||
('World', 'http://www.tnr.com/rss/articles/World'),
|
||||
('Film', 'http://www.tnr.com/rss/articles/Film'),
|
||||
('Books', 'http://www.tnr.com/rss/articles/books'),
|
||||
('The Book', 'http://www.tnr.com/rss/book'),
|
||||
('Jonathan Chait', 'http://www.tnr.com/rss/blogs/Jonathan-Chait'),
|
||||
('The Plank', 'http://www.tnr.com/rss/blogs/The-Plank'),
|
||||
('The Treatment', 'http://www.tnr.com/rss/blogs/The-Treatment'),
|
||||
('The Spine', 'http://www.tnr.com/rss/blogs/The-Spine'),
|
||||
('The Vine', 'http://www.tnr.com/rss/blogs/The-Vine'),
|
||||
('The Avenue', 'http://www.tnr.com/rss/blogs/The-Avenue'),
|
||||
('William Galston', 'http://www.tnr.com/rss/blogs/William-Galston'),
|
||||
('Simon Johnson', 'http://www.tnr.com/rss/blogs/Simon-Johnson'),
|
||||
('Ed Kilgore', 'http://www.tnr.com/rss/blogs/Ed-Kilgore'),
|
||||
('Damon Linker', 'http://www.tnr.com/rss/blogs/Damon-Linker'),
|
||||
('John McWhorter', 'http://www.tnr.com/rss/blogs/John-McWhorter')
|
||||
]
|
||||
def parse_index(self):
|
||||
|
||||
def print_version(self, url):
|
||||
return url.replace('http://www.tnr.com/', 'http://www.tnr.com/print/')
|
||||
#Go to the issue
|
||||
soup0 = self.index_to_soup('http://www.tnr.com/magazine-issues')
|
||||
issue = soup0.find('div',attrs={'id':'current_issue'})
|
||||
|
||||
#Find date
|
||||
date = self.tag_to_string(issue.find('div',attrs={'class':'date'})).strip()
|
||||
self.timefmt = u' [%s]'%date
|
||||
|
||||
#Go to the main body
|
||||
current_issue_url = 'http://www.tnr.com' + issue.find('a', href=True)['href']
|
||||
soup = self.index_to_soup(current_issue_url)
|
||||
div = soup.find ('div', attrs={'class':'article_detail_body'})
|
||||
|
||||
|
||||
|
||||
#Find cover
|
||||
self.cover_url = div.find('img',src=True)['src']
|
||||
|
||||
feeds = OrderedDict()
|
||||
section_title = ''
|
||||
subsection_title = ''
|
||||
for post in div.findAll('p'):
|
||||
articles = []
|
||||
em=post.find('em')
|
||||
b=post.find('b')
|
||||
a=post.find('a',href=True)
|
||||
p=post.find('img', src=True)
|
||||
#Find cover
|
||||
if p is not None:
|
||||
self.cover_url = p['src'].strip()
|
||||
if em is not None:
|
||||
section_title = self.tag_to_string(em).strip()
|
||||
subsection_title = ''
|
||||
elif b is not None:
|
||||
subsection_title=self.tag_to_string(b).strip()
|
||||
elif a is not None:
|
||||
prefix = (subsection_title+': ') if subsection_title else ''
|
||||
url=re.sub('www.tnr.com','www.tnr.com/print', a['href'])
|
||||
author=re.sub('.*by\s', '', self.tag_to_string(post), re.DOTALL)
|
||||
title=prefix + self.tag_to_string(a).strip()+ u' (%s)'%author
|
||||
articles.append({'title':title, 'url':url, 'description':'', 'date':''})
|
||||
|
||||
if articles:
|
||||
if section_title not in feeds:
|
||||
feeds[section_title] = []
|
||||
feeds[section_title] += articles
|
||||
ans = [(key, val) for key, val in feeds.iteritems()]
|
||||
return ans
|
||||
|
@ -1,4 +1,4 @@
|
||||
import re, random
|
||||
import random
|
||||
|
||||
from calibre import browser
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
@ -6,48 +6,45 @@ from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
class AdvancedUserRecipe1325006965(BasicNewsRecipe):
|
||||
|
||||
title = u'The Sun UK'
|
||||
description = 'A Recipe for The Sun tabloid UK'
|
||||
description = 'Articles from The Sun tabloid UK'
|
||||
__author__ = 'Dave Asbury'
|
||||
# last updated 29/4/12
|
||||
# last updated 25/7/12
|
||||
language = 'en_GB'
|
||||
oldest_article = 1
|
||||
max_articles_per_feed = 15
|
||||
max_articles_per_feed = 12
|
||||
remove_empty_feeds = True
|
||||
no_stylesheets = True
|
||||
#auto_cleanup = True
|
||||
#articles_are_obfuscated = True
|
||||
|
||||
|
||||
masthead_url = 'http://www.thesun.co.uk/sol/img/global/Sun-logo.gif'
|
||||
encoding = 'UTF-8'
|
||||
|
||||
remove_empty_feeds = True
|
||||
remove_javascript = True
|
||||
no_stylesheets = True
|
||||
|
||||
|
||||
|
||||
#preprocess_regexps = [
|
||||
# (re.compile(r'<div class="foot-copyright".*?</div>', re.IGNORECASE | re.DOTALL), lambda match: '')]
|
||||
|
||||
|
||||
extra_css = '''
|
||||
body{ text-align: justify; font-family:Arial,Helvetica,sans-serif; font-size:11px; font-size-adjust:none; font-stretch:normal; font-style:normal; font-variant:normal; font-weight:normal;}
|
||||
'''
|
||||
|
||||
preprocess_regexps = [
|
||||
(re.compile(r'<div class="foot-copyright".*?</div>', re.IGNORECASE | re.DOTALL), lambda match: '')]
|
||||
|
||||
|
||||
|
||||
'''
|
||||
keep_only_tags = [
|
||||
dict(name='h1'),dict(name='h2',attrs={'class' : 'medium centered'}),
|
||||
dict(name='div',attrs={'class' : 'text-center'}),
|
||||
dict(name='div',attrs={'id' : 'bodyText'})
|
||||
# dict(name='p')
|
||||
]
|
||||
remove_tags=[
|
||||
#dict(name='head'),
|
||||
dict(attrs={'class' : ['mystery-meat-link','ltbx-container','ltbx-var ltbx-hbxpn','ltbx-var ltbx-nav-loop','ltbx-var ltbx-url']}),
|
||||
dict(name='div',attrs={'class' : 'cf'}),
|
||||
dict(attrs={'title' : 'download flash'}),
|
||||
dict(attrs={'style' : 'padding: 5px'})
|
||||
dict(name='div',attrs={'class' : 'intro'}),
|
||||
dict(name='h3'),
|
||||
dict(name='div',attrs={'id' : 'articlebody'}),
|
||||
#dict(attrs={'class' : ['right_col_branding','related-stories','mystery-meat-link','ltbx-container','ltbx-var ltbx-hbxpn','ltbx-var ltbx-nav-loop','ltbx-var ltbx-url']}),
|
||||
# dict(name='div',attrs={'class' : 'cf'}),
|
||||
# dict(attrs={'title' : 'download flash'}),
|
||||
# dict(attrs={'style' : 'padding: 5px'})
|
||||
|
||||
]
|
||||
|
||||
remove_tags_after = [dict(id='bodyText')]
|
||||
remove_tags=[
|
||||
dict(name='li'),
|
||||
dict(attrs={'class' : 'grid-4 right-hand-column'}),
|
||||
]
|
||||
|
||||
feeds = [
|
||||
(u'News', u'http://www.thesun.co.uk/sol/homepage/news/rss'),
|
||||
@ -72,22 +69,18 @@ class AdvancedUserRecipe1325006965(BasicNewsRecipe):
|
||||
cov2 = str(cov)
|
||||
cov2=cov2[27:-18]
|
||||
#cov2 now is pic url, now go back to original function
|
||||
|
||||
br = browser()
|
||||
br.set_handle_redirect(False)
|
||||
try:
|
||||
br.open_novisit(cov2)
|
||||
cover_url = cov2
|
||||
except:
|
||||
cover_url = random.choice((
|
||||
cover_url = random.choice([
|
||||
'http://img.thesun.co.uk/multimedia/archive/00905/errorpage6_677961a_905507a.jpg'
|
||||
,'http://img.thesun.co.uk/multimedia/archive/00905/errorpage7_677962a_905505a.jpg'
|
||||
,'http://img.thesun.co.uk/multimedia/archive/00905/errorpage5_677960a_905512a.jpg'
|
||||
,'http://img.thesun.co.uk/multimedia/archive/00905/errorpage2_677957a_905502a.jpg'
|
||||
,'http://img.thesun.co.uk/multimedia/archive/00905/errorpage3_677958a_905503a.jpg'
|
||||
))
|
||||
])
|
||||
|
||||
return cover_url
|
||||
|
||||
|
||||
|
||||
|
@ -16,6 +16,8 @@ class TheTorontoStar(BasicNewsRecipe):
|
||||
language = 'en_CA'
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets = True
|
||||
#auto_cleanup = True
|
||||
#auto_cleanup_keep = '//div[@class="topsContent topsContentActive"]'
|
||||
use_embedded_content = False
|
||||
delay = 2
|
||||
publisher = 'The Toronto Star'
|
||||
@ -28,18 +30,18 @@ class TheTorontoStar(BasicNewsRecipe):
|
||||
,'publisher' : publisher
|
||||
}
|
||||
|
||||
keep_only_tags = [dict(name='div', attrs={'class':'ts-article'})]
|
||||
remove_tags_before = dict(name='div',attrs={'id':'ts-article_header'})
|
||||
#keep_only_tags = [dict(name='div', attrs={'class':'ts-article'})]
|
||||
#remove_tags_before = dict(name='div',attrs={'id':'ts-article_header'})
|
||||
|
||||
feeds = [
|
||||
(u'News' , u'http://www.thestar.com/rss/82672?' )
|
||||
,(u'Opinion' , u'http://www.thestar.com/rss/82863?' )
|
||||
,(u'Business' , u'http://www.thestar.com/rss/82796?' )
|
||||
,(u'Sports' , u'http://www.thestar.com/rss/82758?' )
|
||||
,(u'Entertainment', u'http://www.thestar.com/rss/117741?' )
|
||||
,(u'Living' , u'http://www.thestar.com/rss/82839?' )
|
||||
,(u'Travel' , u'http://www.thestar.com/rss/82858?' )
|
||||
,(u'Science' , u'http://www.thestar.com/rss/82848?')
|
||||
(u'News' , u'http://www.thestar.com/rss/?categories=293' )
|
||||
,(u'Opinion' , u'http://www.thestar.com/rss/?categories=303' )
|
||||
,(u'Business' , u'http://www.thestar.com/rss/?categories=294' )
|
||||
,(u'Sports' , u'http://www.thestar.com/rss/?categories=295' )
|
||||
,(u'Entertainment', u'http://www.toronto.com/rss?categories=6298' )
|
||||
,(u'Living' , u'http://www.thestar.com/rss/?categories=297' )
|
||||
,(u'Travel' , u'http://www.thestar.com/rss/list/1042246?' )
|
||||
,(u'Science' , u'http://www.thestar.com/rss?categories=6481')
|
||||
]
|
||||
|
||||
def print_version(self, url):
|
||||
@ -47,3 +49,4 @@ class TheTorontoStar(BasicNewsRecipe):
|
||||
artid = artl.rpartition('/')[2]
|
||||
return 'http://www.thestar.com/printarticle/' + artid
|
||||
|
||||
|
||||
|
@ -1,136 +1,314 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
__license__ = 'GPL v3'
|
||||
|
||||
'''
|
||||
www.canada.com
|
||||
'''
|
||||
|
||||
import re
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
from calibre.ebooks.BeautifulSoup import BeautifulStoneSoup, Tag
|
||||
|
||||
|
||||
class CanWestPaper(BasicNewsRecipe):
|
||||
|
||||
# un-comment the following three lines for the Vancouver Province
|
||||
postmedia_index_pages = [
|
||||
(u'Headlines',u'/index.html'),
|
||||
(u'Ottawa & Area',u'/news/ottawa/index.html'),
|
||||
(u'Vancouver',u'/news/vancouver/index.html'),
|
||||
(u'Calgary',u'/news/calgary/index.html'),
|
||||
(u'Edmonton',u'/news/edmonton/index.html'),
|
||||
(u'Montreal',u'/news/montreal/index.html'),
|
||||
(u'Fraser Valley',u'/news/fraser-valley/index.html'),
|
||||
(u'British Columbia',u'/news/bc/index.html'),
|
||||
(u'Alberta',u'/news/alberta/index.html'),
|
||||
(u'Canada',u'/news/canada/index.html'),
|
||||
(u'National',u'/news/national/index.html'),
|
||||
(u'Politics',u'/news/politics/index.html'),
|
||||
(u'Insight',u'/news/insight/index.html'),
|
||||
(u'Special Reports',u'/news/specialreports/index.html'),
|
||||
(u'Gangs',u'/news/gangs/index.html'),
|
||||
(u'Education',u'/news/education/index.html'),
|
||||
(u'Health',u'/news/health/index.html'),
|
||||
(u'Environment',u'/news/environment/index.html'),
|
||||
(u'World',u'/news/world/index.html'),
|
||||
(u'Police Blotter',u'/news/crime-and-justice/index.html'),
|
||||
(u'Crime',u'/news/blotter/index.html'),
|
||||
(u'Around Town',u'/news/topic.html?t=keyword&q=Around+Town'),
|
||||
(u'Diplomatica',u'/news/diplomatica/index.html'),
|
||||
(u'Opinion',u'/opinion/index.html'),
|
||||
(u'Columnists',u'/columnists/index.html'),
|
||||
(u'Editorials',u'/opinion/editorials/index.html'),
|
||||
(u'Letters',u'/opinion/letters/index.html'),
|
||||
(u'Business',u'/business/index.html'),
|
||||
(u'Sports',u'/sports/index.html'),
|
||||
(u'Arts',u'/entertainment/index.html'),
|
||||
(u'Life',u'/life/index.html'),
|
||||
(u'Technology',u'/technology/index.html'),
|
||||
(u'Travel',u'/travel/index.html'),
|
||||
(u'Health',u'/health/index.html')
|
||||
]
|
||||
|
||||
|
||||
# un-comment the following six lines for the Vancouver Province
|
||||
title = u'Vancouver Province'
|
||||
url_prefix = 'http://www.theprovince.com'
|
||||
description = u'News from Vancouver, BC'
|
||||
std_logo_url = 'http://www.theprovince.com/images/logo_theprovince.jpg'
|
||||
logo_url = 'vplogo.jpg'
|
||||
fp_tag = 'CAN_TP'
|
||||
|
||||
# un-comment the following three lines for the Vancouver Sun
|
||||
#title = u'Vancouver Sun'
|
||||
#url_prefix = 'http://www.vancouversun.com'
|
||||
#description = u'News from Vancouver, BC'
|
||||
# un-comment the following six lines for the Vancouver Sun
|
||||
## title = u'Vancouver Sun'
|
||||
## url_prefix = 'http://www.vancouversun.com'
|
||||
## description = u'News from Vancouver, BC'
|
||||
## std_logo_url = 'http://www.vancouversun.com/images/logo_vancouversun.jpg'
|
||||
## logo_url = 'vslogo.jpg'
|
||||
## fp_tag = 'CAN_VS'
|
||||
|
||||
# un-comment the following three lines for the Edmonton Journal
|
||||
#title = u'Edmonton Journal'
|
||||
#url_prefix = 'http://www.edmontonjournal.com'
|
||||
#description = u'News from Edmonton, AB'
|
||||
# un-comment the following six lines for the Calgary Herald
|
||||
## title = u'Calgary Herald'
|
||||
## url_prefix = 'http://www.calgaryherald.com'
|
||||
## description = u'News from Calgary, AB'
|
||||
## std_logo_url = 'http://www.calgaryherald.com/images/logo_calgaryherald.jpg'
|
||||
## logo_url = 'chlogo.jpg'
|
||||
## fp_tag = 'CAN_CH'
|
||||
|
||||
# un-comment the following three lines for the Calgary Herald
|
||||
#title = u'Calgary Herald'
|
||||
#url_prefix = 'http://www.calgaryherald.com'
|
||||
#description = u'News from Calgary, AB'
|
||||
# un-comment the following six lines for the Edmonton Journal
|
||||
## title = u'Edmonton Journal'
|
||||
## url_prefix = 'http://www.edmontonjournal.com'
|
||||
## description = u'News from Edmonton, AB'
|
||||
## std_logo_url = 'http://www.edmontonjournal.com/images/logo_edmontonjournal.jpg'
|
||||
## logo_url = 'ejlogo.jpg'
|
||||
## fp_tag = 'CAN_EJ'
|
||||
|
||||
# un-comment the following three lines for the Regina Leader-Post
|
||||
#title = u'Regina Leader-Post'
|
||||
#url_prefix = 'http://www.leaderpost.com'
|
||||
#description = u'News from Regina, SK'
|
||||
# un-comment the following six lines for the Ottawa Citizen
|
||||
## title = u'Ottawa Citizen'
|
||||
## url_prefix = 'http://www.ottawacitizen.com'
|
||||
## description = u'News from Ottawa, ON'
|
||||
## std_logo_url = 'http://www.ottawacitizen.com/images/logo_ottawacitizen.jpg'
|
||||
## logo_url = 'oclogo.jpg'
|
||||
## fp_tag = 'CAN_OC'
|
||||
|
||||
# un-comment the following three lines for the Saskatoon Star-Phoenix
|
||||
#title = u'Saskatoon Star-Phoenix'
|
||||
#url_prefix = 'http://www.thestarphoenix.com'
|
||||
#description = u'News from Saskatoon, SK'
|
||||
|
||||
# un-comment the following three lines for the Windsor Star
|
||||
#title = u'Windsor Star'
|
||||
#url_prefix = 'http://www.windsorstar.com'
|
||||
#description = u'News from Windsor, ON'
|
||||
|
||||
# un-comment the following three lines for the Ottawa Citizen
|
||||
#title = u'Ottawa Citizen'
|
||||
#url_prefix = 'http://www.ottawacitizen.com'
|
||||
#description = u'News from Ottawa, ON'
|
||||
|
||||
# un-comment the following three lines for the Montreal Gazette
|
||||
#title = u'Montreal Gazette'
|
||||
#url_prefix = 'http://www.montrealgazette.com'
|
||||
#description = u'News from Montreal, QC'
|
||||
# un-comment the following six lines for the Montreal Gazette
|
||||
## title = u'Montreal Gazette'
|
||||
## url_prefix = 'http://www.montrealgazette.com'
|
||||
## description = u'News from Montreal, QC'
|
||||
## std_logo_url = 'http://www.montrealgazette.com/images/logo_montrealgazette.jpg'
|
||||
## logo_url = 'mglogo.jpg'
|
||||
## fp_tag = 'CAN_MG'
|
||||
|
||||
Kindle_Fire=False
|
||||
masthead_url = std_logo_url
|
||||
|
||||
url_list = []
|
||||
language = 'en_CA'
|
||||
__author__ = 'Nick Redding'
|
||||
no_stylesheets = True
|
||||
timefmt = ' [%b %d]'
|
||||
timefmt = ' [%b %d]'
|
||||
encoding = 'utf-8'
|
||||
extra_css = '''
|
||||
.timestamp { font-size:xx-small; display: block; }
|
||||
#storyheader { font-size: medium; }
|
||||
#storyheader h1 { font-size: x-large; }
|
||||
#storyheader h2 { font-size: large; font-style: italic; }
|
||||
#storyheader h2 { font-size: small; font-style: italic; }
|
||||
.byline { font-size:xx-small; }
|
||||
#photocaption { font-size: small; font-style: italic }
|
||||
#photocredit { font-size: xx-small; }'''
|
||||
keep_only_tags = [dict(name='div', attrs={'id':'storyheader'}),dict(name='div', attrs={'id':'storycontent'})]
|
||||
#photocaption { font-size: xx-small; font-style: italic; font-weight: normal; }
|
||||
.photocaption { font-size: xx-small; font-style: italic; font-weight: normal; }
|
||||
#photocredit { font-size: xx-small; font-weight: normal; }'''
|
||||
|
||||
keep_only_tags = [dict(name='div', attrs={'id':'storyheader'}),dict(name='div', attrs={'id':'storycontent'}),dict(name='div', attrs={'id':'storyphoto'})]
|
||||
|
||||
remove_tags = [{'class':'comments'},
|
||||
dict(name='div', attrs={'class':'navbar'}),dict(name='div', attrs={'class':'morelinks'}),
|
||||
dict(name='h2', attrs={'id':'photocredit'}),
|
||||
dict(name='div', attrs={'class':'viewmore'}),dict(name='li', attrs={'class':'email'}),
|
||||
dict(name='div', attrs={'class':'story_tool_hr'}),dict(name='div', attrs={'class':'clear'}),
|
||||
dict(name='div', attrs={'class':'story_tool'}),dict(name='div', attrs={'class':'copyright'}),
|
||||
dict(name='div', attrs={'class':'rule_grey_solid'}),
|
||||
dict(name='li', attrs={'class':'print'}),dict(name='li', attrs={'class':'share'}),dict(name='ul', attrs={'class':'bullet'})]
|
||||
|
||||
|
||||
def get_cover_url(self):
|
||||
from datetime import timedelta, date
|
||||
cover = 'http://webmedia.newseum.org/newseum-multimedia/dfp/jpg'+str(date.today().day)+'/lg/'+self.fp_tag+'.jpg'
|
||||
br = BasicNewsRecipe.get_browser()
|
||||
daysback=1
|
||||
try:
|
||||
br.open(cover)
|
||||
except:
|
||||
while daysback<7:
|
||||
cover = 'http://webmedia.newseum.org/newseum-multimedia/dfp/jpg'+str((date.today() - timedelta(days=daysback)).day)+'/lg/'+self.fp_tag+'.jpg'
|
||||
br = BasicNewsRecipe.get_browser()
|
||||
try:
|
||||
br.open(cover)
|
||||
except:
|
||||
daysback = daysback+1
|
||||
continue
|
||||
break
|
||||
if daysback==7:
|
||||
self.log("\nCover unavailable")
|
||||
cover = None
|
||||
return cover
|
||||
|
||||
def prepare_masthead_image(self, path_to_image, out_path):
|
||||
if self.Kindle_Fire:
|
||||
from calibre.utils.magick import Image, create_canvas
|
||||
img = Image()
|
||||
img.open(path_to_image)
|
||||
width, height = img.size
|
||||
img2 = create_canvas(width, height)
|
||||
img2.compose(img)
|
||||
img2.save(out_path)
|
||||
else:
|
||||
BasicNewsRecipe.prepare_masthead_image(self, path_to_image, out_path)
|
||||
|
||||
def fixChars(self,string):
|
||||
# Replace lsquo (\x91)
|
||||
fixed = re.sub("\x91","‘",string)
|
||||
# Replace rsquo (\x92)
|
||||
fixed = re.sub("\x92","’",fixed)
|
||||
# Replace ldquo (\x93)
|
||||
fixed = re.sub("\x93","“",fixed)
|
||||
# Replace rdquo (\x94)
|
||||
fixed = re.sub("\x94","”",fixed)
|
||||
# Replace ndash (\x96)
|
||||
fixed = re.sub("\x96","–",fixed)
|
||||
# Replace mdash (\x97)
|
||||
fixed = re.sub("\x97","—",fixed)
|
||||
fixed = re.sub("’","’",fixed)
|
||||
return fixed
|
||||
|
||||
def massageNCXText(self, description):
|
||||
# Kindle TOC descriptions won't render certain characters
|
||||
if description:
|
||||
massaged = unicode(BeautifulStoneSoup(description, convertEntities=BeautifulStoneSoup.HTML_ENTITIES))
|
||||
# Replace '&' with '&'
|
||||
massaged = re.sub("&","&", massaged)
|
||||
return self.fixChars(massaged)
|
||||
else:
|
||||
return description
|
||||
|
||||
def populate_article_metadata(self, article, soup, first):
|
||||
if first:
|
||||
picdiv = soup.find('body').find('img')
|
||||
if picdiv is not None:
|
||||
self.add_toc_thumbnail(article,re.sub(r'links\\link\d+\\','',picdiv['src']))
|
||||
xtitle = article.text_summary.strip()
|
||||
if len(xtitle) == 0:
|
||||
desc = soup.find('meta',attrs={'property':'og:description'})
|
||||
if desc is not None:
|
||||
article.summary = article.text_summary = desc['content']
|
||||
|
||||
def strip_anchors(self,soup):
|
||||
paras = soup.findAll(True)
|
||||
for para in paras:
|
||||
aTags = para.findAll('a')
|
||||
for a in aTags:
|
||||
if a.img is None:
|
||||
a.replaceWith(a.renderContents().decode('cp1252','replace'))
|
||||
return soup
|
||||
|
||||
|
||||
def preprocess_html(self,soup):
|
||||
#delete iempty id attributes--they screw up the TOC for unknow reasons
|
||||
#delete empty id attributes--they screw up the TOC for unknown reasons
|
||||
divtags = soup.findAll('div',attrs={'id':''})
|
||||
if divtags:
|
||||
for div in divtags:
|
||||
del(div['id'])
|
||||
return soup
|
||||
|
||||
pgall = soup.find('div',attrs={'id':'storyphoto'})
|
||||
if pgall is not None: # photo gallery perhaps
|
||||
if (soup.find('div',attrs={'id':'storycontent'}) is None):
|
||||
allpics = Tag(soup,'div')
|
||||
first_img = pgall.find('div','storyimage')
|
||||
if first_img is not None:
|
||||
first_img.extract()
|
||||
tlist = pgall.find('div',attrs={'id':'relatedthumbs'})
|
||||
if tlist is not None:
|
||||
for atag in tlist.findAll('a'):
|
||||
img = Tag(soup,'img')
|
||||
srcpre, sep, srcpost = atag.img['src'].partition('?')
|
||||
img['src'] = srcpre
|
||||
pdesc = Tag(soup,'p')
|
||||
pdesc.insert(0,atag.img['alt'])
|
||||
pdesc['class']='photocaption'
|
||||
div = Tag(soup,'div')
|
||||
div.insert(0,pdesc)
|
||||
div.insert(0,img)
|
||||
allpics.append(div)
|
||||
pgall.replaceWith(allpics)
|
||||
|
||||
for pg in soup.findAll('div',attrs={'id':'storyphoto'}):
|
||||
pg.extract()
|
||||
return self.strip_anchors(soup)
|
||||
|
||||
|
||||
|
||||
def parse_index(self):
|
||||
soup = self.index_to_soup(self.url_prefix+'/news/todays-paper/index.html')
|
||||
|
||||
articles = {}
|
||||
key = 'News'
|
||||
ans = ['News']
|
||||
ans = []
|
||||
|
||||
# Find each instance of class="sectiontitle", class="featurecontent"
|
||||
for divtag in soup.findAll('div',attrs={'class' : ["section_title02","featurecontent"]}):
|
||||
#self.log(" div class = %s" % divtag['class'])
|
||||
if divtag['class'].startswith('section_title'):
|
||||
# div contains section title
|
||||
if not divtag.h3:
|
||||
continue
|
||||
key = self.tag_to_string(divtag.h3,False)
|
||||
ans.append(key)
|
||||
self.log("Section name %s" % key)
|
||||
continue
|
||||
# div contains article data
|
||||
h1tag = divtag.find('h1')
|
||||
if not h1tag:
|
||||
continue
|
||||
atag = h1tag.find('a',href=True)
|
||||
if not atag:
|
||||
continue
|
||||
url = self.url_prefix+'/news/todays-paper/'+atag['href']
|
||||
#self.log("Section %s" % key)
|
||||
#self.log("url %s" % url)
|
||||
title = self.tag_to_string(atag,False)
|
||||
#self.log("title %s" % title)
|
||||
pubdate = ''
|
||||
description = ''
|
||||
ptag = divtag.find('p');
|
||||
if ptag:
|
||||
description = self.tag_to_string(ptag,False)
|
||||
#self.log("description %s" % description)
|
||||
author = ''
|
||||
autag = divtag.find('h4')
|
||||
if autag:
|
||||
author = self.tag_to_string(autag,False)
|
||||
#self.log("author %s" % author)
|
||||
if not articles.has_key(key):
|
||||
articles[key] = []
|
||||
articles[key].append(dict(title=title,url=url,date=pubdate,description=description,author=author,content=''))
|
||||
|
||||
def handle_article(adiv,key):
|
||||
h1tag = adiv.h1
|
||||
if h1tag is not None:
|
||||
atag = h1tag.a
|
||||
if atag is not None:
|
||||
url = atag['href']
|
||||
if atag['href'].startswith('http'):
|
||||
return
|
||||
elif atag['href'].startswith('/'):
|
||||
url = self.url_prefix+atag['href']
|
||||
else:
|
||||
url = self.url_prefix+'/'+atag['href']
|
||||
if url in self.url_list:
|
||||
return
|
||||
self.url_list.append(url)
|
||||
title = self.tag_to_string(atag,False)
|
||||
if 'VIDEO' in title.upper():
|
||||
return
|
||||
if 'GALLERY' in title.upper():
|
||||
return
|
||||
if 'PHOTOS' in title.upper():
|
||||
return
|
||||
dtag = adiv.find('div','content')
|
||||
description=''
|
||||
print("URL "+url)
|
||||
print("TITLE "+title)
|
||||
if dtag is not None:
|
||||
stag = dtag.span
|
||||
if stag is not None:
|
||||
if stag['class'] != 'timestamp':
|
||||
description = self.tag_to_string(stag,False)
|
||||
else:
|
||||
description = self.tag_to_string(dtag,False)
|
||||
print("DESCRIPTION: "+description)
|
||||
if not articles.has_key(key):
|
||||
articles[key] = []
|
||||
articles[key].append(dict(title=title,url=url,date='',description=description,author='',content=''))
|
||||
|
||||
def parse_web_index(key, keyurl):
|
||||
try:
|
||||
soup = self.index_to_soup(self.url_prefix+keyurl)
|
||||
except:
|
||||
return
|
||||
ans.append(key)
|
||||
mainsoup = soup.find('div','bodywrapper')
|
||||
footer = mainsoup.find(attrs={'id':'footerfeature'})
|
||||
if footer is not None:
|
||||
footer.extract()
|
||||
print("Section: "+key)
|
||||
for wdiv in mainsoup.findAll('div',attrs={'id':re.compile('^HorizontalFeatureSlider_1_Story')}):
|
||||
handle_article(wdiv,key)
|
||||
wdiv.extract()
|
||||
for wdiv in mainsoup.findAll(attrs={'id':['featurewidget','textfeature','textlinks_timestamp']}):
|
||||
for adiv in wdiv.findAll('div','featurecontent'):
|
||||
handle_article(adiv,key)
|
||||
|
||||
for (k,url) in self.postmedia_index_pages:
|
||||
parse_web_index(k,url)
|
||||
ans = [(key, articles[key]) for key in ans if articles.has_key(key)]
|
||||
return ans
|
||||
|
||||
|
@ -1,105 +1,136 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
|
||||
'''
|
||||
www.canada.com
|
||||
'''
|
||||
|
||||
import re
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
from calibre.ebooks.BeautifulSoup import BeautifulStoneSoup
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
from calibre.ebooks.BeautifulSoup import BeautifulStoneSoup, Tag
|
||||
|
||||
|
||||
class CanWestPaper(BasicNewsRecipe):
|
||||
|
||||
# un-comment the following four lines for the Victoria Times Colonist
|
||||
## title = u'Victoria Times Colonist'
|
||||
## url_prefix = 'http://www.timescolonist.com'
|
||||
## description = u'News from Victoria, BC'
|
||||
## fp_tag = 'CAN_TC'
|
||||
postmedia_index_pages = [
|
||||
(u'Headlines',u'/index.html'),
|
||||
(u'Ottawa & Area',u'/news/ottawa/index.html'),
|
||||
(u'Vancouver',u'/news/vancouver/index.html'),
|
||||
(u'Calgary',u'/news/calgary/index.html'),
|
||||
(u'Edmonton',u'/news/edmonton/index.html'),
|
||||
(u'Montreal',u'/news/montreal/index.html'),
|
||||
(u'Fraser Valley',u'/news/fraser-valley/index.html'),
|
||||
(u'British Columbia',u'/news/bc/index.html'),
|
||||
(u'Alberta',u'/news/alberta/index.html'),
|
||||
(u'Canada',u'/news/canada/index.html'),
|
||||
(u'National',u'/news/national/index.html'),
|
||||
(u'Politics',u'/news/politics/index.html'),
|
||||
(u'Insight',u'/news/insight/index.html'),
|
||||
(u'Special Reports',u'/news/specialreports/index.html'),
|
||||
(u'Gangs',u'/news/gangs/index.html'),
|
||||
(u'Education',u'/news/education/index.html'),
|
||||
(u'Health',u'/news/health/index.html'),
|
||||
(u'Environment',u'/news/environment/index.html'),
|
||||
(u'World',u'/news/world/index.html'),
|
||||
(u'Police Blotter',u'/news/crime-and-justice/index.html'),
|
||||
(u'Crime',u'/news/blotter/index.html'),
|
||||
(u'Around Town',u'/news/topic.html?t=keyword&q=Around+Town'),
|
||||
(u'Diplomatica',u'/news/diplomatica/index.html'),
|
||||
(u'Opinion',u'/opinion/index.html'),
|
||||
(u'Columnists',u'/columnists/index.html'),
|
||||
(u'Editorials',u'/opinion/editorials/index.html'),
|
||||
(u'Letters',u'/opinion/letters/index.html'),
|
||||
(u'Business',u'/business/index.html'),
|
||||
(u'Sports',u'/sports/index.html'),
|
||||
(u'Arts',u'/entertainment/index.html'),
|
||||
(u'Life',u'/life/index.html'),
|
||||
(u'Technology',u'/technology/index.html'),
|
||||
(u'Travel',u'/travel/index.html'),
|
||||
(u'Health',u'/health/index.html')
|
||||
]
|
||||
|
||||
# un-comment the following four lines for the Vancouver Province
|
||||
|
||||
# un-comment the following six lines for the Vancouver Province
|
||||
## title = u'Vancouver Province'
|
||||
## url_prefix = 'http://www.theprovince.com'
|
||||
## description = u'News from Vancouver, BC'
|
||||
## fp_tag = 'CAN_VP'
|
||||
## std_logo_url = 'http://www.theprovince.com/images/logo_theprovince.jpg'
|
||||
## logo_url = 'vplogo.jpg'
|
||||
## fp_tag = 'CAN_TP'
|
||||
|
||||
# un-comment the following four lines for the Vancouver Sun
|
||||
# un-comment the following six lines for the Vancouver Sun
|
||||
title = u'Vancouver Sun'
|
||||
url_prefix = 'http://www.vancouversun.com'
|
||||
description = u'News from Vancouver, BC'
|
||||
std_logo_url = 'http://www.vancouversun.com/images/logo_vancouversun.jpg'
|
||||
logo_url = 'vslogo.jpg'
|
||||
fp_tag = 'CAN_VS'
|
||||
|
||||
# un-comment the following four lines for the Edmonton Journal
|
||||
## title = u'Edmonton Journal'
|
||||
## url_prefix = 'http://www.edmontonjournal.com'
|
||||
## description = u'News from Edmonton, AB'
|
||||
## fp_tag = 'CAN_EJ'
|
||||
|
||||
# un-comment the following four lines for the Calgary Herald
|
||||
# un-comment the following six lines for the Calgary Herald
|
||||
## title = u'Calgary Herald'
|
||||
## url_prefix = 'http://www.calgaryherald.com'
|
||||
## description = u'News from Calgary, AB'
|
||||
## std_logo_url = 'http://www.calgaryherald.com/images/logo_calgaryherald.jpg'
|
||||
## logo_url = 'chlogo.jpg'
|
||||
## fp_tag = 'CAN_CH'
|
||||
|
||||
# un-comment the following four lines for the Regina Leader-Post
|
||||
## title = u'Regina Leader-Post'
|
||||
## url_prefix = 'http://www.leaderpost.com'
|
||||
## description = u'News from Regina, SK'
|
||||
## fp_tag = ''
|
||||
# un-comment the following six lines for the Edmonton Journal
|
||||
## title = u'Edmonton Journal'
|
||||
## url_prefix = 'http://www.edmontonjournal.com'
|
||||
## description = u'News from Edmonton, AB'
|
||||
## std_logo_url = 'http://www.edmontonjournal.com/images/logo_edmontonjournal.jpg'
|
||||
## logo_url = 'ejlogo.jpg'
|
||||
## fp_tag = 'CAN_EJ'
|
||||
|
||||
# un-comment the following four lines for the Saskatoon Star-Phoenix
|
||||
## title = u'Saskatoon Star-Phoenix'
|
||||
## url_prefix = 'http://www.thestarphoenix.com'
|
||||
## description = u'News from Saskatoon, SK'
|
||||
## fp_tag = ''
|
||||
|
||||
# un-comment the following four lines for the Windsor Star
|
||||
## title = u'Windsor Star'
|
||||
## url_prefix = 'http://www.windsorstar.com'
|
||||
## description = u'News from Windsor, ON'
|
||||
## fp_tag = 'CAN_'
|
||||
|
||||
# un-comment the following four lines for the Ottawa Citizen
|
||||
# un-comment the following six lines for the Ottawa Citizen
|
||||
## title = u'Ottawa Citizen'
|
||||
## url_prefix = 'http://www.ottawacitizen.com'
|
||||
## description = u'News from Ottawa, ON'
|
||||
## std_logo_url = 'http://www.ottawacitizen.com/images/logo_ottawacitizen.jpg'
|
||||
## logo_url = 'oclogo.jpg'
|
||||
## fp_tag = 'CAN_OC'
|
||||
|
||||
# un-comment the following four lines for the Montreal Gazette
|
||||
# un-comment the following six lines for the Montreal Gazette
|
||||
## title = u'Montreal Gazette'
|
||||
## url_prefix = 'http://www.montrealgazette.com'
|
||||
## description = u'News from Montreal, QC'
|
||||
## std_logo_url = 'http://www.montrealgazette.com/images/logo_montrealgazette.jpg'
|
||||
## logo_url = 'mglogo.jpg'
|
||||
## fp_tag = 'CAN_MG'
|
||||
|
||||
Kindle_Fire=False
|
||||
masthead_url = std_logo_url
|
||||
|
||||
url_list = []
|
||||
language = 'en_CA'
|
||||
__author__ = 'Nick Redding'
|
||||
no_stylesheets = True
|
||||
timefmt = ' [%b %d]'
|
||||
timefmt = ' [%b %d]'
|
||||
encoding = 'utf-8'
|
||||
extra_css = '''
|
||||
.timestamp { font-size:xx-small; display: block; }
|
||||
#storyheader { font-size: medium; }
|
||||
#storyheader h1 { font-size: x-large; }
|
||||
#storyheader h2 { font-size: large; font-style: italic; }
|
||||
#storyheader h2 { font-size: small; font-style: italic; }
|
||||
.byline { font-size:xx-small; }
|
||||
#photocaption { font-size: small; font-style: italic }
|
||||
#photocredit { font-size: xx-small; }'''
|
||||
keep_only_tags = [dict(name='div', attrs={'id':'storyheader'}),dict(name='div', attrs={'id':'storycontent'})]
|
||||
#photocaption { font-size: xx-small; font-style: italic; font-weight: normal; }
|
||||
.photocaption { font-size: xx-small; font-style: italic; font-weight: normal; }
|
||||
#photocredit { font-size: xx-small; font-weight: normal; }'''
|
||||
|
||||
keep_only_tags = [dict(name='div', attrs={'id':'storyheader'}),dict(name='div', attrs={'id':'storycontent'}),dict(name='div', attrs={'id':'storyphoto'})]
|
||||
|
||||
remove_tags = [{'class':'comments'},
|
||||
dict(name='div', attrs={'class':'navbar'}),dict(name='div', attrs={'class':'morelinks'}),
|
||||
dict(name='h2', attrs={'id':'photocredit'}),
|
||||
dict(name='div', attrs={'class':'viewmore'}),dict(name='li', attrs={'class':'email'}),
|
||||
dict(name='div', attrs={'class':'story_tool_hr'}),dict(name='div', attrs={'class':'clear'}),
|
||||
dict(name='div', attrs={'class':'story_tool'}),dict(name='div', attrs={'class':'copyright'}),
|
||||
dict(name='div', attrs={'class':'rule_grey_solid'}),
|
||||
dict(name='li', attrs={'class':'print'}),dict(name='li', attrs={'class':'share'}),dict(name='ul', attrs={'class':'bullet'})]
|
||||
|
||||
|
||||
def get_cover_url(self):
|
||||
from datetime import timedelta, date
|
||||
if self.fp_tag=='':
|
||||
return None
|
||||
cover = 'http://webmedia.newseum.org/newseum-multimedia/dfp/jpg'+str(date.today().day)+'/lg/'+self.fp_tag+'.jpg'
|
||||
br = BasicNewsRecipe.get_browser()
|
||||
daysback=1
|
||||
@ -120,6 +151,18 @@ class CanWestPaper(BasicNewsRecipe):
|
||||
cover = None
|
||||
return cover
|
||||
|
||||
def prepare_masthead_image(self, path_to_image, out_path):
|
||||
if self.Kindle_Fire:
|
||||
from calibre.utils.magick import Image, create_canvas
|
||||
img = Image()
|
||||
img.open(path_to_image)
|
||||
width, height = img.size
|
||||
img2 = create_canvas(width, height)
|
||||
img2.compose(img)
|
||||
img2.save(out_path)
|
||||
else:
|
||||
BasicNewsRecipe.prepare_masthead_image(self, path_to_image, out_path)
|
||||
|
||||
def fixChars(self,string):
|
||||
# Replace lsquo (\x91)
|
||||
fixed = re.sub("\x91","‘",string)
|
||||
@ -166,55 +209,106 @@ class CanWestPaper(BasicNewsRecipe):
|
||||
a.replaceWith(a.renderContents().decode('cp1252','replace'))
|
||||
return soup
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
|
||||
def preprocess_html(self,soup):
|
||||
#delete empty id attributes--they screw up the TOC for unknown reasons
|
||||
divtags = soup.findAll('div',attrs={'id':''})
|
||||
if divtags:
|
||||
for div in divtags:
|
||||
del(div['id'])
|
||||
|
||||
pgall = soup.find('div',attrs={'id':'storyphoto'})
|
||||
if pgall is not None: # photo gallery perhaps
|
||||
if (soup.find('div',attrs={'id':'storycontent'}) is None):
|
||||
allpics = Tag(soup,'div')
|
||||
first_img = pgall.find('div','storyimage')
|
||||
if first_img is not None:
|
||||
first_img.extract()
|
||||
tlist = pgall.find('div',attrs={'id':'relatedthumbs'})
|
||||
if tlist is not None:
|
||||
for atag in tlist.findAll('a'):
|
||||
img = Tag(soup,'img')
|
||||
srcpre, sep, srcpost = atag.img['src'].partition('?')
|
||||
img['src'] = srcpre
|
||||
pdesc = Tag(soup,'p')
|
||||
pdesc.insert(0,atag.img['alt'])
|
||||
pdesc['class']='photocaption'
|
||||
div = Tag(soup,'div')
|
||||
div.insert(0,pdesc)
|
||||
div.insert(0,img)
|
||||
allpics.append(div)
|
||||
pgall.replaceWith(allpics)
|
||||
|
||||
for pg in soup.findAll('div',attrs={'id':'storyphoto'}):
|
||||
pg.extract()
|
||||
return self.strip_anchors(soup)
|
||||
|
||||
|
||||
|
||||
def parse_index(self):
|
||||
soup = self.index_to_soup(self.url_prefix+'/news/todays-paper/index.html')
|
||||
|
||||
articles = {}
|
||||
key = 'News'
|
||||
ans = ['News']
|
||||
ans = []
|
||||
|
||||
# Find each instance of class="sectiontitle", class="featurecontent"
|
||||
for divtag in soup.findAll('div',attrs={'class' : ["section_title02","featurecontent"]}):
|
||||
#self.log(" div class = %s" % divtag['class'])
|
||||
if divtag['class'].startswith('section_title'):
|
||||
# div contains section title
|
||||
if not divtag.h3:
|
||||
continue
|
||||
key = self.tag_to_string(divtag.h3,False)
|
||||
ans.append(key)
|
||||
self.log("Section name %s" % key)
|
||||
continue
|
||||
# div contains article data
|
||||
h1tag = divtag.find('h1')
|
||||
if not h1tag:
|
||||
continue
|
||||
atag = h1tag.find('a',href=True)
|
||||
if not atag:
|
||||
continue
|
||||
url = self.url_prefix+'/news/todays-paper/'+atag['href']
|
||||
#self.log("Section %s" % key)
|
||||
#self.log("url %s" % url)
|
||||
title = self.tag_to_string(atag,False)
|
||||
#self.log("title %s" % title)
|
||||
pubdate = ''
|
||||
description = ''
|
||||
ptag = divtag.find('p');
|
||||
if ptag:
|
||||
description = self.tag_to_string(ptag,False)
|
||||
#self.log("description %s" % description)
|
||||
author = ''
|
||||
autag = divtag.find('h4')
|
||||
if autag:
|
||||
author = self.tag_to_string(autag,False)
|
||||
#self.log("author %s" % author)
|
||||
if not articles.has_key(key):
|
||||
articles[key] = []
|
||||
articles[key].append(dict(title=title,url=url,date=pubdate,description=description,author=author,content=''))
|
||||
|
||||
def handle_article(adiv,key):
|
||||
h1tag = adiv.h1
|
||||
if h1tag is not None:
|
||||
atag = h1tag.a
|
||||
if atag is not None:
|
||||
url = atag['href']
|
||||
if atag['href'].startswith('http'):
|
||||
return
|
||||
elif atag['href'].startswith('/'):
|
||||
url = self.url_prefix+atag['href']
|
||||
else:
|
||||
url = self.url_prefix+'/'+atag['href']
|
||||
if url in self.url_list:
|
||||
return
|
||||
self.url_list.append(url)
|
||||
title = self.tag_to_string(atag,False)
|
||||
if 'VIDEO' in title.upper():
|
||||
return
|
||||
if 'GALLERY' in title.upper():
|
||||
return
|
||||
if 'PHOTOS' in title.upper():
|
||||
return
|
||||
dtag = adiv.find('div','content')
|
||||
description=''
|
||||
print("URL "+url)
|
||||
print("TITLE "+title)
|
||||
if dtag is not None:
|
||||
stag = dtag.span
|
||||
if stag is not None:
|
||||
if stag['class'] != 'timestamp':
|
||||
description = self.tag_to_string(stag,False)
|
||||
else:
|
||||
description = self.tag_to_string(dtag,False)
|
||||
print("DESCRIPTION: "+description)
|
||||
if not articles.has_key(key):
|
||||
articles[key] = []
|
||||
articles[key].append(dict(title=title,url=url,date='',description=description,author='',content=''))
|
||||
|
||||
def parse_web_index(key, keyurl):
|
||||
try:
|
||||
soup = self.index_to_soup(self.url_prefix+keyurl)
|
||||
except:
|
||||
return
|
||||
ans.append(key)
|
||||
mainsoup = soup.find('div','bodywrapper')
|
||||
footer = mainsoup.find(attrs={'id':'footerfeature'})
|
||||
if footer is not None:
|
||||
footer.extract()
|
||||
print("Section: "+key)
|
||||
for wdiv in mainsoup.findAll('div',attrs={'id':re.compile('^HorizontalFeatureSlider_1_Story')}):
|
||||
handle_article(wdiv,key)
|
||||
wdiv.extract()
|
||||
for wdiv in mainsoup.findAll(attrs={'id':['featurewidget','textfeature','textlinks_timestamp']}):
|
||||
for adiv in wdiv.findAll('div','featurecontent'):
|
||||
handle_article(adiv,key)
|
||||
|
||||
for (k,url) in self.postmedia_index_pages:
|
||||
parse_web_index(k,url)
|
||||
ans = [(key, articles[key]) for key in ans if articles.has_key(key)]
|
||||
return ans
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
import re
|
||||
|
||||
class AdvancedUserRecipe1312886443(BasicNewsRecipe):
|
||||
class WNP(BasicNewsRecipe):
|
||||
title = u'WNP'
|
||||
cover_url= 'http://k.wnp.pl/images/wnpLogo.gif'
|
||||
__author__ = 'fenuks'
|
||||
@ -12,7 +12,7 @@ class AdvancedUserRecipe1312886443(BasicNewsRecipe):
|
||||
oldest_article = 8
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets= True
|
||||
remove_tags=[dict(attrs={'class':'printF'})]
|
||||
remove_tags=[dict(attrs={'class':['printF', 'border3B2 clearfix', 'articleMenu clearfix']})]
|
||||
feeds = [(u'Wiadomości gospodarcze', u'http://www.wnp.pl/rss/serwis_rss.xml'),
|
||||
(u'Serwis Energetyka - Gaz', u'http://www.wnp.pl/rss/serwis_rss_1.xml'),
|
||||
(u'Serwis Nafta - Chemia', u'http://www.wnp.pl/rss/serwis_rss_2.xml'),
|
||||
|
@ -12,7 +12,7 @@ class cdnet(BasicNewsRecipe):
|
||||
|
||||
title = 'zdnet'
|
||||
description = 'zdnet security'
|
||||
__author__ = 'Oliver Niesner'
|
||||
__author__ = 'Oliver Niesner, Krittika Goyal'
|
||||
language = 'en'
|
||||
|
||||
use_embedded_content = False
|
||||
@ -20,41 +20,42 @@ class cdnet(BasicNewsRecipe):
|
||||
max_articles_per_feed = 40
|
||||
no_stylesheets = True
|
||||
encoding = 'latin1'
|
||||
auto_cleanup = True
|
||||
|
||||
|
||||
|
||||
remove_tags = [dict(id='eyebrows'),
|
||||
dict(id='header'),
|
||||
dict(id='search'),
|
||||
dict(id='nav'),
|
||||
dict(id='blog-author-info'),
|
||||
dict(id='post-tags'),
|
||||
dict(id='bio-naraine'),
|
||||
dict(id='bio-kennedy'),
|
||||
dict(id='author-short-disclosure-kennedy'),
|
||||
dict(id=''),
|
||||
dict(name='div', attrs={'class':'banner'}),
|
||||
dict(name='div', attrs={'class':'int'}),
|
||||
dict(name='div', attrs={'class':'talkback clear space-2'}),
|
||||
dict(name='div', attrs={'class':'content-1 clear'}),
|
||||
dict(name='div', attrs={'class':'space-2'}),
|
||||
dict(name='div', attrs={'class':'space-3'}),
|
||||
dict(name='div', attrs={'class':'thumb-2 left'}),
|
||||
dict(name='div', attrs={'class':'hotspot'}),
|
||||
dict(name='div', attrs={'class':'hed hed-1 space-1'}),
|
||||
dict(name='div', attrs={'class':'view-1 clear content-3 space-2'}),
|
||||
dict(name='div', attrs={'class':'hed hed-1 space-1'}),
|
||||
dict(name='div', attrs={'class':'hed hed-1'}),
|
||||
dict(name='div', attrs={'class':'post-header'}),
|
||||
dict(name='div', attrs={'class':'lvl-nav clear'}),
|
||||
dict(name='div', attrs={'class':'t-share-overlay overlay-pop contain-overlay-4'}),
|
||||
dict(name='p', attrs={'class':'tags'}),
|
||||
dict(name='span', attrs={'class':'follow'}),
|
||||
dict(name='span', attrs={'class':'int'}),
|
||||
dict(name='h4', attrs={'class':'h s-4'}),
|
||||
dict(name='a', attrs={'href':'http://www.twitter.com/ryanaraine'}),
|
||||
dict(name='div', attrs={'class':'special1'})]
|
||||
remove_tags_after = [dict(name='div', attrs={'class':'clear'})]
|
||||
#remove_tags = [dict(id='eyebrows'),
|
||||
#dict(id='header'),
|
||||
#dict(id='search'),
|
||||
#dict(id='nav'),
|
||||
#dict(id='blog-author-info'),
|
||||
#dict(id='post-tags'),
|
||||
#dict(id='bio-naraine'),
|
||||
#dict(id='bio-kennedy'),
|
||||
#dict(id='author-short-disclosure-kennedy'),
|
||||
#dict(id=''),
|
||||
#dict(name='div', attrs={'class':'banner'}),
|
||||
#dict(name='div', attrs={'class':'int'}),
|
||||
#dict(name='div', attrs={'class':'talkback clear space-2'}),
|
||||
#dict(name='div', attrs={'class':'content-1 clear'}),
|
||||
#dict(name='div', attrs={'class':'space-2'}),
|
||||
#dict(name='div', attrs={'class':'space-3'}),
|
||||
#dict(name='div', attrs={'class':'thumb-2 left'}),
|
||||
#dict(name='div', attrs={'class':'hotspot'}),
|
||||
#dict(name='div', attrs={'class':'hed hed-1 space-1'}),
|
||||
#dict(name='div', attrs={'class':'view-1 clear content-3 space-2'}),
|
||||
#dict(name='div', attrs={'class':'hed hed-1 space-1'}),
|
||||
#dict(name='div', attrs={'class':'hed hed-1'}),
|
||||
#dict(name='div', attrs={'class':'post-header'}),
|
||||
#dict(name='div', attrs={'class':'lvl-nav clear'}),
|
||||
#dict(name='div', attrs={'class':'t-share-overlay overlay-pop contain-overlay-4'}),
|
||||
#dict(name='p', attrs={'class':'tags'}),
|
||||
#dict(name='span', attrs={'class':'follow'}),
|
||||
#dict(name='span', attrs={'class':'int'}),
|
||||
#dict(name='h4', attrs={'class':'h s-4'}),
|
||||
#dict(name='a', attrs={'href':'http://www.twitter.com/ryanaraine'}),
|
||||
#dict(name='div', attrs={'class':'special1'})]
|
||||
#remove_tags_after = [dict(name='div', attrs={'class':'clear'})]
|
||||
|
||||
feeds = [ ('zdnet', 'http://feeds.feedburner.com/zdnet/security') ]
|
||||
|
||||
@ -63,6 +64,3 @@ class cdnet(BasicNewsRecipe):
|
||||
for item in soup.findAll(style=True):
|
||||
del item['style']
|
||||
return soup
|
||||
|
||||
|
||||
|
||||
|
@ -1,35 +1,229 @@
|
||||
body { background-color: white; }
|
||||
|
||||
/*
|
||||
** The following rules apply principally to the line items shown in the
|
||||
** Authors, Titles, Genres, Series, and Recently Added sections. Rules for the
|
||||
** Descriptions section are grouped together later in the file.
|
||||
** ------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
** <div> grouping an author's works together
|
||||
** Used in Sections:
|
||||
** Authors
|
||||
**
|
||||
** Minimize widows and orphans by logically grouping chunks
|
||||
** Some reports of problems with Sony (ADE) ereaders
|
||||
** ADE: page-break-inside:avoid;
|
||||
** iBooks: display:inline-block;
|
||||
** width:100%;
|
||||
*/
|
||||
div.author_logical_group {
|
||||
page-break-inside:avoid;
|
||||
}
|
||||
|
||||
/*
|
||||
** Force page break when starting new initial letter
|
||||
** Used in Sections:
|
||||
** Authors
|
||||
** Titles
|
||||
*/
|
||||
div.initial_letter {
|
||||
page-break-before:always;
|
||||
}
|
||||
|
||||
/*
|
||||
** Author name
|
||||
** Used in Sections:
|
||||
** Authors
|
||||
** Genres
|
||||
** Recently Added
|
||||
*/
|
||||
p.author_index {
|
||||
clear:both;
|
||||
font-size:large;
|
||||
font-weight:bold;
|
||||
text-align:left;
|
||||
margin-top:0.25px;
|
||||
margin-bottom:-2px;
|
||||
text-indent: 0em;
|
||||
}
|
||||
|
||||
/*
|
||||
** Index letter
|
||||
** Used in Sections:
|
||||
** Authors
|
||||
** Titles
|
||||
*/
|
||||
p.author_title_letter_index {
|
||||
clear:both;
|
||||
font-size:x-large;
|
||||
text-align:center;
|
||||
font-weight:bold;
|
||||
margin-top:0px;
|
||||
margin-bottom:0px;
|
||||
}
|
||||
|
||||
/*
|
||||
** Index letter
|
||||
** Used in Sections:
|
||||
** Series
|
||||
*/
|
||||
p.series_letter_index {
|
||||
font-size:x-large;
|
||||
text-align:center;
|
||||
font-weight:bold;
|
||||
margin-top:1em;
|
||||
margin-bottom:0px;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Month-Year
|
||||
** Used in Sections:
|
||||
** Recently Added
|
||||
*/
|
||||
p.date_index {
|
||||
clear:both;
|
||||
font-size:x-large;
|
||||
text-align:center;
|
||||
font-weight:bold;
|
||||
margin-top:1em;
|
||||
margin-bottom:0px;
|
||||
}
|
||||
|
||||
p.date_read {
|
||||
clear:both;
|
||||
text-align:left;
|
||||
margin-top:0px;
|
||||
margin-bottom:0px;
|
||||
margin-left:6em;
|
||||
text-indent:-6em;
|
||||
}
|
||||
|
||||
/*
|
||||
** Series name
|
||||
** Used in Sections:
|
||||
** Authors
|
||||
** Series
|
||||
** Genres
|
||||
** Recently Added
|
||||
** Optimized for ePub
|
||||
*/
|
||||
p.series {
|
||||
clear:both;
|
||||
font-style:italic;
|
||||
margin-top:0.10em;
|
||||
margin-bottom:0em;
|
||||
margin-left:1.5em;
|
||||
text-align:left;
|
||||
text-indent:-1.25em;
|
||||
}
|
||||
|
||||
/*
|
||||
** Series name
|
||||
** Used in Sections:
|
||||
** Authors
|
||||
** Series
|
||||
** Genres
|
||||
** Recently Added
|
||||
** Optimized for mobi
|
||||
*/
|
||||
p.series_mobi {
|
||||
clear:both;
|
||||
font-style:italic;
|
||||
margin-top:0em;
|
||||
margin-bottom:0em;
|
||||
margin-left:0em;
|
||||
text-align:left;
|
||||
text-indent:-30px;
|
||||
}
|
||||
|
||||
/*
|
||||
** Section title
|
||||
** Used in Sections:
|
||||
** Authors
|
||||
** Titles
|
||||
** Series
|
||||
** Genres
|
||||
** Recently Added
|
||||
** Descriptions
|
||||
*/
|
||||
p.title {
|
||||
margin-top:0em;
|
||||
margin-bottom:0em;
|
||||
text-align:center;
|
||||
font-style:italic;
|
||||
font-size:xx-large;
|
||||
}
|
||||
|
||||
/*
|
||||
** Line item book listing
|
||||
** Used in Sections:
|
||||
** Authors
|
||||
** Titles
|
||||
** Series
|
||||
** Genres
|
||||
** Recently Added
|
||||
*/
|
||||
p.line_item {
|
||||
clear: both;
|
||||
font-family:monospace;
|
||||
margin-top:0px;
|
||||
margin-bottom:0px;
|
||||
margin-left:2em;
|
||||
text-align:left;
|
||||
text-indent:-2em;
|
||||
}
|
||||
|
||||
/*
|
||||
** Prefix
|
||||
** Used in Sections:
|
||||
** Authors
|
||||
** Titles
|
||||
** Series
|
||||
** Genres
|
||||
** Recently Added
|
||||
*/
|
||||
span.prefix {
|
||||
float:left;
|
||||
margin-left: 0.25em;
|
||||
text-align: left;
|
||||
vertical-align: middle;
|
||||
width: 1.5em;
|
||||
}
|
||||
|
||||
/*
|
||||
** Book details entry
|
||||
** Used in Sections:
|
||||
** Authors
|
||||
** Titles
|
||||
** Series
|
||||
** Genres
|
||||
** Recently Added
|
||||
*/
|
||||
span.entry {
|
||||
font-family: serif;
|
||||
vertical-align:middle;
|
||||
}
|
||||
|
||||
/*
|
||||
** The following rules apply to Descriptions
|
||||
** -----------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
** Link to Series
|
||||
*/
|
||||
a.series_id {
|
||||
font-style:normal;
|
||||
font-size:large;
|
||||
}
|
||||
|
||||
/*
|
||||
* Minimize widows and orphans by logically grouping chunks
|
||||
* Some reports of problems with Sony (ADE) ereaders
|
||||
* ADE: page-break-inside:avoid;
|
||||
* iBooks: display:inline-block;
|
||||
* width:100%;
|
||||
** Various dividers
|
||||
*/
|
||||
div.author_logical_group {
|
||||
page-break-inside:avoid;
|
||||
}
|
||||
|
||||
div.description > p:first-child {
|
||||
margin: 0 0 0 0;
|
||||
text-indent: 0em;
|
||||
}
|
||||
|
||||
div.description {
|
||||
margin: 0 0 0 0;
|
||||
text-indent: 1em;
|
||||
}
|
||||
|
||||
div.initial_letter {
|
||||
page-break-before:always;
|
||||
}
|
||||
|
||||
hr.annotations_divider {
|
||||
width:50%;
|
||||
margin-left:1em;
|
||||
@ -63,47 +257,21 @@ hr.merged_comments_divider {
|
||||
border-left: solid white 0px;
|
||||
}
|
||||
|
||||
p.date_read {
|
||||
text-align:left;
|
||||
margin-top:0px;
|
||||
margin-bottom:0px;
|
||||
margin-left:6em;
|
||||
text-indent:-6em;
|
||||
}
|
||||
|
||||
/*
|
||||
** Author name
|
||||
*/
|
||||
p.author {
|
||||
clear:both;
|
||||
font-size:large;
|
||||
margin-top:0em;
|
||||
margin-bottom:0em;
|
||||
margin-bottom:0.1em;
|
||||
text-align: center;
|
||||
text-indent: 0em;
|
||||
}
|
||||
|
||||
p.author_index {
|
||||
font-size:large;
|
||||
font-weight:bold;
|
||||
text-align:left;
|
||||
margin-top:0px;
|
||||
margin-bottom:-2px;
|
||||
text-indent: 0em;
|
||||
}
|
||||
|
||||
p.author_title_letter_index {
|
||||
font-size:x-large;
|
||||
text-align:center;
|
||||
font-weight:bold;
|
||||
margin-top:0px;
|
||||
margin-bottom:0px;
|
||||
}
|
||||
|
||||
p.date_index {
|
||||
font-size:x-large;
|
||||
text-align:center;
|
||||
font-weight:bold;
|
||||
margin-top:1em;
|
||||
margin-bottom:0px;
|
||||
}
|
||||
|
||||
/*
|
||||
** Formats
|
||||
*/
|
||||
p.formats {
|
||||
font-size:90%;
|
||||
margin-top:0em;
|
||||
@ -112,6 +280,9 @@ p.formats {
|
||||
text-indent: 0.0in;
|
||||
}
|
||||
|
||||
/*
|
||||
** Genres
|
||||
*/
|
||||
p.genres {
|
||||
font-style:normal;
|
||||
margin-top:0.5em;
|
||||
@ -120,59 +291,55 @@ p.genres {
|
||||
text-indent: 0.0in;
|
||||
}
|
||||
|
||||
p.series {
|
||||
font-style:italic;
|
||||
margin-top:0.25em;
|
||||
margin-bottom:0em;
|
||||
margin-left:2em;
|
||||
text-align:left;
|
||||
text-indent:-2em;
|
||||
}
|
||||
|
||||
/*
|
||||
** Series name
|
||||
*/
|
||||
p.series_id {
|
||||
margin-top:0em;
|
||||
margin-bottom:0em;
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
p.series_letter_index {
|
||||
font-size:x-large;
|
||||
text-align:center;
|
||||
font-weight:bold;
|
||||
margin-top:1em;
|
||||
margin-bottom:0px;
|
||||
}
|
||||
|
||||
p.title {
|
||||
margin-top:0em;
|
||||
margin-bottom:0em;
|
||||
text-align:center;
|
||||
font-style:italic;
|
||||
font-size:xx-large;
|
||||
}
|
||||
|
||||
p.wishlist_item, p.unread_book, p.read_book {
|
||||
text-align:left;
|
||||
margin-top:0px;
|
||||
margin-bottom:0px;
|
||||
margin-left:2em;
|
||||
text-indent:-2em;
|
||||
}
|
||||
|
||||
/*
|
||||
** Publisher, Publication Date
|
||||
*/
|
||||
td.publisher, td.date {
|
||||
font-weight:bold;
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
/*
|
||||
** Rating
|
||||
*/
|
||||
td.rating{
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
/*
|
||||
** Additional notes
|
||||
*/
|
||||
td.notes {
|
||||
font-size: 100%;
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
/*
|
||||
** Thumbnail
|
||||
*/
|
||||
td.thumbnail img {
|
||||
-webkit-box-shadow: 4px 4px 12px #999;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Comments
|
||||
*/
|
||||
div.description {
|
||||
margin: 0 0 0 0;
|
||||
text-indent: 1em;
|
||||
}
|
||||
div.description > p:first-child {
|
||||
margin: 0 0 0 0;
|
||||
text-indent: 0em;
|
||||
}
|
||||
|
||||
|
@ -506,13 +506,11 @@ compile_gpm_templates = True
|
||||
# default_tweak_format = 'remember'
|
||||
default_tweak_format = None
|
||||
|
||||
#: Enable multi-character first-letters in the tag browser
|
||||
# Some languages have letters that can be represented by multiple characters.
|
||||
# For example, Czech has a 'character' "ch" that sorts between "h" and "i".
|
||||
# If this tweak is True, then the tag browser will take these characters into
|
||||
# consideration when partitioning by first letter.
|
||||
# Examples:
|
||||
# enable_multicharacters_in_tag_browser = True
|
||||
# enable_multicharacters_in_tag_browser = False
|
||||
enable_multicharacters_in_tag_browser = True
|
||||
#: Do not preselect a completion when editing authors/tags/series/etc.
|
||||
# This means that you can make changes and press Enter and your changes will
|
||||
# not be overwritten by a matching completion. However, if you wish to use the
|
||||
# completions you will now have to press Tab to select one before pressing
|
||||
# Enter. Which technique you prefer will depend on the state of metadata in
|
||||
# your library and your personal editing style.
|
||||
preselect_first_completion = False
|
||||
|
||||
|
BIN
resources/images/devices/galaxy_s3.png
Normal file
After Width: | Height: | Size: 101 KiB |
BIN
resources/images/dot_green.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
resources/images/dot_red.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
resources/images/scroll.png
Normal file
After Width: | Height: | Size: 22 KiB |
@ -27,6 +27,7 @@
|
||||
*/
|
||||
.cbj_banner {
|
||||
background: #eee;
|
||||
color: black;
|
||||
border: thin solid black;
|
||||
margin: 1em;
|
||||
padding: 1em;
|
||||
|
@ -822,7 +822,6 @@ application/x-lzh lzh
|
||||
application/x-lzx lzx
|
||||
application/x-maker book fb fbdoc fm frame frm maker
|
||||
application/x-mif mif
|
||||
application/x-mobipocket-ebook mobi prc
|
||||
application/x-ms-application application
|
||||
application/x-ms-wmd wmd
|
||||
application/x-ms-wmz wmz
|
||||
@ -1371,11 +1370,11 @@ application/x-sony-bbeb lrf lrx
|
||||
application/adobe-page-template+xml xpgt
|
||||
application/x-font-opentype otf
|
||||
application/x-font-truetype ttf
|
||||
application/x-mobipocket-ebook mobi prc azw
|
||||
application/x-mobipocket-ebook mobi prc
|
||||
application/vnd.amazon.ebook azw3 azw azw2 azw4
|
||||
application/x-cbz cbz
|
||||
application/x-cbr cbr
|
||||
application/x-cb7 cb7
|
||||
application/x-koboreader-ebook kobo
|
||||
image/wmf wmf
|
||||
application/ereader pdb
|
||||
|
||||
|
2436
resources/viewer/mathjax/MathJax.js
Normal file
286
resources/viewer/mathjax/extensions/FontWarnings.js
Normal file
@ -0,0 +1,286 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* MathJax/extensions/FontWarnings.js
|
||||
*
|
||||
* Implements a font warning message window that appears when
|
||||
* the image fonts, no fonts, or web fonts are used, informing
|
||||
* the user where to download the fonts, or to update to a more
|
||||
* modern browser. The window will fade out automatically after
|
||||
* a time, and the user can dismiss it by a close box.
|
||||
*
|
||||
* To include font warning messages, add "FontWarnings.js" to the
|
||||
* extensions array in your MathJax configuration.
|
||||
*
|
||||
* You can customize the warning messages in a number of ways. Use the
|
||||
* FontWarnings section of the configuration to specify any of the items
|
||||
* shown in the CONFIG variable below. These include
|
||||
*
|
||||
* messageStyle the style to apply to the warning box that is
|
||||
* displayed when MathJax uses one of its fallback
|
||||
* methods.
|
||||
*
|
||||
* removeAfter the amount of time to show the warning message (in ms)
|
||||
* fadeoutTime how long the message should take to fade out
|
||||
* fadeoutSteps how many separate steps to use during the fade out
|
||||
* (set to 0 to use no fadeout and simply remove the window)
|
||||
*
|
||||
* Messages stores the descriptions of the messages to use for the
|
||||
* various warnings (webFonts, imageFonts, and noFonts).
|
||||
* These are arrays of strings to be inserted into the window,
|
||||
* or identifiers within brackets, which refer to the HTML
|
||||
* snippets in the HTML section described below. To disable a
|
||||
* specific message, set its value to null (see example below).
|
||||
*
|
||||
* HTML stores snippets of HTML descriptions for various
|
||||
* common parts of the error messages. These include
|
||||
* the closeBox, the message about web fonts being available
|
||||
* in modern browser, and messages about downloadable fonts.
|
||||
* The STIX and TeX font messages are used when only one
|
||||
* of these is in the availableFonts list. The data for these
|
||||
* are arrays of either strings to include or a description of
|
||||
* an HTML item enclosed in square brackets. That description
|
||||
* has (up to) three parts: the name of the tag to be included,
|
||||
* a list (enclosed in braces) of attributes and their values
|
||||
* to be set on the tag (optional), and an array of the contents
|
||||
* of the tag (optional). See the definitions below for examples.
|
||||
*
|
||||
* For example,
|
||||
*
|
||||
* MathJax.Hub.Config({
|
||||
* ...
|
||||
* extensions: ["FontWarnings.js"],
|
||||
* FontWarnings: {
|
||||
* removeAfter: 20*1000, // 20 seconds
|
||||
* messageStyle: {
|
||||
* border: "2px solid black",
|
||||
* padding: "2em"
|
||||
* },
|
||||
* Message: {
|
||||
* webFont: null // no webfont messages (only image and no fonts)
|
||||
* }
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* would extend the time the message is displayed from 12 seconds to 20,
|
||||
* and changes the border to a solid black one, with 2em of padding
|
||||
* rather than the default of 1em.
|
||||
*
|
||||
* ---------------------------------------------------------------------
|
||||
*
|
||||
* Copyright (c) 2010-2012 Design Science, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
(function (HUB,HTML) {
|
||||
var VERSION = "2.0";
|
||||
|
||||
var CONFIG = HUB.CombineConfig("FontWarnings",{
|
||||
//
|
||||
// The CSS for the message window
|
||||
//
|
||||
messageStyle: {
|
||||
position:"fixed", bottom:"4em", left:"3em", width:"40em",
|
||||
border: "3px solid #880000", "background-color": "#E0E0E0", color: "black",
|
||||
padding: "1em", "font-size":"small", "white-space":"normal",
|
||||
|
||||
"border-radius": ".75em", // Opera 10.5 and IE9
|
||||
"-webkit-border-radius": ".75em", // Safari and Chrome
|
||||
"-moz-border-radius": ".75em", // Firefox
|
||||
"-khtml-border-radius": ".75em", // Konqueror
|
||||
|
||||
"box-shadow": "4px 4px 10px #AAAAAA", // Opera 10.5 and IE9
|
||||
"-webkit-box-shadow": "4px 4px 10px #AAAAAA", // Safari 3 and Chrome
|
||||
"-moz-box-shadow": "4px 4px 10px #AAAAAA", // Forefox 3.5
|
||||
"-khtml-box-shadow": "4px 4px 10px #AAAAAA", // Konqueror
|
||||
filter: "progid:DXImageTransform.Microsoft.dropshadow(OffX=3, OffY=3, Color='gray', Positive='true')" // IE
|
||||
},
|
||||
|
||||
//
|
||||
// The messages for the various situations
|
||||
//
|
||||
Message: {
|
||||
webFont: [
|
||||
["closeBox"],
|
||||
"MathJax is using web-based fonts to display the mathematics ",
|
||||
"on this page. These take time to download, so the page would ",
|
||||
"render faster if you installed math fonts directly in your ",
|
||||
"system's font folder.",
|
||||
["fonts"]
|
||||
],
|
||||
|
||||
imageFonts: [
|
||||
["closeBox"],
|
||||
"MathJax is using its image fonts rather than local or web-based fonts. ",
|
||||
"This will render slower than usual, and the mathematics may not print ",
|
||||
"at the full resolution of your printer.",
|
||||
["fonts"],
|
||||
["webfonts"]
|
||||
],
|
||||
|
||||
noFonts: [
|
||||
["closeBox"],
|
||||
"MathJax is unable to locate a font to use to display ",
|
||||
"its mathematics, and image fonts are not available, so it ",
|
||||
"is falling back on generic unicode characters in hopes that ",
|
||||
"your browser will be able to display them. Some characters ",
|
||||
"may not show up properly, or possibly not at all.",
|
||||
["fonts"],
|
||||
["webfonts"]
|
||||
]
|
||||
},
|
||||
|
||||
//
|
||||
// HTML objects that can be referred to in the message definitions
|
||||
//
|
||||
HTML: {
|
||||
//
|
||||
// The definition of the close box
|
||||
//
|
||||
closeBox: [[
|
||||
"div",{
|
||||
style: {
|
||||
position:"absolute", overflow:"hidden", top:".1em", right:".1em",
|
||||
border: "1px outset", width:"1em", height:"1em",
|
||||
"text-align": "center", cursor: "pointer",
|
||||
"background-color": "#EEEEEE", color:"#606060",
|
||||
|
||||
"border-radius": ".5em", // Opera 10.5
|
||||
"-webkit-border-radius": ".5em", // Safari and Chrome
|
||||
"-moz-border-radius": ".5em", // Firefox
|
||||
"-khtml-border-radius": ".5em" // Konqueror
|
||||
},
|
||||
onclick: function () {
|
||||
if (DATA.div && DATA.fade === 0)
|
||||
{if (DATA.timer) {clearTimeout(DATA.timer)}; DATA.div.style.display = "none"}
|
||||
}
|
||||
},
|
||||
[["span",{style:{position:"relative", bottom:".2em"}},["x"]]]
|
||||
]],
|
||||
|
||||
webfonts: [
|
||||
["p"],
|
||||
"Most modern browsers allow for fonts to be downloaded over the web. ",
|
||||
"Updating to a more recent version of your browser (or changing browsers) ",
|
||||
"could improve the quality of the mathematics on this page."
|
||||
],
|
||||
|
||||
fonts: [
|
||||
["p"],
|
||||
"MathJax can use either the ",
|
||||
["a",{href:"http://www.stixfonts.org/",target:"_blank"},"STIX fonts"],
|
||||
" or the ",
|
||||
["a",{href:"http://www.mathjax.org/help-v2/fonts/",target:"_blank"},["MathJax TeX fonts"]],
|
||||
". Download and install either one to improve your MathJax experience."
|
||||
],
|
||||
|
||||
STIXfonts: [
|
||||
["p"],
|
||||
"This page is designed to use the ",
|
||||
["a",{href:"http://www.stixfonts.org/",target:"_blank"},"STIX fonts"],
|
||||
". Download and install those fonts to improve your MathJax experience."
|
||||
],
|
||||
|
||||
TeXfonts: [
|
||||
["p"],
|
||||
"This page is designed to use the ",
|
||||
["a",{href:"http://www.mathjax.org/help-v2/fonts/",target:"_blank"},["MathJax TeX fonts"]],
|
||||
". Download and install those fonts to improve your MathJax experience."
|
||||
]
|
||||
|
||||
},
|
||||
|
||||
removeAfter: 12*1000, // time to show message (in ms)
|
||||
fadeoutSteps: 10, // fade-out steps
|
||||
fadeoutTime: 1.5*1000 // fadeout over this amount of time (in ms)
|
||||
|
||||
});
|
||||
if (MathJax.Hub.Browser.isIE9 && document.documentMode >= 9)
|
||||
{delete CONFIG.messageStyle.filter}
|
||||
|
||||
//
|
||||
// Data for the window
|
||||
//
|
||||
var DATA = {
|
||||
div: null, // the message window, when displayed
|
||||
fade: 0 // number of fade-out steps so far
|
||||
};
|
||||
|
||||
//
|
||||
// Create the message window and start the fade-out timer
|
||||
//
|
||||
var CREATEMESSAGE = function (data) {
|
||||
if (DATA.div) return;
|
||||
var HTMLCSS = MathJax.OutputJax["HTML-CSS"], frame = document.body;
|
||||
if (HUB.Browser.isMSIE) {
|
||||
if (CONFIG.messageStyle.position === "fixed") {
|
||||
MathJax.Message.Init(); // make sure MathJax_MSIE_frame exists
|
||||
frame = document.getElementById("MathJax_MSIE_Frame");
|
||||
CONFIG.messageStyle.position = "absolute";
|
||||
}
|
||||
} else {delete CONFIG.messageStyle.filter}
|
||||
CONFIG.messageStyle.maxWidth = (document.body.clientWidth-75) + "px";
|
||||
var i = 0; while (i < data.length) {
|
||||
if (data[i] instanceof Array && CONFIG.HTML[data[i][0]])
|
||||
{data.splice.apply(data,[i,1].concat(CONFIG.HTML[data[i][0]]))} else {i++}
|
||||
}
|
||||
DATA.div = HTMLCSS.addElement(frame,"div",{id:"MathJax_FontWarning",style:CONFIG.messageStyle},data);
|
||||
if (CONFIG.removeAfter) {
|
||||
HUB.Register.StartupHook("End",function ()
|
||||
{DATA.timer = setTimeout(FADEOUT,CONFIG.removeAfter)});
|
||||
}
|
||||
HTML.Cookie.Set("fontWarn",{warned:true});
|
||||
};
|
||||
|
||||
//
|
||||
// Set the opacity based on the number of steps taken so far
|
||||
// and remove the window when it gets to 0
|
||||
//
|
||||
var FADEOUT = function () {
|
||||
DATA.fade++; if (DATA.timer) {delete DATA.timer}
|
||||
if (DATA.fade < CONFIG.fadeoutSteps) {
|
||||
var opacity = 1 - DATA.fade/CONFIG.fadeoutSteps;
|
||||
DATA.div.style.opacity = opacity;
|
||||
DATA.div.style.filter = "alpha(opacity="+Math.floor(100*opacity)+")";
|
||||
setTimeout(FADEOUT,CONFIG.fadeoutTime/CONFIG.fadeoutSteps);
|
||||
} else {
|
||||
DATA.div.style.display = "none";
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// Check that we haven't already issued a warning
|
||||
//
|
||||
if (!HTML.Cookie.Get("fontWarn").warned) {
|
||||
//
|
||||
// Hook into the Startup signal and look for font warning messages.
|
||||
// When one comes, issue the correct message.
|
||||
//
|
||||
HUB.Startup.signal.Interest(function (message) {
|
||||
if (message.match(/HTML-CSS Jax - /) && !DATA.div) {
|
||||
var HTMLCSS = MathJax.OutputJax["HTML-CSS"], FONTS = HTMLCSS.config.availableFonts, MSG;
|
||||
var localFonts = (FONTS && FONTS.length);
|
||||
if (!localFonts) {CONFIG.HTML.fonts = [""]}
|
||||
else if (FONTS.length === 1) {CONFIG.HTML.fonts = CONFIG.HTML[FONTS[0]+"fonts"]}
|
||||
if (HTMLCSS.allowWebFonts) {CONFIG.HTML.webfonts = [""]}
|
||||
if (message.match(/- Web-Font/)) {if (localFonts) {MSG = "webFont"}}
|
||||
else if (message.match(/- using image fonts/)) {MSG = "imageFonts"}
|
||||
else if (message.match(/- no valid font/)) {MSG = "noFonts"}
|
||||
if (MSG && CONFIG.Message[MSG]) {CREATEMESSAGE(CONFIG.Message[MSG])}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
})(MathJax.Hub,MathJax.HTML);
|
||||
|
||||
MathJax.Ajax.loadComplete("[MathJax]/extensions/FontWarnings.js");
|
531
resources/viewer/mathjax/extensions/MathEvents.js
Normal file
@ -0,0 +1,531 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* MathJax/extensions/MathEvents.js
|
||||
*
|
||||
* Implements the event handlers needed by the output jax to perform
|
||||
* menu, hover, and other events.
|
||||
*
|
||||
* ---------------------------------------------------------------------
|
||||
*
|
||||
* Copyright (c) 2011-2012 Design Science, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
(function (HUB,HTML,AJAX,CALLBACK,OUTPUT,INPUT) {
|
||||
var VERSION = "2.0";
|
||||
|
||||
var EXTENSION = MathJax.Extension;
|
||||
var ME = EXTENSION.MathEvents = {version: VERSION};
|
||||
|
||||
var SETTINGS = HUB.config.menuSettings;
|
||||
|
||||
var CONFIG = {
|
||||
hover: 500, // time required to be considered a hover
|
||||
frame: {
|
||||
x: 3.5, y: 5, // frame padding and
|
||||
bwidth: 1, // frame border width (in pixels)
|
||||
bcolor: "#A6D", // frame border color
|
||||
hwidth: "15px", // haze width
|
||||
hcolor: "#83A" // haze color
|
||||
},
|
||||
button: {
|
||||
x: -4, y: -3, // menu button offsets
|
||||
wx: -2, // button offset for full-width equations
|
||||
src: AJAX.fileURL(OUTPUT.imageDir+"/MenuArrow-15.png") // button image
|
||||
},
|
||||
fadeinInc: .2, // increment for fade-in
|
||||
fadeoutInc: .05, // increment for fade-out
|
||||
fadeDelay: 50, // delay between fade-in or fade-out steps
|
||||
fadeoutStart: 400, // delay before fade-out after mouseout
|
||||
fadeoutDelay: 15*1000, // delay before automatic fade-out
|
||||
|
||||
styles: {
|
||||
".MathJax_Hover_Frame": {
|
||||
"border-radius": ".25em", // Opera 10.5 and IE9
|
||||
"-webkit-border-radius": ".25em", // Safari and Chrome
|
||||
"-moz-border-radius": ".25em", // Firefox
|
||||
"-khtml-border-radius": ".25em", // Konqueror
|
||||
|
||||
"box-shadow": "0px 0px 15px #83A", // Opera 10.5 and IE9
|
||||
"-webkit-box-shadow": "0px 0px 15px #83A", // Safari and Chrome
|
||||
"-moz-box-shadow": "0px 0px 15px #83A", // Forefox
|
||||
"-khtml-box-shadow": "0px 0px 15px #83A", // Konqueror
|
||||
|
||||
border: "1px solid #A6D ! important",
|
||||
display: "inline-block", position:"absolute"
|
||||
},
|
||||
|
||||
".MathJax_Hover_Arrow": {
|
||||
position:"absolute",
|
||||
width:"15px", height:"11px",
|
||||
cursor:"pointer"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Common event-handling code
|
||||
//
|
||||
var EVENT = ME.Event = {
|
||||
|
||||
LEFTBUTTON: 0, // the event.button value for left button
|
||||
RIGHTBUTTON: 2, // the event.button value for right button
|
||||
MENUKEY: "altKey", // the event value for alternate context menu
|
||||
|
||||
Mousedown: function (event) {return EVENT.Handler(event,"Mousedown",this)},
|
||||
Mouseup: function (event) {return EVENT.Handler(event,"Mouseup",this)},
|
||||
Mousemove: function (event) {return EVENT.Handler(event,"Mousemove",this)},
|
||||
Mouseover: function (event) {return EVENT.Handler(event,"Mouseover",this)},
|
||||
Mouseout: function (event) {return EVENT.Handler(event,"Mouseout",this)},
|
||||
Click: function (event) {return EVENT.Handler(event,"Click",this)},
|
||||
DblClick: function (event) {return EVENT.Handler(event,"DblClick",this)},
|
||||
Menu: function (event) {return EVENT.Handler(event,"ContextMenu",this)},
|
||||
|
||||
//
|
||||
// Call the output jax's event handler or the zoom handler
|
||||
//
|
||||
Handler: function (event,type,math) {
|
||||
if (AJAX.loadingMathMenu) {return EVENT.False(event)}
|
||||
var jax = OUTPUT[math.jaxID];
|
||||
if (!event) {event = window.event}
|
||||
event.isContextMenu = (type === "ContextMenu");
|
||||
if (jax[type]) {return jax[type](event,math)}
|
||||
if (EXTENSION.MathZoom) {return EXTENSION.MathZoom.HandleEvent(event,type,math)}
|
||||
},
|
||||
|
||||
//
|
||||
// Try to cancel the event in every way we can
|
||||
//
|
||||
False: function (event) {
|
||||
if (!event) {event = window.event}
|
||||
if (event) {
|
||||
if (event.preventDefault) {event.preventDefault()}
|
||||
if (event.stopPropagation) {event.stopPropagation()}
|
||||
event.cancelBubble = true;
|
||||
event.returnValue = false;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
//
|
||||
// Load the contextual menu code, if needed, and post the menu
|
||||
//
|
||||
ContextMenu: function (event,math,force) {
|
||||
//
|
||||
// Check if we are showing menus
|
||||
//
|
||||
var JAX = OUTPUT[math.jaxID], jax = JAX.getJaxFromMath(math);
|
||||
var show = (JAX.config.showMathMenu != null ? JAX : HUB).config.showMathMenu;
|
||||
if (!show || (SETTINGS.context !== "MathJax" && !force)) return;
|
||||
|
||||
//
|
||||
// Remove selections, remove hover fades
|
||||
//
|
||||
if (ME.msieEventBug) {event = window.event || event}
|
||||
EVENT.ClearSelection(); HOVER.ClearHoverTimer();
|
||||
if (jax.hover) {
|
||||
if (jax.hover.remove) {clearTimeout(jax.hover.remove); delete jax.hover.remove}
|
||||
jax.hover.nofade = true;
|
||||
}
|
||||
|
||||
//
|
||||
// If the menu code is loaded, post the menu
|
||||
// Otherwse lad the menu code and try again
|
||||
//
|
||||
var MENU = MathJax.Menu;
|
||||
if (MENU) {
|
||||
MENU.jax = jax;
|
||||
var source = MENU.menu.Find("Show Math As").menu;
|
||||
source.items[1].name = (INPUT[jax.inputJax].sourceMenuTitle||"Original Form");
|
||||
source.items[0].hidden = (jax.inputJax === "Error"); // hide MathML choice for error messages
|
||||
var MathPlayer = MENU.menu.Find("Math Settings","MathPlayer");
|
||||
MathPlayer.hidden = !(jax.outputJax === "NativeMML" && HUB.Browser.hasMathPlayer);
|
||||
return MENU.menu.Post(event);
|
||||
} else {
|
||||
if (!AJAX.loadingMathMenu) {
|
||||
AJAX.loadingMathMenu = true;
|
||||
var ev = {
|
||||
pageX:event.pageX, pageY:event.pageY,
|
||||
clientX:event.clientX, clientY:event.clientY
|
||||
};
|
||||
CALLBACK.Queue(
|
||||
AJAX.Require("[MathJax]/extensions/MathMenu.js"),
|
||||
function () {delete AJAX.loadingMathMenu; if (!MathJax.Menu) {MathJax.Menu = {}}},
|
||||
["ContextMenu",this,ev,math,force] // call this function again
|
||||
);
|
||||
}
|
||||
return EVENT.False(event);
|
||||
}
|
||||
},
|
||||
|
||||
//
|
||||
// Mousedown handler for alternate means of accessing menu
|
||||
//
|
||||
AltContextMenu: function (event,math) {
|
||||
var JAX = OUTPUT[math.jaxID];
|
||||
var show = (JAX.config.showMathMenu != null ? JAX : HUB).config.showMathMenu;
|
||||
if (show) {
|
||||
show = (JAX.config.showMathMenuMSIE != null ? JAX : HUB).config.showMathMenuMSIE;
|
||||
if (SETTINGS.context === "MathJax" && !SETTINGS.mpContext && show) {
|
||||
if (!ME.noContextMenuBug || event.button !== EVENT.RIGHTBUTTON) return;
|
||||
} else {
|
||||
if (!event[EVENT.MENUKEY] || event.button !== EVENT.LEFTBUTTON) return;
|
||||
}
|
||||
return JAX.ContextMenu(event,math,true);
|
||||
}
|
||||
},
|
||||
|
||||
ClearSelection: function () {
|
||||
if (ME.safariContextMenuBug) {setTimeout("window.getSelection().empty()",0)}
|
||||
if (document.selection) {setTimeout("document.selection.empty()",0)}
|
||||
},
|
||||
|
||||
getBBox: function (span) {
|
||||
span.appendChild(ME.topImg);
|
||||
var h = ME.topImg.offsetTop, d = span.offsetHeight-h, w = span.offsetWidth;
|
||||
span.removeChild(ME.topImg);
|
||||
return {w:w, h:h, d:d};
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
//
|
||||
// Handle hover "discoverability"
|
||||
//
|
||||
var HOVER = ME.Hover = {
|
||||
|
||||
//
|
||||
// Check if we are moving from a non-MathJax element to a MathJax one
|
||||
// and either start fading in again (if it is fading out) or start the
|
||||
// timer for the hover
|
||||
//
|
||||
Mouseover: function (event,math) {
|
||||
if (SETTINGS.discoverable || SETTINGS.zoom === "Hover") {
|
||||
var from = event.fromElement || event.relatedTarget,
|
||||
to = event.toElement || event.target;
|
||||
if (from && to && (from.isMathJax != to.isMathJax ||
|
||||
HUB.getJaxFor(from) !== HUB.getJaxFor(to))) {
|
||||
var jax = this.getJaxFromMath(math);
|
||||
if (jax.hover) {HOVER.ReHover(jax)} else {HOVER.HoverTimer(jax,math)}
|
||||
return EVENT.False(event);
|
||||
}
|
||||
}
|
||||
},
|
||||
//
|
||||
// Check if we are moving from a MathJax element to a non-MathJax one
|
||||
// and either start fading out, or clear the timer if we haven't
|
||||
// hovered yet
|
||||
//
|
||||
Mouseout: function (event,math) {
|
||||
if (SETTINGS.discoverable || SETTINGS.zoom === "Hover") {
|
||||
var from = event.fromElement || event.relatedTarget,
|
||||
to = event.toElement || event.target;
|
||||
if (from && to && (from.isMathJax != to.isMathJax ||
|
||||
HUB.getJaxFor(from) !== HUB.getJaxFor(to))) {
|
||||
var jax = this.getJaxFromMath(math);
|
||||
if (jax.hover) {HOVER.UnHover(jax)} else {HOVER.ClearHoverTimer()}
|
||||
return EVENT.False(event);
|
||||
}
|
||||
}
|
||||
},
|
||||
//
|
||||
// Restart hover timer if the mouse moves
|
||||
//
|
||||
Mousemove: function (event,math) {
|
||||
if (SETTINGS.discoverable || SETTINGS.zoom === "Hover") {
|
||||
var jax = this.getJaxFromMath(math); if (jax.hover) return;
|
||||
if (HOVER.lastX == event.clientX && HOVER.lastY == event.clientY) return;
|
||||
HOVER.lastX = event.clientX; HOVER.lastY = event.clientY;
|
||||
HOVER.HoverTimer(jax,math);
|
||||
return EVENT.False(event);
|
||||
}
|
||||
},
|
||||
|
||||
//
|
||||
// Clear the old timer and start a new one
|
||||
//
|
||||
HoverTimer: function (jax,math) {
|
||||
this.ClearHoverTimer();
|
||||
this.hoverTimer = setTimeout(CALLBACK(["Hover",this,jax,math]),CONFIG.hover);
|
||||
},
|
||||
ClearHoverTimer: function () {
|
||||
if (this.hoverTimer) {clearTimeout(this.hoverTimer); delete this.hoverTimer}
|
||||
},
|
||||
|
||||
//
|
||||
// Handle putting up the hover frame
|
||||
//
|
||||
Hover: function (jax,math) {
|
||||
//
|
||||
// Check if Zoom handles the hover event
|
||||
//
|
||||
if (EXTENSION.MathZoom && EXTENSION.MathZoom.Hover({},math)) return;
|
||||
//
|
||||
// Get the hover data
|
||||
//
|
||||
var JAX = OUTPUT[jax.outputJax],
|
||||
span = JAX.getHoverSpan(jax,math),
|
||||
bbox = JAX.getHoverBBox(jax,span,math),
|
||||
show = (JAX.config.showMathMenu != null ? JAX : HUB).config.showMathMenu;
|
||||
var dx = CONFIG.frame.x, dy = CONFIG.frame.y, dd = CONFIG.frame.bwidth; // frame size
|
||||
if (ME.msieBorderWidthBug) {dd = 0}
|
||||
jax.hover = {opacity:0, id:jax.inputID+"-Hover"};
|
||||
//
|
||||
// The frame and menu button
|
||||
//
|
||||
var frame = HTML.Element("span",{
|
||||
id:jax.hover.id, isMathJax: true,
|
||||
style:{display:"inline-block", width:0, height:0, position:"relative"}
|
||||
},[["span",{
|
||||
className:"MathJax_Hover_Frame", isMathJax: true,
|
||||
style:{
|
||||
display:"inline-block", position:"absolute",
|
||||
top:this.Px(-bbox.h-dy-dd-(bbox.y||0)), left:this.Px(-dx-dd+(bbox.x||0)),
|
||||
width:this.Px(bbox.w+2*dx), height:this.Px(bbox.h+bbox.d+2*dy),
|
||||
opacity:0, filter:"alpha(opacity=0)"
|
||||
}}
|
||||
]]
|
||||
);
|
||||
var button = HTML.Element("span",{
|
||||
isMathJax: true, id:jax.hover.id+"Menu",
|
||||
style:{display:"inline-block", "z-index": 1, width:0, height:0, position:"relative"}
|
||||
},[["img",{
|
||||
className: "MathJax_Hover_Arrow", isMathJax: true, math: math,
|
||||
src: CONFIG.button.src, onclick: this.HoverMenu, jax:JAX.id,
|
||||
style: {
|
||||
left:this.Px(bbox.w+dx+dd+(bbox.x||0)+CONFIG.button.x),
|
||||
top:this.Px(-bbox.h-dy-dd-(bbox.y||0)-CONFIG.button.y),
|
||||
opacity:0, filter:"alpha(opacity=0)"
|
||||
}
|
||||
}]]
|
||||
);
|
||||
if (bbox.width) {
|
||||
frame.style.width = button.style.width = bbox.width;
|
||||
frame.style.marginRight = button.style.marginRight = "-"+bbox.width;
|
||||
frame.firstChild.style.width = bbox.width;
|
||||
button.firstChild.style.left = "";
|
||||
button.firstChild.style.right = this.Px(CONFIG.button.wx);
|
||||
}
|
||||
//
|
||||
// Add the frame and button
|
||||
//
|
||||
span.parentNode.insertBefore(frame,span);
|
||||
if (show) {span.parentNode.insertBefore(button,span)}
|
||||
if (span.style) {span.style.position = "relative"} // so math is on top of hover frame
|
||||
//
|
||||
// Start the hover fade-in
|
||||
//
|
||||
this.ReHover(jax);
|
||||
},
|
||||
//
|
||||
// Restart the hover fade in and fade-out timers
|
||||
//
|
||||
ReHover: function (jax) {
|
||||
if (jax.hover.remove) {clearTimeout(jax.hover.remove)}
|
||||
jax.hover.remove = setTimeout(CALLBACK(["UnHover",this,jax]),CONFIG.fadeoutDelay);
|
||||
this.HoverFadeTimer(jax,CONFIG.fadeinInc);
|
||||
},
|
||||
//
|
||||
// Start the fade-out
|
||||
//
|
||||
UnHover: function (jax) {
|
||||
if (!jax.hover.nofade) {this.HoverFadeTimer(jax,-CONFIG.fadeoutInc,CONFIG.fadeoutStart)}
|
||||
},
|
||||
//
|
||||
// Handle the fade-in and fade-out
|
||||
//
|
||||
HoverFade: function (jax) {
|
||||
delete jax.hover.timer;
|
||||
jax.hover.opacity = Math.max(0,Math.min(1,jax.hover.opacity + jax.hover.inc));
|
||||
jax.hover.opacity = Math.floor(1000*jax.hover.opacity)/1000;
|
||||
var frame = document.getElementById(jax.hover.id),
|
||||
button = document.getElementById(jax.hover.id+"Menu");
|
||||
frame.firstChild.style.opacity = jax.hover.opacity;
|
||||
frame.firstChild.style.filter = "alpha(opacity="+Math.floor(100*jax.hover.opacity)+")";
|
||||
if (button) {
|
||||
button.firstChild.style.opacity = jax.hover.opacity;
|
||||
button.firstChild.style.filter = frame.style.filter;
|
||||
}
|
||||
if (jax.hover.opacity === 1) {return}
|
||||
if (jax.hover.opacity > 0) {this.HoverFadeTimer(jax,jax.hover.inc); return}
|
||||
frame.parentNode.removeChild(frame);
|
||||
if (button) {button.parentNode.removeChild(button)}
|
||||
if (jax.hover.remove) {clearTimeout(jax.hover.remove)}
|
||||
delete jax.hover;
|
||||
},
|
||||
//
|
||||
// Set the fade to in or out (via inc) and start the timer, if needed
|
||||
//
|
||||
HoverFadeTimer: function (jax,inc,delay) {
|
||||
jax.hover.inc = inc;
|
||||
if (!jax.hover.timer) {
|
||||
jax.hover.timer = setTimeout(CALLBACK(["HoverFade",this,jax]),(delay||CONFIG.fadeDelay));
|
||||
}
|
||||
},
|
||||
|
||||
//
|
||||
// Handle a click on the menu button
|
||||
//
|
||||
HoverMenu: function (event) {
|
||||
if (!event) {event = window.event}
|
||||
return OUTPUT[this.jax].ContextMenu(event,this.math,true);
|
||||
},
|
||||
|
||||
//
|
||||
// Clear all hover timers
|
||||
//
|
||||
ClearHover: function (jax) {
|
||||
if (jax.hover.remove) {clearTimeout(jax.hover.remove)}
|
||||
if (jax.hover.timer) {clearTimeout(jax.hover.timer)}
|
||||
HOVER.ClearHoverTimer();
|
||||
delete jax.hover;
|
||||
},
|
||||
|
||||
//
|
||||
// Make a measurement in pixels
|
||||
//
|
||||
Px: function (m) {
|
||||
if (Math.abs(m) < .006) {return "0px"}
|
||||
return m.toFixed(2).replace(/\.?0+$/,"") + "px";
|
||||
},
|
||||
|
||||
//
|
||||
// Preload images so they show up with the menu
|
||||
//
|
||||
getImages: function () {
|
||||
var menu = new Image();
|
||||
menu.src = CONFIG.button.src;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
//
|
||||
// Handle touch events.
|
||||
//
|
||||
// Use double-tap-and-hold as a replacement for context menu event.
|
||||
// Use double-tap as a replacement for double click.
|
||||
//
|
||||
var TOUCH = ME.Touch = {
|
||||
|
||||
last: 0, // time of last tap event
|
||||
delay: 500, // delay time for double-click
|
||||
|
||||
//
|
||||
// Check if this is a double-tap, and if so, start the timer
|
||||
// for the double-tap and hold (to trigger the contextual menu)
|
||||
//
|
||||
start: function (event) {
|
||||
var now = new Date().getTime();
|
||||
var dblTap = (now - TOUCH.last < TOUCH.delay);
|
||||
TOUCH.last = now;
|
||||
if (dblTap) {
|
||||
TOUCH.timeout = setTimeout(TOUCH.menu,TOUCH.delay,event,this);
|
||||
event.preventDefault();
|
||||
}
|
||||
},
|
||||
|
||||
//
|
||||
// Check if there is a timeout pending, i.e., we have a
|
||||
// double-tap and were waiting to see if it is held long
|
||||
// enough for the menu. Since we got the end before the
|
||||
// timeout, it is a double-click, not a double-tap-and-hold.
|
||||
// Prevent the default action and issue a double click.
|
||||
//
|
||||
end: function (event) {
|
||||
if (TOUCH.timeout) {
|
||||
clearTimeout(TOUCH.timeout);
|
||||
delete TOUCH.timeout; TOUCH.last = 0;
|
||||
event.preventDefault();
|
||||
return EVENT.Handler((event.touches[0]||event.touch),"DblClick",this);
|
||||
}
|
||||
},
|
||||
|
||||
//
|
||||
// If the timeout passes without an end event, we issue
|
||||
// the contextual menu event.
|
||||
//
|
||||
menu: function (event,math) {
|
||||
delete TOUCH.timeout; TOUCH.last = 0;
|
||||
return EVENT.Handler((event.touches[0]||event.touch),"ContextMenu",math);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
//
|
||||
// Mobile screens are small, so use larger version of arrow
|
||||
//
|
||||
if (HUB.Browser.isMobile) {
|
||||
var arrow = CONFIG.styles[".MathJax_Hover_Arrow"];
|
||||
arrow.width = "25px"; arrow.height = "18px";
|
||||
CONFIG.button.x = -6;
|
||||
}
|
||||
|
||||
//
|
||||
// Set up browser-specific values
|
||||
//
|
||||
HUB.Browser.Select({
|
||||
MSIE: function (browser) {
|
||||
var mode = (document.documentMode || 0);
|
||||
var isIE8 = browser.versionAtLeast("8.0");
|
||||
ME.msieBorderWidthBug = (document.compatMode === "BackCompat"); // borders are inside offsetWidth/Height
|
||||
ME.msieEventBug = browser.isIE9; // must get event from window even though event is passed
|
||||
ME.msieAlignBug = (!isIE8 || mode < 8); // inline-block spans don't rest on baseline
|
||||
if (mode < 9) {EVENT.LEFTBUTTON = 1} // IE < 9 has wrong event.button values
|
||||
},
|
||||
Safari: function (browser) {
|
||||
ME.safariContextMenuBug = true; // selection can be started by contextmenu event
|
||||
},
|
||||
Opera: function (browser) {
|
||||
ME.operaPositionBug = true; // position is wrong unless border is used
|
||||
},
|
||||
Konqueror: function (browser) {
|
||||
ME.noContextMenuBug = true; // doesn't produce contextmenu event
|
||||
}
|
||||
});
|
||||
|
||||
//
|
||||
// Used in measuring zoom and hover positions
|
||||
//
|
||||
ME.topImg = (ME.msieAlignBug ?
|
||||
HTML.Element("img",{style:{width:0,height:0,position:"relative"},src:"about:blank"}) :
|
||||
HTML.Element("span",{style:{width:0,height:0,display:"inline-block"}})
|
||||
);
|
||||
if (ME.operaPositionBug) {ME.topImg.style.border="1px solid"}
|
||||
|
||||
//
|
||||
// Get configuration from user
|
||||
//
|
||||
ME.config = CONFIG = HUB.CombineConfig("MathEvents",CONFIG);
|
||||
var SETFRAME = function () {
|
||||
var haze = CONFIG.styles[".MathJax_Hover_Frame"];
|
||||
haze.border = CONFIG.frame.bwidth+"px solid "+CONFIG.frame.bcolor+" ! important";
|
||||
haze["box-shadow"] = haze["-webkit-box-shadow"] =
|
||||
haze["-moz-box-shadow"] = haze["-khtml-box-shadow"] =
|
||||
"0px 0px "+CONFIG.frame.hwidth+" "+CONFIG.frame.hcolor;
|
||||
};
|
||||
|
||||
//
|
||||
// Queue the events needed for startup
|
||||
//
|
||||
CALLBACK.Queue(
|
||||
HUB.Register.StartupHook("End Config",{}), // wait until config is complete
|
||||
[SETFRAME],
|
||||
["getImages",HOVER],
|
||||
["Styles",AJAX,CONFIG.styles],
|
||||
["Post",HUB.Startup.signal,"MathEvents Ready"],
|
||||
["loadComplete",AJAX,"[MathJax]/extensions/MathEvents.js"]
|
||||
);
|
||||
|
||||
})(MathJax.Hub,MathJax.HTML,MathJax.Ajax,MathJax.Callback,MathJax.OutputJax,MathJax.InputJax);
|
1025
resources/viewer/mathjax/extensions/MathMenu.js
Normal file
316
resources/viewer/mathjax/extensions/MathZoom.js
Normal file
@ -0,0 +1,316 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* MathJax/extensions/MathZoom.js
|
||||
*
|
||||
* Implements the zoom feature for enlarging math expressions. It is
|
||||
* loaded automatically when the Zoom menu selection changes from "None".
|
||||
*
|
||||
* ---------------------------------------------------------------------
|
||||
*
|
||||
* Copyright (c) 2010-2012 Design Science, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
(function (HUB,HTML,AJAX,HTMLCSS,nMML) {
|
||||
var VERSION = "2.0";
|
||||
|
||||
var CONFIG = HUB.CombineConfig("MathZoom",{
|
||||
styles: {
|
||||
//
|
||||
// The styles for the MathZoom display box
|
||||
//
|
||||
"#MathJax_Zoom": {
|
||||
position:"absolute", "background-color":"#F0F0F0", overflow:"auto",
|
||||
display:"block", "z-index":301, padding:".5em", border:"1px solid black", margin:0,
|
||||
"font-weight":"normal", "font-style":"normal",
|
||||
"text-align":"left", "text-indent":0, "text-transform":"none",
|
||||
"line-height":"normal", "letter-spacing":"normal", "word-spacing":"normal",
|
||||
"word-wrap":"normal", "white-space":"nowrap", "float":"none",
|
||||
"box-shadow":"5px 5px 15px #AAAAAA", // Opera 10.5 and IE9
|
||||
"-webkit-box-shadow":"5px 5px 15px #AAAAAA", // Safari 3 and Chrome
|
||||
"-moz-box-shadow":"5px 5px 15px #AAAAAA", // Forefox 3.5
|
||||
"-khtml-box-shadow":"5px 5px 15px #AAAAAA", // Konqueror
|
||||
filter: "progid:DXImageTransform.Microsoft.dropshadow(OffX=2, OffY=2, Color='gray', Positive='true')" // IE
|
||||
},
|
||||
|
||||
//
|
||||
// The styles for the hidden overlay (should not need to be adjusted by the page author)
|
||||
//
|
||||
"#MathJax_ZoomOverlay": {
|
||||
position:"absolute", left:0, top:0, "z-index":300, display:"inline-block",
|
||||
width:"100%", height:"100%", border:0, padding:0, margin:0,
|
||||
"background-color":"white", opacity:0, filter:"alpha(opacity=0)"
|
||||
},
|
||||
|
||||
"#MathJax_ZoomEventTrap": {
|
||||
position:"absolute", left:0, top:0, "z-index":302,
|
||||
display:"inline-block", border:0, padding:0, margin:0,
|
||||
"background-color":"white", opacity:0, filter:"alpha(opacity=0)"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var FALSE, HOVER, EVENT;
|
||||
MathJax.Hub.Register.StartupHook("MathEvents Ready",function () {
|
||||
EVENT = MathJax.Extension.MathEvents.Event;
|
||||
FALSE = MathJax.Extension.MathEvents.Event.False;
|
||||
HOVER = MathJax.Extension.MathEvents.Hover;
|
||||
});
|
||||
|
||||
/*************************************************************/
|
||||
|
||||
var ZOOM = MathJax.Extension.MathZoom = {
|
||||
version: VERSION,
|
||||
settings: HUB.config.menuSettings,
|
||||
scrollSize: 18, // width of scrool bars
|
||||
|
||||
//
|
||||
// Process events passed from output jax
|
||||
//
|
||||
HandleEvent: function (event,type,math) {
|
||||
if (ZOOM.settings.CTRL && !event.ctrlKey) return true;
|
||||
if (ZOOM.settings.ALT && !event.altKey) return true;
|
||||
if (ZOOM.settings.CMD && !event.metaKey) return true;
|
||||
if (ZOOM.settings.Shift && !event.shiftKey) return true;
|
||||
if (!ZOOM[type]) return true;
|
||||
return ZOOM[type](event,math);
|
||||
},
|
||||
|
||||
//
|
||||
// Zoom on click
|
||||
//
|
||||
Click: function (event,math) {
|
||||
if (this.settings.zoom === "Click") {return this.Zoom(event,math)}
|
||||
},
|
||||
|
||||
//
|
||||
// Zoom on double click
|
||||
//
|
||||
DblClick: function (event,math) {
|
||||
if (this.settings.zoom === "Double-Click") {return this.Zoom(event,math)}
|
||||
},
|
||||
|
||||
//
|
||||
// Zoom on hover (called by MathEvents.Hover)
|
||||
//
|
||||
Hover: function (event,math) {
|
||||
if (this.settings.zoom === "Hover") {this.Zoom(event,math); return true}
|
||||
return false;
|
||||
},
|
||||
|
||||
|
||||
//
|
||||
// Handle the actual zooming
|
||||
//
|
||||
Zoom: function (event,math) {
|
||||
//
|
||||
// Remove any other zoom and clear timers
|
||||
//
|
||||
this.Remove(); HOVER.ClearHoverTimer(); EVENT.ClearSelection();
|
||||
|
||||
//
|
||||
// Find the jax
|
||||
//
|
||||
var JAX = MathJax.OutputJax[math.jaxID];
|
||||
var jax = JAX.getJaxFromMath(math);
|
||||
if (jax.hover) {HOVER.UnHover(jax)}
|
||||
|
||||
//
|
||||
// Create the DOM elements for the zoom box
|
||||
//
|
||||
var Mw = Math.floor(.85*document.body.clientWidth),
|
||||
Mh = Math.floor(.85*Math.max(document.body.clientHeight,document.documentElement.clientHeight));
|
||||
var div = HTML.Element(
|
||||
"span",{
|
||||
style: {position:"relative", display:"inline-block", height:0, width:0},
|
||||
id:"MathJax_ZoomFrame"
|
||||
},[
|
||||
["span",{id:"MathJax_ZoomOverlay", onmousedown:this.Remove}],
|
||||
["span",{
|
||||
id:"MathJax_Zoom", onclick:this.Remove,
|
||||
style:{
|
||||
visibility:"hidden", fontSize:this.settings.zscale,
|
||||
"max-width":Mw+"px", "max-height":Mh+"px"
|
||||
}
|
||||
},[["span",{style:{display:"inline-block", "white-space":"nowrap"}}]]
|
||||
]]
|
||||
);
|
||||
var zoom = div.lastChild, span = zoom.firstChild, overlay = div.firstChild;
|
||||
math.parentNode.insertBefore(div,math);
|
||||
if (span.addEventListener) {span.addEventListener("mousedown",this.Remove,true)}
|
||||
|
||||
if (this.msieTrapEventBug) {
|
||||
var trap = HTML.Element("span",{id:"MathJax_ZoomEventTrap", onmousedown:this.Remove});
|
||||
div.insertBefore(trap,zoom);
|
||||
}
|
||||
|
||||
//
|
||||
// Display the zoomed math
|
||||
//
|
||||
if (this.msieZIndexBug) {
|
||||
// MSIE doesn't do z-index properly, so move the div to the document.body,
|
||||
// and use an image as a tracker for the usual position
|
||||
var tracker = HTML.addElement(document.body,"img",{
|
||||
src:"about:blank", id:"MathJax_ZoomTracker", width:0, height:0,
|
||||
style:{width:0, height:0, position:"relative"}
|
||||
});
|
||||
div.style.position = "relative";
|
||||
div.style.zIndex = CONFIG.styles["#MathJax_ZoomOverlay"]["z-index"];
|
||||
div = tracker;
|
||||
}
|
||||
|
||||
var bbox = JAX.Zoom(jax,span,math,Mw,Mh);
|
||||
|
||||
//
|
||||
// Fix up size and position for browsers with bugs (IE)
|
||||
//
|
||||
if (this.msiePositionBug) {
|
||||
if (this.msieSizeBug)
|
||||
{zoom.style.height = bbox.zH+"px"; zoom.style.width = bbox.zW+"px"} // IE8 gets the dimensions completely wrong
|
||||
if (zoom.offsetHeight > Mh) {zoom.style.height = Mh+"px"; zoom.style.width = (bbox.zW+this.scrollSize)+"px"} // IE doesn't do max-height?
|
||||
if (zoom.offsetWidth > Mw) {zoom.style.width = Mw+"px"; zoom.style.height = (bbox.zH+this.scrollSize)+"px"}
|
||||
}
|
||||
if (this.operaPositionBug) {zoom.style.width = Math.min(Mw,bbox.zW)+"px"} // Opera gets width as 0?
|
||||
if (zoom.offsetWidth < Mw && zoom.offsetHeight < Mh) {zoom.style.overflow = "visible"}
|
||||
this.Position(zoom,bbox);
|
||||
if (this.msieTrapEventBug) {
|
||||
trap.style.height = zoom.clientHeight+"px"; trap.style.width = zoom.clientWidth+"px";
|
||||
trap.style.left = (parseFloat(zoom.style.left)+zoom.clientLeft)+"px";
|
||||
trap.style.top = (parseFloat(zoom.style.top)+zoom.clientTop)+"px";
|
||||
}
|
||||
zoom.style.visibility = "";
|
||||
|
||||
//
|
||||
// Add event handlers
|
||||
//
|
||||
if (this.settings.zoom === "Hover") {overlay.onmouseover = this.Remove}
|
||||
if (window.addEventListener) {addEventListener("resize",this.Resize,false)}
|
||||
else if (window.attachEvent) {attachEvent("onresize",this.Resize)}
|
||||
else {this.onresize = window.onresize; window.onresize = this.Resize}
|
||||
|
||||
//
|
||||
// Let others know about the zoomed math
|
||||
//
|
||||
HUB.signal.Post(["math zoomed",jax]);
|
||||
|
||||
//
|
||||
// Canel further actions
|
||||
//
|
||||
return FALSE(event);
|
||||
},
|
||||
|
||||
//
|
||||
// Set the position of the zoom box and overlay
|
||||
//
|
||||
Position: function (zoom,bbox) {
|
||||
var XY = this.Resize(), x = XY.x, y = XY.y, W = bbox.mW;
|
||||
var dx = -Math.floor((zoom.offsetWidth-W)/2), dy = bbox.Y;
|
||||
zoom.style.left = Math.max(dx,10-x)+"px"; zoom.style.top = Math.max(dy,10-y)+"px";
|
||||
if (!ZOOM.msiePositionBug) {ZOOM.SetWH()} // refigure overlay width/height
|
||||
},
|
||||
|
||||
//
|
||||
// Handle resizing of overlay while zoom is displayed
|
||||
//
|
||||
Resize: function (event) {
|
||||
if (ZOOM.onresize) {ZOOM.onresize(event)}
|
||||
var x = 0, y = 0, obj,
|
||||
div = document.getElementById("MathJax_ZoomFrame"),
|
||||
overlay = document.getElementById("MathJax_ZoomOverlay");
|
||||
obj = div; while (obj.offsetParent) {x += obj.offsetLeft; obj = obj.offsetParent}
|
||||
if (ZOOM.operaPositionBug) {div.style.border = "1px solid"} // to get vertical position right
|
||||
obj = div; while (obj.offsetParent) {y += obj.offsetTop; obj = obj.offsetParent}
|
||||
if (ZOOM.operaPositionBug) {div.style.border = ""}
|
||||
overlay.style.left = (-x)+"px"; overlay.style.top = (-y)+"px";
|
||||
if (ZOOM.msiePositionBug) {setTimeout(ZOOM.SetWH,0)} else {ZOOM.SetWH()}
|
||||
return {x:x, y:y};
|
||||
},
|
||||
SetWH: function () {
|
||||
var overlay = document.getElementById("MathJax_ZoomOverlay");
|
||||
overlay.style.width = overlay.style.height = "1px"; // so scrollWidth/Height will be right below
|
||||
var doc = document.documentElement || document.body;
|
||||
overlay.style.width = doc.scrollWidth + "px";
|
||||
overlay.style.height = Math.max(doc.clientHeight,doc.scrollHeight) + "px";
|
||||
},
|
||||
|
||||
//
|
||||
// Remove zoom display and event handlers
|
||||
//
|
||||
Remove: function (event) {
|
||||
var div = document.getElementById("MathJax_ZoomFrame");
|
||||
if (div) {
|
||||
var JAX = MathJax.OutputJax[div.nextSibling.jaxID];
|
||||
var jax = JAX.getJaxFromMath(div.nextSibling);
|
||||
HUB.signal.Post(["math unzoomed",jax]);
|
||||
div.parentNode.removeChild(div);
|
||||
div = document.getElementById("MathJax_ZoomTracker");
|
||||
if (div) {div.parentNode.removeChild(div)}
|
||||
if (ZOOM.operaRefreshBug) {
|
||||
// force a redisplay of the page
|
||||
// (Opera doesn't refresh properly after the zoom is removed)
|
||||
var overlay = HTML.addElement(document.body,"div",{
|
||||
style:{position:"fixed", left:0, top:0, width:"100%", height:"100%",
|
||||
backgroundColor:"white", opacity:0},
|
||||
id: "MathJax_OperaDiv"
|
||||
});
|
||||
document.body.removeChild(overlay);
|
||||
}
|
||||
if (window.removeEventListener) {removeEventListener("resize",ZOOM.Resize,false)}
|
||||
else if (window.detachEvent) {detachEvent("onresize",ZOOM.Resize)}
|
||||
else {window.onresize = ZOOM.onresize; delete ZOOM.onresize}
|
||||
}
|
||||
return FALSE(event);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
/*************************************************************/
|
||||
|
||||
HUB.Browser.Select({
|
||||
MSIE: function (browser) {
|
||||
var mode = (document.documentMode || 0);
|
||||
var isIE9 = (mode >= 9);
|
||||
ZOOM.msiePositionBug = !isIE9;
|
||||
ZOOM.msieSizeBug = browser.versionAtLeast("7.0") &&
|
||||
(!document.documentMode || mode === 7 || mode === 8);
|
||||
ZOOM.msieZIndexBug = (mode <= 7);
|
||||
ZOOM.msieInlineBlockAlignBug = (mode <= 7);
|
||||
ZOOM.msieTrapEventBug = !window.addEventListener;
|
||||
if (document.compatMode === "BackCompat") {ZOOM.scrollSize = 52} // don't know why this is so far off
|
||||
if (isIE9) {delete CONFIG.styles["#MathJax_Zoom"].filter}
|
||||
},
|
||||
|
||||
Opera: function (browser) {
|
||||
ZOOM.operaPositionBug = true;
|
||||
ZOOM.operaRefreshBug = true;
|
||||
}
|
||||
});
|
||||
|
||||
ZOOM.topImg = (ZOOM.msieInlineBlockAlignBug ?
|
||||
HTML.Element("img",{style:{width:0,height:0,position:"relative"},src:"about:blank"}) :
|
||||
HTML.Element("span",{style:{width:0,height:0,display:"inline-block"}})
|
||||
);
|
||||
if (ZOOM.operaPositionBug || ZOOM.msieTopBug) {ZOOM.topImg.style.border="1px solid"}
|
||||
|
||||
/*************************************************************/
|
||||
|
||||
MathJax.Callback.Queue(
|
||||
["StartupHook",MathJax.Hub.Register,"Begin Styles",{}],
|
||||
["Styles",AJAX,CONFIG.styles],
|
||||
["Post",HUB.Startup.signal,"MathZoom Ready"],
|
||||
["loadComplete",AJAX,"[MathJax]/extensions/MathZoom.js"]
|
||||
);
|
||||
|
||||
})(MathJax.Hub,MathJax.HTML,MathJax.Ajax,MathJax.OutputJax["HTML-CSS"],MathJax.OutputJax.NativeMML);
|
567
resources/viewer/mathjax/extensions/TeX/AMSmath.js
Normal file
@ -0,0 +1,567 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* MathJax/extensions/TeX/AMSmath.js
|
||||
*
|
||||
* Implements AMS math environments and macros.
|
||||
*
|
||||
* ---------------------------------------------------------------------
|
||||
*
|
||||
* Copyright (c) 2009-2012 Design Science, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
MathJax.Extension["TeX/AMSmath"] = {
|
||||
version: "2.0",
|
||||
|
||||
number: 0, // current equation number
|
||||
startNumber: 0, // current starting equation number (for when equation is restarted)
|
||||
labels: {}, // the set of labels
|
||||
eqlabels: {}, // labels in the current equation
|
||||
refs: [] // array of jax with unresolved references
|
||||
};
|
||||
|
||||
MathJax.Hub.Register.StartupHook("TeX Jax Ready",function () {
|
||||
|
||||
var MML = MathJax.ElementJax.mml,
|
||||
TEX = MathJax.InputJax.TeX,
|
||||
AMS = MathJax.Extension["TeX/AMSmath"];
|
||||
|
||||
var TEXDEF = TEX.Definitions,
|
||||
STACKITEM = TEX.Stack.Item,
|
||||
CONFIG = TEX.config.equationNumbers;
|
||||
|
||||
var COLS = function (W) {return W.join("em ") + "em"};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
TEXDEF.Add({
|
||||
macros: {
|
||||
mathring: ['Accent','2DA'], // or 0x30A
|
||||
|
||||
nobreakspace: 'Tilde',
|
||||
negmedspace: ['Spacer',MML.LENGTH.NEGATIVEMEDIUMMATHSPACE],
|
||||
negthickspace: ['Spacer',MML.LENGTH.NEGATIVETHICKMATHSPACE],
|
||||
|
||||
intI: ['Macro','\\mathchoice{\\!}{}{}{}\\!\\!\\int'],
|
||||
// iint: ['MultiIntegral','\\int\\intI'], // now in core TeX input jax
|
||||
// iiint: ['MultiIntegral','\\int\\intI\\intI'], // now in core TeX input jax
|
||||
iiiint: ['MultiIntegral','\\int\\intI\\intI\\intI'],
|
||||
idotsint: ['MultiIntegral','\\int\\cdots\\int'],
|
||||
|
||||
dddot: ['Macro','\\mathop{#1}\\limits^{\\textstyle \\mathord{.}\\mathord{.}\\mathord{.}}',1],
|
||||
ddddot: ['Macro','\\mathop{#1}\\limits^{\\textstyle \\mathord{.}\\mathord{.}\\mathord{.}\\mathord{.}}',1],
|
||||
|
||||
sideset: ['Macro','\\mathop{\\mathop{\\rlap{\\phantom{#3}}}\\nolimits#1\\!\\mathop{#3}\\nolimits#2}',3],
|
||||
|
||||
boxed: ['Macro','\\fbox{$\\displaystyle{#1}$}',1],
|
||||
|
||||
tag: 'HandleTag',
|
||||
notag: 'HandleNoTag',
|
||||
label: 'HandleLabel',
|
||||
ref: 'HandleRef',
|
||||
eqref: ['HandleRef',true],
|
||||
|
||||
substack: ['Macro','\\begin{subarray}{c}#1\\end{subarray}',1],
|
||||
|
||||
injlim: ['Macro','\\mathop{\\rm inj\\,lim}'],
|
||||
projlim: ['Macro','\\mathop{\\rm proj\\,lim}'],
|
||||
varliminf: ['Macro','\\mathop{\\underline{\\rm lim}}'],
|
||||
varlimsup: ['Macro','\\mathop{\\overline{\\rm lim}}'],
|
||||
varinjlim: ['Macro','\\mathop{\\underrightarrow{\\rm lim\\Rule{-1pt}{0pt}{1pt}}\\Rule{0pt}{0pt}{.45em}}'],
|
||||
varprojlim: ['Macro','\\mathop{\\underleftarrow{\\rm lim\\Rule{-1pt}{0pt}{1pt}}\\Rule{0pt}{0pt}{.45em}}'],
|
||||
|
||||
DeclareMathOperator: 'HandleDeclareOp',
|
||||
operatorname: 'HandleOperatorName',
|
||||
|
||||
genfrac: 'Genfrac',
|
||||
frac: ['Genfrac',"","","",""],
|
||||
tfrac: ['Genfrac',"","","",1],
|
||||
dfrac: ['Genfrac',"","","",0],
|
||||
binom: ['Genfrac',"(",")","0em",""],
|
||||
tbinom: ['Genfrac',"(",")","0em",1],
|
||||
dbinom: ['Genfrac',"(",")","0em",0],
|
||||
|
||||
cfrac: 'CFrac',
|
||||
|
||||
shoveleft: ['HandleShove',MML.ALIGN.LEFT],
|
||||
shoveright: ['HandleShove',MML.ALIGN.RIGHT],
|
||||
|
||||
xrightarrow: ['xArrow',0x2192,5,6],
|
||||
xleftarrow: ['xArrow',0x2190,7,3]
|
||||
},
|
||||
|
||||
environment: {
|
||||
align: ['AMSarray',null,true,true, 'rlrlrlrlrlrl',COLS([5/18,2,5/18,2,5/18,2,5/18,2,5/18,2,5/18])],
|
||||
'align*': ['AMSarray',null,false,true, 'rlrlrlrlrlrl',COLS([5/18,2,5/18,2,5/18,2,5/18,2,5/18,2,5/18])],
|
||||
multline: ['Multline',null,true],
|
||||
'multline*': ['Multline',null,false],
|
||||
split: ['AMSarray',null,false,false,'rl',COLS([5/18])],
|
||||
gather: ['AMSarray',null,true,true, 'c'],
|
||||
'gather*': ['AMSarray',null,false,true, 'c'],
|
||||
|
||||
alignat: ['AlignAt',null,true,true],
|
||||
'alignat*': ['AlignAt',null,false,true],
|
||||
alignedat: ['AlignAt',null,false,false],
|
||||
|
||||
aligned: ['AlignedArray',null,null,null,'rlrlrlrlrlrl',COLS([5/18,2,5/18,2,5/18,2,5/18,2,5/18,2,5/18]),".5em",'D'],
|
||||
gathered: ['AlignedArray',null,null,null,'c',null,".5em",'D'],
|
||||
|
||||
subarray: ['Array',null,null,null,null,COLS([0,0,0,0]),"0.1em",'S',1],
|
||||
smallmatrix: ['Array',null,null,null,'c',COLS([1/3]),".2em",'S',1],
|
||||
|
||||
'equation': ['EquationBegin','Equation',true],
|
||||
'equation*': ['EquationBegin','EquationStar',false]
|
||||
},
|
||||
|
||||
delimiter: {
|
||||
'\\lvert': ['2223',{texClass:MML.TEXCLASS.OPEN}],
|
||||
'\\rvert': ['2223',{texClass:MML.TEXCLASS.CLOSE}],
|
||||
'\\lVert': ['2225',{texClass:MML.TEXCLASS.OPEN}],
|
||||
'\\rVert': ['2225',{texClass:MML.TEXCLASS.CLOSE}]
|
||||
}
|
||||
},null,true);
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
TEX.Parse.Augment({
|
||||
|
||||
/*
|
||||
* Add the tag to the environment (to be added to the table row later)
|
||||
*/
|
||||
HandleTag: function (name) {
|
||||
var star = this.GetStar();
|
||||
var arg = this.trimSpaces(this.GetArgument(name)), tag = arg;
|
||||
if (!star) {arg = CONFIG.formatTag(arg)}
|
||||
var global = this.stack.global; global.tagID = tag;
|
||||
if (global.notags) {TEX.Error(name+" not allowed in "+global.notags+" environment")}
|
||||
if (global.tag) {TEX.Error("Multiple "+name)}
|
||||
global.tag = MML.mtd.apply(MML,this.InternalMath(arg)).With({id:CONFIG.formatID(tag)});
|
||||
},
|
||||
HandleNoTag: function (name) {
|
||||
if (this.stack.global.tag) {delete this.stack.global.tag}
|
||||
this.stack.global.notag = true; // prevent auto-tagging
|
||||
},
|
||||
|
||||
/*
|
||||
* Record a label name for a tag
|
||||
*/
|
||||
HandleLabel: function (name) {
|
||||
var global = this.stack.global, label = this.GetArgument(name);
|
||||
if (!AMS.refUpdate) {
|
||||
if (global.label) {TEX.Error("Multiple "+name+"'s")}
|
||||
global.label = label;
|
||||
if (AMS.labels[label] || AMS.eqlabels[label]) {TEX.Error("Label '"+label+"' mutiply defined")}
|
||||
AMS.eqlabels[label] = "???"; // will be replaced by tag value later
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* Handle a label reference
|
||||
*/
|
||||
HandleRef: function (name,eqref) {
|
||||
var label = this.GetArgument(name);
|
||||
var ref = AMS.labels[label] || AMS.eqlabels[label];
|
||||
if (!ref) {ref = "??"; AMS.badref = !AMS.refUpdate}
|
||||
var tag = ref; if (eqref) {tag = CONFIG.formatTag(tag)}
|
||||
if (CONFIG.useLabelIds) {ref = label}
|
||||
this.Push(MML.mrow.apply(MML,this.InternalMath(tag)).With({
|
||||
href:CONFIG.formatURL(CONFIG.formatID(ref)), "class":"MathJax_ref"
|
||||
}));
|
||||
},
|
||||
|
||||
/*
|
||||
* Handle \DeclareMathOperator
|
||||
*/
|
||||
HandleDeclareOp: function (name) {
|
||||
var limits = (this.GetStar() ? "\\limits" : "");
|
||||
var cs = this.trimSpaces(this.GetArgument(name));
|
||||
if (cs.charAt(0) == "\\") {cs = cs.substr(1)}
|
||||
var op = this.GetArgument(name);
|
||||
op = op.replace(/\*/g,'\\text{*}').replace(/-/g,'\\text{-}');
|
||||
TEX.Definitions.macros[cs] = ['Macro','\\mathop{\\rm '+op+'}'+limits];
|
||||
},
|
||||
|
||||
HandleOperatorName: function (name) {
|
||||
var limits = (this.GetStar() ? "\\limits" : "\\nolimits");
|
||||
var op = this.trimSpaces(this.GetArgument(name));
|
||||
op = op.replace(/\*/g,'\\text{*}').replace(/-/g,'\\text{-}');
|
||||
this.string = '\\mathop{\\rm '+op+'}'+limits+" "+this.string.slice(this.i);
|
||||
this.i = 0;
|
||||
},
|
||||
|
||||
/*
|
||||
* Record presence of \shoveleft and \shoveright
|
||||
*/
|
||||
HandleShove: function (name,shove) {
|
||||
var top = this.stack.Top();
|
||||
if (top.type !== "multline" || top.data.length) {TEX.Error(name+" must come at the beginning of the line")}
|
||||
top.data.shove = shove;
|
||||
},
|
||||
|
||||
/*
|
||||
* Handle \cfrac
|
||||
*/
|
||||
CFrac: function (name) {
|
||||
var lr = this.trimSpaces(this.GetBrackets(name,"")),
|
||||
num = this.GetArgument(name),
|
||||
den = this.GetArgument(name);
|
||||
var frac = MML.mfrac(TEX.Parse('\\strut\\textstyle{'+num+'}',this.stack.env).mml(),
|
||||
TEX.Parse('\\strut\\textstyle{'+den+'}',this.stack.env).mml());
|
||||
lr = ({l:MML.ALIGN.LEFT, r:MML.ALIGN.RIGHT,"":""})[lr];
|
||||
if (lr == null) {TEX.Error("Illegal alignment specified in "+name)}
|
||||
if (lr) {frac.numalign = frac.denomalign = lr}
|
||||
this.Push(frac);
|
||||
},
|
||||
|
||||
/*
|
||||
* Implement AMS generalized fraction
|
||||
*/
|
||||
Genfrac: function (name,left,right,thick,style) {
|
||||
if (left == null) {left = this.GetDelimiterArg(name)} else {left = this.convertDelimiter(left)}
|
||||
if (right == null) {right = this.GetDelimiterArg(name)} else {right = this.convertDelimiter(right)}
|
||||
if (thick == null) {thick = this.GetArgument(name)}
|
||||
if (style == null) {style = this.trimSpaces(this.GetArgument(name))}
|
||||
var num = this.ParseArg(name);
|
||||
var den = this.ParseArg(name);
|
||||
var frac = MML.mfrac(num,den);
|
||||
if (thick !== "") {frac.linethickness = thick}
|
||||
if (left || right) {frac = MML.mfenced(frac).With({open: left, close: right})}
|
||||
if (style !== "") {
|
||||
var STYLE = (["D","T","S","SS"])[style];
|
||||
if (STYLE == null) {TEX.Error("Bad math style for "+name)}
|
||||
frac = MML.mstyle(frac);
|
||||
if (STYLE === "D") {frac.displaystyle = true; frac.scriptlevel = 0}
|
||||
else {frac.displaystyle = false; frac.scriptlevel = style - 1}
|
||||
}
|
||||
this.Push(frac);
|
||||
},
|
||||
|
||||
/*
|
||||
* Implements multline environment (mostly handled through STACKITEM below)
|
||||
*/
|
||||
Multline: function (begin,numbered) {
|
||||
this.Push(begin); this.checkEqnEnv();
|
||||
return STACKITEM.multline(numbered,this.stack).With({
|
||||
arraydef: {
|
||||
displaystyle: true,
|
||||
rowspacing: ".5em",
|
||||
width: TEX.config.MultLineWidth, columnwidth:"100%",
|
||||
side: TEX.config.TagSide,
|
||||
minlabelspacing: TEX.config.TagIndent
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/*
|
||||
* Handle AMS aligned environments
|
||||
*/
|
||||
AMSarray: function (begin,numbered,taggable,align,spacing) {
|
||||
this.Push(begin); if (taggable) {this.checkEqnEnv()}
|
||||
align = align.replace(/[^clr]/g,'').split('').join(' ');
|
||||
align = align.replace(/l/g,'left').replace(/r/g,'right').replace(/c/g,'center');
|
||||
return STACKITEM.AMSarray(begin.name,numbered,taggable,this.stack).With({
|
||||
arraydef: {
|
||||
displaystyle: true,
|
||||
rowspacing: ".5em",
|
||||
columnalign: align,
|
||||
columnspacing: (spacing||"1em"),
|
||||
rowspacing: "3pt",
|
||||
side: TEX.config.TagSide,
|
||||
minlabelspacing: TEX.config.TagIndent
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/*
|
||||
* Handle alignat environments
|
||||
*/
|
||||
AlignAt: function (begin,numbered,taggable) {
|
||||
var n, valign, align = "", spacing = [];
|
||||
if (!taggable) {valign = this.GetBrackets("\\begin{"+begin.name+"}")}
|
||||
n = this.GetArgument("\\begin{"+begin.name+"}");
|
||||
if (n.match(/[^0-9]/)) {TEX.Error("Argument to \\begin{"+begin.name+"} must me a positive integer")}
|
||||
while (n > 0) {align += "rl"; spacing.push("0em 0em"); n--}
|
||||
spacing = spacing.join(" ");
|
||||
if (taggable) {return this.AMSarray(begin,numbered,taggable,align,spacing)}
|
||||
var array = this.Array.call(this,begin,null,null,align,spacing,".5em",'D');
|
||||
return this.setArrayAlign(array,valign);
|
||||
},
|
||||
|
||||
/*
|
||||
* Handle equation environment
|
||||
*/
|
||||
EquationBegin: function (begin,force) {
|
||||
this.checkEqnEnv();
|
||||
this.stack.global.forcetag = (force && CONFIG.autoNumber !== "none");
|
||||
return begin;
|
||||
},
|
||||
EquationStar: function (begin,row) {
|
||||
this.stack.global.tagged = true; // prevent automatic tagging
|
||||
return row;
|
||||
},
|
||||
|
||||
/*
|
||||
* Check for bad nesting of equation environments
|
||||
*/
|
||||
checkEqnEnv: function () {
|
||||
if (this.stack.global.eqnenv) {TEX.Error("Erroneous nesting of equation structures")}
|
||||
this.stack.global.eqnenv = true;
|
||||
},
|
||||
|
||||
/*
|
||||
* Handle multiple integrals (make a mathop if followed by limits)
|
||||
*/
|
||||
MultiIntegral: function (name,integral) {
|
||||
var next = this.GetNext();
|
||||
if (next === "\\") {
|
||||
var i = this.i; next = this.GetArgument(name); this.i = i;
|
||||
if (next === "\\limits") {
|
||||
if (name === "\\idotsint") {integral = "\\!\\!\\mathop{\\,\\,"+integral+"}"}
|
||||
else {integral = "\\!\\!\\!\\mathop{\\,\\,\\,"+integral+"}"}
|
||||
}
|
||||
}
|
||||
this.string = integral + " " + this.string.slice(this.i);
|
||||
this.i = 0;
|
||||
},
|
||||
|
||||
/*
|
||||
* Handle stretchable arrows
|
||||
*/
|
||||
xArrow: function (name,chr,l,r) {
|
||||
var def = {width: "+"+(l+r)+"mu", lspace: l+"mu"};
|
||||
var bot = this.GetBrackets(name),
|
||||
top = this.ParseArg(name);
|
||||
var arrow = MML.mo(MML.chars(String.fromCharCode(chr))).With({
|
||||
stretchy: true, texClass: MML.TEXCLASS.REL
|
||||
});
|
||||
var mml = MML.munderover(arrow);
|
||||
mml.SetData(mml.over,MML.mpadded(top).With(def).With({voffset:".15em"}));
|
||||
if (bot) {
|
||||
bot = TEX.Parse(bot,this.stack.env).mml()
|
||||
mml.SetData(mml.under,MML.mpadded(bot).With(def).With({voffset:"-.24em"}));
|
||||
}
|
||||
this.Push(mml);
|
||||
},
|
||||
|
||||
/*
|
||||
* Get a delimiter or empty argument
|
||||
*/
|
||||
GetDelimiterArg: function (name) {
|
||||
var c = this.trimSpaces(this.GetArgument(name));
|
||||
if (c == "") {return null}
|
||||
if (TEXDEF.delimiter[c] == null) {TEX.Error("Missing or unrecognized delimiter for "+name)}
|
||||
return this.convertDelimiter(c);
|
||||
},
|
||||
|
||||
/*
|
||||
* Get a star following a control sequence name, if any
|
||||
*/
|
||||
GetStar: function () {
|
||||
var star = (this.GetNext() === "*");
|
||||
if (star) {this.i++}
|
||||
return star;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
STACKITEM.Augment({
|
||||
/*
|
||||
* Increment equation number and form tag mtd element
|
||||
*/
|
||||
autoTag: function () {
|
||||
var global = this.global;
|
||||
if (!global.notag) {
|
||||
AMS.number++; global.tagID = CONFIG.formatNumber(AMS.number.toString());
|
||||
var mml = TEX.Parse("\\text{"+CONFIG.formatTag(global.tagID)+"}",{}).mml();
|
||||
global.tag = MML.mtd(mml.With({id:CONFIG.formatID(global.tagID)}));
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* Get the tag and record the label, if any
|
||||
*/
|
||||
getTag: function () {
|
||||
var global = this.global, tag = global.tag; global.tagged = true;
|
||||
if (global.label) {
|
||||
AMS.eqlabels[global.label] = global.tagID;
|
||||
if (CONFIG.useLabelIds) {tag.id = CONFIG.formatID(global.label)}
|
||||
}
|
||||
delete global.tag; delete global.tagID; delete global.label;
|
||||
return tag;
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
* Implement multline environment via a STACKITEM
|
||||
*/
|
||||
STACKITEM.multline = STACKITEM.array.Subclass({
|
||||
type: "multline",
|
||||
Init: function (numbered,stack) {
|
||||
this.SUPER(arguments).Init.apply(this);
|
||||
this.numbered = (numbered && CONFIG.autoNumber !== "none");
|
||||
this.save = {notag: stack.global.notag};
|
||||
stack.global.tagged = !numbered && !stack.global.forcetag; // prevent automatic tagging in starred environments
|
||||
},
|
||||
EndEntry: function () {
|
||||
var mtd = MML.mtd.apply(MML,this.data);
|
||||
if (this.data.shove) {mtd.columnalign = this.data.shove}
|
||||
this.row.push(mtd);
|
||||
this.data = [];
|
||||
},
|
||||
EndRow: function () {
|
||||
if (this.row.length != 1) {TEX.Error("multline rows must have exactly one column")}
|
||||
this.table.push(this.row); this.row = [];
|
||||
},
|
||||
EndTable: function () {
|
||||
this.SUPER(arguments).EndTable.call(this);
|
||||
if (this.table.length) {
|
||||
var m = this.table.length-1, i, label = -1;
|
||||
if (!this.table[0][0].columnalign) {this.table[0][0].columnalign = MML.ALIGN.LEFT}
|
||||
if (!this.table[m][0].columnalign) {this.table[m][0].columnalign = MML.ALIGN.RIGHT}
|
||||
if (!this.global.tag && this.numbered) {this.autoTag()}
|
||||
if (this.global.tag && !this.global.notags) {
|
||||
label = (this.arraydef.side === "left" ? 0 : this.table.length - 1);
|
||||
this.table[label] = [this.getTag()].concat(this.table[label]);
|
||||
}
|
||||
for (i = 0, m = this.table.length; i < m; i++) {
|
||||
var mtr = (i === label ? MML.mlabeledtr : MML.mtr);
|
||||
this.table[i] = mtr.apply(MML,this.table[i]);
|
||||
}
|
||||
}
|
||||
this.global.notag = this.save.notag;
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
* Save data about numbering and taging equations, and add
|
||||
* tags at the ends of rows.
|
||||
*/
|
||||
STACKITEM.AMSarray = STACKITEM.array.Subclass({
|
||||
type: "AMSarray",
|
||||
Init: function (name,numbered,taggable,stack) {
|
||||
this.SUPER(arguments).Init.apply(this);
|
||||
this.numbered = (numbered && CONFIG.autoNumber !== "none");
|
||||
this.save = {notags: stack.global.notags, notag: stack.global.notag};
|
||||
stack.global.notags = (taggable ? null : name);
|
||||
stack.global.tagged = !numbered && !stack.global.forcetag; // prevent automatic tagging in starred environments
|
||||
},
|
||||
EndRow: function () {
|
||||
var mtr = MML.mtr;
|
||||
if (!this.global.tag && this.numbered) {this.autoTag()}
|
||||
if (this.global.tag &&! this.global.notags) {
|
||||
this.row = [this.getTag()].concat(this.row);
|
||||
mtr = MML.mlabeledtr;
|
||||
}
|
||||
if (this.numbered) {delete this.global.notag}
|
||||
this.table.push(mtr.apply(MML,this.row)); this.row = [];
|
||||
},
|
||||
EndTable: function () {
|
||||
this.SUPER(arguments).EndTable.call(this);
|
||||
this.global.notags = this.save.notags;
|
||||
this.global.notag = this.save.notag;
|
||||
}
|
||||
});
|
||||
|
||||
//
|
||||
// Look for \tag on a formula and make an mtable to include it
|
||||
//
|
||||
STACKITEM.start.Augment({
|
||||
oldCheckItem: STACKITEM.start.prototype.checkItem,
|
||||
checkItem: function (item) {
|
||||
if (item.type === "stop") {
|
||||
var mml = this.mmlData(), global = this.global;
|
||||
if (AMS.display && !global.tag && !global.tagged && !global.isInner &&
|
||||
(CONFIG.autoNumber === "all" || global.forcetag)) {this.autoTag()}
|
||||
if (global.tag) {
|
||||
var row = [this.getTag(),MML.mtd(mml)];
|
||||
var def = {
|
||||
side: TEX.config.TagSide,
|
||||
minlabelspacing: TEX.config.TagIndent,
|
||||
columnalign: mml.displayAlign
|
||||
};
|
||||
if (mml.displayAlign === MML.INDENTALIGN.LEFT) {
|
||||
def.width = "100%";
|
||||
if (mml.displayIndent && !String(mml.displayIndent).match(/^0+(\.0*)?($|[a-z%])/)) {
|
||||
def.columnwidth = mml.displayIndent + " fit"; def.columnspacing = "0"
|
||||
row = [row[0],MML.mtd(),row[1]];
|
||||
}
|
||||
} else if (mml.displayAlign === MML.INDENTALIGN.RIGHT) {
|
||||
def.width = "100%";
|
||||
if (mml.displayIndent && !String(mml.displayIndent).match(/^0+(\.0*)?($|[a-z%])/)) {
|
||||
def.columnwidth = "fit "+mml.displayIndent; def.columnspacing = "0"
|
||||
row[2] = MML.mtd();
|
||||
}
|
||||
}
|
||||
mml = MML.mtable(MML.mlabeledtr.apply(MML,row)).With(def);
|
||||
}
|
||||
return STACKITEM.mml(mml);
|
||||
}
|
||||
return this.oldCheckItem.call(this,item);
|
||||
}
|
||||
});
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
/*
|
||||
* Add pre- and post-filters to handle the equation number maintainance.
|
||||
*/
|
||||
TEX.prefilterHooks.Add(function (data) {
|
||||
AMS.display = data.display;
|
||||
AMS.number = AMS.startNumber; // reset equation numbers (in case the equation restarted)
|
||||
AMS.eqlabels = {}; AMS.badref = false;
|
||||
if (AMS.refUpdate) {AMS.number = data.script.MathJax.startNumber}
|
||||
});
|
||||
TEX.postfilterHooks.Add(function (data) {
|
||||
data.script.MathJax.startNumber = AMS.startNumber;
|
||||
AMS.startNumber = AMS.number; // equation numbers for next equation
|
||||
MathJax.Hub.Insert(AMS.labels,AMS.eqlabels); // save labels from this equation
|
||||
if (AMS.badref && !data.math.texError) {AMS.refs.push(data.script)} // reprocess later
|
||||
});
|
||||
|
||||
MathJax.Hub.Register.MessageHook("Begin Math Input",function () {
|
||||
AMS.refs = []; // array of jax with bad references
|
||||
AMS.refUpdate = false;
|
||||
});
|
||||
MathJax.Hub.Register.MessageHook("End Math Input",function (message) {
|
||||
if (AMS.refs.length) {
|
||||
AMS.refUpdate = true;
|
||||
for (var i = 0, m = AMS.refs.length; i < m; i++)
|
||||
{AMS.refs[i].MathJax.state = MathJax.ElementJax.STATE.UPDATE}
|
||||
return MathJax.Hub.processInput({
|
||||
scripts:AMS.refs,
|
||||
start: new Date().getTime(),
|
||||
i:0, j:0, jax:{}, jaxIDs:[]
|
||||
});
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
//
|
||||
// Clear the equation numbers and labels
|
||||
//
|
||||
TEX.resetEquationNumbers = function (n,keepLabels) {
|
||||
AMS.startNumber = (n || 0);
|
||||
if (!keepLabels) {AMS.labels = {}}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
MathJax.Hub.Startup.signal.Post("TeX AMSmath Ready");
|
||||
|
||||
});
|
||||
|
||||
MathJax.Ajax.loadComplete("[MathJax]/extensions/TeX/AMSmath.js");
|
401
resources/viewer/mathjax/extensions/TeX/AMSsymbols.js
Normal file
@ -0,0 +1,401 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* MathJax/extensions/TeX/AMSsymbols.js
|
||||
*
|
||||
* Implements macros for accessing the AMS symbol fonts.
|
||||
*
|
||||
* ---------------------------------------------------------------------
|
||||
*
|
||||
* Copyright (c) 2009-2012 Design Science, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
MathJax.Extension["TeX/AMSsymbols"] = {
|
||||
version: "2.0"
|
||||
};
|
||||
|
||||
MathJax.Hub.Register.StartupHook("TeX Jax Ready",function () {
|
||||
var MML = MathJax.ElementJax.mml,
|
||||
TEXDEF = MathJax.InputJax.TeX.Definitions;
|
||||
|
||||
TEXDEF.Add({
|
||||
|
||||
mathchar0mi: {
|
||||
// Lowercase Greek letters
|
||||
digamma: '03DD',
|
||||
varkappa: '03F0',
|
||||
|
||||
// Uppercase Greek letters
|
||||
varGamma: ['0393',{mathvariant: MML.VARIANT.ITALIC}],
|
||||
varDelta: ['0394',{mathvariant: MML.VARIANT.ITALIC}],
|
||||
varTheta: ['0398',{mathvariant: MML.VARIANT.ITALIC}],
|
||||
varLambda: ['039B',{mathvariant: MML.VARIANT.ITALIC}],
|
||||
varXi: ['039E',{mathvariant: MML.VARIANT.ITALIC}],
|
||||
varPi: ['03A0',{mathvariant: MML.VARIANT.ITALIC}],
|
||||
varSigma: ['03A3',{mathvariant: MML.VARIANT.ITALIC}],
|
||||
varUpsilon: ['03A5',{mathvariant: MML.VARIANT.ITALIC}],
|
||||
varPhi: ['03A6',{mathvariant: MML.VARIANT.ITALIC}],
|
||||
varPsi: ['03A8',{mathvariant: MML.VARIANT.ITALIC}],
|
||||
varOmega: ['03A9',{mathvariant: MML.VARIANT.ITALIC}],
|
||||
|
||||
// Hebrew letters
|
||||
beth: '2136',
|
||||
gimel: '2137',
|
||||
daleth: '2138',
|
||||
|
||||
// Miscellaneous symbols
|
||||
// hbar: '0127', // in MathJax_Main
|
||||
backprime: ['2035',{variantForm: true}],
|
||||
hslash: ['210F',{variantForm: true}],
|
||||
varnothing: ['2205',{variantForm: true}],
|
||||
blacktriangle: '25B2',
|
||||
triangledown: '25BD',
|
||||
blacktriangledown: '25BC',
|
||||
square: '25A1',
|
||||
Box: '25A1',
|
||||
blacksquare: '25A0',
|
||||
lozenge: '25CA',
|
||||
Diamond: '25CA',
|
||||
blacklozenge: '29EB',
|
||||
circledS: ['24C8',{mathvariant: MML.VARIANT.NORMAL}],
|
||||
bigstar: '2605',
|
||||
// angle: '2220', // in MathJax_Main
|
||||
sphericalangle: '2222',
|
||||
measuredangle: '2221',
|
||||
nexists: '2204',
|
||||
complement: '2201',
|
||||
mho: '2127',
|
||||
eth: ['00F0',{mathvariant: MML.VARIANT.NORMAL}],
|
||||
Finv: '2132',
|
||||
diagup: '2571',
|
||||
Game: '2141',
|
||||
diagdown: '2572',
|
||||
Bbbk: ['006B',{mathvariant: MML.VARIANT.DOUBLESTRUCK}],
|
||||
|
||||
yen: '00A5',
|
||||
circledR: '00AE',
|
||||
checkmark: '2713',
|
||||
maltese: '2720'
|
||||
},
|
||||
|
||||
mathchar0mo: {
|
||||
// Binary operators
|
||||
dotplus: '2214',
|
||||
ltimes: '22C9',
|
||||
smallsetminus: ['2216',{variantForm: true}],
|
||||
rtimes: '22CA',
|
||||
Cap: '22D2',
|
||||
doublecap: '22D2',
|
||||
leftthreetimes: '22CB',
|
||||
Cup: '22D3',
|
||||
doublecup: '22D3',
|
||||
rightthreetimes: '22CC',
|
||||
barwedge: '22BC',
|
||||
curlywedge: '22CF',
|
||||
veebar: '22BB',
|
||||
curlyvee: '22CE',
|
||||
doublebarwedge: '2A5E',
|
||||
boxminus: '229F',
|
||||
circleddash: '229D',
|
||||
boxtimes: '22A0',
|
||||
circledast: '229B',
|
||||
boxdot: '22A1',
|
||||
circledcirc: '229A',
|
||||
boxplus: '229E',
|
||||
centerdot: '22C5',
|
||||
divideontimes: '22C7',
|
||||
intercal: '22BA',
|
||||
|
||||
// Binary relations
|
||||
leqq: '2266',
|
||||
geqq: '2267',
|
||||
leqslant: '2A7D',
|
||||
geqslant: '2A7E',
|
||||
eqslantless: '2A95',
|
||||
eqslantgtr: '2A96',
|
||||
lesssim: '2272',
|
||||
gtrsim: '2273',
|
||||
lessapprox: '2A85',
|
||||
gtrapprox: '2A86',
|
||||
approxeq: '224A',
|
||||
lessdot: '22D6',
|
||||
gtrdot: '22D7',
|
||||
lll: '22D8',
|
||||
llless: '22D8',
|
||||
ggg: '22D9',
|
||||
gggtr: '22D9',
|
||||
lessgtr: '2276',
|
||||
gtrless: '2277',
|
||||
lesseqgtr: '22DA',
|
||||
gtreqless: '22DB',
|
||||
lesseqqgtr: '2A8B',
|
||||
gtreqqless: '2A8C',
|
||||
doteqdot: '2251',
|
||||
Doteq: '2251',
|
||||
eqcirc: '2256',
|
||||
risingdotseq: '2253',
|
||||
circeq: '2257',
|
||||
fallingdotseq: '2252',
|
||||
triangleq: '225C',
|
||||
backsim: '223D',
|
||||
thicksim: ['223C',{variantForm: true}],
|
||||
backsimeq: '22CD',
|
||||
thickapprox: ['2248',{variantForm: true}],
|
||||
subseteqq: '2AC5',
|
||||
supseteqq: '2AC6',
|
||||
Subset: '22D0',
|
||||
Supset: '22D1',
|
||||
sqsubset: '228F',
|
||||
sqsupset: '2290',
|
||||
preccurlyeq: '227C',
|
||||
succcurlyeq: '227D',
|
||||
curlyeqprec: '22DE',
|
||||
curlyeqsucc: '22DF',
|
||||
precsim: '227E',
|
||||
succsim: '227F',
|
||||
precapprox: '2AB7',
|
||||
succapprox: '2AB8',
|
||||
vartriangleleft: '22B2',
|
||||
lhd: '22B2',
|
||||
vartriangleright: '22B3',
|
||||
rhd: '22B3',
|
||||
trianglelefteq: '22B4',
|
||||
unlhd: '22B4',
|
||||
trianglerighteq: '22B5',
|
||||
unrhd: '22B5',
|
||||
vDash: '22A8',
|
||||
Vdash: '22A9',
|
||||
Vvdash: '22AA',
|
||||
smallsmile: '2323',
|
||||
shortmid: ['2223',{variantForm: true}],
|
||||
smallfrown: '2322',
|
||||
shortparallel: ['2225',{variantForm: true}],
|
||||
bumpeq: '224F',
|
||||
between: '226C',
|
||||
Bumpeq: '224E',
|
||||
pitchfork: '22D4',
|
||||
varpropto: '221D',
|
||||
backepsilon: '220D',
|
||||
blacktriangleleft: '25C0',
|
||||
blacktriangleright: '25B6',
|
||||
therefore: '2234',
|
||||
because: '2235',
|
||||
eqsim: '2242',
|
||||
vartriangle: ['25B3',{variantForm: true}],
|
||||
Join: '22C8',
|
||||
|
||||
// Negated relations
|
||||
nless: '226E',
|
||||
ngtr: '226F',
|
||||
nleq: '2270',
|
||||
ngeq: '2271',
|
||||
nleqslant: ['2A87',{variantForm: true}],
|
||||
ngeqslant: ['2A88',{variantForm: true}],
|
||||
nleqq: ['2270',{variantForm: true}],
|
||||
ngeqq: ['2271',{variantForm: true}],
|
||||
lneq: '2A87',
|
||||
gneq: '2A88',
|
||||
lneqq: '2268',
|
||||
gneqq: '2269',
|
||||
lvertneqq: ['2268',{variantForm: true}],
|
||||
gvertneqq: ['2269',{variantForm: true}],
|
||||
lnsim: '22E6',
|
||||
gnsim: '22E7',
|
||||
lnapprox: '2A89',
|
||||
gnapprox: '2A8A',
|
||||
nprec: '2280',
|
||||
nsucc: '2281',
|
||||
npreceq: ['22E0',{variantForm: true}],
|
||||
nsucceq: ['22E1',{variantForm: true}],
|
||||
precneqq: '2AB5',
|
||||
succneqq: '2AB6',
|
||||
precnsim: '22E8',
|
||||
succnsim: '22E9',
|
||||
precnapprox: '2AB9',
|
||||
succnapprox: '2ABA',
|
||||
nsim: '2241',
|
||||
ncong: '2246',
|
||||
nshortmid: ['2224',{variantForm: true}],
|
||||
nshortparallel: ['2226',{variantForm: true}],
|
||||
nmid: '2224',
|
||||
nparallel: '2226',
|
||||
nvdash: '22AC',
|
||||
nvDash: '22AD',
|
||||
nVdash: '22AE',
|
||||
nVDash: '22AF',
|
||||
ntriangleleft: '22EA',
|
||||
ntriangleright: '22EB',
|
||||
ntrianglelefteq: '22EC',
|
||||
ntrianglerighteq: '22ED',
|
||||
nsubseteq: '2288',
|
||||
nsupseteq: '2289',
|
||||
nsubseteqq: ['2288',{variantForm: true}],
|
||||
nsupseteqq: ['2289',{variantForm: true}],
|
||||
subsetneq: '228A',
|
||||
supsetneq: '228B',
|
||||
varsubsetneq: ['228A',{variantForm: true}],
|
||||
varsupsetneq: ['228B',{variantForm: true}],
|
||||
subsetneqq: '2ACB',
|
||||
supsetneqq: '2ACC',
|
||||
varsubsetneqq: ['2ACB',{variantForm: true}],
|
||||
varsupsetneqq: ['2ACC',{variantForm: true}],
|
||||
|
||||
|
||||
// Arrows
|
||||
leftleftarrows: '21C7',
|
||||
rightrightarrows: '21C9',
|
||||
leftrightarrows: '21C6',
|
||||
rightleftarrows: '21C4',
|
||||
Lleftarrow: '21DA',
|
||||
Rrightarrow: '21DB',
|
||||
twoheadleftarrow: '219E',
|
||||
twoheadrightarrow: '21A0',
|
||||
leftarrowtail: '21A2',
|
||||
rightarrowtail: '21A3',
|
||||
looparrowleft: '21AB',
|
||||
looparrowright: '21AC',
|
||||
leftrightharpoons: '21CB',
|
||||
rightleftharpoons: ['21CC',{variantForm: true}],
|
||||
curvearrowleft: '21B6',
|
||||
curvearrowright: '21B7',
|
||||
circlearrowleft: '21BA',
|
||||
circlearrowright: '21BB',
|
||||
Lsh: '21B0',
|
||||
Rsh: '21B1',
|
||||
upuparrows: '21C8',
|
||||
downdownarrows: '21CA',
|
||||
upharpoonleft: '21BF',
|
||||
upharpoonright: '21BE',
|
||||
downharpoonleft: '21C3',
|
||||
restriction: '21BE',
|
||||
multimap: '22B8',
|
||||
downharpoonright: '21C2',
|
||||
leftrightsquigarrow: '21AD',
|
||||
rightsquigarrow: '21DD',
|
||||
leadsto: '21DD',
|
||||
dashrightarrow: '21E2',
|
||||
dashleftarrow: '21E0',
|
||||
|
||||
// Negated arrows
|
||||
nleftarrow: '219A',
|
||||
nrightarrow: '219B',
|
||||
nLeftarrow: '21CD',
|
||||
nRightarrow: '21CF',
|
||||
nleftrightarrow: '21AE',
|
||||
nLeftrightarrow: '21CE'
|
||||
},
|
||||
|
||||
delimiter: {
|
||||
// corners
|
||||
"\\ulcorner": '231C',
|
||||
"\\urcorner": '231D',
|
||||
"\\llcorner": '231E',
|
||||
"\\lrcorner": '231F'
|
||||
},
|
||||
|
||||
macros: {
|
||||
implies: ['Macro','\\;\\Longrightarrow\\;'],
|
||||
impliedby: ['Macro','\\;\\Longleftarrow\\;']
|
||||
}
|
||||
|
||||
},null,true);
|
||||
|
||||
var REL = MML.mo.OPTYPES.REL;
|
||||
|
||||
MathJax.Hub.Insert(MML.mo.prototype,{
|
||||
OPTABLE: {
|
||||
infix: {
|
||||
'\u2322': REL, // smallfrown
|
||||
'\u2323': REL, // smallsmile
|
||||
'\u25B3': REL, // vartriangle
|
||||
'\uE006': REL, // nshortmid
|
||||
'\uE007': REL, // nshortparallel
|
||||
'\uE00C': REL, // lvertneqq
|
||||
'\uE00D': REL, // gvertneqq
|
||||
'\uE00E': REL, // ngeqq
|
||||
'\uE00F': REL, // ngeqslant
|
||||
'\uE010': REL, // nleqslant
|
||||
'\uE011': REL, // nleqq
|
||||
'\uE016': REL, // nsubseteqq
|
||||
'\uE017': REL, // varsubsetneqq
|
||||
'\uE018': REL, // nsupseteqq
|
||||
'\uE019': REL, // varsupsetneqq
|
||||
'\uE01A': REL, // varsubsetneq
|
||||
'\uE01B': REL, // varsupsetneq
|
||||
'\uE04B': REL, // npreceq
|
||||
'\uE04F': REL // nsucceq
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
MathJax.Hub.Startup.signal.Post("TeX AMSsymbols Ready");
|
||||
|
||||
});
|
||||
|
||||
MathJax.Hub.Register.StartupHook("HTML-CSS Jax Ready",function () {
|
||||
var HTMLCSS = MathJax.OutputJax["HTML-CSS"];
|
||||
var VARIANT = HTMLCSS.FONTDATA.VARIANT;
|
||||
if (HTMLCSS.fontInUse === "TeX") {
|
||||
VARIANT["-TeX-variant"] = {
|
||||
fonts: ["MathJax_AMS","MathJax_Main","MathJax_Size1"],
|
||||
remap: {0x2268: 0xE00C, 0x2269: 0xE00D, 0x2270: 0xE011, 0x2271: 0xE00E,
|
||||
0x2A87: 0xE010, 0x2A88: 0xE00F, 0x2224: 0xE006, 0x2226: 0xE007,
|
||||
0x2288: 0xE016, 0x2289: 0xE018, 0x228A: 0xE01A, 0x228B: 0xE01B,
|
||||
0x2ACB: 0xE017, 0x2ACC: 0xE019, 0x03DC: 0xE008, 0x03F0: 0xE009}
|
||||
};
|
||||
if (HTMLCSS.msieIE6) {
|
||||
MathJax.Hub.Insert(VARIANT["-TeX-variant"].remap,{
|
||||
0x2190:[0xE2C1,"-WinIE6"], 0x2192:[0xE2C0,"-WinIE6"],
|
||||
0x2223:[0xE2C2,"-WinIE6"], 0x2225:[0xE2C3,"-WinIE6"],
|
||||
0x223C:[0xE2C4,"-WinIE6"], 0x25B3:[0xE2D3,"-WinIE6"]
|
||||
});
|
||||
}
|
||||
}
|
||||
if (HTMLCSS.fontInUse === "STIX") {
|
||||
MathJax.Hub.Register.StartupHook("TeX Jax Ready",function () {
|
||||
var TEXDEF = MathJax.InputJax.TeX.Definitions;
|
||||
TEXDEF.mathchar0mi.varnothing = '2205';
|
||||
TEXDEF.mathchar0mi.hslash = '210F';
|
||||
TEXDEF.mathchar0mi.blacktriangle = '25B4';
|
||||
TEXDEF.mathchar0mi.blacktriangledown = '25BE';
|
||||
TEXDEF.mathchar0mi.square = '25FB';
|
||||
TEXDEF.mathchar0mi.blacksquare = '25FC';
|
||||
TEXDEF.mathchar0mi.vartriangle = ['25B3',{mathsize:"71%"}];
|
||||
TEXDEF.mathchar0mi.triangledown = ['25BD',{mathsize:"71%"}];
|
||||
TEXDEF.mathchar0mo.blacktriangleleft = '25C2';
|
||||
TEXDEF.mathchar0mo.blacktriangleright = '25B8';
|
||||
TEXDEF.mathchar0mo.smallsetminus = '2216';
|
||||
MathJax.Hub.Insert(VARIANT["-STIX-variant"],{
|
||||
remap: {0x2A87: 0xE010, 0x2A88: 0xE00F, 0x2270: 0xE011, 0x2271: 0xE00E,
|
||||
0x22E0: 0xE04B, 0x22E1: 0xE04F, 0x2288: 0xE016, 0x2289: 0xE018}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
MathJax.Hub.Register.StartupHook("SVG Jax Ready",function () {
|
||||
var SVG = MathJax.OutputJax.SVG;
|
||||
var VARIANT = SVG.FONTDATA.VARIANT;
|
||||
|
||||
VARIANT["-TeX-variant"] = {
|
||||
fonts: ["MathJax_AMS","MathJax_Main","MathJax_Size1"],
|
||||
remap: {0x2268: 0xE00C, 0x2269: 0xE00D, 0x2270: 0xE011, 0x2271: 0xE00E,
|
||||
0x2A87: 0xE010, 0x2A88: 0xE00F, 0x2224: 0xE006, 0x2226: 0xE007,
|
||||
0x2288: 0xE016, 0x2289: 0xE018, 0x228A: 0xE01A, 0x228B: 0xE01B,
|
||||
0x2ACB: 0xE017, 0x2ACC: 0xE019, 0x03DC: 0xE008, 0x03F0: 0xE009}
|
||||
};
|
||||
});
|
||||
|
||||
MathJax.Ajax.loadComplete("[MathJax]/extensions/TeX/AMSsymbols.js");
|
103
resources/viewer/mathjax/extensions/TeX/HTML.js
Normal file
@ -0,0 +1,103 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* MathJax/extensions/TeX/HTML.js
|
||||
*
|
||||
* Implements the \href, \class, \style, \cssId macros.
|
||||
*
|
||||
* ---------------------------------------------------------------------
|
||||
*
|
||||
* Copyright (c) 2010-2012 Design Science, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
MathJax.Extension["TeX/HTML"] = {
|
||||
version: "2.0"
|
||||
};
|
||||
|
||||
MathJax.Hub.Register.StartupHook("TeX Jax Ready",function () {
|
||||
|
||||
var TEX = MathJax.InputJax.TeX;
|
||||
var TEXDEF = TEX.Definitions;
|
||||
|
||||
TEXDEF.Add({
|
||||
macros: {
|
||||
href: 'HREF_attribute',
|
||||
"class": 'CLASS_attribute',
|
||||
style: 'STYLE_attribute',
|
||||
cssId: 'ID_attribute'
|
||||
}
|
||||
});
|
||||
|
||||
TEX.Parse.Augment({
|
||||
|
||||
//
|
||||
// Implements \href{url}{math}
|
||||
//
|
||||
HREF_attribute: function (name) {
|
||||
var url = this.GetArgument(name),
|
||||
arg = this.GetArgumentMML(name);
|
||||
this.Push(arg.With({href:url}));
|
||||
},
|
||||
|
||||
//
|
||||
// Implements \class{name}{math}
|
||||
//
|
||||
CLASS_attribute: function (name) {
|
||||
var CLASS = this.GetArgument(name),
|
||||
arg = this.GetArgumentMML(name);
|
||||
if (arg["class"] != null) {CLASS = arg["class"] + " " + CLASS}
|
||||
this.Push(arg.With({"class":CLASS}));
|
||||
},
|
||||
|
||||
//
|
||||
// Implements \style{style-string}{math}
|
||||
//
|
||||
STYLE_attribute: function (name) {
|
||||
var style = this.GetArgument(name),
|
||||
arg = this.GetArgumentMML(name);
|
||||
// check that it looks like a style string
|
||||
if (arg.style != null) {
|
||||
if (style.charAt(style.length-1) !== ";") {style += ";"}
|
||||
style = arg.style + " " + style;
|
||||
}
|
||||
this.Push(arg.With({style: style}));
|
||||
},
|
||||
|
||||
//
|
||||
// Implements \cssId{id}{math}
|
||||
//
|
||||
ID_attribute: function (name) {
|
||||
var ID = this.GetArgument(name),
|
||||
arg = this.GetArgumentMML(name);
|
||||
this.Push(arg.With({id:ID}));
|
||||
},
|
||||
|
||||
//
|
||||
// returns an argument that is a single MathML element
|
||||
// (in an mrow if necessary)
|
||||
//
|
||||
GetArgumentMML: function (name) {
|
||||
var arg = this.ParseArg(name);
|
||||
if (arg.inferred && arg.data.length == 1)
|
||||
{arg = arg.data[0]} else {delete arg.inferred}
|
||||
return arg;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
MathJax.Hub.Startup.signal.Post("TeX HTML Ready");
|
||||
|
||||
});
|
||||
|
||||
MathJax.Ajax.loadComplete("[MathJax]/extensions/TeX/HTML.js");
|
76
resources/viewer/mathjax/extensions/TeX/action.js
Normal file
@ -0,0 +1,76 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* MathJax/extensions/TeX/action.js
|
||||
*
|
||||
* Implements the \mathtip, \texttip, and \toggle macros, which give
|
||||
* access from TeX to the <maction> tag in the MathML that underlies
|
||||
* MathJax's internal format.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* \mathtip{math}{tip} % use "tip" (in math mode) as tooltip for "math"
|
||||
* \texttip{math}{tip} % use "tip" (in text mode) as tooltip for "math"
|
||||
* \toggle{math1}{math2}...\endtoggle
|
||||
* % show math1, and when clicked, show math2, and so on.
|
||||
* % When the last one is clicked, go back to math1.
|
||||
*
|
||||
* ---------------------------------------------------------------------
|
||||
*
|
||||
* Copyright (c) 2011-2012 Design Science, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
MathJax.Extension["TeX/action"] = {
|
||||
version: "2.0"
|
||||
};
|
||||
|
||||
MathJax.Hub.Register.StartupHook("TeX Jax Ready",function () {
|
||||
var TEX = MathJax.InputJax.TeX,
|
||||
MML = MathJax.ElementJax.mml;
|
||||
|
||||
//
|
||||
// Set up control sequenecs
|
||||
//
|
||||
TEX.Definitions.macros.toggle = 'Toggle';
|
||||
TEX.Definitions.macros.mathtip = 'Mathtip';
|
||||
TEX.Definitions.macros.texttip = ['Macro','\\mathtip{#1}{\\text{#2}}',2];
|
||||
|
||||
TEX.Parse.Augment({
|
||||
|
||||
//
|
||||
// Implement \toggle {math1} {math2} ... \endtoggle
|
||||
// (as an <maction actiontype="toggle">)
|
||||
//
|
||||
Toggle: function (name) {
|
||||
var data = [], arg;
|
||||
while ((arg = this.GetArgument(name)) !== "\\endtoggle")
|
||||
{data.push(TEX.Parse(arg,this.stack.env).mml())}
|
||||
this.Push(MML.maction.apply(MML,data).With({actiontype: MML.ACTIONTYPE.TOGGLE}));
|
||||
},
|
||||
|
||||
//
|
||||
// Implement \mathtip{math}{tip}
|
||||
// (an an <maction actiontype="tooltip">)
|
||||
//
|
||||
Mathtip: function(name) {
|
||||
var arg = this.ParseArg(name), tip = this.ParseArg(name);
|
||||
this.Push(MML.maction(arg,tip).With({actiontype: MML.ACTIONTYPE.TOOLTIP}));
|
||||
}
|
||||
});
|
||||
|
||||
MathJax.Hub.Startup.signal.Post("TeX action Ready");
|
||||
|
||||
});
|
||||
|
||||
MathJax.Ajax.loadComplete("[MathJax]/extensions/TeX/action.js");
|
45
resources/viewer/mathjax/extensions/TeX/autobold.js
Normal file
@ -0,0 +1,45 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* MathJax/extensions/TeX/autobold.js
|
||||
*
|
||||
* Adds \boldsymbol around mathematics that appears in a section
|
||||
* of an HTML page that is in bold.
|
||||
*
|
||||
* Copyright (c) 2009-2012 Design Science, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
MathJax.Extension["TeX/autobold"] = {
|
||||
version: "2.0"
|
||||
};
|
||||
|
||||
MathJax.Hub.Register.StartupHook("TeX Jax Ready",function () {
|
||||
var TEX = MathJax.InputJax.TeX;
|
||||
|
||||
TEX.prefilterHooks.Add(function (data) {
|
||||
var span = data.script.parentNode.insertBefore(document.createElement("span"),data.script);
|
||||
span.visibility = "hidden";
|
||||
span.style.fontFamily = "Times, serif";
|
||||
span.appendChild(document.createTextNode("ABCXYZabcxyz"));
|
||||
var W = span.offsetWidth;
|
||||
span.style.fontWeight = "bold";
|
||||
if (W && span.offsetWidth === W) {data.math = "\\boldsymbol{"+data.math+"}"}
|
||||
span.parentNode.removeChild(span);
|
||||
});
|
||||
|
||||
MathJax.Hub.Startup.signal.Post("TeX autobold Ready");
|
||||
|
||||
});
|
||||
|
||||
MathJax.Ajax.loadComplete("[MathJax]/extensions/TeX/autobold.js");
|
71
resources/viewer/mathjax/extensions/TeX/autoload-all.js
Normal file
@ -0,0 +1,71 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* MathJax/extensions/TeX/autoload-all.js
|
||||
*
|
||||
* Provides pre-defined macros to autoload all the extensions
|
||||
* so that all macros that MathJax knows about are available.
|
||||
*
|
||||
* ---------------------------------------------------------------------
|
||||
*
|
||||
* Copyright (c) 2012 Design Science, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
MathJax.Extension["TeX/autoload-all"] = {
|
||||
version: "2.0"
|
||||
};
|
||||
|
||||
MathJax.Hub.Register.StartupHook("TeX Jax Ready",function () {
|
||||
var TEX = MathJax.InputJax.TeX,
|
||||
MACROS = TEX.Definitions.macros,
|
||||
ENVS = TEX.Definitions.environment;
|
||||
|
||||
|
||||
var EXTENSIONS = {
|
||||
action: ["mathtip","texttip","toggle"],
|
||||
AMSmath: ["mathring","nobreakspace","negmedspace","negthickspace","intI",
|
||||
"iiiint","idotsint","dddot","ddddot","sideset","boxed",
|
||||
"substack","injlim","projlim","varliminf","varlimsup",
|
||||
"varinjlim","varprojlim","DeclareMathOperator","operatorname",
|
||||
"genfrac","tfrac","dfrac","binom","tbinom","dbinom","cfrac",
|
||||
"shoveleft","shoveright","xrightarrow","xleftarrow"],
|
||||
begingroup: ["begingroup","endgroup","gdef","global"],
|
||||
cancel: ["cancel","bcancel","xcancel","cancelto"],
|
||||
color: ["color","colorbox","fcolorbox","DefineColor"],
|
||||
enclose: ["enclose"],
|
||||
extpfeil: ["Newextarrow","xlongequal","xmapsto","xtofrom",
|
||||
"xtwoheadleftarrow","xtwoheadrightarrow"],
|
||||
mhchem: ["ce","cee","cf"]
|
||||
};
|
||||
|
||||
for (var name in EXTENSIONS) {if (EXTENSIONS.hasOwnProperty(name)) {
|
||||
var macros = EXTENSIONS[name];
|
||||
for (var i = 0, m = macros.length; i < m; i++) {
|
||||
MACROS[macros[i]] = ["Extension",name];
|
||||
}
|
||||
}}
|
||||
|
||||
ENVS["subarray"] = ['ExtensionEnv',null,'AMSmath'];
|
||||
ENVS["smallmatrix"] = ['ExtensionEnv',null,'AMSmath'];
|
||||
ENVS["equation"] = ['ExtensionEnv',null,'AMSmath'];
|
||||
ENVS["equation*"] = ['ExtensionEnv',null,'AMSmath'];
|
||||
|
||||
MathJax.Hub.Startup.signal.Post("TeX autoload-all Ready");
|
||||
|
||||
});
|
||||
|
||||
MathJax.Callback.Queue(
|
||||
["Require",MathJax.Ajax,"[MathJax]/extensions/TeX/AMSsymbols.js"],
|
||||
["loadComplete",MathJax.Ajax,"[MathJax]/extensions/TeX/autoload-all.js"]
|
||||
);
|
90
resources/viewer/mathjax/extensions/TeX/bbox.js
Normal file
@ -0,0 +1,90 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* MathJax/extensions/TeX/bbox.js
|
||||
*
|
||||
* This file implements the \bbox macro, which creates an box that
|
||||
* can be styled (for background colors, and so on). You can include
|
||||
* an optional dimension that tells how much extra padding to include
|
||||
* around the bounding box for the mathematics, or a color specification
|
||||
* for the background color to use, or both. E.g.,
|
||||
*
|
||||
* \bbox[2pt]{x+y} % an invisible box around x+y with 2pt of extra space
|
||||
* \bbox[green]{x+y} % a green box around x+y
|
||||
* \bbox[green,2pt]{x+y} % a green box with 2pt of extra space
|
||||
*
|
||||
* You can also specify style attributes, for example
|
||||
*
|
||||
* \bbox[red,border:3px solid blue,5px]{x+y}
|
||||
*
|
||||
* would give a red background with a 3px solid blue border that has 5px
|
||||
* of padding between the border and the mathematics. Note that not all
|
||||
* output formats support the style specifications. In particular, the
|
||||
* NativeMML output depends on the browser to render the attributes, and
|
||||
* not all MathML renderers will honor them (e.g., MathPlayer2 doesn't
|
||||
* render border styles).
|
||||
*
|
||||
* This file will be loaded automatically when \bbox is first used.
|
||||
*
|
||||
* ---------------------------------------------------------------------
|
||||
*
|
||||
* Copyright (c) 2011-2012 Design Science, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
MathJax.Extension["TeX/bbox"] = {
|
||||
version: "2.0"
|
||||
};
|
||||
|
||||
MathJax.Hub.Register.StartupHook("TeX Jax Ready",function () {
|
||||
|
||||
var TEX = MathJax.InputJax.TeX,
|
||||
MML = MathJax.ElementJax.mml;
|
||||
|
||||
TEX.Definitions.macros.bbox = "BBox";
|
||||
|
||||
TEX.Parse.Augment({
|
||||
BBox: function (name) {
|
||||
var bbox = this.GetBrackets(name,""),
|
||||
math = this.ParseArg(name);
|
||||
var parts = bbox.split(/,/), def, background, style;
|
||||
for (var i in parts) {
|
||||
var part = parts[i].replace(/^\s+/,'').replace(/\s+$/,'');
|
||||
var match = part.match(/^(\.\d+|\d+(\.\d*)?)(pt|em|ex|mu|px|in|cm|mm)$/);
|
||||
if (match) {
|
||||
var pad = match[1]+match[3];
|
||||
if (def) {TEX.Error("Padding specified twice in "+name)}
|
||||
def = {height:"+"+pad, depth:"+"+pad, lspace:pad, width:"+"+(2*match[1])+match[3]};
|
||||
} else if (part.match(/^([a-z0-9]+|\#[0-9a-f]{6}|\#[0-9a-f]{3})$/i)) {
|
||||
if (background) {TEX.Error("Background specified twice in "+name)}
|
||||
background = part;
|
||||
} else if (part.match(/^[-a-z]+:/i)) {
|
||||
if (style) {TEX.Error("Style specified twice in "+name)}
|
||||
style = part;
|
||||
} else if (part !== "") {
|
||||
TEX.Error("'"+part+"' doesn't look like a color, a padding dimension, or a style");
|
||||
}
|
||||
}
|
||||
if (def) {math = MML.mpadded(math).With(def)}
|
||||
if (background || style) {
|
||||
math = MML.mstyle(math).With({mathbackground:background, style:style});
|
||||
}
|
||||
this.Push(math);
|
||||
}
|
||||
});
|
||||
|
||||
MathJax.Hub.Startup.signal.Post("TeX bbox Ready");
|
||||
|
||||
});
|
||||
|
||||
MathJax.Ajax.loadComplete("[MathJax]/extensions/TeX/bbox.js");
|
296
resources/viewer/mathjax/extensions/TeX/begingroup.js
Normal file
@ -0,0 +1,296 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* MathJax/extensions/TeX/begingroup.js
|
||||
*
|
||||
* Implements \begingroup and \endgroup commands that make local
|
||||
* definitions possible and are removed when the \endgroup occurs.
|
||||
*
|
||||
* ---------------------------------------------------------------------
|
||||
*
|
||||
* Copyright (c) 2011-2012 Design Science, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
MathJax.Extension["TeX/begingroup"] = {
|
||||
version: "2.0"
|
||||
};
|
||||
|
||||
MathJax.Hub.Register.StartupHook("TeX Jax Ready",function () {
|
||||
|
||||
var TEX = MathJax.InputJax.TeX,
|
||||
TEXDEF = TEX.Definitions;
|
||||
|
||||
/****************************************************/
|
||||
|
||||
//
|
||||
// A namespace for localizing macros and environments
|
||||
// (\begingroup and \endgroup create and destroy these)
|
||||
//
|
||||
var NSFRAME = MathJax.Object.Subclass({
|
||||
macros: null, // the local macro definitions
|
||||
environments: null, // the local environments
|
||||
Init: function (macros,environments) {
|
||||
this.macros = (macros || {});
|
||||
this.environments = (environments || {});
|
||||
},
|
||||
//
|
||||
// Find a macro or environment by name
|
||||
//
|
||||
Find: function (name,type) {if (this[type][name]) {return this[type][name]}},
|
||||
//
|
||||
// Define or remove a macro or environment
|
||||
//
|
||||
Def: function (name,value,type) {this[type][name] = value},
|
||||
Undef: function (name,type) {delete this[type][name]},
|
||||
//
|
||||
// Merge two namespaces (used when the equation namespace is combined with the root one)
|
||||
//
|
||||
Merge: function (frame) {
|
||||
MathJax.Hub.Insert(this.macros,frame.macros);
|
||||
MathJax.Hub.Insert(this.environments,frame.environments);
|
||||
},
|
||||
//
|
||||
// Move global macros to the stack (globally) and remove from the frame
|
||||
//
|
||||
MergeGlobals: function (stack) {
|
||||
var macros = this.macros;
|
||||
for (var cs in macros) {if (macros.hasOwnProperty(cs) && macros[cs].global) {
|
||||
stack.Def(cs,macros[cs],"macros",true);
|
||||
delete macros[cs].global; delete macros[cs];
|
||||
}}
|
||||
},
|
||||
//
|
||||
// Clear the macro and environment lists
|
||||
// (but not global macros unless "all" is true)
|
||||
//
|
||||
Clear: function (all) {
|
||||
this.environments = {};
|
||||
if (all) {this.macros = {}} else {
|
||||
var macros = this.macros;
|
||||
for (var cs in macros) {
|
||||
if (macros.hasOwnProperty(cs) && !macros[cs].global) {delete macros[cs]}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
});
|
||||
|
||||
/****************************************************/
|
||||
|
||||
//
|
||||
// A Stack of namespace frames
|
||||
//
|
||||
var NSSTACK = TEX.nsStack = MathJax.Object.Subclass({
|
||||
stack: null, // the namespace frames
|
||||
top: 0, // the current top one (we don't pop for real until the equation completes)
|
||||
isEqn: false, // true if this is the equation stack (not the global one)
|
||||
//
|
||||
// Set up the initial stack frame
|
||||
//
|
||||
Init: function (eqn) {
|
||||
this.isEqn = eqn; this.stack = [];
|
||||
if (!eqn) {this.Push(NSFRAME(TEXDEF.macros,TEXDEF.environments))}
|
||||
else {this.Push(NSFRAME())}
|
||||
},
|
||||
//
|
||||
// Define a macro or environment in the top frame
|
||||
//
|
||||
Def: function (name,value,type,global) {
|
||||
var n = this.top-1;
|
||||
if (global) {
|
||||
//
|
||||
// Define global macros in the base frame and remove that cs
|
||||
// from all other frames. Mark the global ones in equations
|
||||
// so they can be made global when merged with the root stack.
|
||||
//
|
||||
while (n > 0) {this.stack[n].Undef(name,type); n--}
|
||||
if (!(value instanceof Array)) {value = [value]}
|
||||
if (this.isEqn) {value.global = true}
|
||||
}
|
||||
this.stack[n].Def(name,value,type);
|
||||
},
|
||||
//
|
||||
// Push a new namespace frame on the stack
|
||||
//
|
||||
Push: function (frame) {
|
||||
this.stack.push(frame);
|
||||
this.top = this.stack.length;
|
||||
},
|
||||
//
|
||||
// Pop the top stack frame
|
||||
// (if it is the root, just keep track of the pop so we can
|
||||
// reset it if the equation is reprocessed)
|
||||
//
|
||||
Pop: function () {
|
||||
var top;
|
||||
if (this.top > 1) {
|
||||
top = this.stack[--this.top];
|
||||
if (this.isEqn) {this.stack.pop()}
|
||||
} else if (this.isEqn) {
|
||||
this.Clear();
|
||||
}
|
||||
return top;
|
||||
},
|
||||
//
|
||||
// Search the stack from top to bottom for the first
|
||||
// definition of the given control sequence in the given type
|
||||
//
|
||||
Find: function (name,type) {
|
||||
for (var i = this.top-1; i >= 0; i--) {
|
||||
var def = this.stack[i].Find(name,type);
|
||||
if (def) {return def}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
//
|
||||
// Combine the equation stack with the global one
|
||||
// (The bottom frame of the equation goes with the top frame of the global one,
|
||||
// and the remainder are pushed on the global stack, truncated to the
|
||||
// position where items were poped from it.)
|
||||
//
|
||||
Merge: function (stack) {
|
||||
stack.stack[0].MergeGlobals(this);
|
||||
this.stack[this.top-1].Merge(stack.stack[0]);
|
||||
var data = [this.top,this.stack.length-this.top].concat(stack.stack.slice(1));
|
||||
this.stack.splice.apply(this.stack,data);
|
||||
this.top = this.stack.length;
|
||||
},
|
||||
//
|
||||
// Put back the temporarily poped items
|
||||
//
|
||||
Reset: function () {this.top = this.stack.length},
|
||||
//
|
||||
// Clear the stack and start with a blank frame
|
||||
//
|
||||
Clear: function (all) {
|
||||
this.stack = [this.stack[0].Clear()];
|
||||
this.top = this.stack.length;
|
||||
}
|
||||
},{
|
||||
nsFrame: NSFRAME
|
||||
});
|
||||
|
||||
/****************************************************/
|
||||
|
||||
//
|
||||
// Define the new macros
|
||||
//
|
||||
TEXDEF.macros.begingroup = "BeginGroup";
|
||||
TEXDEF.macros.endgroup = "EndGroup";
|
||||
TEXDEF.macros.global = ["Extension","newcommand"];
|
||||
TEXDEF.macros.gdef = ["Extension","newcommand"];
|
||||
|
||||
TEX.Parse.Augment({
|
||||
//
|
||||
// Implement \begingroup
|
||||
//
|
||||
BeginGroup: function (name) {
|
||||
TEX.eqnStack.Push(NSFRAME());
|
||||
},
|
||||
//
|
||||
// Implements \endgroup
|
||||
//
|
||||
EndGroup: function (name) {
|
||||
//
|
||||
// If the equation has pushed frames, pop one,
|
||||
// Otherwise clear the equation stack and pop the top global one
|
||||
//
|
||||
if (TEX.eqnStack.top > 1) {
|
||||
TEX.eqnStack.Pop();
|
||||
} else if (TEX.rootStack.top === 1) {
|
||||
TEX.Error("Extra "+name+" or missing \\begingroup");
|
||||
} else {
|
||||
TEX.eqnStack.Clear();
|
||||
TEX.rootStack.Pop();
|
||||
}
|
||||
},
|
||||
|
||||
//
|
||||
// Replace the original routines with ones that looks through the
|
||||
// equation and root stacks for the given name
|
||||
//
|
||||
csFindMacro: function (name) {
|
||||
return (TEX.eqnStack.Find(name,"macros") || TEX.rootStack.Find(name,"macros"));
|
||||
},
|
||||
envFindName: function (name) {
|
||||
return (TEX.eqnStack.Find(name,"environments") || TEX.rootStack.Find(name,"environments"));
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
/****************************************************/
|
||||
|
||||
TEX.rootStack = NSSTACK(); // the global namespace stack
|
||||
TEX.eqnStack = NSSTACK(true); // the equation stack
|
||||
|
||||
//
|
||||
// Reset the global stack and clear the equation stack
|
||||
// (this gets us back to the initial stack state as it was
|
||||
// before the equation was first processed, in case the equation
|
||||
// get restarted due to an autoloaded file)
|
||||
//
|
||||
TEX.prefilterHooks.Add(function () {TEX.rootStack.Reset(); TEX.eqnStack.Clear(true)});
|
||||
|
||||
//
|
||||
// We only get here if there were no errors and the equation is fully
|
||||
// processed (all restarts are complete). So we merge the equation
|
||||
// stack into the global stack, thus making the changes from this
|
||||
// equation permanent.
|
||||
//
|
||||
TEX.postfilterHooks.Add(function () {TEX.rootStack.Merge(TEX.eqnStack)});
|
||||
|
||||
/*********************************************************/
|
||||
|
||||
MathJax.Hub.Register.StartupHook("TeX newcommand Ready",function () {
|
||||
|
||||
//
|
||||
// Add the commands that depend on the newcommand code
|
||||
//
|
||||
TEXDEF.macros.global = "Global";
|
||||
TEXDEF.macros.gdef = ["Macro","\\global\\def"];
|
||||
|
||||
TEX.Parse.Augment({
|
||||
//
|
||||
// Modify the way macros and environments are defined
|
||||
// to make them go into the equation namespace stack
|
||||
//
|
||||
setDef: function (name,value) {
|
||||
value.isUser = true;
|
||||
TEX.eqnStack.Def(name,value,"macros",this.stack.env.isGlobal);
|
||||
delete this.stack.env.isGlobal;
|
||||
},
|
||||
setEnv: function (name,value) {
|
||||
value.isUser = true;
|
||||
TEX.eqnStack.Def(name,value,"environments")
|
||||
},
|
||||
|
||||
//
|
||||
// Implement \global (for \global\let, \global\def and \global\newcommand)
|
||||
//
|
||||
Global: function (name) {
|
||||
var i = this.i; var cs = this.GetCSname(name); this.i = i;
|
||||
if (cs !== "let" && cs !== "def" && cs !== "newcommand")
|
||||
{TEX.Error(name+" not followed by \\let, \\def, or \\newcommand")}
|
||||
this.stack.env.isGlobal = true;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
MathJax.Hub.Startup.signal.Post("TeX begingroup Ready");
|
||||
|
||||
});
|
||||
|
||||
MathJax.Ajax.loadComplete("[MathJax]/extensions/TeX/begingroup.js");
|
126
resources/viewer/mathjax/extensions/TeX/boldsymbol.js
Normal file
@ -0,0 +1,126 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* MathJax/extensions/TeX/boldsymbol.js
|
||||
*
|
||||
* Implements the \boldsymbol{...} command to make bold
|
||||
* versions of all math characters (not just variables).
|
||||
*
|
||||
* ---------------------------------------------------------------------
|
||||
*
|
||||
* Copyright (c) 2009-2012 Design Science, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
MathJax.Extension["TeX/boldsymbol"] = {
|
||||
version: "2.0"
|
||||
};
|
||||
|
||||
MathJax.Hub.Register.StartupHook("TeX Jax Ready",function () {
|
||||
|
||||
var MML = MathJax.ElementJax.mml;
|
||||
var TEX = MathJax.InputJax.TeX;
|
||||
var TEXDEF = TEX.Definitions;
|
||||
|
||||
var BOLDVARIANT = {};
|
||||
BOLDVARIANT[MML.VARIANT.NORMAL] = MML.VARIANT.BOLD;
|
||||
BOLDVARIANT[MML.VARIANT.ITALIC] = MML.VARIANT.BOLDITALIC;
|
||||
BOLDVARIANT[MML.VARIANT.FRAKTUR] = MML.VARIANT.BOLDFRAKTUR;
|
||||
BOLDVARIANT[MML.VARIANT.SCRIPT] = MML.VARIANT.BOLDSCRIPT;
|
||||
BOLDVARIANT[MML.VARIANT.SANSSERIF] = MML.VARIANT.BOLDSANSSERIF;
|
||||
BOLDVARIANT["-tex-caligraphic"] = "-tex-caligraphic-bold";
|
||||
BOLDVARIANT["-tex-oldstyle"] = "-tex-oldstyle-bold";
|
||||
|
||||
TEXDEF.macros.boldsymbol = 'Boldsymbol';
|
||||
|
||||
TEX.Parse.Augment({
|
||||
mmlToken: function (token) {
|
||||
if (this.stack.env.boldsymbol) {
|
||||
var variant = token.Get("mathvariant");
|
||||
if (variant == null) {token.mathvariant = MML.VARIANT.BOLD}
|
||||
else {token.mathvariant = (BOLDVARIANT[variant]||variant)}
|
||||
}
|
||||
return token;
|
||||
},
|
||||
|
||||
Boldsymbol: function (name) {
|
||||
var boldsymbol = this.stack.env.boldsymbol,
|
||||
font = this.stack.env.font;
|
||||
this.stack.env.boldsymbol = true;
|
||||
this.stack.env.font = null;
|
||||
var mml = this.ParseArg(name);
|
||||
this.stack.env.font = font;
|
||||
this.stack.env.boldsymbol = boldsymbol;
|
||||
this.Push(mml);
|
||||
}
|
||||
});
|
||||
|
||||
MathJax.Hub.Startup.signal.Post("TeX boldsymbol Ready");
|
||||
|
||||
});
|
||||
|
||||
MathJax.Hub.Register.StartupHook("HTML-CSS Jax Ready",function () {
|
||||
var HTMLCSS = MathJax.OutputJax["HTML-CSS"];
|
||||
var FONTS = HTMLCSS.FONTDATA.FONTS;
|
||||
var VARIANT = HTMLCSS.FONTDATA.VARIANT;
|
||||
|
||||
if (HTMLCSS.fontInUse === "TeX") {
|
||||
FONTS["MathJax_Caligraphic-bold"] = "Caligraphic/Bold/Main.js";
|
||||
|
||||
VARIANT["-tex-caligraphic-bold"] =
|
||||
{fonts:["MathJax_Caligraphic-bold","MathJax_Main-bold","MathJax_Main","MathJax_Math","MathJax_Size1"],
|
||||
offsetA: 0x41, variantA: "bold-italic"};
|
||||
VARIANT["-tex-oldstyle-bold"] =
|
||||
{fonts:["MathJax_Caligraphic-bold","MathJax_Main-bold","MathJax_Main","MathJax_Math","MathJax_Size1"]};
|
||||
|
||||
if (HTMLCSS.msieCheckGreek && HTMLCSS.Font.testFont({
|
||||
family:"MathJax_Greek", weight:"bold", style:"italic", testString: HTMLCSS.msieCheckGreek
|
||||
})) {
|
||||
VARIANT["bold-italic"].offsetG = 0x391; VARIANT["bold-italic"].variantG = "-Greek-Bold-Italic";
|
||||
VARIANT["-Greek-Bold-Italic"] = {fonts:["MathJax_Greek-bold-italic"]};
|
||||
FONTS["MathJax_Greek-bold-italic"] = "Greek/BoldItalic/Main.js";
|
||||
}
|
||||
|
||||
if (MathJax.Hub.Browser.isChrome && !MathJax.Hub.Browser.versionAtLeast("5.0")) {
|
||||
VARIANT["-tex-caligraphic-bold"].remap = {0x54: [0xE2F0,"-WinChrome"]};
|
||||
}
|
||||
|
||||
} else if (HTMLCSS.fontInUse === "STIX") {
|
||||
VARIANT["-tex-caligraphic-bold"] = {
|
||||
fonts:["STIXGeneral-bold-italic","STIXNonUnicode-bold-italic","STIXNonUnicode","STIXGeneral","STIXSizeOneSym"],
|
||||
offsetA: 0xE247, noLowerCase: 1
|
||||
};
|
||||
VARIANT["-tex-oldstyle-bold"] = {
|
||||
fonts:["STIXGeneral-bold","STIXNonUnicode-bold","STIXGeneral","STIXSizeOneSym"], offsetN: 0xE263,
|
||||
remap: {0xE264: 0xE267, 0xE265: 0xE26B, 0xE266: 0xE26F, 0xE267: 0xE273,
|
||||
0xE268: 0xE277, 0xE269: 0xE27B, 0xE26A: 0xE27F, 0xE26B: 0xE283,
|
||||
0xE26C: 0xE287}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
MathJax.Hub.Register.StartupHook("SVG Jax Ready",function () {
|
||||
var SVG = MathJax.OutputJax.SVG;
|
||||
var FONTS = SVG.FONTDATA.FONTS;
|
||||
var VARIANT = SVG.FONTDATA.VARIANT;
|
||||
|
||||
FONTS["MathJax_Caligraphic-bold"] = "Caligraphic/Bold/Main.js";
|
||||
|
||||
VARIANT["-tex-caligraphic-bold"] =
|
||||
{fonts:["MathJax_Caligraphic-bold","MathJax_Main-bold","MathJax_Main","MathJax_Math","MathJax_Size1"],
|
||||
offsetA: 0x41, variantA: "bold-italic"};
|
||||
VARIANT["-tex-oldstyle-bold"] =
|
||||
{fonts:["MathJax_Caligraphic-bold","MathJax_Main-bold","MathJax_Main","MathJax_Math","MathJax_Size1"]};
|
||||
});
|
||||
|
||||
MathJax.Ajax.loadComplete("[MathJax]/extensions/TeX/boldsymbol.js");
|
105
resources/viewer/mathjax/extensions/TeX/cancel.js
Normal file
@ -0,0 +1,105 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* MathJax/extensions/TeX/cancel.js
|
||||
*
|
||||
* Implements the \cancel, \bcancel, \xcancel, and \cancelto macros.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* \cancel{math} % strikeout math from lower left to upper right
|
||||
* \bcancel{math} % strikeout from upper left to lower right
|
||||
* \xcancel{math} % strikeout with an X
|
||||
* \cancelto{value}{math} % strikeout with arrow going to value
|
||||
*
|
||||
* ---------------------------------------------------------------------
|
||||
*
|
||||
* Copyright (c) 2011-2012 Design Science, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
MathJax.Extension["TeX/cancel"] = {
|
||||
version: "2.0",
|
||||
|
||||
//
|
||||
// The attributes allowed in \enclose{notation}[attributes]{math}
|
||||
//
|
||||
ALLOWED: {
|
||||
arrow: 1,
|
||||
color: 1, mathcolor: 1,
|
||||
background: 1, mathbackground: 1,
|
||||
padding: 1,
|
||||
thickness: 1
|
||||
}
|
||||
};
|
||||
|
||||
MathJax.Hub.Register.StartupHook("TeX Jax Ready",function () {
|
||||
var TEX = MathJax.InputJax.TeX,
|
||||
MACROS = TEX.Definitions.macros,
|
||||
MML = MathJax.ElementJax.mml,
|
||||
CANCEL = MathJax.Extension["TeX/cancel"];
|
||||
|
||||
CANCEL.setAttributes = function (def,attr) {
|
||||
if (attr !== "") {
|
||||
attr = attr.replace(/ /g,"").split(/,/);
|
||||
for (var i = 0, m = attr.length; i < m; i++) {
|
||||
var keyvalue = attr[i].split(/[:=]/);
|
||||
if (CANCEL.ALLOWED[keyvalue[0]]) {
|
||||
if (keyvalue[1] === "true") {keyvalue[1] = true}
|
||||
if (keyvalue[1] === "false") {keyvalue[1] = false}
|
||||
def[keyvalue[0]] = keyvalue[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
return def;
|
||||
};
|
||||
|
||||
//
|
||||
// Set up macro
|
||||
//
|
||||
MACROS.cancel = ['Cancel',MML.NOTATION.UPDIAGONALSTRIKE];
|
||||
MACROS.bcancel = ['Cancel',MML.NOTATION.DOWNDIAGONALSTRIKE];
|
||||
MACROS.xcancel = ['Cancel',MML.NOTATION.UPDIAGONALSTRIKE+" "+MML.NOTATION.DOWNDIAGONALSTRIKE];
|
||||
MACROS.cancelto = 'CancelTo';
|
||||
|
||||
TEX.Parse.Augment({
|
||||
//
|
||||
// Implement \cancel[attributes]{math},
|
||||
// \bcancel[attributes]{math}, and
|
||||
// \xcancel[attributes]{math}
|
||||
//
|
||||
Cancel: function(name,notation) {
|
||||
var attr = this.GetBrackets(name,""), math = this.ParseArg(name);
|
||||
var def = CANCEL.setAttributes({notation: notation},attr);
|
||||
this.Push(MML.menclose(math).With(def));
|
||||
},
|
||||
|
||||
//
|
||||
// Implement \cancelto{value}[attributes]{math}
|
||||
//
|
||||
CancelTo: function(name,notation) {
|
||||
var value = this.ParseArg(name),
|
||||
attr = this.GetBrackets(name,""),
|
||||
math = this.ParseArg(name);
|
||||
var def = CANCEL.setAttributes({notation: MML.NOTATION.UPDIAGONALSTRIKE, arrow:true},attr);
|
||||
value = MML.mpadded(value).With({depth:"-.1em",height:"+.1em",voffset:".1em"});
|
||||
this.Push(MML.msup(MML.menclose(math).With(def),value));
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
MathJax.Hub.Startup.signal.Post("TeX cancel Ready");
|
||||
|
||||
});
|
||||
|
||||
MathJax.Ajax.loadComplete("[MathJax]/extensions/TeX/cancel.js");
|
229
resources/viewer/mathjax/extensions/TeX/color.js
Normal file
@ -0,0 +1,229 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* MathJax/extensions/TeX/color.js
|
||||
*
|
||||
* Implements LaTeX-compatible \color macro rather than MathJax's
|
||||
* original (non-standard) version. It includes the rgb, gray, and
|
||||
* named color models, and the \definecolor macro.
|
||||
*
|
||||
* ---------------------------------------------------------------------
|
||||
*
|
||||
* Copyright (c) 2011-2012 Design Science, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
//
|
||||
// The configuration defaults, augmented by the user settings
|
||||
//
|
||||
MathJax.Extension["TeX/color"] = {
|
||||
version: "2.0",
|
||||
|
||||
config: MathJax.Hub.CombineConfig("TeX.color",{
|
||||
padding: "5px",
|
||||
border: "2px"
|
||||
}),
|
||||
|
||||
colors: {
|
||||
Apricot: "#FBB982",
|
||||
Aquamarine: "#00B5BE",
|
||||
Bittersweet: "#C04F17",
|
||||
Black: "#221E1F",
|
||||
Blue: "#2D2F92",
|
||||
BlueGreen: "#00B3B8",
|
||||
BlueViolet: "#473992",
|
||||
BrickRed: "#B6321C",
|
||||
Brown: "#792500",
|
||||
BurntOrange: "#F7921D",
|
||||
CadetBlue: "#74729A",
|
||||
CarnationPink: "#F282B4",
|
||||
Cerulean: "#00A2E3",
|
||||
CornflowerBlue: "#41B0E4",
|
||||
Cyan: "#00AEEF",
|
||||
Dandelion: "#FDBC42",
|
||||
DarkOrchid: "#A4538A",
|
||||
Emerald: "#00A99D",
|
||||
ForestGreen: "#009B55",
|
||||
Fuchsia: "#8C368C",
|
||||
Goldenrod: "#FFDF42",
|
||||
Gray: "#949698",
|
||||
Green: "#00A64F",
|
||||
GreenYellow: "#DFE674",
|
||||
JungleGreen: "#00A99A",
|
||||
Lavender: "#F49EC4",
|
||||
LimeGreen: "#8DC73E",
|
||||
Magenta: "#EC008C",
|
||||
Mahogany: "#A9341F",
|
||||
Maroon: "#AF3235",
|
||||
Melon: "#F89E7B",
|
||||
MidnightBlue: "#006795",
|
||||
Mulberry: "#A93C93",
|
||||
NavyBlue: "#006EB8",
|
||||
OliveGreen: "#3C8031",
|
||||
Orange: "#F58137",
|
||||
OrangeRed: "#ED135A",
|
||||
Orchid: "#AF72B0",
|
||||
Peach: "#F7965A",
|
||||
Periwinkle: "#7977B8",
|
||||
PineGreen: "#008B72",
|
||||
Plum: "#92268F",
|
||||
ProcessBlue: "#00B0F0",
|
||||
Purple: "#99479B",
|
||||
RawSienna: "#974006",
|
||||
Red: "#ED1B23",
|
||||
RedOrange: "#F26035",
|
||||
RedViolet: "#A1246B",
|
||||
Rhodamine: "#EF559F",
|
||||
RoyalBlue: "#0071BC",
|
||||
RoyalPurple: "#613F99",
|
||||
RubineRed: "#ED017D",
|
||||
Salmon: "#F69289",
|
||||
SeaGreen: "#3FBC9D",
|
||||
Sepia: "#671800",
|
||||
SkyBlue: "#46C5DD",
|
||||
SpringGreen: "#C6DC67",
|
||||
Tan: "#DA9D76",
|
||||
TealBlue: "#00AEB3",
|
||||
Thistle: "#D883B7",
|
||||
Turquoise: "#00B4CE",
|
||||
Violet: "#58429B",
|
||||
VioletRed: "#EF58A0",
|
||||
White: "#FFFFFF",
|
||||
WildStrawberry: "#EE2967",
|
||||
Yellow: "#FFF200",
|
||||
YellowGreen: "#98CC70",
|
||||
YellowOrange: "#FAA21A"
|
||||
},
|
||||
|
||||
/*
|
||||
* Look up a color based on its model and definition
|
||||
*/
|
||||
getColor: function (model,def) {
|
||||
if (!model) {model = "named"}
|
||||
var fn = this["get_"+model];
|
||||
if (!fn) {this.TEX.Error("Color model '"+model+"' not defined")}
|
||||
return fn.call(this,def);
|
||||
},
|
||||
|
||||
/*
|
||||
* Get an RGB color
|
||||
*/
|
||||
get_rgb: function (rgb) {
|
||||
rgb = rgb.split(/,/); var RGB = "#";
|
||||
if (rgb.length !== 3) {this.TEX.Error("RGB colors require 3 decimal numbers")}
|
||||
for (var i = 0; i < 3; i++) {
|
||||
if (!rgb[i].match(/^(\d+(\.\d*)?|\.\d+)$/)) {this.TEX.Error("Invalid decimal number")}
|
||||
var n = parseFloat(rgb[i]);
|
||||
if (n < 0 || n > 1) {this.TEX.Error("RGB values must be between 0 and 1")}
|
||||
n = Math.floor(n*255).toString(16); if (n.length < 2) {n = "0"+n}
|
||||
RGB += n;
|
||||
}
|
||||
return RGB;
|
||||
},
|
||||
|
||||
/*
|
||||
* Get a gray-scale value
|
||||
*/
|
||||
get_gray: function (gray) {
|
||||
if (!gray.match(/^(\d+(\.\d*)?|\.\d+)$/)) {this.TEX.Error("Invalid decimal number")}
|
||||
var n = parseFloat(gray);
|
||||
if (n < 0 || n > 1) {this.TEX.Error("Grey-scale values must be between 0 and 1")}
|
||||
n = Math.floor(n*255).toString(16); if (n.length < 2) {n = "0"+n}
|
||||
return "#"+n+n+n;
|
||||
},
|
||||
|
||||
/*
|
||||
* Get a named value
|
||||
*/
|
||||
get_named: function (name) {
|
||||
if (this.colors[name]) {return this.colors[name]}
|
||||
return name;
|
||||
},
|
||||
|
||||
padding: function () {
|
||||
var pad = "+"+this.config.padding;
|
||||
var unit = this.config.padding.replace(/^.*?([a-z]*)$/,"$1");
|
||||
var pad2 = "+"+(2*parseFloat(pad))+unit;
|
||||
return {width:pad2, height:pad, depth:pad, lspace:this.config.padding};
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
MathJax.Hub.Register.StartupHook("TeX Jax Ready",function () {
|
||||
var TEX = MathJax.InputJax.TeX,
|
||||
MML = MathJax.ElementJax.mml;
|
||||
var STACKITEM = TEX.Stack.Item;
|
||||
var COLOR = MathJax.Extension["TeX/color"];
|
||||
|
||||
COLOR.TEX = TEX; // for reference in getColor above
|
||||
|
||||
TEX.Definitions.macros.color = "Color";
|
||||
TEX.Definitions.macros.definecolor = "DefineColor";
|
||||
TEX.Definitions.macros.colorbox = "ColorBox";
|
||||
TEX.Definitions.macros.fcolorbox = "fColorBox";
|
||||
|
||||
TEX.Parse.Augment({
|
||||
|
||||
//
|
||||
// Override \color macro definition
|
||||
//
|
||||
Color: function (name) {
|
||||
var model = this.GetBrackets(name),
|
||||
color = this.GetArgument(name);
|
||||
color = COLOR.getColor(model,color);
|
||||
var mml = STACKITEM.style().With({styles:{mathcolor:color}});
|
||||
this.stack.env.color = color;
|
||||
this.Push(mml);
|
||||
},
|
||||
|
||||
//
|
||||
// Define the \definecolor macro
|
||||
//
|
||||
DefineColor: function (name) {
|
||||
var cname = this.GetArgument(name),
|
||||
model = this.GetArgument(name),
|
||||
def = this.GetArgument(name);
|
||||
COLOR.colors[cname] = COLOR.getColor(model,def);
|
||||
},
|
||||
|
||||
//
|
||||
// Produce a text box with a colored background
|
||||
//
|
||||
ColorBox: function (name) {
|
||||
var cname = this.GetArgument(name),
|
||||
arg = this.InternalMath(this.GetArgument(name));
|
||||
this.Push(MML.mpadded.apply(MML,arg).With({
|
||||
mathbackground:COLOR.getColor("named",cname)
|
||||
}).With(COLOR.padding()));
|
||||
},
|
||||
|
||||
//
|
||||
// Procude a framed text box with a colored background
|
||||
//
|
||||
fColorBox: function (name) {
|
||||
var fname = this.GetArgument(name),
|
||||
cname = this.GetArgument(name),
|
||||
arg = this.InternalMath(this.GetArgument(name));
|
||||
this.Push(MML.mpadded.apply(MML,arg).With({
|
||||
mathbackground: COLOR.getColor("named",cname),
|
||||
style: "border: "+COLOR.config.border+" solid "+COLOR.getColor("named",fname)
|
||||
}).With(COLOR.padding()));
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
MathJax.Hub.Startup.signal.Post("TeX color Ready");
|
||||
|
||||
});
|
||||
|
||||
MathJax.Ajax.loadComplete("[MathJax]/extensions/TeX/color.js");
|
86
resources/viewer/mathjax/extensions/TeX/enclose.js
Normal file
@ -0,0 +1,86 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* MathJax/extensions/TeX/enclose.js
|
||||
*
|
||||
* Implements the \enclose macros, which give access from TeX to the
|
||||
* <menclose> tag in the MathML that underlies MathJax's internal format.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* \enclose{notation}{math} % enclose math using given notation
|
||||
* \enclose{notation,notation,...}{math} % enclose with several notations
|
||||
* \enclose{notation}[attributes]{math} % enclose with attributes
|
||||
*
|
||||
* ---------------------------------------------------------------------
|
||||
*
|
||||
* Copyright (c) 2011-2012 Design Science, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
MathJax.Extension["TeX/enclose"] = {
|
||||
version: "2.0",
|
||||
|
||||
//
|
||||
// The attributes allowed in \enclose{notation}[attributes]{math}
|
||||
//
|
||||
ALLOWED: {
|
||||
arrow: 1,
|
||||
color: 1, mathcolor: 1,
|
||||
background: 1, mathbackground: 1,
|
||||
padding: 1,
|
||||
thickness: 1
|
||||
}
|
||||
};
|
||||
|
||||
MathJax.Hub.Register.StartupHook("TeX Jax Ready",function () {
|
||||
var TEX = MathJax.InputJax.TeX,
|
||||
MML = MathJax.ElementJax.mml,
|
||||
ALLOW = MathJax.Extension["TeX/enclose"].ALLOWED;
|
||||
|
||||
//
|
||||
// Set up macro
|
||||
//
|
||||
TEX.Definitions.macros.enclose = 'Enclose';
|
||||
|
||||
TEX.Parse.Augment({
|
||||
//
|
||||
// Implement \enclose{notation}[attr]{math}
|
||||
// (create <menclose notation="notation">math</menclose>)
|
||||
//
|
||||
Enclose: function(name) {
|
||||
var notation = this.GetArgument(name),
|
||||
attr = this.GetBrackets(name),
|
||||
math = this.ParseArg(name);
|
||||
var def = {notation: notation.replace(/,/g," ")};
|
||||
if (attr) {
|
||||
attr = attr.replace(/ /g,"").split(/,/);
|
||||
for (var i = 0, m = attr.length; i < m; i++) {
|
||||
var keyvalue = attr[i].split(/[:=]/);
|
||||
if (ALLOW[keyvalue[0]]) {
|
||||
keyvalue[1] = keyvalue[1].replace(/^"(.*)"$/,"$1");
|
||||
if (keyvalue[1] === "true") {keyvalue[1] = true}
|
||||
if (keyvalue[1] === "false") {keyvalue[1] = false}
|
||||
def[keyvalue[0]] = keyvalue[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
this.Push(MML.menclose(math).With(def));
|
||||
}
|
||||
});
|
||||
|
||||
MathJax.Hub.Startup.signal.Post("TeX enclose Ready");
|
||||
|
||||
});
|
||||
|
||||
MathJax.Ajax.loadComplete("[MathJax]/extensions/TeX/enclose.js");
|
87
resources/viewer/mathjax/extensions/TeX/extpfeil.js
Normal file
@ -0,0 +1,87 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* MathJax/extensions/TeX/extpfeil.js
|
||||
*
|
||||
* Implements additional stretchy arrow macros.
|
||||
*
|
||||
* ---------------------------------------------------------------------
|
||||
*
|
||||
* Copyright (c) 2011-2012 Design Science, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
MathJax.Extension["TeX/extpfeil"] = {
|
||||
version: "2.0"
|
||||
};
|
||||
|
||||
MathJax.Hub.Register.StartupHook("TeX Jax Ready",function () {
|
||||
|
||||
var TEX = MathJax.InputJax.TeX,
|
||||
TEXDEF = TEX.Definitions;
|
||||
|
||||
//
|
||||
// Define the arrows to load the AMSmath extension
|
||||
// (since they need its xArrow method)
|
||||
//
|
||||
MathJax.Hub.Insert(TEXDEF,{
|
||||
macros: {
|
||||
xtwoheadrightarrow: ['Extension','AMSmath'],
|
||||
xtwoheadleftarrow: ['Extension','AMSmath'],
|
||||
xmapsto: ['Extension','AMSmath'],
|
||||
xlongequal: ['Extension','AMSmath'],
|
||||
xtofrom: ['Extension','AMSmath'],
|
||||
Newextarrow: ['Extension','AMSmath']
|
||||
}
|
||||
});
|
||||
|
||||
//
|
||||
// Redefine the macros when AMSmath is loaded
|
||||
//
|
||||
MathJax.Hub.Register.StartupHook("TeX AMSmath Ready",function () {
|
||||
MathJax.Hub.Insert(TEXDEF,{
|
||||
macros: {
|
||||
xtwoheadrightarrow: ['xArrow',0x21A0,12,16],
|
||||
xtwoheadleftarrow: ['xArrow',0x219E,17,13],
|
||||
xmapsto: ['xArrow',0x21A6,6,7],
|
||||
xlongequal: ['xArrow',0x003D,7,7],
|
||||
xtofrom: ['xArrow',0x21C4,12,12],
|
||||
Newextarrow: 'NewExtArrow'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
//
|
||||
// Implements \Newextarrow to define a new arrow (not compatible with \newextarrow, but
|
||||
// the equivalent for MathJax)
|
||||
//
|
||||
TEX.Parse.Augment({
|
||||
NewExtArrow: function (name) {
|
||||
var cs = this.GetArgument(name),
|
||||
space = this.GetArgument(name),
|
||||
chr = this.GetArgument(name);
|
||||
if (!cs.match(/^\\([a-z]+|.)$/i))
|
||||
{TEX.Error("First argument to "+name+" must be a control sequence name")}
|
||||
if (!space.match(/^(\d+),(\d+)$/))
|
||||
{TEX.Error("Second argument to "+name+" must be two integers separated by a comma")}
|
||||
if (!chr.match(/^(\d+|0x[0-9A-F]+)$/i))
|
||||
{TEX.Error("Third argument to "+name+" must be a unicode character number")}
|
||||
cs = cs.substr(1); space = space.split(","); chr = parseInt(chr);
|
||||
TEXDEF.macros[cs] = ['xArrow',chr,parseInt(space[0]),parseInt(space[1])];
|
||||
}
|
||||
});
|
||||
|
||||
MathJax.Hub.Startup.signal.Post("TeX extpfeil Ready");
|
||||
});
|
||||
|
||||
MathJax.Ajax.loadComplete("[MathJax]/extensions/TeX/extpfeil.js");
|
78
resources/viewer/mathjax/extensions/TeX/mathchoice.js
Normal file
@ -0,0 +1,78 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* MathJax/extensions/TeX/mathchoice.js
|
||||
*
|
||||
* Implements the \mathchoice macro (rarely used)
|
||||
*
|
||||
* ---------------------------------------------------------------------
|
||||
*
|
||||
* Copyright (c) 2009-2012 Design Science, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
MathJax.Hub.Register.StartupHook("TeX Jax Ready",function () {
|
||||
var VERSION = "2.0";
|
||||
|
||||
var MML = MathJax.ElementJax.mml;
|
||||
var TEX = MathJax.InputJax.TeX;
|
||||
var TEXDEF = TEX.Definitions;
|
||||
|
||||
TEXDEF.macros.mathchoice = 'MathChoice';
|
||||
|
||||
TEX.Parse.Augment({
|
||||
|
||||
MathChoice: function (name) {
|
||||
var D = this.ParseArg(name),
|
||||
T = this.ParseArg(name),
|
||||
S = this.ParseArg(name),
|
||||
SS = this.ParseArg(name);
|
||||
this.Push(MML.TeXmathchoice(D,T,S,SS));
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
MML.TeXmathchoice = MML.mbase.Subclass({
|
||||
type: "TeXmathchoice",
|
||||
choice: function () {
|
||||
var values = this.getValues("displaystyle","scriptlevel");
|
||||
if (values.scriptlevel > 0) {return Math.min(3,values.scriptlevel + 1)}
|
||||
return (values.displaystyle ? 0 : 1);
|
||||
},
|
||||
setTeXclass: function (prev) {return this.Core().setTeXclass(prev)},
|
||||
isSpacelike: function () {return this.Core().isSpacelike()},
|
||||
isEmbellished: function () {return this.Core().isEmbellished()},
|
||||
Core: function () {return this.data[this.choice()]},
|
||||
toHTML: function (span) {
|
||||
span = this.HTMLcreateSpan(span);
|
||||
span.bbox = this.Core().toHTML(span).bbox;
|
||||
// Firefox doesn't correctly handle a span with a negatively sized content,
|
||||
// so move marginLeft to main span (this is a hack to get \iiiint to work).
|
||||
// FIXME: This is a symptom of a more general problem with Firefox, and
|
||||
// there probably needs to be a more general solution (e.g., modifying
|
||||
// HTMLhandleSpace() to get the width and adjust the right margin to
|
||||
// compensate for negative-width contents)
|
||||
if (span.firstChild && span.firstChild.style.marginLeft) {
|
||||
span.style.marginLeft = span.firstChild.style.marginLeft;
|
||||
span.firstChild.style.marginLeft = "";
|
||||
}
|
||||
return span;
|
||||
},
|
||||
toSVG: function () {return this.Core().toSVG()}
|
||||
});
|
||||
|
||||
MathJax.Hub.Startup.signal.Post("TeX mathchoice Ready");
|
||||
|
||||
});
|
||||
|
||||
MathJax.Ajax.loadComplete("[MathJax]/extensions/TeX/mathchoice.js");
|
413
resources/viewer/mathjax/extensions/TeX/mhchem.js
Normal file
@ -0,0 +1,413 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* MathJax/extensions/TeX/mhchem.js
|
||||
*
|
||||
* Implements the \ce command for handling chemical formulas
|
||||
* from the mhchem LaTeX package.
|
||||
*
|
||||
* ---------------------------------------------------------------------
|
||||
*
|
||||
* Copyright (c) 2011-2012 Design Science, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
MathJax.Extension["TeX/mhchem"] = {
|
||||
version: "2.0"
|
||||
};
|
||||
|
||||
MathJax.Hub.Register.StartupHook("TeX Jax Ready",function () {
|
||||
|
||||
var TEX = MathJax.InputJax.TeX,
|
||||
MACROS = TEX.Definitions.macros;
|
||||
|
||||
/*
|
||||
* This is the main class for handing the \ce and related commands.
|
||||
* Its main method is Parse() which takes the argument to \ce and
|
||||
* returns the corresponding TeX string.
|
||||
*/
|
||||
|
||||
var CE = MathJax.Object.Subclass({
|
||||
string: "", // the \ce string being parsed
|
||||
i: 0, // the current position in the string
|
||||
tex: "", // the processed TeX result
|
||||
atom: false, // last processed token is an atom
|
||||
sup: "", // pending superscript
|
||||
sub: "", // pending subscript
|
||||
|
||||
//
|
||||
// Store the string when a CE object is created
|
||||
//
|
||||
Init: function (string) {this.string = string},
|
||||
|
||||
//
|
||||
// These are the special characters and the methods that
|
||||
// handle them. All others are passed through verbatim.
|
||||
//
|
||||
ParseTable: {
|
||||
'-': "Minus",
|
||||
'+': "Plus",
|
||||
'(': "Open",
|
||||
')': "Close",
|
||||
'[': "Open",
|
||||
']': "Close",
|
||||
'<': "Less",
|
||||
'^': "Superscript",
|
||||
'_': "Subscript",
|
||||
'*': "Dot",
|
||||
'.': "Dot",
|
||||
'=': "Equal",
|
||||
'#': "Pound",
|
||||
'$': "Math",
|
||||
'\\': "Macro",
|
||||
' ': "Space"
|
||||
},
|
||||
//
|
||||
// Basic arrow names for reactions
|
||||
//
|
||||
Arrows: {
|
||||
'->': "rightarrow",
|
||||
'<-': "leftarrow",
|
||||
'<->': "leftrightarrow",
|
||||
'<=>': "rightleftharpoons",
|
||||
'<=>>': "Rightleftharpoons",
|
||||
'^': "uparrow",
|
||||
'v': "downarrow"
|
||||
},
|
||||
|
||||
//
|
||||
// Implementations for the various bonds
|
||||
// (the ~ ones are hacks that don't work well in NativeMML)
|
||||
//
|
||||
Bonds: {
|
||||
'-': "-",
|
||||
'=': "=",
|
||||
'#': "\\equiv",
|
||||
'~': "\\tripledash",
|
||||
'~-': "\\begin{CEstack}{}\\tripledash\\\\-\\end{CEstack}",
|
||||
'~=': "\\raise2mu{\\begin{CEstack}{}\\tripledash\\\\-\\\\-\\end{CEstack}}",
|
||||
'~--': "\\raise2mu{\\begin{CEstack}{}\\tripledash\\\\-\\\\-\\end{CEstack}}",
|
||||
'-~-': "\\raise2mu{\\begin{CEstack}{}-\\\\\\tripledash\\\\-\\end{CEstack}}",
|
||||
'...': "{\\cdot}{\\cdot}{\\cdot}",
|
||||
'....': "{\\cdot}{\\cdot}{\\cdot}{\\cdot}",
|
||||
'->': "\\rightarrow",
|
||||
'<-': "\\leftarrow",
|
||||
'??': "\\text{??}" // unknown bond
|
||||
},
|
||||
|
||||
//
|
||||
// This converts the CE string to a TeX string.
|
||||
// It loops through the string and calls the proper
|
||||
// method depending on the ccurrent character.
|
||||
//
|
||||
Parse: function () {
|
||||
this.tex = ""; this.atom = false;
|
||||
while (this.i < this.string.length) {
|
||||
var c = this.string.charAt(this.i);
|
||||
if (c.match(/[a-z]/i)) {this.ParseLetter()}
|
||||
else if (c.match(/[0-9]/)) {this.ParseNumber()}
|
||||
else {this["Parse"+(this.ParseTable[c]||"Other")](c)}
|
||||
}
|
||||
this.FinishAtom();
|
||||
return this.tex;
|
||||
},
|
||||
|
||||
//
|
||||
// Make an atom name or a down arrow
|
||||
//
|
||||
ParseLetter: function () {
|
||||
this.FinishAtom();
|
||||
if (this.Match(/^v( |$)/)) {
|
||||
this.tex += "{\\"+this.Arrows["v"]+"}";
|
||||
} else {
|
||||
this.tex += "\\text{"+this.Match(/^[a-z]+/i)+"}";
|
||||
this.atom = true;
|
||||
}
|
||||
},
|
||||
|
||||
//
|
||||
// Make a number of fraction preceeding an atom,
|
||||
// or a subscript for an atom.
|
||||
//
|
||||
ParseNumber: function () {
|
||||
var n = this.Match(/^\d+/);
|
||||
if (this.atom && !this.sub) {
|
||||
this.sub = n;
|
||||
} else {
|
||||
this.FinishAtom();
|
||||
var match = this.Match(/^\/\d+/);
|
||||
if (match) {
|
||||
var frac = "\\frac{"+n+"}{"+match.substr(1)+"}";
|
||||
this.tex += "\\mathchoice{\\textstyle"+frac+"}{"+frac+"}{"+frac+"}{"+frac+"}";
|
||||
} else {
|
||||
this.tex += n;
|
||||
if (this.i < this.string.length) {this.tex += "\\,"}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
//
|
||||
// Make a superscript minus, or an arrow, or a single bond.
|
||||
//
|
||||
ParseMinus: function (c) {
|
||||
if (this.atom && (this.i === this.string.length-1 || this.string.charAt(this.i+1) === " ")) {
|
||||
this.sup += c;
|
||||
} else {
|
||||
this.FinishAtom();
|
||||
if (this.string.substr(this.i,2) === "->") {this.i += 2; this.AddArrow("->"); return}
|
||||
else {this.tex += "{-}"}
|
||||
}
|
||||
this.i++;
|
||||
},
|
||||
|
||||
//
|
||||
// Make a superscript plus, or pass it through
|
||||
//
|
||||
ParsePlus: function (c) {
|
||||
if (this.atom) {this.sup += c} else {this.FinishAtom(); this.tex += c}
|
||||
this.i++;
|
||||
},
|
||||
|
||||
//
|
||||
// Handle dots and double or triple bonds
|
||||
//
|
||||
ParseDot: function (c) {this.FinishAtom(); this.tex += "\\cdot "; this.i++},
|
||||
ParseEqual: function (c) {this.FinishAtom(); this.tex += "{=}"; this.i++},
|
||||
ParsePound: function (c) {this.FinishAtom(); this.tex += "{\\equiv}"; this.i++},
|
||||
|
||||
//
|
||||
// Look for (v) or (^), or pass it through
|
||||
//
|
||||
ParseOpen: function (c) {
|
||||
this.FinishAtom();
|
||||
var match = this.Match(/^\([v^]\)/);
|
||||
if (match) {this.tex += "{\\"+this.Arrows[match.charAt(1)]+"}"}
|
||||
else {this.tex += "{"+c; this.i++}
|
||||
},
|
||||
//
|
||||
// Allow ) and ] to get super- and subscripts
|
||||
//
|
||||
ParseClose: function (c) {this.FinishAtom(); this.atom = true; this.tex += c+"}"; this.i++},
|
||||
|
||||
//
|
||||
// Make the proper arrow
|
||||
//
|
||||
ParseLess: function (c) {
|
||||
this.FinishAtom();
|
||||
var arrow = this.Match(/^(<->?|<=>>?)/);
|
||||
if (!arrow) {this.tex += c; this.i++} else {this.AddArrow(arrow)}
|
||||
},
|
||||
|
||||
//
|
||||
// Look for a superscript, or an up arrow
|
||||
//
|
||||
ParseSuperscript: function (c) {
|
||||
c = this.string.charAt(++this.i);
|
||||
if (c === "{") {
|
||||
this.i++; var m = this.Find("}");
|
||||
if (m === "-.") {this.sup += "{-}{\\cdot}"} else if (m) {this.sup += CE(m).Parse()}
|
||||
} else if (c === " " || c === "") {
|
||||
this.tex += "{\\"+this.Arrows["^"]+"}"; this.i++;
|
||||
} else {
|
||||
var n = this.Match(/^(\d+|-\.)/);
|
||||
if (n) {this.sup += n}
|
||||
}
|
||||
},
|
||||
//
|
||||
// Look for subscripts
|
||||
//
|
||||
ParseSubscript: function (c) {
|
||||
if (this.string.charAt(++this.i) == "{") {
|
||||
this.i++; this.sub += CE(this.Find("}")).Parse();
|
||||
} else {
|
||||
var n = this.Match(/^\d+/);
|
||||
if (n) {this.sub += n}
|
||||
}
|
||||
},
|
||||
|
||||
//
|
||||
// Look for raw TeX code to include
|
||||
//
|
||||
ParseMath: function (c) {
|
||||
this.FinishAtom();
|
||||
this.i++; this.tex += this.Find(c);
|
||||
},
|
||||
|
||||
//
|
||||
// Look for specific macros for bonds
|
||||
// and allow \} to have subscripts
|
||||
//
|
||||
ParseMacro: function (c) {
|
||||
this.FinishAtom();
|
||||
this.i++; var match = this.Match(/^([a-z]+|.)/i)||" ";
|
||||
if (match === "sbond") {this.tex += "{-}"}
|
||||
else if (match === "dbond") {this.tex += "{=}"}
|
||||
else if (match === "tbond") {this.tex += "{\\equiv}"}
|
||||
else if (match === "bond") {
|
||||
var bond = (this.Match(/^\{.*?\}/)||"");
|
||||
bond = bond.substr(1,bond.length-2);
|
||||
this.tex += "{"+(this.Bonds[bond]||"\\text{??}")+"}";
|
||||
}
|
||||
else if (match === "{") {this.tex += "{\\{"}
|
||||
else if (match === "}") {this.tex += "\\}}"; this.atom = true}
|
||||
else {this.tex += c+match}
|
||||
},
|
||||
|
||||
//
|
||||
// Ignore spaces
|
||||
//
|
||||
ParseSpace: function (c) {this.FinishAtom(); this.i++},
|
||||
|
||||
//
|
||||
// Pass anything else on verbatim
|
||||
//
|
||||
ParseOther: function (c) {this.FinishAtom(); this.tex += c; this.i++},
|
||||
|
||||
//
|
||||
// Process an arrow (looking for brackets for above and below)
|
||||
//
|
||||
AddArrow: function (arrow) {
|
||||
var c = this.Match(/^[CT]\[/);
|
||||
if (c) {this.i--; c = c.charAt(0)}
|
||||
var above = this.GetBracket(c), below = this.GetBracket(c);
|
||||
arrow = this.Arrows[arrow];
|
||||
if (above || below) {
|
||||
if (below) {arrow += "["+below+"]"}
|
||||
arrow += "{"+above+"}";
|
||||
arrow = "\\mathrel{\\x"+arrow+"}";
|
||||
} else {
|
||||
arrow = "\\long"+arrow+" ";
|
||||
}
|
||||
this.tex += arrow;
|
||||
},
|
||||
|
||||
//
|
||||
// Handle the super and subscripts for an atom
|
||||
//
|
||||
FinishAtom: function () {
|
||||
if (this.sup || this.sub) {
|
||||
if (this.sup && this.sub && !this.atom) {
|
||||
//
|
||||
// right-justify super- and subscripts when they are before the atom
|
||||
//
|
||||
var n = Math.abs(this.sup.length-this.sub.length);
|
||||
if (n) {
|
||||
var zeros = "0000000000".substr(0,n);
|
||||
var script = (this.sup.length > this.sub.length ? "sub" : "sup");
|
||||
this[script] = "\\phantom{"+zeros+"}" + this[script];
|
||||
}
|
||||
}
|
||||
if (!this.sup) {this.sup = "\\Space{0pt}{0pt}{.2em}"} // forces subscripts to align properly
|
||||
this.tex += "^{"+this.sup+"}_{"+this.sub+"}";
|
||||
this.sup = this.sub = "";
|
||||
}
|
||||
this.atom = false;
|
||||
},
|
||||
|
||||
//
|
||||
// Find a bracket group and handle C and T prefixes
|
||||
//
|
||||
GetBracket: function (c) {
|
||||
if (this.string.charAt(this.i) !== "[") {return ""}
|
||||
this.i++; var bracket = this.Find("]");
|
||||
if (c === "C") {bracket = "\\ce{"+bracket+"}"} else
|
||||
if (c === "T") {
|
||||
if (!bracket.match(/^\{.*\}$/)) {bracket = "{"+bracket+"}"}
|
||||
bracket = "\\text"+bracket;
|
||||
};
|
||||
return bracket;
|
||||
},
|
||||
|
||||
//
|
||||
// Check if the string matches a regular expression
|
||||
// and move past it if so, returning the match
|
||||
//
|
||||
Match: function (regex) {
|
||||
var match = regex.exec(this.string.substr(this.i));
|
||||
if (match) {match = match[0]; this.i += match.length}
|
||||
return match;
|
||||
},
|
||||
|
||||
//
|
||||
// Find a particular character, skipping over braced groups
|
||||
//
|
||||
Find: function (c) {
|
||||
var m = this.string.length, i = this.i, braces = 0;
|
||||
while (this.i < m) {
|
||||
var C = this.string.charAt(this.i++);
|
||||
if (C === c && braces === 0) {return this.string.substr(i,this.i-i-1)}
|
||||
if (C === "{") {braces++} else
|
||||
if (C === "}") {
|
||||
if (braces) {braces--}
|
||||
else {TEX.Error("Extra close brace or missing open brace")}
|
||||
}
|
||||
}
|
||||
if (braces) {TEX.Error("Missing close brace")};
|
||||
TEX.Error("Can't find closing "+c);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
//
|
||||
// Set up the macros for chemistry
|
||||
//
|
||||
MACROS.ce = 'CE';
|
||||
MACROS.cf = 'CE';
|
||||
MACROS.cee = 'CE';
|
||||
|
||||
//
|
||||
// Include some missing arrows (some are hacks)
|
||||
//
|
||||
MACROS.xleftrightarrow = ['xArrow',0x2194,6,6];
|
||||
MACROS.xrightleftharpoons = ['xArrow',0x21CC,5,7]; // FIXME: doesn't stretch in HTML-CSS output
|
||||
MACROS.xRightleftharpoons = ['xArrow',0x21CC,5,7]; // FIXME: how should this be handled?
|
||||
|
||||
// FIXME: These don't work well in FF NativeMML mode
|
||||
MACROS.longrightleftharpoons = ["Macro","\\stackrel{\\textstyle{{-}\\!\\!{\\rightharpoonup}}}{\\smash{{\\leftharpoondown}\\!\\!{-}}}"];
|
||||
MACROS.longRightleftharpoons = ["Macro","\\stackrel{\\textstyle{-}\\!\\!{\\rightharpoonup}}{\\small\\smash\\leftharpoondown}"];
|
||||
|
||||
//
|
||||
// Needed for \bond for the ~ forms
|
||||
//
|
||||
MACROS.tripledash = ["Macro","\\raise3mu{\\tiny\\text{-}\\kern2mu\\text{-}\\kern2mu\\text{-}}"];
|
||||
TEX.Definitions.environment.CEstack = ['Array',null,null,null,'r',null,"0.001em",'T',1]
|
||||
|
||||
//
|
||||
// Add \hyphen used in some mhchem examples
|
||||
//
|
||||
MACROS.hyphen = ["Macro","\\text{-}"];
|
||||
|
||||
TEX.Parse.Augment({
|
||||
|
||||
//
|
||||
// Implements \ce and friends
|
||||
//
|
||||
CE: function (name) {
|
||||
var arg = this.GetArgument(name);
|
||||
var tex = CE(arg).Parse();
|
||||
this.string = tex + this.string.substr(this.i); this.i = 0;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
//
|
||||
// Indicate that the extension is ready
|
||||
//
|
||||
MathJax.Hub.Startup.signal.Post("TeX mhchem Ready");
|
||||
|
||||
});
|
||||
|
||||
MathJax.Ajax.loadComplete("[MathJax]/extensions/TeX/mhchem.js");
|
237
resources/viewer/mathjax/extensions/TeX/newcommand.js
Normal file
@ -0,0 +1,237 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* MathJax/extensions/TeX/newcommand.js
|
||||
*
|
||||
* Implements the \newcommand, \newenvironment and \def
|
||||
* macros, and is loaded automatically when needed.
|
||||
*
|
||||
* ---------------------------------------------------------------------
|
||||
*
|
||||
* Copyright (c) 2009-2012 Design Science, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
MathJax.Extension["TeX/newcommand"] = {
|
||||
version: "2.0"
|
||||
};
|
||||
|
||||
MathJax.Hub.Register.StartupHook("TeX Jax Ready",function () {
|
||||
|
||||
var TEX = MathJax.InputJax.TeX;
|
||||
var TEXDEF = TEX.Definitions;
|
||||
|
||||
TEXDEF.Add({
|
||||
macros: {
|
||||
newcommand: 'NewCommand',
|
||||
renewcommand: 'NewCommand',
|
||||
newenvironment: 'NewEnvironment',
|
||||
renewenvironment: 'NewEnvironment',
|
||||
def: 'MacroDef',
|
||||
let: 'Let'
|
||||
}
|
||||
},null,true);
|
||||
|
||||
TEX.Parse.Augment({
|
||||
|
||||
/*
|
||||
* Implement \newcommand{\name}[n]{...}
|
||||
*/
|
||||
NewCommand: function (name) {
|
||||
var cs = this.trimSpaces(this.GetArgument(name)),
|
||||
n = this.GetBrackets(name),
|
||||
opt = this.GetBrackets(name),
|
||||
def = this.GetArgument(name);
|
||||
if (cs.charAt(0) === "\\") {cs = cs.substr(1)}
|
||||
if (!cs.match(/^(.|[a-z]+)$/i)) {TEX.Error("Illegal control sequence name for "+name)}
|
||||
if (n) {
|
||||
n = this.trimSpaces(n);
|
||||
if (!n.match(/^[0-9]+$/)) {TEX.Error("Illegal number of parameters specified in "+name)}
|
||||
}
|
||||
this.setDef(cs,['Macro',def,n,opt]);
|
||||
},
|
||||
|
||||
/*
|
||||
* Implement \newenvironment{name}[n][default]{begincmd}{endcmd}
|
||||
*/
|
||||
NewEnvironment: function (name) {
|
||||
var env = this.trimSpaces(this.GetArgument(name)),
|
||||
n = this.GetBrackets(name),
|
||||
bdef = this.GetArgument(name),
|
||||
edef = this.GetArgument(name);
|
||||
if (n) {
|
||||
n = this.trimSpaces(n);
|
||||
if (!n.match(/^[0-9]+$/)) {TEX.Error("Illegal number of parameters specified in "+name)}
|
||||
}
|
||||
this.setEnv(env,['BeginEnv','EndEnv',bdef,edef,n]);
|
||||
},
|
||||
|
||||
/*
|
||||
* Implement \def command
|
||||
*/
|
||||
MacroDef: function (name) {
|
||||
var cs = this.GetCSname(name),
|
||||
params = this.GetTemplate(name,"\\"+cs),
|
||||
def = this.GetArgument(name);
|
||||
if (!(params instanceof Array)) {this.setDef(cs,['Macro',def,params])}
|
||||
else {this.setDef(cs,['MacroWithTemplate',def].concat(params))}
|
||||
},
|
||||
|
||||
/*
|
||||
* Implements the \let command
|
||||
*/
|
||||
Let: function (name) {
|
||||
var cs = this.GetCSname(name), macro;
|
||||
var c = this.GetNext(); if (c === "=") {this.i++; c = this.GetNext()}
|
||||
//
|
||||
// All \let commands create entries in the macros array, but we
|
||||
// have to look in the various mathchar and delimiter arrays if
|
||||
// the source isn't a macro already, and attach the data to a
|
||||
// macro with the proper routine to process it.
|
||||
//
|
||||
// A command of the form \let\cs=char produces a macro equivalent
|
||||
// to \def\cs{char}, which is as close as MathJax can get for this.
|
||||
// So \let\bgroup={ is possible, but doesn't work as it does in TeX.
|
||||
//
|
||||
if (c === "\\") {
|
||||
name = this.GetCSname(name);
|
||||
macro = this.csFindMacro(name);
|
||||
if (!macro) {
|
||||
if (TEXDEF.mathchar0mi[name]) {macro = ["csMathchar0mi",TEXDEF.mathchar0mi[name]]} else
|
||||
if (TEXDEF.mathchar0mo[name]) {macro = ["csMathchar0mo",TEXDEF.mathchar0mo[name]]} else
|
||||
if (TEXDEF.mathchar7[name]) {macro = ["csMathchar7",TEXDEF.mathchar7[name]]} else
|
||||
if (TEXDEF.delimiter["\\"+name] != null) {macro = ["csDelimiter",TEXDEF.delimiter["\\"+name]]}
|
||||
}
|
||||
} else {macro = ["Macro",c]; this.i++}
|
||||
this.setDef(cs,macro);
|
||||
},
|
||||
|
||||
/*
|
||||
* Routines to set the macro and environment definitions
|
||||
* (overridden by begingroup to make localized versions)
|
||||
*/
|
||||
setDef: function (name,value) {value.isUser = true; TEXDEF.macros[name] = value},
|
||||
setEnv: function (name,value) {value.isUser = true; TEXDEF.environment[name] = value},
|
||||
|
||||
/*
|
||||
* Get a CS name or give an error
|
||||
*/
|
||||
GetCSname: function (cmd) {
|
||||
var c = this.GetNext();
|
||||
if (c !== "\\") {TEX.Error("\\ must be followed by a control sequence")}
|
||||
var cs = this.trimSpaces(this.GetArgument(cmd));
|
||||
return cs.substr(1);
|
||||
},
|
||||
|
||||
/*
|
||||
* Get a \def parameter template
|
||||
*/
|
||||
GetTemplate: function (cmd,cs) {
|
||||
var c, params = [], n = 0;
|
||||
c = this.GetNext(); var i = this.i;
|
||||
while (this.i < this.string.length) {
|
||||
c = this.GetNext();
|
||||
if (c === '#') {
|
||||
if (i !== this.i) {params[n] = this.string.substr(i,this.i-i)}
|
||||
c = this.string.charAt(++this.i);
|
||||
if (!c.match(/^[1-9]$/)) {TEX.Error("Illegal use of # in template for "+cs)}
|
||||
if (parseInt(c) != ++n) {TEX.Error("Parameters for "+cs+" must be numbered sequentially")}
|
||||
i = this.i+1;
|
||||
} else if (c === '{') {
|
||||
if (i !== this.i) {params[n] = this.string.substr(i,this.i-i)}
|
||||
if (params.length > 0) {return [n,params]} else {return n}
|
||||
}
|
||||
this.i++;
|
||||
}
|
||||
TEX.Error("Missing replacement string for definition of "+cmd);
|
||||
},
|
||||
|
||||
/*
|
||||
* Process a macro with a parameter template
|
||||
*/
|
||||
MacroWithTemplate: function (name,text,n,params) {
|
||||
if (n) {
|
||||
var args = []; this.GetNext();
|
||||
if (params[0] && !this.MatchParam(params[0]))
|
||||
{TEX.Error("Use of "+name+" doesn't match its definition")}
|
||||
for (var i = 0; i < n; i++) {args.push(this.GetParameter(name,params[i+1]))}
|
||||
text = this.SubstituteArgs(args,text);
|
||||
}
|
||||
this.string = this.AddArgs(text,this.string.slice(this.i));
|
||||
this.i = 0;
|
||||
if (++this.macroCount > TEX.config.MAXMACROS)
|
||||
{TEX.Error("MathJax maximum macro substitution count exceeded; is there a recursive macro call?")}
|
||||
},
|
||||
|
||||
/*
|
||||
* Process a user-defined environment
|
||||
*/
|
||||
BeginEnv: function (begin,bdef,edef,n) {
|
||||
if (n) {
|
||||
var args = [];
|
||||
for (var i = 0; i < n; i++) {args.push(this.GetArgument("\\begin{"+name+"}"))}
|
||||
bdef = this.SubstituteArgs(args,bdef);
|
||||
edef = this.SubstituteArgs(args,edef);
|
||||
}
|
||||
begin.edef = edef;
|
||||
this.string = this.AddArgs(bdef,this.string.slice(this.i)); this.i = 0;
|
||||
return begin;
|
||||
},
|
||||
EndEnv: function (begin,row) {
|
||||
this.string = this.AddArgs(begin.edef,this.string.slice(this.i)); this.i = 0
|
||||
return row;
|
||||
},
|
||||
|
||||
/*
|
||||
* Find a single parameter delimited by a trailing template
|
||||
*/
|
||||
GetParameter: function (name,param) {
|
||||
if (param == null) {return this.GetArgument(name)}
|
||||
var i = this.i, j = 0, hasBraces = 0;
|
||||
while (this.i < this.string.length) {
|
||||
if (this.string.charAt(this.i) === '{') {
|
||||
if (this.i === i) {hasBraces = 1}
|
||||
this.GetArgument(name); j = this.i - i;
|
||||
} else if (this.MatchParam(param)) {
|
||||
if (hasBraces) {i++; j -= 2}
|
||||
return this.string.substr(i,j);
|
||||
} else {
|
||||
this.i++; j++; hasBraces = 0;
|
||||
}
|
||||
}
|
||||
TEX.Error("Runaway argument for "+name+"?");
|
||||
},
|
||||
|
||||
/*
|
||||
* Check if a template is at the current location.
|
||||
* (The match must be exact, with no spacing differences. TeX is
|
||||
* a little more forgiving than this about spaces after macro names)
|
||||
*/
|
||||
MatchParam: function (param) {
|
||||
if (this.string.substr(this.i,param.length) !== param) {return 0}
|
||||
this.i += param.length;
|
||||
return 1;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
TEX.Environment = function (name) {
|
||||
TEXDEF.environment[name] = ['BeginEnv','EndEnv'].concat([].slice.call(arguments,1));
|
||||
TEXDEF.environment[name].isUser = true;
|
||||
}
|
||||
|
||||
MathJax.Hub.Startup.signal.Post("TeX newcommand Ready");
|
||||
|
||||
});
|
||||
|
||||
MathJax.Ajax.loadComplete("[MathJax]/extensions/TeX/newcommand.js");
|
306
resources/viewer/mathjax/extensions/TeX/noErrors.js
Normal file
@ -0,0 +1,306 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* MathJax/extensions/TeX/noErrors.js
|
||||
*
|
||||
* Prevents the TeX error messages from being displayed and shows the
|
||||
* original TeX code instead. You can configure whether the dollar signs
|
||||
* are shown or not for in-line math, and whether to put all the TeX on
|
||||
* one line or use multiple-lines.
|
||||
*
|
||||
* To configure this extension, use
|
||||
*
|
||||
* MathJax.Hub.Config({
|
||||
* TeX: {
|
||||
* noErrors: {
|
||||
* inlineDelimiters: ["",""], // or ["$","$"] or ["\\(","\\)"]
|
||||
* multiLine: true, // false for TeX on all one line
|
||||
* style: {
|
||||
* "font-size": "90%",
|
||||
* "text-align": "left",
|
||||
* "color": "black",
|
||||
* "padding": "1px 3px",
|
||||
* "border": "1px solid"
|
||||
* // add any additional CSS styles that you want
|
||||
* // (be sure there is no extra comma at the end of the last item)
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* Display-style math is always shown in multi-line format, and without
|
||||
* delimiters, as it will already be set off in its own centered
|
||||
* paragraph, like standard display mathematics.
|
||||
*
|
||||
* The default settings place the invalid TeX in a multi-line box with a
|
||||
* black border. If you want it to look as though the TeX is just part of
|
||||
* the paragraph, use
|
||||
*
|
||||
* MathJax.Hub.Config({
|
||||
* TeX: {
|
||||
* noErrors: {
|
||||
* inlineDelimiters: ["$","$"], // or ["",""] or ["\\(","\\)"]
|
||||
* multiLine: false,
|
||||
* style: {
|
||||
* "font-size": "normal",
|
||||
* "border": ""
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* You may also wish to set the font family, as the default is "serif"
|
||||
*
|
||||
* ---------------------------------------------------------------------
|
||||
*
|
||||
* Copyright (c) 2009-2012 Design Science, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
(function (HUB,HTML) {
|
||||
var VERSION = "2.0";
|
||||
|
||||
var CONFIG = HUB.CombineConfig("TeX.noErrors",{
|
||||
disabled: false, // set to true to return to original error messages
|
||||
multiLine: true,
|
||||
inlineDelimiters: ["",""], // or use ["$","$"] or ["\\(","\\)"]
|
||||
style: {
|
||||
"font-size": "90%",
|
||||
"text-align": "left",
|
||||
"color": "black",
|
||||
"padding": "1px 3px",
|
||||
"border": "1px solid"
|
||||
}
|
||||
});
|
||||
|
||||
var NBSP = "\u00A0";
|
||||
|
||||
//
|
||||
// The configuration defaults, augmented by the user settings
|
||||
//
|
||||
MathJax.Extension["TeX/noErrors"] = {
|
||||
version: VERSION,
|
||||
config: CONFIG
|
||||
};
|
||||
|
||||
HUB.Register.StartupHook("TeX Jax Ready",function () {
|
||||
var FORMAT = MathJax.InputJax.TeX.formatError;
|
||||
|
||||
MathJax.InputJax.TeX.Augment({
|
||||
//
|
||||
// Make error messages be the original TeX code
|
||||
// Mark them as errors and multi-line or not, and for
|
||||
// multi-line TeX, make spaces non-breakable (to get formatting right)
|
||||
//
|
||||
formatError: function (err,math,displaystyle,script) {
|
||||
if (CONFIG.disabled) {return FORMAT.apply(this,arguments)}
|
||||
var message = err.message.replace(/\n.*/,"");
|
||||
HUB.signal.Post(["TeX Jax - parse error",message,math,displaystyle,script]);
|
||||
var delim = CONFIG.inlineDelimiters;
|
||||
var multiLine = (displaystyle || CONFIG.multiLine);
|
||||
if (!displaystyle) {math = delim[0] + math + delim[1]}
|
||||
if (multiLine) {math = math.replace(/ /g,NBSP)} else {math = math.replace(/\n/g," ")}
|
||||
return MathJax.ElementJax.mml.merror(math).With({isError:true, multiLine: multiLine});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/*******************************************************************
|
||||
*
|
||||
* Fix HTML-CSS output
|
||||
*/
|
||||
|
||||
HUB.Register.StartupHook("HTML-CSS Jax Config",function () {
|
||||
HUB.Config({
|
||||
"HTML-CSS": {
|
||||
styles: {
|
||||
".MathJax .noError": HUB.Insert({
|
||||
"vertical-align": (HUB.Browser.isMSIE && CONFIG.multiLine ? "-2px" : "")
|
||||
},CONFIG.style)
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
HUB.Register.StartupHook("HTML-CSS Jax Ready",function () {
|
||||
var MML = MathJax.ElementJax.mml;
|
||||
var HTMLCSS = MathJax.OutputJax["HTML-CSS"];
|
||||
|
||||
var MATH = MML.math.prototype.toHTML,
|
||||
MERROR = MML.merror.prototype.toHTML;
|
||||
|
||||
//
|
||||
// Override math toHTML routine so that error messages
|
||||
// don't have the clipping and other unneeded overhead
|
||||
//
|
||||
MML.math.Augment({
|
||||
toHTML: function (span,node) {
|
||||
var data = this.data[0];
|
||||
if (data && data.data[0] && data.data[0].isError) {
|
||||
span.style.fontSize = "";
|
||||
span = this.HTMLcreateSpan(span);
|
||||
span.bbox = data.data[0].toHTML(span).bbox;
|
||||
} else {
|
||||
span = MATH.call(this,span,node);
|
||||
}
|
||||
return span;
|
||||
}
|
||||
});
|
||||
|
||||
//
|
||||
// Override merror toHTML routine so that it puts out the
|
||||
// TeX code in an inline-block with line breaks as in the original
|
||||
//
|
||||
MML.merror.Augment({
|
||||
toHTML: function (span) {
|
||||
if (!this.isError) {return MERROR.call(this,span)}
|
||||
span = this.HTMLcreateSpan(span); span.className = "noError"
|
||||
if (this.multiLine) {span.style.display = "inline-block"}
|
||||
var text = this.data[0].data[0].data.join("").split(/\n/);
|
||||
for (var i = 0, m = text.length; i < m; i++) {
|
||||
HTMLCSS.addText(span,text[i]);
|
||||
if (i !== m-1) {HTMLCSS.addElement(span,"br",{isMathJax:true})}
|
||||
}
|
||||
var HD = HTMLCSS.getHD(span.parentNode), W = HTMLCSS.getW(span.parentNode);
|
||||
if (m > 1) {
|
||||
var H = (HD.h + HD.d)/2, x = HTMLCSS.TeX.x_height/2;
|
||||
span.parentNode.style.verticalAlign = HTMLCSS.Em(HD.d+(x-H));
|
||||
HD.h = x + H; HD.d = H - x;
|
||||
}
|
||||
span.bbox = {h: HD.h, d: HD.d, w: W, lw: 0, rw: W};
|
||||
return span;
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
/*******************************************************************
|
||||
*
|
||||
* Fix SVG output
|
||||
*/
|
||||
|
||||
HUB.Register.StartupHook("SVG Jax Config",function () {
|
||||
HUB.Config({
|
||||
"SVG": {
|
||||
styles: {
|
||||
".MathJax_SVG .noError": HUB.Insert({
|
||||
"vertical-align": (HUB.Browser.isMSIE && CONFIG.multiLine ? "-2px" : "")
|
||||
},CONFIG.style)
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
HUB.Register.StartupHook("SVG Jax Ready",function () {
|
||||
var MML = MathJax.ElementJax.mml;
|
||||
|
||||
var MATH = MML.math.prototype.toSVG,
|
||||
MERROR = MML.merror.prototype.toSVG;
|
||||
|
||||
//
|
||||
// Override math toSVG routine so that error messages
|
||||
// don't have the clipping and other unneeded overhead
|
||||
//
|
||||
MML.math.Augment({
|
||||
toSVG: function (span,node) {
|
||||
var data = this.data[0];
|
||||
if (data && data.data[0] && data.data[0].isError)
|
||||
{span = data.data[0].toSVG(span)} else {span = MATH.call(this,span,node)}
|
||||
return span;
|
||||
}
|
||||
});
|
||||
|
||||
//
|
||||
// Override merror toSVG routine so that it puts out the
|
||||
// TeX code in an inline-block with line breaks as in the original
|
||||
//
|
||||
MML.merror.Augment({
|
||||
toSVG: function (span) {
|
||||
if (!this.isError || this.Parent().type !== "math") {return MERROR.call(this,span)}
|
||||
span = HTML.addElement(span,"span",{className: "noError", isMathJax:true});
|
||||
if (this.multiLine) {span.style.display = "inline-block"}
|
||||
var text = this.data[0].data[0].data.join("").split(/\n/);
|
||||
for (var i = 0, m = text.length; i < m; i++) {
|
||||
HTML.addText(span,text[i]);
|
||||
if (i !== m-1) {HTML.addElement(span,"br",{isMathJax:true})}
|
||||
}
|
||||
if (m > 1) {
|
||||
var H = span.offsetHeight/2;
|
||||
span.style.verticalAlign = (-H+(H/m))+"px";
|
||||
}
|
||||
return span;
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
/*******************************************************************
|
||||
*
|
||||
* Fix NativeMML output
|
||||
*/
|
||||
|
||||
HUB.Register.StartupHook("NativeMML Jax Ready",function () {
|
||||
var MML = MathJax.ElementJax.mml;
|
||||
var CONFIG = MathJax.Extension["TeX/noErrors"].config;
|
||||
|
||||
var MATH = MML.math.prototype.toNativeMML,
|
||||
MERROR = MML.merror.prototype.toNativeMML;
|
||||
|
||||
//
|
||||
// Override math toNativeMML routine so that error messages
|
||||
// don't get placed inside math tags.
|
||||
//
|
||||
MML.math.Augment({
|
||||
toNativeMML: function (span) {
|
||||
var data = this.data[0];
|
||||
if (data && data.data[0] && data.data[0].isError)
|
||||
{span = data.data[0].toNativeMML(span)} else {span = MATH.call(this,span)}
|
||||
return span;
|
||||
}
|
||||
});
|
||||
|
||||
//
|
||||
// Override merror toNativeMML routine so that it puts out the
|
||||
// TeX code in an inline-block with line breaks as in the original
|
||||
//
|
||||
MML.merror.Augment({
|
||||
toNativeMML: function (span) {
|
||||
if (!this.isError) {return MERROR.call(this,span)}
|
||||
span = span.appendChild(document.createElement("span"));
|
||||
var text = this.data[0].data[0].data.join("").split(/\n/);
|
||||
for (var i = 0, m = text.length; i < m; i++) {
|
||||
span.appendChild(document.createTextNode(text[i]));
|
||||
if (i !== m-1) {span.appendChild(document.createElement("br"))}
|
||||
}
|
||||
if (this.multiLine) {
|
||||
span.style.display = "inline-block";
|
||||
if (m > 1) {span.style.verticalAlign = "middle"}
|
||||
}
|
||||
for (var id in CONFIG.style) {if (CONFIG.style.hasOwnProperty(id)) {
|
||||
var ID = id.replace(/-./g,function (c) {return c.charAt(1).toUpperCase()});
|
||||
span.style[ID] = CONFIG.style[id];
|
||||
}}
|
||||
return span;
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
/*******************************************************************/
|
||||
|
||||
HUB.Startup.signal.Post("TeX noErrors Ready");
|
||||
|
||||
})(MathJax.Hub,MathJax.HTML);
|
||||
|
||||
|
||||
MathJax.Ajax.loadComplete("[MathJax]/extensions/TeX/noErrors.js");
|
69
resources/viewer/mathjax/extensions/TeX/noUndefined.js
Normal file
@ -0,0 +1,69 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* MathJax/extensions/TeX/noUndefined.js
|
||||
*
|
||||
* This causes undefined control sequences to be shown as their macro
|
||||
* names rather than producing an error message. So $X_{\xxx}$ would
|
||||
* display as an X with a subscript consiting of the text "\xxx".
|
||||
*
|
||||
* To configure this extension, use for example
|
||||
*
|
||||
* MathJax.Hub.Config({
|
||||
* TeX: {
|
||||
* noUndefined: {
|
||||
* attributes: {
|
||||
* mathcolor: "red",
|
||||
* mathbackground: "#FFEEEE",
|
||||
* mathsize: "90%"
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* ---------------------------------------------------------------------
|
||||
*
|
||||
* Copyright (c) 2010-2012 Design Science, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
//
|
||||
// The configuration defaults, augmented by the user settings
|
||||
//
|
||||
MathJax.Extension["TeX/noUndefined"] = {
|
||||
version: "2.0",
|
||||
config: MathJax.Hub.CombineConfig("TeX.noUndefined",{
|
||||
disabled: false, // set to true to return to original error messages
|
||||
attributes: {
|
||||
mathcolor: "red"
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
MathJax.Hub.Register.StartupHook("TeX Jax Ready",function () {
|
||||
var CONFIG = MathJax.Extension["TeX/noUndefined"].config;
|
||||
var MML = MathJax.ElementJax.mml;
|
||||
var UNDEFINED = MathJax.InputJax.TeX.Parse.prototype.csUndefined;
|
||||
|
||||
MathJax.InputJax.TeX.Parse.Augment({
|
||||
csUndefined: function (name) {
|
||||
if (CONFIG.disabled) {return UNDEFINED.apply(this,arguments)}
|
||||
MathJax.Hub.signal.Post(["TeX Jax - undefined control sequence",name]);
|
||||
this.Push(MML.mtext(name).With(CONFIG.attributes));
|
||||
}
|
||||
});
|
||||
|
||||
MathJax.Hub.Startup.signal.Post("TeX noUndefined Ready");
|
||||
});
|
||||
|
||||
MathJax.Ajax.loadComplete("[MathJax]/extensions/TeX/noUndefined.js");
|
164
resources/viewer/mathjax/extensions/TeX/unicode.js
Normal file
@ -0,0 +1,164 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* MathJax/extensions/TeX/unicode.js
|
||||
*
|
||||
* Implements the \unicode extension to TeX to allow arbitrary unicode
|
||||
* code points to be entered into the TeX file. You can specify
|
||||
* the height and depth of the character (the width is determined by
|
||||
* the browser), and the default font from which to take the character.
|
||||
*
|
||||
* Examples:
|
||||
* \unicode{65} % the character 'A'
|
||||
* \unicode{x41} % the character 'A'
|
||||
* \unicode[.55,0.05]{x22D6} % less-than with dot, with height .55 and depth 0.05
|
||||
* \unicode[.55,0.05][Geramond]{x22D6} % same taken from Geramond font
|
||||
* \unicode[Garamond]{x22D6} % same, but with default height, depth of .8,.2
|
||||
*
|
||||
* Once a size and font are provided for a given code point, they need
|
||||
* not be specified again in subsequent \unicode calls for that character.
|
||||
* Note that a font list can be given, but Internet Explorer has a buggy
|
||||
* implementation of font-family where it only looks in the first
|
||||
* available font and if the glyph is not in that, it does not look at
|
||||
* later fonts, but goes directly to the default font as set in the
|
||||
* Internet-Options/Font panel. For this reason, the default font list is
|
||||
* "STIXGeneral,'Arial Unicode MS'", so if the user has STIX fonts, the
|
||||
* symbol will be taken from that (almost all the symbols are in
|
||||
* STIXGeneral), otherwise Arial Unicode MS is tried.
|
||||
*
|
||||
* To configure the default font list, use
|
||||
*
|
||||
* MathJax.Hub.Config({
|
||||
* TeX: {
|
||||
* unicode: {
|
||||
* fonts: "STIXGeneral,'Arial Unicode MS'"
|
||||
* }
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* The result of \unicode will have TeX class ORD (i.e., it will act like a
|
||||
* variable). Use \mathbin, \mathrel, etc, to specify a different class.
|
||||
*
|
||||
* ---------------------------------------------------------------------
|
||||
*
|
||||
* Copyright (c) 2009-2012 Design Science, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
//
|
||||
// The configuration defaults, augmented by the user settings
|
||||
//
|
||||
MathJax.Extension["TeX/unicode"] = {
|
||||
version: "2.0",
|
||||
unicode: {},
|
||||
config: MathJax.Hub.CombineConfig("TeX.unicode",{
|
||||
fonts: "STIXGeneral,'Arial Unicode MS'"
|
||||
})
|
||||
};
|
||||
|
||||
MathJax.Hub.Register.StartupHook("TeX Jax Ready",function () {
|
||||
var TEX = MathJax.InputJax.TeX;
|
||||
var MML = MathJax.ElementJax.mml;
|
||||
var UNICODE = MathJax.Extension["TeX/unicode"].unicode;
|
||||
|
||||
//
|
||||
// Add \unicode macro
|
||||
//
|
||||
TEX.Definitions.macros.unicode = 'Unicode';
|
||||
//
|
||||
// Implementation of \unicode in parser
|
||||
//
|
||||
TEX.Parse.Augment({
|
||||
Unicode: function(name) {
|
||||
var HD = this.GetBrackets(name), font;
|
||||
if (HD) {
|
||||
if (HD.replace(/ /g,"").match(/^(\d+(\.\d*)?|\.\d+),(\d+(\.\d*)?|\.\d+)$/))
|
||||
{HD = HD.replace(/ /g,"").split(/,/); font = this.GetBrackets(name)}
|
||||
else {font = HD; HD = null}
|
||||
}
|
||||
var n = this.trimSpaces(this.GetArgument(name)),
|
||||
N = parseInt(n.match(/^x/) ? "0"+n : n);
|
||||
if (!UNICODE[N]) {UNICODE[N] = [800,200,font,N]}
|
||||
else if (!font) {font = UNICODE[N][2]}
|
||||
if (HD) {
|
||||
UNICODE[N][0] = Math.floor(HD[0]*1000);
|
||||
UNICODE[N][1] = Math.floor(HD[1]*1000);
|
||||
}
|
||||
var variant = this.stack.env.font, def = {};
|
||||
if (font) {
|
||||
UNICODE[N][2] = def.fontfamily = font.replace(/"/g,"'");
|
||||
if (variant) {
|
||||
if (variant.match(/bold/)) {def.fontweight = "bold"}
|
||||
if (variant.match(/italic|-mathit/)) {def.fontstyle = "italic"}
|
||||
}
|
||||
} else if (variant) {def.mathvariant = variant}
|
||||
def.unicode = [].concat(UNICODE[N]); // make a copy
|
||||
this.Push(MML.mtext(MML.entity("#"+n)).With(def));
|
||||
}
|
||||
});
|
||||
|
||||
MathJax.Hub.Startup.signal.Post("TeX unicode Ready");
|
||||
|
||||
});
|
||||
|
||||
MathJax.Hub.Register.StartupHook("HTML-CSS Jax Ready",function () {
|
||||
var MML = MathJax.ElementJax.mml;
|
||||
var FONTS = MathJax.Extension["TeX/unicode"].config.fonts;
|
||||
|
||||
//
|
||||
// Override getVariant to make one that includes the font and size
|
||||
//
|
||||
var GETVARIANT = MML.mbase.prototype.HTMLgetVariant;
|
||||
MML.mbase.Augment({
|
||||
HTMLgetVariant: function () {
|
||||
var variant = GETVARIANT.call(this);
|
||||
if (variant.unicode) {delete variant.unicode; delete variant.FONTS} // clear font cache in case of restart
|
||||
if (!this.unicode) {return variant}
|
||||
variant.unicode = true;
|
||||
if (!variant.defaultFont) {
|
||||
variant = MathJax.Hub.Insert({},variant); // make a copy
|
||||
variant.defaultFont = {family:FONTS};
|
||||
}
|
||||
var family = this.unicode[2]; if (family) {family += ","+FONTS} else {family = FONTS}
|
||||
variant.defaultFont[this.unicode[3]] = [
|
||||
this.unicode[0],this.unicode[1],500,0,500,
|
||||
{isUnknown:true, isUnicode:true, font:family}
|
||||
];
|
||||
return variant;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
MathJax.Hub.Register.StartupHook("SVG Jax Ready",function () {
|
||||
var MML = MathJax.ElementJax.mml;
|
||||
var FONTS = MathJax.Extension["TeX/unicode"].config.fonts;
|
||||
|
||||
//
|
||||
// Override getVariant to make one that includes the font and size
|
||||
//
|
||||
var GETVARIANT = MML.mbase.prototype.SVGgetVariant;
|
||||
MML.mbase.Augment({
|
||||
SVGgetVariant: function () {
|
||||
var variant = GETVARIANT.call(this);
|
||||
if (variant.unicode) {delete variant.unicode; delete variant.FONTS} // clear font cache in case of restart
|
||||
if (!this.unicode) {return variant}
|
||||
variant.unicode = true;
|
||||
if (!variant.forceFamily) {variant = MathJax.Hub.Insert({},variant)} // make a copy
|
||||
variant.defaultFamily = FONTS; variant.noRemap = true;
|
||||
variant.h = this.unicode[0]; variant.d = this.unicode[1];
|
||||
return variant;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
MathJax.Ajax.loadComplete("[MathJax]/extensions/TeX/unicode.js");
|
58
resources/viewer/mathjax/extensions/TeX/verb.js
Normal file
@ -0,0 +1,58 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* MathJax/extensions/TeX/verb.js
|
||||
*
|
||||
* Implements the \verb|...| command for including text verbatim
|
||||
* (with no processing of macros or special characters).
|
||||
*
|
||||
* ---------------------------------------------------------------------
|
||||
*
|
||||
* Copyright (c) 2009-2012 Design Science, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
MathJax.Extension["TeX/verb"] = {
|
||||
version: "2.0"
|
||||
};
|
||||
|
||||
MathJax.Hub.Register.StartupHook("TeX Jax Ready",function () {
|
||||
|
||||
var MML = MathJax.ElementJax.mml;
|
||||
var TEX = MathJax.InputJax.TeX;
|
||||
var TEXDEF = TEX.Definitions;
|
||||
|
||||
TEXDEF.macros.verb = 'Verb';
|
||||
|
||||
TEX.Parse.Augment({
|
||||
|
||||
/*
|
||||
* Implement \verb|...|
|
||||
*/
|
||||
Verb: function (name) {
|
||||
var c = this.GetNext(); var start = ++this.i;
|
||||
if (c == "" ) {TEX.Error(name+" requires an argument")}
|
||||
while (this.i < this.string.length && this.string.charAt(this.i) != c) {this.i++}
|
||||
if (this.i == this.string.length)
|
||||
{TEX.Error("Can't find closing delimiter for "+name)}
|
||||
var text = this.string.slice(start,this.i); this.i++;
|
||||
this.Push(MML.mtext(text).With({mathvariant:MML.VARIANT.MONOSPACE}));
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
MathJax.Hub.Startup.signal.Post("TeX verb Ready");
|
||||
|
||||
});
|
||||
|
||||
MathJax.Ajax.loadComplete("[MathJax]/extensions/TeX/verb.js");
|