Index: main/trunk/greenstone2/bin/linux/xpdf-tools/ANNOUNCE
===================================================================
--- main/trunk/greenstone2/bin/linux/xpdf-tools/ANNOUNCE	(revision 32205)
+++ main/trunk/greenstone2/bin/linux/xpdf-tools/ANNOUNCE	(revision 32205)
@@ -0,0 +1,35 @@
+Subject: ANNOUNCE: Xpdf 4.00 - a PDF viewer and related tools
+
+Glyph & Cog, LLC is pleased to announce a new version of Xpdf, the
+open source Portable Document Format (PDF) viewer.  The Xpdf project
+also includes a PDF text extractor, PDF-to-PostScript converter, and
+various other utilities.
+
+*** The Xpdf web site has moved to www.xpdfreader.com ***
+
+Xpdf uses the Qt toolkit and runs on Linux and Windows.  The
+non-graphical components (pdftops, pdftotext, etc.) run on Linux,
+Unix, Windows, MacOSX, and pretty much any other system with a decent
+C++ compiler.
+
+Major changes:
+* Complete rewrite of the xpdf GUI, using Qt instead of Motif.
+* Multithreaded rasterization in the viewer - pages are now rasterized
+  in a separate thread so that scrolling and other UI activity is not
+  blocked.
+* Added a linear text selection mode.
+
+See the `CHANGES' file for a complete list of changes.
+
+Source (C++ and C) is available, and it should be fairly easy to
+compile for Linux and Windows.
+
+More information, source code, and precompiled binaries are on the
+xpdf web page:
+
+    http://www.xpdfreader.com/
+
+For information on commercial licensing and consulting, please see the
+Glyph & Cog web site:
+
+    http://www.glyphandcog.com/
Index: main/trunk/greenstone2/bin/linux/xpdf-tools/CHANGES
===================================================================
--- main/trunk/greenstone2/bin/linux/xpdf-tools/CHANGES	(revision 32205)
+++ main/trunk/greenstone2/bin/linux/xpdf-tools/CHANGES	(revision 32205)
@@ -0,0 +1,2456 @@
+0.2 (95-dec-12)
+---------------
+First public release.
+
+
+0.3 (96-jan-13)
+---------------
+LZW patent workaround.
+Implemented inline images.
+Fixed (mostly) disjoint polygon fills.
+Added remote server stuff.
+Added page number on command line.
+Fixed problem with font encodings which caused character misalignment.
+Fixed inverted CCITT decoding and inverted image mask drawing.
+Now compiles under gcc 2.7.x (ignore those stupid 'unused parameter'
+  warnings).
+Many minor bug fixes and optimizations.
+
+
+0.4 (96-apr-24)
+---------------
+Implemented DCT filter.
+Implemented PostScript output; wrote pdftops program.
+Implemented links.
+Implemented font rotation -- I was wrong: X11R6 servers *do* support
+  font rotation (by specifying a matrix in place of a size).
+Added bindings for Home/End, Page Up/Down, arrow keys.
+Added initialZoom resource and -z option.
+Added geometry resource and -g option.
+Fixed image size off-by-one bug.
+Fixed bug where page content is reference to an array of streams.
+Cleaned up uninitialized variables which were causing random problems
+  on various platforms.
+Manually skip whitespace before calling atoi() for startxref.
+Replaced calls to XrmCombineFileDatabase() with calls to
+  XrmGetFileDatabase() and XrmMergeDatabases() so it will work under
+  older versions of X.
+Fixed problem with reading multiple xref tables in updated PDF files.
+Check for encryption and print appropriate error message.
+Rudimentary dithering of images.
+Fixed bug in CCITTFax filter (pass mode followed by horizontal mode).
+Optimized drawImage() and drawImageMask().
+Changed several things to ease porting:
+  - changed '__inline' to 'inline' (to adhere to the ANSI standard)
+  - surrounded interface/implementation pragmas with #ifdef _GNUC__
+  - got rid of empty array initializer lists
+  - moved Operator type definition from Gfx.cc to Gfx.h
+  - renamed String, uint, etc.
+  - ability to uncompress to file (NO_POPEN and USE_GZIP flags)
+  - added definitions of XK_Page_Up/Down and XPointer for old versions
+    of X
+For VMS port:
+  - use correct Xdefaults name for VMS, get rid of ltkGetHomeDir()
+  - added '#include <stddef.h>' before all X includes
+  - renamed files with multiple periods in their names
+Fixed window resizing infinite oscillation bug.
+Fixed problem with string-type (as opposed to stream-type) indexed
+  color space lookup tables (which are used in inline images).
+If an X font is not found, try smaller and then larger sizes (this is
+  useful for old or broken X servers which can't scale bitmap fonts).
+Added -rgb (color cube size) option.
+Various minor bug fixes.
+
+
+0.5 (96-may-23)
+---------------
+Fixed bug in LTKWindow which broke the remote server mode.
+Fixed PostScript output:
+  - doesn't seg fault if file is unwritable.
+  - incorrect DSC comment - need colon in '%%Page:'.
+  - use 'imagemask' command for masks.
+  - output filters in the correct order.
+  - Stream::isBinary() checks the next-to-bottom, not top, stream.
+  - if page width > height, rotate it 90 degrees.
+  - if page is larger than paper size, scale it down.
+Set default MediaBox to 8.5" x 11" to deal with non-compliant PDF
+  files which don't specify a MediaBox.
+Added DEBUG_MEM stuff in gmem.c and gmempp.cc.
+Fixed memory leaks:
+  - LTKWindow didn't delete the LTKBox.
+  - LinkAction needs a virtual destructor.
+Use $(RANLIB) variable in goo/Makefile and ltk/Makefile.
+Allocate image data after calling XCreateImage, using
+  image->bytes_per_line -- works in 24-bit mode now.
+DCTStream rounds width of rowBuf lines up to the next multiple of
+  mcuWidth, so last MCU doesn't run off end of buffer.
+Increase size of block (from 255 to 1024 bytes) read at end of file to
+  search for 'startxref'.
+Skip past garbage at start of file, look for '%PDF'.
+Moved more compiler options out of Makefiles into Makefile.config.
+Top-level Makefile uses '$(MAKE)' instead of 'make' for making
+  subdirectories.
+Space/PageDown/Next and Backspace/PageUp/Previous now moves to
+  next/previous page if already scrolled to bottom/top of current
+  page.
+
+
+0.5a (96-jul-09)
+----------------
+[not a public release]
+For PDF 1.2 (a.k.a. Amber, a.k.a. Acrobat 3) support:
+  - look for trailer after first xref instead of at end of file.
+Deal with font subsets by converting character names of the form
+  'Cnnnn' to the appropriate character from the standard encoding.
+Extract encoding from embedded Type 1 fonts.
+Kludge to fill one-pixel thick polygons.
+Changed X font encoding to use endash for hyphen (gets rid of too-long
+  hyphens).
+Handle Resources key in Pages dictionaries (needed for pstoedit
+  output).
+Fix comment handling in Parser (needed for pstoedit output).
+Move Bezier curve conversion from GfxState to XOutputDev; look at
+  flatness parameter in GfxState.
+Change all of the path functions in XOutputDev (stroke, fill, clip) to
+  use the same path transformation/conversion function.
+Rewrote PostScript output driver as a subclass of OutputDev; removed
+  duplicated code (ps_ functions) from Gfx.
+Fixed bug in xref code with small (< 1024 bytes) PDF files.
+Implemented BX/EX operators.
+Added PDFDoc class.
+
+
+0.6 (96-nov-12)
+---------------
+Add support for PostScript output to stdout (-) and to a command
+  (|lpr); added -ps option and psFile resource.
+Decryption is implemented but not included in the distribution due to
+  legal restrictions: the decryption algorithm is a trade secret of
+  RSA, Inc., and the U.S.A. still has bogus export controls on
+  cryptography software.
+Added .xpdfrc config file:
+  - Added fontmap parameter: user can map PDF font names to X fonts.
+  - Added fontpath parameter: search for Type 1 font if encoding is
+    not in PDF file.
+Incremental display: display is updated after every 200 commands.
+Added forward and backward by-10-page buttons.
+Links:
+  - Implement links with "Launch" actions that point to PDF files.
+  - Draw borders around links.
+  - Handle links with named destinations.
+  - GoToR links specify a page number instead of a page reference.
+Optimizations:
+  - Rewrote Stream to use buffering, and added lookChar() functions;
+    rewrote Lexer to take advantage of this.
+  - Use hash tables for name->code mapping in font encodings.
+  - Made XOutputDev::doCurve() iterative, changed /2 to *0.5, and
+    changed the flatness test.
+Added file name to window title.
+Implemented RunLength filter.
+Implemented forms.
+Convert ObjType to an enum.
+Changed isDict("Pages") to isDict() (in Catalog.cc) to deal with
+  incorrect PDF files.
+Changed color selection so that very pale colors don't map to white.
+Fixed bug in CCITTFax filter (multiple make-up codes).
+In GString::clear(): need to set length to 0 before calling resize().
+Base initial window size on first displayed page, not page 1; deal
+  correctly with rotated pages.
+Added ltkGetIntResource() and LTKApp::getIntResource().
+PostScript output fixes:
+  - Escape backslashes in strings.
+  - When doing ASCII85 encoding, keep both chars of EOF marker ('~>')
+    on same line.
+  - Add extra line '%-EOD-' after image data streams; call wrapper
+    functions for image and imagemask which look for this line -- this
+    should fix the 'too much data in stream' bug.
+  - Font tags can be reused for different fonts on different pages --
+    so use font object reference (number/generation) instead.
+Initialize character widths to zero (this caused crashes on OSF/1).
+Handle image masks which go outside of pixmap.
+Makefile.config changes:
+  - Remove -o in C++ compile rule.
+  - Add $(AR) variable.
+Code which read char widths from font dictionary read all but the last
+  width.
+Add 'return 0;' to main() in xpdf and pdftops.
+Allow fonts to use StandardEncoding.
+Convert man pages to VMS help files.
+
+0.7 (97-may-28)
+---------------
+Implemented FlateDecode filter (for PDF 1.2).
+Basic xref table reconstruction for damaged files
+New pdftotext program converts PDF to plain text.
+Implemented menus in LTK; added a menu to xpdf.
+Added open and save functions; allow xpdf to start without any PDF
+  file.
+Implemented text find.
+Implemented text select/copy.
+Change mouse cursor when it's over a link.
+Embed Type 1 fonts in PostScript output.
+Moved rotate functions to menu; added quit to menu.
+Fixed stroke color bug in PostScript output (was using fill color
+  instead of stroke color; this sometimes caused lines to be missing
+  (white) in PostScript output).
+Support Launch-type links -- pops up a dialog before executing
+  anything.  Expects the A (action) dictionary to contain a Unix
+  dictionary with F (file) and P (paremeter) keys just like the Win
+  dictionary.
+A moveto op all by itself should just be discarded, instead of
+  generating a subpath with one point (this was causing seg faults).
+Balanced parentheses in strings don't need to be escaped.
+Tj operator in PostScript prolog didn't check for zero when dividing
+  by length of string.
+Implemented selection in LTK; TextIn widgets support dragging/copy/
+  paste.
+Handle font subsets that use hex character codes.
+Added icon pixmap; added the XPMLIB and NO_XPM variables to
+  Makefile.config.
+Fixed subtle bug in use of horizontal scaling parameter (it affects
+  only the width of drawn characters, not positioning done in text
+  space).
+Memory testing (with DEBUG_MEM):
+  - gmalloc now fills memory blocks with garbage to catch unitialized
+    fields.
+  - gfree fills memory blocks with garbage to catch uses of freed
+    blocks.
+Handle image masks which go off the pixmap on the top and/or left.
+Fixed inline functions which had two return statements (to make the
+  HP, SCO, and other cfront-based compilers happy).
+Fixed bug which caused seg faults when following a link to a different
+  file (info in LinkGoto object was used after link was deleted by
+  loadFile).
+If page content is an array of streams, the streams are concatenated;
+  objects and commands can span multiple streams.
+If file open fails, try lower-casing and upper-casing the file name.
+Commands should end when lexer sees a '/' character.
+GString::append(char *, int) was broken.
+Changed LTKScrollingCanvas redraw to be more efficient: copy as much
+  as possible from window before copying from off-screen pixmap.
+Ignore gs (set extended graphics state) operator.
+Handle colorspaces (CalGray/RGB are treated as DeviceGray/RGB; the
+  weird colorspaces are not yet implemented).
+Named destinations (for links) can be strings as well as names; deal
+  with the names tree in the catalog.
+Clip to the page CropBox.
+Added '-q' to gzip options (to suppress warnings, in case user has -v
+  in GZIP env var).
+Added 'include Makefile.config' to top-level Makefile.
+Added INSTALL variable to Makefile.config; used in top-level
+  Makefile.
+Always initialize LinkDest left/bottom/top/right/zoom fields (bogus
+  floating point values were causing crashes on Alpha).
+Added Makefile.config options for Digital Unix (DEC compilers), HP-UX
+  (HP compilers), SCO Unix, and Evans & Sutherland ES/OS.
+Added flag to set stream mode in fopen call for VMS.
+Rewrote Link module.
+Pages with no contents shouldn't cause an error message.
+In PostScript output: pdfImM needs to set fill color before doing
+  imagemask.
+If font doesn't specify character widths, use widths from built-in
+  font, based on font flags.
+Fixed LTK scrollbar to delay before repeating and to control the
+  period between repeats.
+Removed window/widget copy() methods (they were untested and unused).
+Unknown filter types produce a single error message instead of a
+  stream of errors.
+Added a dummy target in top-level Makefile so making individual
+  executables (e.g., 'make pdftops') should now work.
+Added optional xpdf-flip.ltk with buttons on right side instead of
+  bottom of window.
+
+0.7a (98-feb-22)
+----------------
+Moved find command from menu to toolbar button ('f' key still works).
+Support TrueColor visuals.
+Added a -cmap option and a installCmap resource to install a private
+  colormap.
+Mouse button 2 pans the window.
+Selecting a URI link now executes a configurable command (from the
+  urlCommand resource).
+Added a "link info" display which shows the URL or file for the link
+  under the mouse.
+Don't draw (or convert to PostScript) text drawn in render modes 3 and
+  7 -- this is invisible text, used by Acrobat Capture; this text is
+  still passed to the TextPage object so that selection works.
+Recognize (and quietly ignore) marked content operators (BMC, BDC,
+  EMC, MP, DP).
+Recognize new color-setting operators (scn, SCN).
+Added A4_PAPER option.
+Embed external Type 1 font files (this currently only works with PFA
+  files).
+Added "-level1" option (in xpdf and pdftops) to generate Level 1
+  PostScript.
+Setup autoconf -- replaced Makefile.config.  Added SELECT_TAKES_INT
+  flag, and use configure to autodetect (for HP-UX).
+Fixed appendToPath() to behave reasonably when appending ".." to root
+  directory.
+Fixed array size in FlateStream::compHuffmanCodes() (was causing xpdf
+  to crash under OSF/1).
+ASCII85Stream, ASCIIHexStream, and DCTStream didn't check for EOF and
+  could run past the end of the stream in damaged files.
+Handle hex escapes (#xx) in names.  Still allow the name /# for
+  backward-compatibility.
+Check for NULL characters in encoding array in GfxFont.cc (was calling
+  strcmp() with NULL which crashed under Solaris).
+PageAttrs::PageAttrs() didn't initialize crop box boundaries.
+Changed uses of lookup() to lookupNF() in XRef.cc.
+Fixed type checking of operators which take a variable number of
+  args.
+Gfx::buildImageStream() doesn't need to check for parser (since I got
+  rid of the bogus array-of-command thing).
+XOutputFont matches on font reference instead of font tag (similar to
+  PSOutputDev fix).
+Fixed bug in position calculation for multi-char substitutions in
+  XOutputDev.
+Cleaned up local variables which hid class variables.
+Optimized variable length decoding in CCITTFaxStream.
+Set link border width to zero if Border dictionary entry is missing.
+Throw away zero-length strings in TextOutputDev -- they don't have
+  valid xMin/xMax values.
+Swapped order of XLIBS and XPMLIB in xpdf/Makefile.
+Deleted 'LTKApp::' in function declaration in LTKApp.h.
+Changed '(XKeyEvent *)&event' to '&event.xkey' in LTKApp.cc.
+Check that the link rectangle coordinates are in the correct order,
+  and swap if necessary.
+TextOutputDev didn't set text to NULL, which caused pdftotext to
+  segfault if it couldn't open it's output file.
+Fixed a hash table search bug in GfxFontEncoding::getCharCode().
+Cleaned up colorspace code: rewrote GfxColorSpace and added
+  GfxImageColorMap; cleaned up PSOutputDev::doImage.
+Handle named colorspaces in images.
+Correctly set the default color after a colorspace change.
+Old setcolor operators now set the colorspace.
+Fixed bug with uncompressed blocks in FlateStream.
+Fixed bug with fixed Huffman code table in FlateStream.
+Added hash table of X windows (for LTKWindow and LTKWidget) to LTKApp
+  and replaced calls to XQueryTree with hash table searches -- this
+  avoids a roundtrip to the server for each event and also fixes the
+  problem where XQueryTree crashed if the window no longer existed
+  (with leftover events from a destroyed window).  (Thanks to Yair
+  Lenga for the suggestion.)
+Create a new GC for selection -- xor black and white (instead of LTK
+  foreground and background).
+Fixed crash with blank lines in .xpdfrc.
+Allow spaces in font descriptors in fontmap lines in .xpdfrc.
+Check for bogus object number in XRef::fetch().
+Use MacRomanEncoding for TrueType fonts that don't specify an
+  encoding.
+Certain PDF generators apparently don't include FontDescriptors for
+  Arial, TimesNewRoman, and CourierNew -- set GfxFont flags
+  appropriately.
+Fixed a bug in width guessing in GfxFont -- sans serif and serif were
+  swapped.
+Rewrote XRef::readXRef() to avoid using a parser to read the xref
+  entries.
+Added NO_TEXT_SELECT option.
+Ignore APPn/COM/etc. markers in DCT streams.
+Replaced select() with XMultiplexInput() in LTKApp.cc for VMS.
+Handle WM_DELETE_WINDOW protocol -- if you ask the window manager to
+  delete the xpdf window, xpdf will exit cleanly; other
+  windows/dialogs are simply closed.
+Optimized DCT decoder; switched to integer arithmetic.
+The "/Type /Annots" field in an annotation dictionary is optional.
+Check for null nameTree in Catalog::findDest().
+In XOutputDev, search user font map before default font map.
+Added "normal" SETWIDTH parameter to all font descriptors in
+  XOutputDev (some systems have a narrow-width Helvetica font).
+Added FOPEN_READ_BIN and FOPEN_WRITE_BIN to support Win32.
+Added a hack which allows better font substitution for some Type 3
+  fonts.  Also allow character names of the form /nn and /nnn.
+Added <strings.h> and <bstring.h> to LTKApp.cc (needed by AIX and IRIX
+  for bzero() declaration for FD_ZERO).
+
+0.80 (98-nov-27)
+----------------
+Support for some Japanese fonts (Type 0 fonts using the Adobe-Japan1-2
+  character collection, horizontal only).
+Added pdfinfo application.
+Added pdftopbm application.
+Added pdfimages application.
+Added -papercolor option and .paperColor resource.
+Fixed divide-by-zero problem in XOutputDev Type 3 font matrix kludge.
+Font subset char names can be 'Bxx' as well as 'Cxx' and 'Gxx'.
+Fixed bug in color space conversion in DCTStream filter (YCC->RGB was
+  correct, YCCK->CMYK was broken).
+Added XRef::getDocInfo() and PDFDoc::getDocInfo() to support pdfinfo.
+Optimized GfxImageColorMap.
+Lexer::getStream(), getPos(), and setPos() check for null stream.
+Decryption code now does strings as well as streams.
+ASCII85 decoder rounds short tuples up instead of down.
+CropBox and MediaBox can be non-integers.
+PostScript output:
+  - Use a rectangle operator.
+  - Call setpagedevice with page size.
+  - Insert %%PageOrientation comments.
+  - Add paper size flags (-paperw and -paperh) to xpdf and pdftops.
+  - If HAVE_POPEN is not defined, and user tries to print to '|...',
+    the PSOutputDev destructor tried to write to the PS file.
+  - Added support for forms (pdftops -form).
+Removed error messages for empty paths in stroke, fill, etc. operators.
+Don't allow flatnesses less than 1 in XOutputDev (this speeds up
+  rendering a little bit when there are lots of tiny curves).
+Moved the font subset character name guessing from GfxFont to
+  XOutputDev and TextOutputDev - now these files print correctly.
+Cast argument to XFree() to XPointer; add XPointer definition where
+  necessary (portability fixes).
+Various minor VMS fixes.
+Changes to configure script and Makefiles:
+  - Print a warning if X is missing.
+  - Use C++ when checking select() argument type (HP-UX).
+  - Use 0 instead of NULL when checking select().
+  - Default to gcc instead of c++.
+  - Get rid of AC_C_INLINE -- this is meant for C, not C++.
+  - Changed -USE_GZIP to -DUSE_GZIP.
+  - Added ability to compile ouside of the source tree.
+  - Added .cc.o rule to {goo,ltk,xpdf}/Makefile.in.
+  - Added @LIBS@ to XLIBS in xpdf/Makefile.in.
+  - In top-level Makefile.in: added '-' to clean commands; added
+    distclean rule.
+  - Create install directories.
+  - Use INSTALL_DATA (instead of INSTALL) for man pages.
+  - Changed xpdf-ltk.h rule to avoid leaving an empty file when
+    ltkbuild fails.
+  - Change things so that by default, ltkbuild is not built and
+    xpdf-ltk.h is not rebuilt.
+  - Use AM_PROG_CC_STDC to add compiler flags for ANSI C.
+  - Modify autoconf's builtin macros to check for xlC.
+  - Use Steve Robbins' smr_CHECK_LIB to test for pixmap library (Xpm)
+    -- this should fix the problems on systems that have the library
+    but not the include file.
+  - Added better test for sys/select.h, sys/bsdtypes.h, strings.h,
+    bstring.h.
+  - New VMS make scripts from Martin P.J. Zinser.
+  - Moved dependences into Makefile.in -- this gets rid of problems
+    with 'cc -MM' (which is gcc-specific) and 'include Makefile.dep'
+    (which isn't supported by all make implementations).  Also changed
+    all non-system include files to '#include "..."' (from '<...>').
+Tweaked the TextOutputDev heuristics slightly.
+Modify Gfx to use a stack of resources -- this is necessary for Form
+  XObjects, which can define their own local resources; also modified
+  PSOutputDev to dump fonts used by forms.
+Look for excessively large MediaBox (compared to CropBox) and shrink
+  it to CropBox.
+Minor fix to scrolling when dragging a selection.
+Various patches for pdftex and Win32 support.
+Deal with Separation colorspaces by using their alternate colorspace
+  entry.
+Added PBMOutputDev for pdftopbm application.
+Added ImageOutputDev for pdfimages application.
+Separated XOutputDev into LTKOutputDev + XOutputDev.
+Added support for 1-D and mixed 1-D/2-D (Group 3) decoding to
+  CCITTDecode filter.
+Added resetImage(), getImagePixel(), and skipImageLine() to Stream
+  class; used these in XOutputDev, PSOutputDev, and ImageOutputDev.
+Implemented predictor for LZW and Flate filters.
+In pdfImM1 in PSOutputDev prolog: div should be idiv.
+Changed output from printUsage() function in parseargs to look nicer.
+
+0.90 (99-aug-02)
+----------------
+Added Type 1/1C font rendering, using t1lib.
+Added "backward" and "forward" buttons.
+Added fit-page and fit-page-width zoom factors; replaced zoom-in and
+  zoom-out buttons with a zoom popup menu.
+Type 1C fonts are converted to Type 1 and embedded in PostScript.
+Support vertical Japanese text.
+Added Japanese text support (EUC-JP) to pdftotext.
+Bumped PDF version to 1.3.  Added stub functions for ri and sh
+  operators. (But there are still some missing 1.3 features.)
+Added -raw option to pdftotext.
+Minor changes to allow compiling under MS Visual C++ 5.0.
+Top-level makefile: changed 'mkdir -p' to '-mkdir -p'.
+Configure script: added X_CFLAGS to smr_CHECK_LIB(Xpm).
+Added Xpm_CFLAGS to xpdf/Makefile.in (this is needed to get the -I for
+  the xpm includes).
+Rewrote code that handles font encodings; added support for Type 1C
+  fonts.
+In the setpagedevice dictionary in PostScript output - added a
+  /Policies entry which tells the PS interpreter to scale the page to
+  fit the available paper.
+Changed PageUp behavior slightly: move to bottom (instead of top) of
+  previous page.
+TextPage used character's dx (width + char space) instead of just its
+  width.
+Read base URI from document Catalog (for URI-type links).
+Minor change to configure script to avoid using 'unset'.
+Fixed bugs in CropBox inheritance.
+Fixed a bug in resource stack handling for form objects.
+Display forms even if they have a missing/incorrect FormType.
+Fixed a bug in stream predictors -- the predictor parameters (width,
+  comps, bits) don't need to match the actual image parameters.
+  Completely rearranged the predictor code.
+Fixed PostScript output to correctly handle stream predictors.
+Don't segfault on empty (zero-page) documents.
+Added the xpdf.viKeys feature.
+Added the ffi and ffl ligatures to XOutputDev and TextOutputDev.
+Pdftotext and pdfimages now check okToCopy().
+Added a '-q' flag to all programs (except pdfinfo) to suppress
+  messages and errors.
+Deal with DeviceN colorspaces by using their alternate colorspace
+  entry.
+Change PostScript output so setpagedevice is only called once, at the
+  very beginning of the document (to avoid problems on duplex
+  printers).
+Changes to configure script and makefiles for DOS/DJGPP.
+FontEncoding::getCharCode() looked for (code>0) instead of (code>=0).
+Added keypad arrow keys, etc. to xpdf.
+Minor changes to gfile.{h,cpp} (<windows.h>) to compile under VC++.
+Fixed CCITTFaxStream to correctly handle all parameters.
+Modifications to gfile.{h,cc} for Acorn.
+Some minor changes for OS/2.
+Added 'SHELL = /bin/sh' to Makefiles.
+Compare file version to pdfVersionNum+0.0001 to avoid floating point
+  precision problems.
+Added LDFLAGS to Makefiles.
+Removed strip entirely from configure and Makefiles.
+Fixed a bug in choosing the correct DCTStream color transform.
+
+0.91 (2000-aug-14)
+------------------
+Added TrueType font rendering, using FreeType.
+Support for Chinese fonts (Type 0 fonts using the Adobe-GB1-2
+  character collection).
+Decryption code is included with the main distribution (now that the
+  US export regulations are a little bit less irrational).
+Added (very basic) support for generating PostScript with Japanese
+  fonts -- only tested with ghostscript so far.
+Added support for generating EPS files (pdftops -eps).
+Much-improved image filtering in xpdf (for downsampling and for
+  transforms other than 0/90/180/270-degree rotations).
+Implemented a basic full-screen (presentation) mode (xpdf
+  -fullscreen).  (There is currently no way to switch between window
+  and full-screen modes on the fly -- this will be fixed in a later
+  release.)
+Added "reload" menu item.
+Do a better job with anti-aliased Type 1 fonts on non-white
+  backgrounds.
+Handle Lab color spaces.
+Handle non-null user passwords.
+Avoid security hole with tmpnam()/fopen() -- added openTempFile() in
+  goo/gfile.cc.  [Thanks to Joseph S. Myers for pointing this out.]
+Filter out quote marks (' and ") in URLs before running urlCommand to
+  avoid a potential security hole.  [Thanks to Frank Doepper for
+  pointing this out.]
+Fixed TrueColor detection to look at the complete list of available
+  visuals, not just the default visual.
+In gfile.h, changed NAMLEN(dirent) macro to NAMLEN(d).
+Removed copyright character from start-up banners.
+In the open and save dialogs, if the open/save button is pressed with
+  no file name, the dialog is not canceled.
+Added Xpm_CFLAGS to ltk/Makefile.
+XOutputDev::updateLineAttrs was using dashLength before it was
+  defined.
+In top-level Makefile.in, use INSTALL_PROGRAM instead of INSTALL.
+In man page, recommend -q instead of -err in .mailcap.
+Changes for GNOME / Bonobo support:
+  - Separated Stream classes into BaseStream and FilterStream trees.
+  - Got rid of all FileStream-specific stuff.
+  - Added a PDFDoc constructor that takes a BaseStream* instead of a
+    file name.
+Allow newlines inside strings (Photoshop does this).
+Don't require whitespace between tokens in consecutive content
+  streams (for pages that specify an array of streams).
+Look at MissingWidth when constructing font character widths array.
+Fixed a bug that caused incorrect PostScript output for images that
+  use 8-bit indexed color spaces with < 256 colors in the palette.
+Handle case where embedded font file is bad (this was seg faulting).
+Minor changes for Windows/pdftex.
+Work around a bug in PDF files from the IBM patent server.
+Fixed bugs in PostScript form generation: use pdfStartPage instead of
+  pdfSetup; problem with inline images.
+Minor bug fix in FlateStream::loadFixedCodes().
+Added %%DocumentMedia and %%PageMedia comments to the PostScript so
+  that gsview (a Windows frontend for ghostscript) gets the right
+  paper size.
+Draw AcroForm fields that have appearance annotations.
+Bounds check gray, CMYK, and RGB values (in GfxColor).
+Moved the link border drawing code into Page (from PDFDoc).
+Minor modifications for pdftohtml.
+PSOutputDev: use the Type 3 font scaling kludge from XOutputDev.
+Separation color spaces were handled incorrectly in images.
+Fixed a bug with form bounding boxes.
+Modified the t1lib support -- replace libt1x code with my own code.
+  Type 1 and TrueType fonts are now handled similarly, and clipping
+  works on Type 1 fonts.
+Don't print copyright banner (xpdf); add -v switch to get copyright
+  and version info (all apps); get rid of -err switch (xpdf).
+Automatically reload the PDF file if it has been changed, i.e., if the
+  modification time is different.
+Fixed a memory (malloc size) bug in CCITTFaxStream.
+Fixed two bugs in FontEncoding::hash() -- handle zero-length character
+  names (which were found in a (buggy?) PDF file), and handle
+  character names with high-bit-set characters (use unsigned ints).
+Added PDFDoc::isLinearized() and corresponding code in pdfinfo.cc.
+Handle files with an incorrect page count in the Pages dictionary
+  (FOP, from the Apache project produces a page count of 0).
+Handle TrueType equivalents to the Base14 fonts (Arial, TimesNewRoman,
+  CourierNew) -- Adobe's tools use these names without embedding the
+  fonts.
+Tweaked the Type 3 font sizing kludge.
+Changed pdfimages (ImageOutputDev) so it doesn't output JPEG files for
+  4-component color spaces, since these seem to confuse most image
+  viewers.
+Added support for generating OPI comments (pdftops -opi).
+In XOutputDev::drawImage() and drawImageMask(), check for images that
+  are completely off-page.
+Use the provided alternate or a default (DeviceGray/RGB/CMYK) color
+  space for ICCBased color spaces.
+Incorporated MacOS-specific code from Leonard Rosenthol.
+Configure script switches to C++ for the strings.h/bstring.h test.
+Gfx::opRestore() calls clearPath() to handle (apparently) buggy PDF
+  files produced by FreeHand.
+The /Type field in most dictionaries is optional (PDF 1.3 change).
+Move printCommands variable definition into Gfx.cc.
+If page is smaller than paper, center the PostScript output.
+Fix a minor bug in the SELECT_TAKES_INT detection in the configure
+  script.
+TextOutputDev filters out control characters.
+Changed enough occurrences of 'char *' to 'const char *' to keep gcc
+  2.95 from barfing.
+Support for Latin-2 and Latin-5 in pdftotext (however, this will only
+  work if the PDF file contains correct font encodings, which seems to
+  be rare).
+TextOutputDev converts "eightoldstyle" to "eight", etc.
+Don't use the return value from sprintf() -- most systems return the
+  length, but some return the string.
+Minor fixes for SunOS 4.
+Configure script looks for both select() and fd_set in sys/select.h.
+Configure script checks for gethostbyname() in -lbsd (for LynxOS).
+Fix missing closepath bug in PostScript output.
+Change PostScript portrait/landscape mode selection so it only uses
+  landscape if the page width is greater than the paper width.
+Tweaked the VMS code in makePathAbsolute().
+
+0.91a (2000-oct-11)
+-------------------
+Implemented separable CMYK PostScript output (the -level1sep switch to
+  pdftops).
+Implemented Pattern color spaces with tiling patterns (polygon fills
+  only).
+Implemented Stamp annotations.
+Implemented Named link actions.
+Fixed a really dumb bug in the TrueColor code in SFont (which affects
+  both Type 1 and TrueType font rendering on 16-bit displays).
+Rewrote the GfxColorSpace / GfxColor code.
+Switched from djgppcfg to dj_make.bat (from Michael Richmond).
+Bug in the Type 1 encoding parser -- couldn't handle lines of the form
+  'dup NNN/name put' (with no space between the code and the name).
+Fixed the mkstemp() test in configure.in -- switched from
+  AC_TRY_COMPILE to AC_TRY_LINK and added <unistd.h>.
+Added DESTDIR to top-level Makefile.in.
+Fixed an incorrect OPI comment in PSOutputDev.
+Minor tweak to the CCITTFax code to avoid writing past the end of an
+  array on an invalid data stream.
+Xpdf crashed if the user selected 'reload' when no document was
+  loaded.
+Look for character names of the form "xx" (two hex digits with no
+  leading alphabetic char) and 'cNNN' (decimal digits with a leading
+  alphabetic char that happens to be a hex digit).
+FlateStream didn't correctly handle zero-length streams.
+Xref reconstruction didn't handle the case where the opening "<<"
+  immediately followed "trailer" with no intervening whitespace.
+Fix the %%DocumentSuppliedResources comment in EPS output.
+Scale annotations to fit their rectangles.
+Added Stream::close() to handle cases (e.g., patterns) where a Stream
+  object is used multiple times before it is deleted.
+Added the topLevel arg to Gfx::go() so it doesn't call out->dump() for
+  every pattern element (and form).
+Rearranged the GfxResources class.
+Clean up white space handling in Lexer.
+Make the dpi parameter to PDFDoc::displayPage etc. a double - this
+  avoids margin gaps with fit-page and fit-width.
+Fix a rounding problem in xpdf.cc that was causing the window to
+  sometimes be one pixel too small.
+Fixed a minor bug in dealing with Base-14 TrueType font names.
+Fixed Lab -> RGB color space conversion.
+Added support for opacity values (from PDF 1.4) to GfxState and
+  OutputDev.  [Thanks to Leonard Rosenthol.]
+Implemented type 2 functions; rearranged the Function class
+  hierarchy.
+
+0.91b (2000-oct-29)
+-------------------
+Print a warning about Type 3 fonts (XOutputDev, PSOutputDev).
+Added the scroll lock behavior to 'n' and 'p' keys in xpdf.
+Change FileStream buffer size to a #define'd constant.
+Renamed Pattern to GfxPattern to avoid clashes with Windows and MacOS
+  types.
+Added CNS (Big5) Chinese font support (CHINESE_CNS_SUPPORT); renamed
+  CHINESE_SUPPORT to CHINESE_GB_SUPPORT.
+
+0.91c (2000-nov-19)
+-------------------
+Fix an endianness problem in the Type 1 font code which resulted in an
+  incorrect display with "-t1lib plain" on big-endian systems.
+CCITTFax stream decoder will skip over extra zero bits at end of line,
+  even if EncodedByteAlign flag wasn't set.
+Added Big5 support to pdftotext (with CHINESE_CNS_SUPPORT enabled).
+Fixed a typo in the CNS/Big5 encoding translation table.
+Change the form code in PSOutputDev to store images in arrays of
+  strings.
+The xref reconstruction (for damaged files) now also looks for
+  'endstream' tags, and the parser uses this information when setting
+  up stream objects.
+In pdfinfo, convert Unicode chars in the 00xx range into 8-bit chars;
+  print a warning if there are any other Unicode chars.
+
+0.92 (2000-dec-03)
+------------------
+Fixed %%BeginResource comment (for xpdf procset) in PostScript
+  output.
+Added "-title" switch and "xpdf.title" resource to set the window
+  title.
+Check for <freetype.h> in addition to <freetype/freetype.h>.
+Upgraded the configure script to smr_macros 0.2.4 - this should fix a
+  bug where configure wasn't correctly finding t1lib.
+
+0.92a (2000-dec-17)
+-------------------
+Added 'extern "C" { ... }' in various places for ANSI C++ compliance.
+Tweaked the code that figures out DPI for fit-to-page and fit-to-width
+  modes.
+Fixed the image transformation code in XOutputDev -- no more missing
+  lines.
+Implemented color key image masking in XOutputDev.
+
+0.92b (2001-jan-07)
+-------------------
+Fixed a bug in the error-checking code in the Separation/DeviceN color
+  space parsing functions.  [Thanks to Lidia Mirkin.]
+Added wheel mouse support (mouse buttons 4 and 5).  [Thanks to
+  Thorsten Schreiner.]
+Added preliminary support for FreeType 2 (disabled by default).
+
+0.92c (2001-jun-04)
+-------------------
+Fixed a bug in the new image transformation code.
+Look for character names of the form "<letter><digit><digit>", instead
+  of looking for names beginning with a few specific letters.
+T1FontFile::T1FontFile wasn't initializing vars, and ~T1FontFile
+  wasn't checking before calling T1_DeleteFont -- this caused crashes
+  if it tried to open a nonexistent font file.
+Catalog::Catalog didn't set baseURI to NULL early enough.
+Tweak the check for strings.h in the configure script.
+Yet another fix for the image rotation code in XOutputDev --
+  off-by-one problem when upsampling.
+Handle Type 1/1C encodings when using FreeType 2.
+Allow FreeType2 to render user-supplied Type 1 base fonts.
+Opening a new file from full-screen mode tried to scroll.
+Fixed a bug in GfxFont constructor (missing check for NULL base font
+  name).
+Don't crash if a Type 1 font's FontBBox is non-integer.
+Pdfinfo prints page size.
+Tweak for the alpha hack in T1Font/TTFont: sample the middle pixel
+  instead of the top-left pixel.
+Automatically activate the text input widget in the find window.
+Changed a Japanese char code mapping in XOutputDev and TextOutputDev:
+  period was being incorrectly mapped to small circle (end-of-sentence
+  character).
+Add the 0/+/-/z/w key bindings to control the zoom setting.
+Fixed ImageOutputDev (pdfimages) to correctly handle inline image
+  masks.
+Extract ascent/descent info from font descriptor.
+
+0.92d (2001-jun-26)
+-------------------
+Embed TrueType fonts in PostScript output.  (Added a "-noembtt" flag
+  to pdftops.)
+Extract encoding from TrueType fonts.
+Moved Function classes to a separate file (Function.h/cc).
+Implemented multi-dimensional sampled Functions.
+Implemented Type 4 (PostScript calculator) Functions.
+For Type 0 fonts, FontDescriptor is in descendant font, not parent.
+  [Thanks to Lidia Mirkin.]
+Added the "-htmlmeta" option to pdftotext.
+In TextOutputDev, when computing the number of blank lines to insert,
+  do a sanity check on the result.
+If both FlateDecode and some other filter (e.g., DCTDecode) were
+  applied to an image stream, getPSFilter() crashed instead of just
+  returning NULL.
+Handle the /Identity function.
+
+0.92e (2001-aug-23)
+-------------------
+Widths in font dict should override built-in font widths.
+Changed "rotate left/right" menu items to "rotate
+  clockwise/counterclockwise".
+The link parsing code choked if the Border array was incorrect (too
+  short).
+Modified PSOutputDev to output CMYK for fill/stroke colors.
+
+0.93 (2001-oct-25)
+------------------
+Implement PDF 1.4 (128-bit) decryption.
+Bump supported PDF version number to 1.4.
+Text output for Simplified Chinese.  [Thanks to Cheung Siu Fai.]
+Read an app-defaults file for Xpdf.
+Read a system-wide config file (<prefix>/etc/xpdfrc) if ~/.xpdfrc
+  doesn't exist.
+Accept and verify owner password; if correct, allow all actions.
+Added a "-level2sep" option to pdftops to generate Level 2 separable
+  PostScript.  The PostScript separation convention operators are used
+  to handle custom (spot) colors.  [Thanks to Thomas Freitag for help
+  on this.]
+Add support for FreeType 2 to the configure script.  Warning: this
+  requires FT 2.0.5 or newer.
+Fixed the bounding rectangle overlap test in the disconnected subpath
+  fill hack in XOutputDev.
+Stupid typo in font name table in PSOutputDev.
+Changing the zoom setting with a keyboard shortcut didn't update the
+  displayed setting.
+Modified the mouse wheel support and added the second wheel (mouse
+  buttons 6 and 7).  [Thanks to Michal Pasternak.]
+Character and word spacing is affected by horizontal scaling (display
+  and PS output).  [Thanks to Eddy Ng.]
+Rotation specified by the text matrix, character spacing, and
+  horizontal scaling interacted incorrectly (display and PS output).
+Some broken Type 1/1C fonts have a zero BBox -- kludge around this by
+  assuming a largeish BBox.
+Handle PDF files with an incorrect (too small) xref table size.
+Allow "-?" and "--help" as aliases for "-h" (all apps).
+Correctly handle unescaped parens in strings in Lexer.
+Fixed a bug in LTK where a menu got posted multiple times if you right
+  clicked while a page was being rendered.
+Removed a comma inside a string in configure.in.
+Kludge around broken PDF files that use char 32 but encode it as
+  .notdef instead of space.
+Clean up various compiler warnings: use constructor args like "fooA"
+  if there is a field named "foo".  Everything now compiles cleanly
+  under gcc 2.91.66, 2.95.2, and 3.0.1.
+Page objects now read all of the page rectangles (MediaBox, CropBox,
+  BleedBox, TrimBox, ArtBox), as requested by the pdfTeX folks.
+  Added a new PDFRectangle struct to hold these.
+Use XOutputDev's Type 3 font size hack in TextOutputDev too, so it
+  does a little better job of extracting text in Type 3 fonts.
+Modify pdfimages to write one-bit images as PBM files.
+Work around a bug in cygwin's implementation of fseek.
+
+0.93a (2001-nov-21)
+-------------------
+Implemented the sh (shaded fill) operator for the axial shading type.
+Minor fixes to avoid compiler warnings.
+Cleaned up global variables -- moved many into instance vars and
+  function args.
+Minor fixes for OS/2.
+Fix the system config file path for VMS.
+Fix an uninitialized var in XOutputDev that caused crashes on Alphas.
+Don't incrementally update the display in full-screen mode.
+For Type 1/1C fonts, use the FontBBox from the PDF FontDescriptor
+  (instead of the one in the font file) if present -- this avoids
+  problems with fonts that have non-standard FontMatrixes.
+Add the Euro character to WinAnsiEncoding.
+Track the bounding box of the clip region to make rendering patterns
+  more efficient.
+Fix openTempFile() for Win32.
+
+0.93b (2001-dec-11)
+-------------------
+Added a duplex option to PSOutputDev and a -duplex switch to pdftops.
+Added XRef::PDFgetDocInfoNF() for pdftex project.
+Updated the VMS build script.
+
+0.93c (2001-dec-12)
+-------------------
+Completely rewrote the code that handles font encodings:
+  - everything is Unicode-based
+  - 16-bit fonts are handled much more cleanly
+  - text output encoding can be set more flexibly
+New .xpdfrc config files.
+
+1.00 (2002-feb-01)
+------------------
+More work on the font encoding rewrite:
+  - use the ToUnicode font dict entry
+  - pdfinfo and pdftotext (with '-htmlmeta') convert info strings to
+    the selected text encoding
+Added key bindings for forward ('v') and backward ('b').
+Added the pdffonts program which lists the fonts used in a PDF file.
+Fixed several problems in the TrueType font embedding code (for
+  PostScript output).
+Accept named destination on command line.
+Added several new items to pdfinfo: file size, PDF version, tagged
+  (yes or no), XML metadata (with the -meta option).
+Pdftops didn't get the portrait/landscape setting correct for PDF
+  files with rotated pages.
+The TrueTypeFontFile class (including the Type 42 converter) now
+  understands cmap format 6.
+Improved the "about" window -- mention the GPL, add a list of key
+  bindings.
+Added Zcaron and zcaron characters to WinAnsiEncoding.
+The '0' keyboard shortcut didn't update the zoom popup menu.
+Handle the complete list of alternate names for the Base14 fonts.
+Fixed substitute font scaling in XOutputDev - scale only the width,
+  not the height.
+Implemented stitching (type 3) functions.
+Handle the case of moveto/closepath/clip, which defines an empty
+  clipping region.
+Move dependences into separate Makefile.dep files; get rid of the
+  distdepend target.
+Move all of the configure-script-generated -D options out of the
+  Makefiles and into a top-level .h file (aconf.h).
+Cleaned up the FreeType 1/2 detection code in the configure script.
+Pdfinfo prints dates in a more readable format.
+Fixed a bug in the Paeth image predictor.
+Handle annotations with multiple states.
+Another workaround for buggy X servers: clip points that are way out
+  of bounds.
+Added libpaper support (for Debian).
+Generate PostScript DSC resource comments for PS (not just EPS)
+  files.
+The save and restore (q/Q) operators shouldn't save/restore the path.
+Performance optimization: disable pattern drawing in TextOutputDev.
+
+1.00a (2002-feb-25)
+-------------------
+Added an optimized special case for one-bit images in XOutputDev.
+Implemented CID TrueType font embedding; added a psEmbedCIDTrueType
+  option.
+The initialZoom X resource was broken.
+The reverse MacRoman encoding should return 32 for "space" (not 202,
+  which is an alternate encoding).
+Tweaks to the FreeType 2 support: only disable hinting if the bytecode
+  interpreter is disabled (i.e., disable autohinting but not bytecode
+  hinting); add some padding to the glyph cache for CJK fonts.
+Added level3 and level3Sep options for the psLevel setting and
+  corresponding -level3 and -level3Sep options to pdftops.
+Added a -level2 option to pdftops for consistency.
+Avoid a divide by zero in pdftotext.  [Thanks to William Bader.]
+Added a Greek language support package.  [Thanks to Alexandros
+  Diamantidis and Maria Adaloglou.]
+Don't bother trying to extract a "builtin" encoding from a TrueType
+  font.
+Accept either a page number or a page reference in a link
+  destination.
+Update the fontFixedWidth flag in GfxFont after reading the char
+  widths (used by the Acorn RiscOS port).
+Removed yet another (illegal but not caught by gcc) class specified
+  from a .h file.
+Avoid using snprintf - it's not available everywhere.
+Improved the CMYK->RGB transform.
+Use mkstemps where available.
+
+1.01 (2002-may-20)
+------------------
+Implemented Type 3 fonts.
+Implemented PostScript CID font embedding; added a
+  psEmbedCIDPostScriptFonts option.
+Implemented PostScript 16-bit font substitution; added psNamedFont16
+  and psFont16 options.
+Moved the initialZoom setting from X resources to the xpdfrc file.
+Implemented the radial shading type in the sh (shaded fill) operator.
+  [Thanks to Mike Sweet.]
+Added an 'include' command to the xpdfrc format.
+Added the displayNamedCIDFontX option so different fonts can be used
+  within one character collection.
+Added a simple reverse video mode (-rv switch, xpdf.reverseVideo
+  resource).
+Implemented stroked text in XOutputDev (with t1lib and FreeType2).
+  [Thanks to Leonard Rosenthol.]
+Implemented stroked text in PSOutputDev.
+Added a built-in Unicode map for UCS-2.
+New key binding in xpdf: 'g' activates the page number text field.
+PSOutputDev will now embed external TrueType fonts in addition to
+  external Type 1 fonts.
+The psEmbedType1Fonts and psEmbedTrueTypeFonts options were missing
+  the "Fonts" suffix.
+Documentation in xpdf.1 for -freetype option was wrong.
+Added the Big5ascii Unicode map to the Chinese-traditional support
+  package (maps 7-bit ASCII straight through).  [Thanks to Lawrence
+  Lai.]
+Modified the EUC-CN and EUC-JP encodings to pass 7-bit ASCII straight
+  through.  [Thanks to Lawrence Lai.]
+Avoid a divide by zero in XOutputDev.  [Thanks to Simon Burge.]
+Remove old code in openTempFile that removed an extension from the
+  name returned by tmpnam.
+Tweak the scrolling behavior when switching pages.  [Thanks to Case
+  Jones.]
+In the code that guesses character names (for font subsets), also
+  handle names of the form 'ABnnn'.  [Thanks to Colin Granville.]
+Fix the transform code for annotations.
+Improved the CMap file parser to handle more general PostScript
+  lexical conventions.
+Added '-enc' option to pdfinfo.
+Added the small caps and oldstyle numbers from Adobe's Unicode
+  corporate use area to the Latin1 and ASCII7 Unicode maps.
+The code in TextOutputDev that guesses Type 3 font size could generate
+  a zero size, which resulted in div-by-zero errors.
+Various tools (including Adobe's) occasionally embed Type 1 fonts but
+  label them Type 1C - so check for a '%!' at the start.
+Some tools embed Type 1C fonts with an extra whitespace char at the
+  beginning - just skip over it.
+Fixed a typo in the Simplified Chinese add-to-xpdfrc file.
+Updates to dj_make.bat and the djgpp build instructions.
+Added a Turkish language support package.
+Updated VMS build scripts.  [Thanks to Martin Zinser.]
+Modify the incremental display update code to redraw less often if
+  most of the commands are vector graphics, as opposed to text and
+  images.
+Tweak the Type 1 font bbox code to look at the bboxes in both the PDF
+  font object and the embedded font file.
+Fixed the ETenms-B5-H CMap file (for traditional Chinese) to map the
+  Latin characters to their proportional versions.
+Added an optional displayCIDFontX entry for one of the Arphic TrueType
+  fonts in the traditional Chinese 'add-to-xpdfrc' file.
+Remove leading '-' on include statements in Makefiles.
+Added psASCIIHex parameter.
+Added the GBK Unicode map to the simplified Chinese language pack.
+Pdftotext now opens the text file in binary mode to avoid Microsoft's
+  annoying automatic end-of-line translation stuff.
+Added an executeCommand function in goo/gfile.cc.  [Thanks to Mikhail
+  Kruk.]
+The %ALDImagePosition OPI comment was wrong if the page was scaled to
+  a different paper size.
+The OPI code was saving the default transform matrix before calling
+  setpagedevice, which can change the matrix.
+Fixed a crash when an inline image dictionary contains garbage.
+Upgraded to autoconf 2.53.
+Use unsigned int file offsets, to allow access to PDF files in the 2-4
+  GB size range; use fseek64/ftell64 if available.
+Fixed two floating point exception cases that came up with broken PDF
+  files.
+Avoid a crash when printing an error message regarding an unnamed
+  font.
+Default link border width should be 1.  [Thanks to Michael Pfeiffer.]
+Minor tweak to build with FreeType 2.1.0.
+Handle "weird" characters in PostScript font names.
+PSOutputDev now handles PostScript XObjects.
+Added several more page attributes for the pdftex project.
+Transferred the copyright to Glyph & Cog, LLC.
+
+2.00 (2002-nov-04)
+------------------
+Switched to the Motif toolkit.
+Support multiple open documents (in separate windows).
+Added document outlines to the viewer.
+Modified the text extraction (placement) algorithm.
+Implemented the JBIG2 decoder.
+Added a Latin2 language support package.
+Added support for movie annotations.
+Switched back to native LZW decompression code.
+Text extraction from Type 3 fonts was (partly) broken.
+The owner password checking code was missing a step in the case of
+  128-bit encryption.
+Added the 'printCommands' option to the xpdfrc file.
+Added key binding for '?' to bring up the about/help dialog.
+In TextOutputDev, ignore any text that's outside the page bounding
+  box.
+Text extraction throws away "tiny" characters after the first 20000
+  per page, to avoid really slow runtimes with PDF files that use
+  special fonts to do shading or cross-hatching; added the
+  'textKeepTinyChars' option to disable this behavior.
+Text extraction discards duplicated text (fake boldface, shadow
+  effects).
+Added ctrl-F as a key binding for find.
+Added a "find next" function, bound to ctrl-G.
+Added ctrl-P as a key binding for print.
+Modified the DCT decoder to handle progressive and non-interleaved
+  JPEG streams.
+Added key bindings for ctrl-Home and ctrl-End.
+Allow the initialZoom setting to be made in either the xpdfrc file or
+  as an X resource.
+Added a Hebrew language support package.  [Thanks to Roy Arav.]
+The "make distclean" target now creates (empty) Makefile.dep files in
+  the three subdirectories.
+Initialize XRef::ownerPasswordOk.
+Correctly handle stroking of Type 3 fonts in PSOutputDev.
+Generate correct PostScript for fonts with "weird" character names
+  (e.g., "(").
+Generate correct PostScript for images using Indexed color spaces with
+  DeviceN base color spaces.
+Added lowercase Roman numerals to ISO-2022-CN.unicodeMap (simplified
+  Chinese support package).
+Tweak the image scaling code to better handle flipped (top-bottom
+  and/or left-right) images.
+Generate correct PostScript code for inline images and images in Type
+  3 fonts which are too large for a single PS string.
+Correctly handle indexed color spaces whose base color spaces have
+  component ranges other than [0,1].
+Optimized the DCT decoder.
+Fixed mistakes in the list of key bindings in the about/help dialog.
+Optimized the Flate decoder.
+Add literal names for punctuation and digits to the Unicode name
+  table.
+Cygwin's popen wants mode "r", not "rb".
+Fixed a bug in the Type 4 function parser (the "if" operator wasn't
+  parsed correctly).
+Fix a bug in PS output for TrueType fonts with no PDF encoding.
+Make the bbox size in FTFont more liberal (to avoid problems with
+  fonts that have incorrect bboxes).
+Reverse the colors in PBM files generated by pdfimages, so the common
+  case (an image mask filled with black) comes out correct.
+Add fseeko/ftello support which is basically identical to
+  fseek64/ftell64.  [Thanks to Nassib Nassar.]
+Modified column assignment in text extractor to account for characters
+  that convert to multiple characters in the output encoding.
+Fix TrueType fonts which have an incorrect cmap table length.
+Work around a pragma bug in the version of gcc that ships with MacOS X
+  10.2.  [Thanks to Frank Siegert and Andrew Stone.]
+Fix a problem that was causing an infinite loop when a damaged content
+  stream contains an 'ID' command inside a dictionary.
+Handle the case where systempapername() returns NULL (libpaper
+  support).
+Handle fonts which are defined directly in the font resource
+  dictionary rather than as separate objects.
+Track process colors in Level 1 separable PostScript.
+Pdfinfo now checks the return value from mktime to avoid seg faults in
+  flakey strftime implementations.
+If duplex is not enabled in PostScript output, leave the duplex
+  setting alone, allowing the spooler to insert its own setting.
+Added three missing fclose calls.
+Change the default encoding for TrueType fonts (used when the PDF file
+  doesn't specify an encoding) from MacRomanEncoding to
+  WinAnsiEncoding.
+Move X_CFLAGS to the end of the list in CXXFLAGS (in Makefile.in) to
+  avoid some of the FreeType2 include path problems.
+Fixed an obscure bug in the LZW decoder.  [Thanks to Martin
+  Schroeder.]
+Fixed a bug in decryption when using the newer (PDF 1.4) algorithm
+  with shorter-than-128-bit keys.
+Minor optimization for image data streams: the ImageStream class can
+  return an entire buffered line.
+
+2.01 (2002-dec-05)
+------------------
+Redesigned the text extraction process:
+  - process the text into "reading order"
+  - added a "-layout" flag to pdftotext to switch back to the old
+    style, where physical layout is maintained
+  - use of the "-raw" flag is no longer recommended
+Added the -reload option for xpdf (in remote mode).
+Added support for external CID fonts; added the displayCIDFontT1 and
+  displayNamedCIDFontT1 commands to the xpdfrc file.
+Handle the case of moveto/newpath/clip, which defines an empty
+  clipping region (just like moveto/closepath/clip).
+Accept XYZ link destinations with missing array elements.
+Fix some problems with state save/restore triggered by Type 3 fonts
+  that reference other fonts.
+Accept bogus font names based on "Symbol": Symbol,{Bold,Italic,
+  BoldItalic}.
+Fixed color and font resource names in the xpdf man page.
+Was using delete instead of gfree in OutlineItem::~OutlineItem.
+Set the busy cursor in the find dialog while searching.
+Map variants of the copyright, trademark, and registered trademark
+  symbols to the proper Unicode codes, not to Adobe's corporate use
+  area codes.
+Fixed a floating point exception bug in TextOutputDev (check for a
+  too-small denominator).
+Fixed a typo in TextOutputDev, in the code that generating blank lines
+  to add vertical whitespace.
+Config files whose last line didn't end with a LF (or CR+LF) weren't
+  being handled correctly.
+The code that handled CIDToGIDMaps in Type 2 CIDFonts was broken.
+Check the per-glyph bounding box in Type 3 fonts, and don't try to
+  cache glyphs with bogus bboxes.
+Allow ToUnicode CMaps to use fewer than four hex digits in the Unicode
+  char indexes.
+Added multithreading protection to the GlobalParams class.
+Fixed a bug in end-of-stream detection with the TIFF predictor.
+Added some characters to MacRomanEncoding to match up with Apple's
+  definition.
+
+2.02 (2003-mar-24)
+------------------
+Rewrote the text extractor code that assembles words into lines to
+  better handle vertically overlapping lines.
+Add the "match" option for paper size (in PostScript output).
+Added support for external 16-bit TrueType fonts; added the
+  displayCIDFontTT and displayNamedCIDFontTT commands to the xpdfrc
+  file.
+Added an Arabic language support package.
+Added the Windows-1255 encoding to the Hebrew language package.
+A missing NULL check was causing a crash when closing the file in a
+  single window (which clears out the window, but leaves it open).
+Deal with TrueType fonts whose glyph data is out of order - this
+  affected both FreeType rasterization and PostScript generation.
+Munge font names in PSOutputDev to avoid names that are problematic
+  for ghostscript because they start with an out-of-limits number
+  (e.g., 1e999foo).
+Modify the TrueType font encoding deciphering algorithm in yet another
+  attempt to match up with Acrobat's behavior.
+Bounds check the indexHigh value in indexed color spaces.
+The text extractor no longer bothers trying to get an average
+  character width for Type 3 fonts, since it generally doesn't work
+  very well (because Type 3 metrics are unreliable).
+Don't crash if the user hits ctrl-G ("find again") before doing a
+  find.
+Set the button pixmap foreground color correctly.
+Handle text drawn backward on 180 degree rotated pages.
+Added a magic call to XtUngrabButton after calling XmCreatePopupMenu
+  which appears to prevent some very odd problems (idea taken from the
+  DDD source code).
+Fix the MacOS X fix (needed to include <AvailabilityMacros.h>).
+Fixed a bunch of Motif 1.x / X11R5 incompatibilities.  [Thanks to
+  William Bader and Albert Chin-A-Young.]
+Fixed various bugs in previously untested code in the JBIG2 decoder.
+Modify the XPDFCore destructor to avoid a bogus warning message from
+  OpenMotif 2.2.
+Modified the Type 1C font parser to do proper bounds checking.
+Fixed the bounds checking in the TrueType font parser.
+Text extractor shouldn't do block merging in physical layout mode.
+Fixed a problem in PSOutputDev in level2sep mode with images in a
+  Separation color space and with a non-default Decode array.
+Text extraction with "-raw" was concatenating lines from the bottom of
+  one column and the top of the next.
+Handle Type 1C subroutines in the font converters.
+Correctly handle progressive JPEG images whose scans are slightly
+  different sizes (e.g., the Y scan rounds up to a multiple of 8
+  pixels and the Cb/Cr scans round up to 16 pixels).
+Avoid a potential divide-by-zero problem in TextOutputDev.
+Modified the T1Font and FTFont modules to correctly handle glyphs that
+  are larger than the font's claimed bounding box.
+Tweak dupMaxDeltaX parameter in TextOutputDev to avoid triggering on
+  double characters.
+Improved detection in pdfinfo for ISO paper sizes.  [Thanks to Hartmut
+  Henkel.]
+Xpdf wasn't responding to the TARGETS atom, which prevented pasting
+  the selection into various applications.  [Thanks to Phillip Ezolt.]
+Handle XObjects with recursive references in their Resources
+  dictionaries (in PSOutputDev).
+Change PSOutputDev to deal with invalid PDF files that use
+  non-embedded TrueType fonts with no encoding.
+Check for undersized Widths arrays in fonts.
+Add bounds checking code to Array class.
+Updated VMS build scripts.  [Thanks to Martin Zinser.]
+Tweak the TrueType font handling code (again):
+  - char codes in symbolic fonts may or may not be offset by 0xf000
+  - discard empty tables because they sometimes confuse FreeType
+Fixed bounds checking in the Flate decoder.
+Removed a bogus error message for exponential functions without
+  explicit C0/C1 values.  [Thanks to Hartmut Henkel.]
+Handle the other Unicode cmap type (platform=0) in TrueType fonts.
+Added support for the SGI Motif horizontal paned window widget.
+  [Thanks to Felix Ritter.]
+Ignore extra elements in link destination arrays.
+Accept external Type 1 font files with a suffix of ".ps" or no suffix
+  at all.
+Add a bounds check in the DCT decoder.
+Added instructions for building xpdf.exe under cygwin/XFree86.
+Tweaked the word separation parameter for raw-mode text extraction.
+
+2.03 (2003-oct-10)
+------------------
+Rewrote the text extractor to:
+  - do a better job with rotated text;
+  - handle right-to-left scripts;
+  - be faster.
+Changed the zoom setting to use a percentage (relative to 72 dpi)
+  instead of a zoom "factor".
+If the PDF file has an outline, open the outline pane initially.
+Added -f and -l options to pdfinfo; print multiple page sizes.
+The HAVE_XTAPPSETEXITFLAG test in XPDFApp.cc was backwards.
+The BitsPerComponent entry is optional in image mask objects.
+Render any annotation with an appearance stream, instead of just
+  Widget and Stamp annotations.
+Fix a bug in the TrueType font checker: the test for an unsorted
+  'loca' table was wrong.
+Modify the TrueType cmap selection algorithm yet again to try to match
+  Adobe's behavior.
+Changed sqrt(2) to sqrt(2.0) in pdfinfo.cc to make various compilers
+  happy.
+Fixed a deadlock problem (when MULTITHREADING is set); cleaned up some
+  other problems with the locking code.
+Fixed a bug in the interpolation code for type 0 (sampled) functions.
+Implemented type 1 (function-based) shaded fills.
+Fixed some stupid bugs in the JBIG2 decoder (introduced with the
+  previous optimization work).
+Fixed a typo in the code that parses vertical font metrics for CID
+  fonts that was causing a seg fault.
+Fixed a couple of bugs that were causing seg faults with badly damaged
+  PDF files.
+Limit the number of nested Forms to avoid infinite recursion (in buggy
+  PDF files).
+Add a special case for rectangular clip regions - make sure these
+  don't drop pixels on the right and bottom edges.
+Tell FreeType not to use glyph bitmaps when in anti-aliased mode.
+Read all of the border style info for links.
+All of the shaded fill types now do at least one bisection to avoid
+  problems when the colors at the endpoints of the domain are the
+  same.
+If the Length2 parameter for an embedded Type 1 font was incorrect
+  (too small), pdftops was losing font data.
+Deal with (broken) DCT streams that use the same component ID number
+  for different components.
+The MediaBox page attribute was not being inherited correctly.
+Fixed a bug in the Type 1C font converter related to local
+  subroutines.
+The Type 1C -> Type 1 font converter was allocating the font dictionary
+  one slot too small.
+Added a missing private dictionary entry to Type 1 fonts generated by
+  the Type 1C converter.  [Thanks to Michael Shell.]
+Fixed bugs in the tiling pattern fill code.
+Try the TrueType 0xf000 char code offset hack for the MacRoman
+  encoding too (in addition to MS Symbol).
+Update the font metrics info for the Base 14 fonts to include the Euro
+  character.
+SECURITY HOLE: Escape various characters in URLs before running a web
+  browser (or movie viewer).  [Fixed in 2.02p11]
+SECURITY HOLE: In the dialog used to verify "launch" links, provide a
+  scrolling view if the command to be run is excessively long.  [Fixed
+  in 2.02p11]
+Added an option to disable insertion of page breaks (form feed
+  characters) in extracted text (pdftotext -nopgbrk; xpdfrc
+  "textPageBreaks" option).
+Check for 8-bit fonts that specify an out-of-range FirstChar or
+  LastChar.
+Correctly handle an obsolete Type 2 charstring op (in the Type
+  1C-to-Type 1 font converter).  [Thanks to Helge Blischke.]
+Use the font encoding info to fill in holes in the ToUnicode map.
+Added character names for Bulgarian (in the Cyrillic support pacakage)
+  and Greek.
+Handle clipping to text in xpdf and pdftops.
+Fix color space detection in DCT decoder.  [Thanks to Dwight Kelly.]
+Added the "unicodeToUnicode" xpdfrc option, intended (initially) for
+  Arabic support.
+Handle the case in PSOutputDev where two font objects refer to the
+  same embedded TrueType font, but with different encodings.  [Thanks
+  to Frank Siegert.]
+Kill any pre-existing path before drawing a form (or annotation).
+Save state before rendering page content; restore state afterward.
+Fix Stream::reset/close to work correctly with encoder streams; fix
+  PSOutputDev to use Stream::close consistently.
+Fix a seg fault when hitting the 'back' button after closing a file.
+GfxState::getStrokeGray was returning the fill gray value (this only
+  affected Level 1 PS output).
+Change PSOutputDev to reuse dictionaries in Level 1 mode (since Level
+  1 PS interpreters don't do garbage collection).  [Thanks to Frank
+  Siegert.]
+PSOutputDev was generating incorrect translations for landscape-mode
+  pages.
+Implemented shading pattern color spaces.
+PSOutputDev wasn't correctly handling Type 3 fonts which used image
+  resources (as opposed to inline images).  [Thanks to Frank Siegert.]
+The fix from 1.00 which clipped out-of-bounds points was a bit too
+  aggressive.
+Do proper Floyd-Steinberg dithering in XOutputDev.
+Don't automatically check for a null owner password (to match Adobe's
+  behavior).
+Allow the FlateDecode filter in Level 3 PostScript output.
+Fixed small bugs in the Type 1C -> Type 1 converter and Type 1C ->
+  Type 0 converter.  [Thanks to Tom Kacvinsky.]
+Work around another weird Motif problem with the right button menu
+  (which was sometimes causing the menu to not be displayed).
+Make the code that handles fonts defined directly in the resource dict
+  more robust.
+Add a brief description of the outline pane to the xpdf man page.
+Ignore extra operands to content stream operators.
+Fixed a bug in the CCITTFax decoder.
+Allow the Count entry in a Pages dictionary to be a real number
+  (because some PDF generators actually do this).
+Shading pattern fills weren't being clipped correctly.
+Incorrect shallow copies in GfxRadialShading and StitchingFunction.
+The StitchingFunction destructor wasn't checking for funcs being
+  NULL.
+Change the TrueType code-to-GID mapping code so it looks at the
+  TrueType 'post' table.
+Set the print command in the print dialog once at startup, don't
+  change it each time a file is (re)loaded.
+Generate the %%BoundingBox comment in regular PostScript files (not
+  just EPS files).
+Fixed a bug in the Unicode CMap parser.
+
+3.00 (2004-jan-22)
+------------------
+New PDF rasterizer ("Splash").
+Added support for PDF 1.5:
+  - JPX (JPEG 2000) decoder
+  - XRef streams
+  - object streams
+  - DeviceN color spaces with up to 32 components
+  - Added new CMaps to the CJK language support packages
+Replaced pdftopbm with pdftoppm (which can generate PBM, PGM, and PPM
+  files).
+Reorganized the font file parser code into a new library ("Fofi").
+Removed support for FreeType 1.x.
+Removed support for X server fonts - Xpdf (and pdftoppm) will now
+  search for the URW fonts (from ghostscript).
+Changed the "-t1lib" and "-freetype" switches; replaced the
+  "t1libControl" and "freetypeControl" config file options with
+  "enableT1lib", "enableFreeType", and "antialias".
+Added the "-box" option to pdfinfo.
+Added imageable area support to PSOutputDev (for CUPS); added the
+  "psImageableArea" config file option.
+Added the "-nocrop", "-expand", "-noshrink", and "-nocenter" switches
+  to pdftops; added the "psCrop", "psExpandSmaller", "psShrinkLarger",
+  and "psCenter" config file options.
+Dictionary size was in PostScript code generated for Type 3 fonts.
+The PS code generated for images in Type 3 characters was broken.
+Tweaked the text extractor.
+Accept xref entries that are one byte too short (matching Adobe's
+  behavior).
+Change things so "xpdf -h" and "xpdf -v" work if DISPLAY isn't set.
+Fix a problem in the damaged file repair code that handles the trailer
+  dictionary.
+Use the "Last" entries in "Outlines" objects - this avoids a problem
+  with PDF files generated by buggy software that, e.g., sets the last
+  item's Next pointer to point to itself.
+PSOutputDev was not handling DeviceN color spaces correctly in Level 2
+  images.
+Fixed a stupid little bug that broke PS output for JBIG2 images.
+Work around a Lesstif bug: set up an extra callback so hitting <Enter>
+  in the find dialog performs a search.  [Thanks to Elliott Hughes.]
+Pdftops was crashing on zero page PDF files.
+Add an AC_PREREQ call to configure.in.
+Change the 'find' dialog so the text entry box resizes with the
+  dialog.
+Skip extraneous zero bits at the start of a CCITTFax stream.
+The PostScript text clipping operator was missing a 'newpath'.
+  [Thanks to Frank Siegert.]
+Fix a bug in tiling patterns with bboxes that don't start at (0,0).
+Fix a bug in Type 3 font handling with rotated text.
+The tiled pattern fill code was destroying the current path, which
+  broke the fill+stroke operators when the fill color space was a
+  tiled pattern.
+ICCBased color spaces don't always set their Ranges values correctly,
+  so just use the values from the alternate color space.
+Modified GHash to accept int or void* - this avoids some conversion
+  warnings.
+Check for missing Type 3 CharProcs - avoid a segfault.
+Pdffonts now marks all Type 3 fonts as embedded.
+Outline entries with no Title string weren't being handled correctly,
+  resulting in segfaults.
+PSOutputDev now forces the text horizontal scale factor to be non-zero
+  to avoid singular font matrices in the PS code.
+Tweaked the error recovery in the CCITTFax decoder.
+The LZW/Flate predictor should treat any Predictor value (in the
+  stream dictionary) >= 10 identically.
+PSOutputDev and pdffonts check for NULL font objects (which can
+  happen, e.g., because of missing CMap files).
+Swap the left and right mouse wheel button numbers.
+EPS output ("pdftops -eps") now uses the CropBox instead of the
+  MediaBox as the EPS bounding box.
+
+3.01 (2005-aug-17)
+------------------
+Added the continuous view mode, including the '-cont' switch and the
+  'continuousView' config file option.
+At high zoom levels, don't rasterize the entire page - this avoids
+  problems running out of memory.
+Added "search backward" and "match case" options to the find dialog.
+Support explicitly masked images and soft masked images.
+Add support to DCTStream for 16-bit quant tables.
+Don't segfault if the user clicks on an outline entry with a broken
+  destination.
+Changed the makefiles and configure script to skip building pdftoppm
+  (in addition to xpdf) if X, Motif, or FreeType is not found; changed
+  the error message in the configure script to match.
+Move an inline function in JArithmeticDecoder.cc to avoid compiler
+  errors.
+Fixed a bug in the rasterizer that was sometimes causing infinite
+  loops with round line caps on vertical lines.
+Various rasterizer optimizations.
+Look for intermediate resize events - try to avoid lagging when the
+  user is doing an opaque resize.
+The FormType key in Form XObjects is optional.
+Handle external 16-bit TrueType fonts correctly, using the Unicode
+  cmap.
+Add class declarations to TextOutputDev.h to work with stricter C++
+  compilers.
+Support FreeType's weird include file stuff (ft2build.h, etc.).
+Fixed a bug handling empty paths.
+Fixed a text positioning problem in PostScript output.
+Handle TrueType collections in FoFiTrueType.cc.
+FoFiTrueType constructor was reporting a failure if the post table was
+  bad - this should be non-fatal.
+Type 1 font parser was missing a NULL test.
+Mask chars passed to isdigit in goo/parseargs.c to avoid problems with
+  signed chars.
+Added more error checking to the CCITTFax decoder.
+Fixed a bug (computing the MCU size) in the DCT decoder.
+Change a test in the Splash stroke code to avoid x86 floating point
+  weirdness.
+Reorganized the decryption code to allow security handler plugins;
+  removed the NO_DECRYPTION #ifdefs.
+Added a plugin interface, initially just for security handlers.
+Support color key masked images and explicitly masked images in PS
+  output (Level 2 only).
+When checking for aliases of the Base 14 fonts, ignore spaces in the
+  specified font name.
+Handle encrypted PDF files that are missing the file ID string.
+Handle tiling patterns more efficiently in the PostScript output.
+Rewrote the code that handles color spaces in PostScript output.
+Fixed a bug in the Type 1C font parser - zero-length indexes (and
+  zero-length names) weren't handled correctly.
+Handle shaded fills more efficiently in the PostScript output.
+Implement the remaining shading types (4-7).
+Rearranged the Splash color modes.
+Add the EarlyChange parameter to LZWStream when generating PostScript.
+Check for zero values in line dash arrays in PSOutputDev.
+Fixed an uninitialized variable in JArithmeticDecoder which was
+  causing crashes.
+Treat unknown CMap names as identity mappings (to match Adobe's
+  behavior).
+Fixed bugs in the XRef parser related to XRef streams in updated
+  files.
+Added a missing call to FT_Done_Glyph which was causing a memory leak.
+  [Thanks to Dave Formanek.]
+Fixed a bug in text copying that was causing the last word to be
+  dropped on some pages.
+Tweaked the image width/height computation in Splash::drawImage and
+  Splash::fillImageMask to make striped images work better.
+Ignore minus signs in the middle of numbers (to match Adobe's
+  behavior).
+Missing '%s' in format strings for dates in pdftotext '-htmlmeta'
+  mode.
+Change the TrueType code-to-GID mapping code so it looks at the
+  standard name-to-Unicode mapping before the ToUnicode mapping
+  defined in the font object.
+Added a matteColor setting (command line option and X resource).
+Tweaked the CMYK->RGB transform.
+Fix some problems in tracking the character position (to match up with
+  Adobe's highlight file format).
+Handle moveto/closepath/stroke correctly.
+Check for singular text matrices and font size of zero in PSOutputDev.
+Clip PS output to the size of the page (avoiding any gibberish that
+  lies outside the MediaBox, in the case where the MediaBox is smaller
+  than the paper).
+If the line dash element in an annotation's Border array is of an
+  invalid type (i.e., not an array), don't draw the link at all (this
+  matches Adobe's behavior).
+Don't remap small caps and oldstyle glyphs in the name-to-Unicode
+  table - it messes up TrueType font encodings.
+Pdftoppm wasn't setting the paper color correctly in mono and gray
+  modes (this only showed up on big-endian machines).
+Missing NULL check was causing crashes when attempting to read non-PDF
+  files that happened to contain the string '%PDF'.
+Fixed a problem in the text extractor that was breaking up words.
+Handle vertical text (CJK fonts) in PS output with TrueType fonts that
+  are missing the vertical metrics tables.
+Handle the case where a font object and the corresponding embedded
+  font are different types.
+Handle basic crypt filter functionality.
+Added more value checking in the XRef parser, to avoid potential
+  security problems.
+Updated the CJK language support packages: replaced the
+  displayCIDFontX references with displayCIDFontTT; added pointers to
+  free TrueType fonts.
+Added a missing error message when SplashOutputDev can't parse an
+  embedded TrueType font file.
+PDFCore and TextOutputDev now correctly handle searching for Unicode
+  strings, including real Unicode case-folding.
+Throw away tiling pattern fills that are completely outside the clip
+  region.
+The JPEG 2000 inverse reversible multiple component transform code was
+  wrong.
+Fixed some bugs in shading pattern fills: clipping was wrong, and
+  background color was not implemented.
+Added tool tips for the toolbar buttons.
+Decrease the max depth of recursive patch mesh filling if the pattern
+  has a large number of patches.
+Highlight the find text whenever the find dialog is mapped.
+Handle page boundary boxes with reversed coordinates.
+Fixed a bug in the text extractor code that handles duplicated text.
+Optimization work on SampledFunction::transform().
+Use the CropBox instead of the MediaBox as the display region.
+Dither for PseudoColor (8-bit) displays.
+Fix a bug in DCTStream that was causing an infinite loop with
+  corrupted DCT image data.
+Fix a bug in the ToUnicode CMap parser.
+Fix a bug in the text extractor - negative font sizes weren't being
+  handled correctly.
+Fix a bug in the text extractor - in certain cases, out-of-bounds text
+  could cause crashes (generally only in damaged PDF files).
+Fix a read-past-end-of-array bug in the JBIG2 decoder.
+Fix a case where pdftops was generating lines longer than 255 chars.
+Optimize redraws - don't regenerate the XImage every time redrawRect
+  is called.
+The ASCII85 decoder wasn't skipping whitespace properly.
+Optimize text extraction: skip (non-inline) image setup entirely.
+Added initial transparency support (stroke/fill alpha and blend mode).
+Added support for the overprint setting in PostScript output.
+Fixed various buffer overflow bugs.
+Handle negative font sizes and horizontal scaling correctly - this
+  affected PSOutputDev for all text operators, as well as the TJ
+  operator for all OutputDevs.
+Fixed a buffer overflow in the CCITTFax decoder.
+Fixed an out-of-order entry in the list of font name aliases.
+Fixed a backward loop in the PostScriptFunction code.
+Treat a zero-length base URI the same way as a nonexistent base URI.
+Add a divide-by-zero check in TextOutputDev (the problem was happening
+  in cases of mixed horizontal and vertical text).
+PSOutputDev wasn't rounding the page bounding box coordinates
+  correctly.
+Support the SOF1 marker in DCT (JPEG) image streams.
+Minor changes to GlobalParams.h and JPXStream.h because some compilers
+  don't like anonymous structs inside anonymous unions.
+Xpdf now complains about a negative page number.
+Changed GString::cmp and GString::cmpN to correctly handle '\0' chars
+  in the middle of strings.
+Fixed the radial shading code; corrected the handling of the 'extend'
+  parameters.
+Added the gmallocn and greallocn functions.
+Fixed a bug in the TIFF image component predictor which shows up with
+  components that are not 1 or 8 bits wide.
+Optimized FlateStream::loadFixedCodes().
+For non-embedded Base-14 fonts, don't use the ascent/descent/bbox
+  values from the FontDescriptor - various PDF generators get them
+  wrong.
+Fixed a bug in the text extractor - words on the same line (especially
+  in tables) were being split vertically onto multiple lines.
+Automatically select the correct radio button ("print with command"
+  vs. "print to file") in the print dialog.
+Don't create the "open" and "save as" dialogs until needed - this
+  avoids stat-ing every file in the directory at startup.
+Changed the Big5 and Big5ascii encodings (in the traditional Chinese
+  language support package) to include characters from the Unicode
+  database (which aren't mentioned in the Adobe character collection
+  documentation).
+Added the '-pagecrop' switch to pdftops.
+Tweaked the RGB->gray and CMYK->gray conversion functions to match the
+  PDF spec.
+The JPEG 2000 decoder wasn't correctly handling codeblocks split
+  across multiple packets/layers.
+Fixed a typecast that caused compile errors on 64-bit systems.
+The CMap parser wasn't handling the 'cidchar' construct.
+Handle the case in PSOutputDev where two font objects refer to the
+  same embedded 16-bit TrueType font, but with different CIDToGIDMaps.
+Changed the configure script to report more accurate warnings when
+  it can't find X / Motif / FreeType.
+Encryption with revision=2 always uses a 40-bit key, regardless of the
+  specified Length value.
+Yet another minor change to the TrueType font encoding deciphering
+  algorithm.
+Don't completely invalidate the Catalog if one (or more) of the page
+  objects are bogus -- just skip over those pages.
+Removed the workaround in pdftops for too-small Length2 values in Type
+  1 fonts -- it was causing problems on various PostScript printers.
+Started adding error checking to the JBIG2 decoder (this is nowhere
+  near complete yet).
+Extended the "unicodeToUnicode" config option to also apply to CID
+  fonts.
+Added the narrow Latin characters to the Adobe-Korea1.cidToUnicode
+  file in the Korean language support package.
+Fixed the code that handles page rotation in PSOutputDev.
+When converting a Type 1C glyph to a Type 1 glyph, insert closepath
+  operators as appropriate.
+Check for a sane 'loca' table in TrueType fonts (FoFiTrueType::parse).
+Fix PSOutputDev to correctly handle the case of an empty name in a
+  font encoding.
+
+3.02 (2007-feb-27)
+------------------
+Added anti-aliasing for vector graphics; added the vectorAntialias
+  xpdfrc option; added the "-aaVector" switch to xpdf and pdftoppm.
+Implemented stroke adjustment (always enabled by default, ignoring the
+  SA parameter, to match Adobe's behavior), and added the strokeAdjust
+  xpdfrc command.
+Support PDF 1.6 and PDF 1.7.
+Added support for AES decryption.
+Added support for OpenType fonts (only tested with 8-bit CFF data so
+  far).
+Added user-configurable key/mouse bindings - the bind/unbind xpdfrc
+  commands.
+Cleaned up the full-screen mode code and added the ability to toggle
+  it on the fly (the default key binding is alt-f).
+Pdfimages with the -j option now writes JPEG files for 1-component
+  (grayscale) DCT images, in addition to 3-component (RGB) images.
+Fixed bugs in handling sampled (type 0) functions with 32-bit
+  samples.
+Fixed some things to support DeviceN color spaces with up to 32
+  colorants.
+Pdftops now constructs the %%Creator and %%Title DSC comments from the
+  relevant information in the PDF Info dictionary.
+Tweak the TrueType font encoding deciphering algorithm.
+Added the "mapUnkownCharNames" xpdfrc option.
+Fix a bug (that only showed up with certain window managers) in the
+  intermediate resize event optimization.  [Thanks to Michael Rogers.]
+Check for a broken/missing embedded font (this was causing xpdf to
+  crash).
+Added support for transfer functions in PostScript output.
+Be a bit more tolerant of Link destinations that contain null values
+  for positioning parameters.
+Use ordered dot dithering instead of clustered dot dithering at 
+  resolutions below 300 dpi (for monochrome output).
+Fixed security holes (bounds checking issues) in several places.
+Don't bother creating a SplashFont (allocating memory) for fonts that
+  are only used for hidden text - this avoids problems with fonts of
+  unreasonably large sizes.
+Clipping in TextOutputDev was off for characters on the left edge of
+  the page.
+The scn and SCN operators weren't correctly handling colors with more
+  than four components.
+FoFiType1::writeEncoded wasn't always correctly finding the end of the
+  encoding.
+Use the ColorTransform parameter in the DCTDecode stream dictionary.
+Type 3 fonts are allowed to have a bbox of [0 0 0 0], which means
+  "unspecified" -- don't issue error messages in that case.
+Perform the transform (to device space) in Splash instead of in
+  SplashOutputDev -- this is needed to correctly handle round joins
+  and caps on stroked paths.
+PSOutputDev now rasterizes any pages that use transparency.
+Limit the crop, bleed, trim, and art boxes to the edges of the media
+  box (per the PDF spec).
+Change GString to increase the allocation increment by powers of two.
+Handle whitespace in hex strings in CMap files/streams.
+Use strings instead of names for separation colorant names in
+  PSOutputDev.
+For explicitly masked images where the mask is higher resolution than
+  the image, use the soft mask code.
+Avoid problems with very large x-steps in the PostScript output for
+  tiling pattern fills.
+Avoid a divide-by-zero in stitching functions which have a subfunction
+  with empty bounds.
+Honor the "Hidden", "NoView", and "Print" flags on annotations.
+Rewrote the pixel rendering code in Splash to use a single set of
+  pixel pipeline functions.
+Added support for transparency groups and soft masks.
+Fixed the transparency blend functions to match the addendum published
+  by Adobe.
+Changed Splash/SplashBitmap to store alpha in a separate plane.
+Setting the color space now selects the correct default color for that
+  color space.
+Remove the mutex lock from GlobalParams::getErrQuiet() to avoid a
+  deadlock when parseCIDToUnicode() or parseUnicodeToUnicode() calls
+  it from inside a locked section.
+Added error checking (on the argument count) in the sc/SC/scn/SCN
+  operators.
+Skip over notdef glyphs in TrueType fonts (which sometimes get drawn
+  as little boxes), to match Adobe's behavior.
+Painting operations in a Separation color space with the "None"
+  colorant or a DeviceN color space with all colorants set to "None"
+  never mark the page.
+Fixed an obscure bug in the JPX decoder - it wasn't reading the extra
+  stuffing byte in the case where the last byte of a packet header was
+  0xff.
+Change the TrueType font parser (FoFiTrueType) to change the glyph
+  count rather than report an error if the 'loca' table is too small.
+Fixed a couple of bugs in the JBIG2 decoder.
+Added stochastic clustered dot dithering.
+Added the screenType, screenSize, screenDotRadius, screenGamma,
+  screenBlackThreshold, and screenWhiteThreshold xpdfrc settings.
+PSOutputDev now correctly handles invalid Type 3 charprocs which don't
+  start with a d0 or d1 operator
+FreeType 2.2.x support - get rid of the FT_INTERNAL_OBJECTS_H include,
+  and add some 'const' declarations.
+Handle PDFDocEncoding in Info dictionary strings.
+Tweak the xref repair code - ignore whitespace at the start of lines
+  when looking for objects.
+Added the "-exec" switch to xpdf.
+Removed the xpdf.viKeys X resource.
+Changed the color key / explicit masked image code in PSOutputDev to
+  generate better PS code, including a Level 3 option.
+Tweaked the DEBUG_MEM code for performance.
+Move the JBIG2 global stream reading code into reset() instead of the
+  constructor - this way, pdftotext doesn't end up reading the global
+  stream.
+Added the "-preload" option to pdftops and the psPreload xpdfrc
+  command.
+Added the "zoom to selection" command (on the popup menu).
+Fix a bug (in xpdf/pdftoppm/pdftops) with tiling patterns whose bbox
+  size is different from their xStep/yStep.
+Implemented stroke with pattern color spaces.
+Following a link to a page whose CropBox was different from the
+  MediaBox was resulting in an incorrect scroll position.
+Parse truncated date strings from the Info dictionary correctly.
+Change FoFiType1 to handle Type 1 fonts with two /Encoding keys.
+Extend the PSOutputDev shaded fill code to handle DeviceCMYK shaded
+  fills in level2sep and level3sep modes.
+Detect infinite loops in the Page tree.
+Optimized the ASCII85Encoder code.
+Tweaked the text extractor to do a better job of lining up rows of
+  text.
+Leave images compressed (or re-compress them with RLE) in PostScript
+  output when setting up images for forms and Type 3 fonts (or with
+  -preload).
+Extend FoFiType1 to handle Type 1 fonts with octal character codes in
+  their encodings.
+Use a custom string formatter to avoid problems with locale-based decimal
+  formatting (commas instead of periods) in PS output.
+Allow comments in PostScript-type functions.
+Change the TrueType font parser (FoFiTrueType) to delete glyf table
+  entries that are too short.
+
+3.03 (2011-aug-15)
+------------------
+Added the "fixed pitch" text extraction mode.
+Modified "pdftops -paper match" to handle PDF files with
+  different-sized pages, i.e., it will now select the matching paper
+  size on a page-by-page basis.
+Add ability for pdftoppm to write to stdout.
+Added the pdfdetach tool.
+Implemented 256-bit AES decryption.
+Commented out the t1lib section in the configure script -- t1lib has
+  some potential security holes, and hasn't been updated in years.
+Redesigned the font configuration xpdfrc commands: removed the
+  displayFontT1, displayFontTT, displayNamedCIDFontT1,
+  displayCIDFontT1, displayNamedCIDFontTT, displayCIDFontTT, psFont,
+  psNamedFont16, and psFont16 commands; added the fontFile,
+  fontFileCC, psResidentFont, psResidentFont16, and psResidentFontCC
+  commands.
+Switched from GPLv2 to dual v2/v3 licensing.
+Performance: cache tiling patterns.
+Implemented text fills with pattern color spaces.
+Rewrote the image and image mask rendering code to be more accurate
+  and faster.
+Fixed a bug in PDFCore that could sometimes cause crashes at high zoom
+  levels.
+Implemented embedded CMap streams.
+Added the 'setSelection' command.
+Added the 'rotateCCW' and 'rotateCW' commands.
+Added the 'psFontPassthrough' xpdfrc command.
+Added the 'launchCommand' xpdfrc command.
+Implemented alpha-type soft masks.
+Added "Form: AcroForm|XFA|none" to pdfinfo output.
+Added support for image masks filled with pattern color spaces.
+Text search wasn't finding all occurrences of a string on rotated pages
+  (i.e., pages where the primary rotation, as displayed, was not
+  horizontal).
+The text extractor now uses "ActualText" spans if they are present.
+Modified PSOutputDev so it always check for transparency; if Splash is
+  not available, it now prints a warning.
+Handle the blending color space for soft masks.
+Added the disableFreeTypeHinting xpdfrc option.
+Added the psAlwaysRasterize xpdfrc option.
+Added support for transfer functions in the rasterizer.
+Optimized the JPEG 2000 decoder to use less memory.
+Do fill adjustment (similar to stroke adjustment) on simple
+  rectangular fills.
+Added the antialiasPrinting xpdfrc setting.
+Added '%i', '%j', and '%k' (mouse pointer page and position) to the
+  available options for the 'run' command.
+Links with the underlined border style were being drawn with the lines
+  over, instead of under, the text.
+Add #include <aconf.h> to XPDFTree.cc (to avoid problems with certain
+  compilers).
+Change XRef::fetch() to avoid infinite loops caused by odd damage to
+  the xref table (e.g., where a stream object's "Length" value is an
+  indirect reference that points to another stream object).
+Minor fix in GString.cc to deal with an error in newer C++ compilers
+  (pow() can take int or double args).
+Handle embedded fonts which are declared with the wrong font type,
+  including 8-bit fonts declared as CID fonts and vice versa; this
+  was causing various problems, including crashes and invalid
+  PostScript output.
+In text extractor, don't drop horizontally overlapping words onto a
+  separate line.
+The numbers in the operand to the TJ operator should be multiplied by
+  the current horizontal scaling parameter.
+Fixed a bug in the Type 1C-to-Type 1 font converter -- need to escape
+  strings in the font dictionary.
+The zero-font-size check in PSOutputDev.cc was broken.
+Fixes for the form field appearance regeneration code: handle Unicode
+  strings (by downconverting to Latin1); check for "True" in addition
+  to "Yes" as a button value.
+Modify XPDFTree to limit the widget height to 32767 pixels -- this
+  avoids crashes with very large outlines.
+Modify FoFiType1 to handle PFB headers in Type 1 font files.
+Allow image mask decode arrays to be [1.0 0.0] in addition to [1 0].
+Tweak the form field appearance regeneration code.
+PSOutputDev now sets up resources for DR dictionaries in forms (in
+  case the form field appearances are regenerated).
+For TrueType fonts which are marked symbolic, the cmaps should be
+  ignored.
+Change the handling of numeric characters in Unicode text output --
+  they are now treated as left-to-right, which isn't strictly correct,
+  but does result in correct visual formatting.
+Modify FoFiTrueType to handle bogus loca table entries where the
+  offset is past the end of the glyf table.
+Fixed shading pattern fills to do clipping and background fills
+  correctly.
+Change the code that reads the page tree to be more flexible with
+  regard to improperly constructed trees.
+Changed the PostScript header comment from "Produced by xpdf/pdftops
+  x.yy" to "XpdfVersion: x.yy" to make it (mostly) DSC-compliant.
+Fixed PSOutputDev to handle page rotation correctly where the PDF page
+  is rotated 90 or 270 degrees and the page width (before rotation) is
+  greater than its height (and vice versa for 0 or 180 degrees).
+Unbalanced save/restores inside form XObjects (which can be happen in
+  damaged or incorrectly constructed PDF files) could cause crashes.
+The CCITTFax decoder could go into an infinite loop on certain types
+  of corrupt input.
+Added the "drawAnnotations" xpdfrc command.
+Added the "psUncompressPreloadedImages" xpdfrc command.
+Escape newlines and other non-printable characters in the custom color
+  DSC comments in PostScript output.
+Added the tilingPatternFill and *ShadedFill functions to
+  PreScanOutputDev to speed it up.
+Too many elements in an image's Decode array should not be a fatal
+  error.
+Fixed a buffer overflow security hole in StreamPredictor.
+Empty pages (zero-length content streams and null contents) were
+  causing crashes in continuous view mode.
+Handle line dash arrays that start with a zero element correctly.
+PreScanOutputDev was not correctly detecting all transparency - in
+  some places, it was looking only at the blending mode, and not at
+  the opacity setting.
+Force halftone screen size to be a power of 2.  This allows optimizing
+  the halftoning code.
+Fixed a bug in the JBIG2 MMR decoder (and also in the CCITTFax
+  decoder) that was causing array references with negative indexes.
+Fixed a bug in the transparency code that was causing memory overruns.
+Fixed a 64-bit bug in the ASCII85 encoder.
+The focusToPageNum was crashing in full-screen mode - it should simply
+  do nothing.
+Added '%p' (current page number) to the available options for the 'run'
+  command.
+Tweak the behavior with PDF files that have pages of different widths
+  in continuous mode: scroll horizontally to avoid blank space on the
+  left side (when changing pages via entering a page number or
+  clicking on a link).
+A closepath followed by a lineto should create a new subpath for the
+  lineto.
+Fixed a buffer overflow in the CCITTFax decoder.
+Adobe Acrobat appears to ignore the flatness setting when rasterizing
+  curves, so Xpdf now does the same.  (Oddly, Acrobat passes the
+  flatness setting through to PostScript output, which ends up making
+  the PS file look different from the PDF file - so Xpdf continues to
+  pass flatness through to PS files, too.)
+Pdfimages now ignores tiling pattern fills (for performance reasons).
+Fixed a bug in PDFCore that could sometimes cause crashes at high zoom
+  levels.
+Use std::sort (with functors) in place of qsort (if available) - this
+  can be significantly faster.
+Hitting "close" while Xpdf is in full-screen mode (with only one file
+  open) was crashing.
+Tweak the TrueType font encoding deciphering algorithm.
+Rewrote the CCITTFax decoder inner loop - this fixes a security hole.
+Fixed two security holes (missing bounds checks) in the DCT decoder.
+Do the correct zooming for "Fit" and "FitR" destinations.
+Implement the rotation value in the form field appearance regeneration
+  code.
+When PSOutputDev rasterizes a page (because it contains transparency),
+  the image is broken into stripes if it is above a size threshold.
+Don't clip the other page boxes to the MediaBox at the intermediate
+  (Pages) nodes; only do it at the leaf (Page) nodes - the other boxes
+  can be specified before the MediaBox is specified.
+Split several special cases off from Splash::pipeRun, for performance.
+Add a sanity check for excessively large font sizes (which caused
+  problems because Xpdf tried to allocate memory for a font cache).
+Fixed a bug in the GfxCIDFont constructor involving reading the
+  vertical metrics.
+Rewrote the code that handles annotation transforms - it was not
+  handling non-rectangular transforms correctly.
+Tweak the Type 3 bbox code to allow some slack on the left/bottom
+  edges, as well as the top/right edges.
+Fixed a bug that was causing problems rendering Type 3 fonts to 1-bit
+  monochrome output.
+Handle the case where AES padding is invalid.
+Changed XRef::getNumObjects to return the number of used entries,
+  instead of the same value as returned by XRef::getSize().
+Ignore bogus entries in the TrueType table directory instead of
+  immediately giving up on the font.
+Fixed a bug in the radial shading code that could cause divide-by-zero
+  errors.
+Tweaked the TrueType font fixup code to deal with an extra zero entry
+  at the end of the loca table.
+Increased font cache sizes to improve performance.
+Tweaked the TrueType notdef kludge to skip the notdef glyph only if it
+  was caused by a character for which a Unicode/MacRoman mapping could
+  not be found.
+Added another font size sanity check, this time for Type 3 glyphs.
+Added initial support for optional content (layers) - no GUI yet.
+For CID fonts that specify the 'Adobe-Identity' or 'Adobe-UCS'
+  collection, use an identity mapping for char code-to-Unicode
+  conversion.
+Updated the error function to take a category argument, and to use
+  GString::format instead of printf; added an error callback.
+The PDF spec claims that names are limited to 127 chars, but Distiller
+  8 will produce longer names, and Acrobat 8 will accept longer names
+  -- Xpdf will now handle them, too.
+Change the Catalog code so it doesn't load the entire page tree at
+  startup (this only helps the command line tools - the viewer scans
+  all of the pages at startup).
+Clip opacity values to the range [0,1].
+Handle glyph names of the form 'unixxxx' (similar to Ann, Axx, etc.).
+Resolution of rasterized pages in PostScript output was not being
+  computed correctly (resulting in overly large images).
+Extend the mapUnknownCharNames config command to cover CID fonts.
+Zero-length segments in the middle of stroked paths were not being
+  handled correctly -- they should be completely ignored, except for
+  the special case of a zero-length path with round line caps.
+Various optimizations to the Splash rasterizer.
+Allow "Identity" and "Default" as the transfer function in soft masks.
+Tweaked the ToUnicode CMap parser to allow <00xx> char codes for 8-bit
+  fonts.
+TextPage::clear() was not clearing the lists of underlines and links.
+Changed the CCITTFax decoder to correctly handle the interaction
+  between the EndOfLine and EncodedByteAlign parameters.
+Fixed a bug where xpdf wouldn't go to a destination specified on the
+  command line, if continuous mode was enabled
+  (xpdf -cont file.pdf +foo).
+Fixed a bug in the FreeType interface code which was causing incorrect
+  positioning/sizing of characters.
+Tweaked the FreeType interface code - use 'light' hinting with Type 1
+  fonts (because it generally looks better).
+Tweak the Windows font searching code to handle "Black" fonts.
+Fixed a bug in the PostScript-type function parser -- real numbers
+  that start with a decimal point weren't being handled correctly.
+Changed the way filled-and-stroked text is handled -- use Splash to do
+  both the fill and the stroke (rather than using the font ending to
+  do the fill and Splash to do the stroke); also turn off stroke
+  adjustment when drawing stroked text.
+Ignore generation numbers on compressed objects (to match Adobe's
+  behavior).
+Changed the PostScript PageSize policy from 3 to 6 (choose next
+  largest paper size, don't rescale).
+Check that the mask values for color key masked images are in range --
+  both Ghostscript and Distiller choke on the PostScript if invalid
+  mask colors are specified.
+Fixed a bug in generic region decoding in the JBIG2 decoder.
+Fixed the lexer to handle large real numbers correctly.
+Pdftops wasn't correctly handling tiling patterns that use both fill
+  and stroke operators.
+Report an error if the Size array in a sampled function includes any
+  zeros.
+The PostScriptFunction.codeString field wasn't being initialized
+  correctly.
+Invalid object streams were not being handled correctly.
+Check for loops in xref tables.
+Handle the case where a content stream ends after the 'ID' operator
+  which starts inline image data.
+Check for invalid object streams (i.e., an object stream can't be
+  inside another object stream.
+Add a recursion limit to the object parser - this avoids stack
+  overflows with various sorts of damaged PDF files.
+Handle various parameter settings in the extended graphics state
+  dictionary.
+An invalid document outline (missing fields) could cause a crash.
+Added an overprint preview mode (for CMYK output only).
+Correctly handle FitH/BH/V/BV link destinations that contain null
+  values for positioning parameters.
+If the PDF file doesn't define a BaseURI, set one based on the
+  location of the PDF file -- this allows relative links to be handled
+  correctly.
+Use ResusableStreamDecode when generating Level 3 PostScript for an
+  explicitly masked image.
+Tweak the Type 1 font parser to handle encodings with multiple
+  characters on one line.
+Invert subtractive color components before passing them to the
+  blending function.
+Fix an invalid array access in SplashOutputDev::setSoftMask() in CMYK
+  mode.
+A PS interpreter may attempt to read past the end of a preloaded
+  image, so we need to check for that.
+Fixed several overflow/uninit bugs in JBIG2Stream.
+Fix the CCITTFax decoder to correctly find end-of-file (RTC/EOFB)
+  markers when the EndOfLine parameter is false.
+Don't limit the startxref offset to 10 digits - some PDF generators
+  use extra leading zeros.
+Use the "DV" (default value) field in text annotations if the "V"
+  (value) field is missing.
+Increase the number of digits printed for floating point numbers in
+  PostScript output -- it was running into numerical accuracy problems
+  on large pages.
+Fixed integer overflow bugs in Catalog.cc and GfxFont.cc.
+Zero-length tables in TrueType fonts should be treated as missing.
+ToUnicode CMaps map char codes to Unicode; .cidToUnicode files map
+  CIDs to Unicode -- ToUnicodeCMaps were being handled incorrectly.
+Added the "psRasterResolution" and "psRasterMono" xpdfrc commands.
+Added code to FoFiTrueType to check for entries in the table directory
+  with bogus tags -- this handles the case where the number of tables
+  given in the header is too high.
+Negative shift values in the PostScript bitshift operator were being
+  handled incorrectly.
+GfxICCBasedColorSpace was not correctly bounds-checking the number of
+  components.
+Check SampledFunction input values for NaNs.
+Fix a divide-by-zero when the page width or height is zero.
+Fix a bug positioning text in PostScript output - if the last char in
+  a string has an incorrect width in the PDF font object, that messes
+  up the total string width, so we have to position individual chars.
+In PostScript output, if CID font substitution fails, drop all text in
+  that font.
+Handle PDF files that set the stream/string decryption filters to
+  Identity (i.e., no encryption).
+Avoid passing a zero font size to FreeType.
+Tweak raw mode in the text extractor to handle words on the same line
+  drawn in the wrong order.
+Pdftops was generating a singular transform matrix for annotations
+  whose bounding boxes had zero width or height.
+Handle embedded OpenType CFF fonts with CIDToGIDMaps.
+Remove the old kludge from PSOutputDev that was converting char 32
+  from ".notdef" to "space".
+Allow CCITTFax images to be more than 32k pixels wide.
+Modified the CMap parser to handle usecmap with Identity-H/V.
+Added some parameter checking in the JPX decoder.
+Added checks for infinite loops in PDF objects - for color spaces,
+  functions, optional content, and outline items.  
+Add support for mouse button bindings up to button 32 (old limit was
+  7).
+Fixed a bug in the decryption code for revision 3 with keyLength < 16
+  -- the owner password was not being handled correctly.  [Thanks to
+  Matthias Franz.]
+Optimize SampledFunction: pull index computation code out of the
+  transform function; cache the last transform.
+Tweaked the font naming algorithm in PSOutputDev.
+Treat mirrored (as opposed to rotated) text the same as upright text
+  in pdftotext.
+Fixed buffer overflows in Splash and SplashBitmap.
+Check for bogus character codes (e.g., <ffffffff>) in ToUnicode CMaps.
+The radial shading code (in both Gfx.cc and PSOutputDev.cc) was not
+  computing the s bounds properly.
+Drop empty subpaths in clip and fill (but not stroke) operations -
+  this can significantly speed up clip performance in the weird case
+  where a PDF file does "<x> <y> <w> <h> re <x> <y> m h W n".
+Added code to FoFiTrueType to check for an invalid loca format field
+  in the head table.
+The axial shading code (in Gfx.cc, but not PSOutputDev.cc) was not
+  computing the incremental polygon vertices correctly.
+Set the character width in TextOutputDev to something sensible for
+  vertical fonts.
+Added basic support to the text extractor for vertical writing mode.
+The non-interactive tools (pdftotext, pdftops) now free Page objects
+  after using them, avoiding performance problems with pages that have
+  huge resource dictionaries.
+Check for line dash arrays like [0], and draw nothing at all (to match
+  Acrobat's behavior).
+Add a sanity check for the ascent/descent values in FontDescriptors.
+Single-point paths with a line dash were causing a crash.
+Correctly handle Level 3 PostScript output with masked images inside
+  patterns.
+Tweaked the xref repair code so that it runs if the catalog is messed
+  up (in addition to running if the xref table is damaged).
+If Indexed color space tables (streams or strings) are too small,
+  reduce the max index instead of throwing away the color space.
+Change the CMap parser to allocate memory only when it sees a mapping,
+  not when it sees a 'codespacerange' declaration -- this avoids
+  allocating huge amounts of memory for CMaps with large, unused
+  codespaceranges.
+In monochrome mode, treat lines with width <= 1 as hairlines (to match
+  Acrobat's behavior).
+Cache the last transform for PostScript-type functions.
+Added the "-rawdates" option to pdfinfo.
+Optimized ImageStream::getLine().
+Fixed the Hue, Saturation, Color, and Luminosity blend functions to
+  match Adobe's spec.
+Fixed the non-isolated group compositing computation.
+Skip extraneous unused data at the end of JBIG2 segments.
+Change the algorithm that stroke/fill adjustment uses so that the
+  edges of adjacent strokes/fills line up.
+Do stroke adjustment on end caps when cap style is butt or projecting.
+Fixed a security hole: Gfx.parser was not being initialized to NULL.
+Fixed a security hole: integer bounds check in the Type 1 encoding
+  parser in FoFiType1.cc.
+If an embedded font object is invalid or non-existent, do font
+  substitution (same as if there were no embedded font).
+TextOutputDev was reusing an old font in the case where the font
+  changed but the font size and character positioning stayed the same.
+When starting a transparency group, copy the fill/stroke colors from
+  the graphics state.
+Tweaked the fixed-point code.
+When a TrueType font is declared resident (with a psFont command),
+  don't munge the encoding.
+Look for URIs starting with "www." and treat them as absolute "http:"
+  URIs, not as relative URIs (to match Adobe's behavior).
+Added code to FoFiTrueType to check for a zero-entry cmap table.
+Tweaked the font substitution code to do a better job of scaling the
+  substituted font.
+Require at least two splits in the axial shading color bisection.
+Optimized JBIG2Stream::readGenericBitmap().
+JPXStream wasn't correctly handling row padding for images with fewer
+  than 8 bits per component.
+Optimized the ToUnicode CMap parser.
+Added a "whole words only" option to text searches.
+Check for valid component parameters in the DCT decoder.
+Implement embedding of external 16-bit fonts (without subsetting) in
+  PostScript output.
+Added the minLineWidth xpdfrc command.
+Added warning messages for font substitutions.
+
+3.04 (2014-may-28)
+------------------
+New text extractor.
+Added the pdftohtml tool.
+Added the pdftopng tool.
+New trapezoid-based rasterizer core (for performance).
+Generate appearance streams for Line, PolyLine, and Polygon
+  annotations.
+Added the closeWindowOrQuit command, and changed the default binding
+  for ctrl-W from closeWindow to closeWindowOrQuit.
+Implemented the new AES-256 mode (R=6, Acrobat X).
+Add an object cache.
+Added a small cache for object streams.
+Modify PSOutputDev to use LZW compression instead of RLE, with a
+  fallback to RLE if the "psLZW no" setting is given.
+Pdfinfo now prints page rotation info.
+Modified ImageOutputDev, used by pdfimages, to output the masks and
+  soft masks used when drawing images.
+Remove non-printable characters from error output, just in case they
+  might cause problems for the terminal program.
+Added initial support for Code3of9 bar codes in XFA forms.
+Added the mapExtTrueTypeFontsViaUnicode xpdfrc command.
+Apply stroke adjustment to rectangular images and clipping regions (in
+  addition to strokes and fills).
+Decode JPEG 2000 images at less than full resolution if the full res
+  image isn't needed (i.e., if the raw image is higher resolution than
+  the output). 
+Implemented knockout groups.
+Removed t1lib support.
+Added support for images with 16-bit components.
+Rewrote the Dict class to use a hash table; as a side effect, this
+  handles dictionaries with multiple definitions for a key, which are
+  in violation of the spec, but Acrobat appears to handle.
+The transformed line width computation -- used to implement the
+  minLineWidth setting, and the hairline threshold in monochrome mode --
+  was incorrect.
+Pdftops was not correctly handling the case where it couldn't find
+  a 16-bit font -- this led to crashes and/or invalid PostScript.
+A bug in FlateStream::getBlock() was causing problems with narrow
+  images.
+Use the correct _WIN32 define instead of WIN32.
+Use copy-on-write for the clip path in SplashState (when doing gsave),
+  for performance.
+Added a Solaris-specific entry to the ghostscript font search path.
+SplashState was initializing line width to 0 instead of 1.
+Abort processing on a content stream after getting 500 errors
+  (undefined operator, wrong number of args) -- this avoids very long
+  processing time for malicious PDF files using bogus RLE encoded
+  content streams.
+Added the psUseCropBoxAsPage xpdfrc option; "pdftops -pagecrop" now
+  sets psUseCropBoxAsPage; "pdftops -pagecrop -paper match" now uses
+  the CropBox as the page size.
+Re-architected the AcroForm support code into a separate AcroForm
+  module.
+Fixed the handling of overprinting/transparency interaction, using
+  the CompatibleOverprint blend mode.
+The TIFF predictor code for the 1-bit-per-pixel case was broken.
+For triangle and patch mesh shadings (types 4-7) with color functions,
+  interpolate the function parameter not the color.
+Check the fontFile/fontDir commands before (instead of after) doing
+  Base-14 substitution in PS output.
+Correctly handle non-embedded TrueType fonts that have an Identity
+  ToUnicode mapping (display and PS output were failing).
+Added support for XFA form rendering, including an "enableXFA" xpdfrc
+  setting.
+Handle PFB Type 1 fonts when generating PostScript output.
+Unwind any extraneous saved graphics state at the end of the page
+  (before drawing annotations).
+Added some integer overflow checks in the GString class.
+Handle 16-bit components in JPEG 2000 images.
+ActualText spans can end without a valid font, in which case
+  TextPage::beginWord was crashing.
+The Domain entry in function shadings wasn't being parsed correctly.
+Fixed a bug in the JPEG decoder - successive approximation
+  (progressive mode) coefficients weren't being handled correctly.
+Added a better infinite loop test to the xref parser.
+When generating PostScript, merge reused TrueType fonts (if their
+  code-to-GID mappings are the same).
+Tweak the Gouraud triangle shaded fill code to end the recursive
+  splitting if the triangles get sufficiently small.
+Do bilinear interpolation when upsampling images.
+When skipping extraneous image data from an inline image, look for
+  EI<whitespace> instead of just EI.
+When writing to stdout on Windows, pdftoppm now sets the file mode to
+  binary.  [Thanks to Robert Frunzke.]
+Accept strings as well as names for the BaseFont entry in font
+  objects.
+Removed the TEXTOUT_WORD_LIST config option (with the new text
+  extractor, this is always enabled).
+Fixed a bug in the JBIG2 decoder (the TPGD context for template #3 in
+  readGenericBitmap was incorrect).
+Rewrote the PostScriptFunction code for performance.
+Handle 8-bit OpenType CFF fonts that are missing required tables in
+  the OpenType wrapper.
+Handle tiling patterns with reversed coordinates in their bounding
+  boxes.
+Added support for 64-bit file offsets, i.e., PDF files larger than
+  2GB.
+Optimize the code that rasterizes pattern-filled image masks.
+Added support for Mac OS X system fonts (Base-14 only).
+The backdrop color in luminosity-type soft mask groups was not being
+  handled correctly.
+Modified behavior of "pdftops -paper match -duplex ..." - it will now
+  duplex consecutive same-sized pages.
+Tweak the handling of degenerate fills ('moveto lineto fill') to
+  match Adobe.
+Don't honor the OPM=1 setting with ICCBased CMYK color spaces.
+Whole-word searches were treating certain punctuation (Unicode number
+  separators and terminators) as part of the word, e.g., searching for
+  "foo" would not match "foo,".
+Use the TextString class everywhere it makes sense.
+Removed the unnecessary segment sort in Splash (performance
+  optimization).
+Handle hyperlinks that use Widget-type annotations.
+Fix up the integer overflow checks to avoid issues with clever
+  compilers.  [Thanks to Nickolai Zeldovich.]
+Correctly handle streams with missing Length entries in damaged PDF
+  files.
+Added a compile-time option (LOAD_FONTS_FROM_MEM) to load fonts from
+  memory rather than temporary files on disk.
+Added the psRasterSliceSize xpdfrc option.
+Fixed a case in the JPEG 2000 arithmetic decoder where extra data is
+  present in packet i, and needs to be saved for use in packet i+1.
+Fixed a bug in the JPEG 2000 decoder related to images with fewer than
+  8 bits per component.
+Handle the case in PSOutputDev where slice size overflows a 32-bit
+  integer.
+Add (partial) support for TrueType cmap format 2.
+Always pass FT_LOAD_NO_BITMAP to FreeType -- bitmaps apparently fail
+  with rotated characters.
+Support fonts specified in ExtGState dictionaries.
+Annotations with empty Border arrays should not draw a border.
+Fix the CMap parser to handle large CID ranges.
+Check for Type 3 CharProcs that call q or Q before the d0/d1 operator,
+  and treat them as uncacheable.
+Invert the selection color when starting in reverse video mode.
+Device{Gray,RGB,CMYK} cannot be mapped via a resource dict.
+Changed the PS output for masked images (explicit and color key
+  masking): use a plain old clip path instead of rectclip to avoid
+  array overflows.
+Check the StemSnapH/V arrays when converting Type 1C fonts to Type 1 -
+  if there are any duplicate or out-of-order values, skip that
+  StemSnapH/V array.
+Added the psMinLineWidth xpdfrc setting.
+Fix an obscure issue in converting TrueType fonts to Type 42, related
+  to empty glyph descriptions (12 zero bytes).
+Pdftops now reports an error if there were any I/O errors writing to
+  the PS output file.
+Fix vertical text (CJK fonts) in PS output -- offset the character
+  origin correctly.
+Increased the number of digits used by pdfimages for the image number
+  from three to four.
+Handle right-to-left (e.g., Arabic) ligatures correctly in the text
+  extractor.
+Added the -loc and -locPS options to pdffonts.
+Extend the object parser recursion limit to cover Stream::addFilters()
+  / Stream::makeFilters() - to avoid another possibility of stack
+  overflow.
+Disable FreeType autohinting, because it can fail badly with font
+  subsets that use invalid glyph names -- except in the case of Type 1
+  fonts, which look much better with light autohinting.
+Modified the rasterizer pipeline functions to process a scan line at a
+  time (for performance).
+Removed VMS build support (it hasn't been updated in ages).
+Removed pdftotext's '-htmlmeta' option (use pdftohtml instead).
+PSOutputDev's font/form setup code, and pdffonts, were not scanning
+  soft mask groups in ExtGState dictionaries.
+Invalid DCT input (e.g., from a damaged PDF file) could overflow the
+  dctClip array.
+When upsampling an image mask or image with a large resulting image
+  size, do it in stream mode instead of prescaling the whole image
+  (to avoid running out of memory).
+Added infinite loop detection to pdffonts.
+
+4.00 (2017-aug-10)
+------------------
+Complete rewrite of the xpdf GUI, using Qt instead of Motif.
+Multithreaded rasterization in the viewer - pages are now rasterized
+  in a separate thread so that scrolling and other UI activity is not
+  blocked.
+Added a linear text selection mode.
+Limit transparency groups to the clip rectangle (performance
+  optimization).
+Optimized the SplashScreen code, used in 1-bit monochrome mode.
+Added setmode calls to pdftopng, when writing to stdout on Windows.
+  [Thanks to Robert Schroll.]
+Regenerate the appearance stream for an AcroForm field if there is
+  no appearance stream, even if NeedAppearances isn't set.
+When regenerating AcroForm field appearance streams, use a default
+  font (Helvetica) for fields that don't have a valid font.
+When the line width is increased by the minLineWidth setting, force
+  the line join mode to "bevel" to avoid "whiskers" at sharp corners.
+Change the pdftoppm/pdftopng resolution (-r) argument from an integer
+  to floating point.
+Use the FontDescriptor 'CapHeight' value in place of 'Ascent' if it's
+  available.
+OC (optional content) entries in image XObjects were being ignored.
+Added support for preblended soft-masked images.
+Added optional support for using libjpeg to decode DCT (JPEG)
+  streams.
+Transparency groups weren't being composited correctly in mono1 mode.
+Switched to a different IDCT transform in the DCT (JPEG) decoder, to
+  get better accuracy; the new transform is also a bit faster.
+Modified the polygon scan converter to avoid O(n^2) performance, which
+  can get really bad with paths that have extremely large numbers of
+  segments.
+Added the maxTileWidth, maxTileHeight, tileCacheSize, and
+  workerThreads xpdfrc settings.
+Modified PSOutputDev so it doesn't output resources (fonts, etc.) for
+  pages that are rasterized (because they use transparency).
+Added the -alpha flag to pdftopng.
+Handle TrueType fonts with out-of-bounds loca entries.
+Optimize consectutive "q" (gsave) operations in PostScript output.
+Various improvements to the XFA rendering code.
+Added the -nodiag flag to pdftotext.
+Switched to cmake instead of autoconf.
+Look for Widget-type annotations that aren't attached to the
+  AcroForm, and add them to the AcroForm.
+Added the "-z" option to pdftohtml.
+The JPEG 2000 decoder now treats invalid boxes after the image
+  codestream as non-fatal errors.
+Improved the handling of drop caps in pdftohtml.
+Added the "psNeverRasterize" setting.
+Pdftohtml now converts invisible PDF text to transparent HTML text.
+Check for zero-width/height bitmaps in the JBIG2 decoder -- these can
+  happen in corrupted JBIG2 streams.
+Pdfimages was inverting 1-bit monochrome images.
+Added CCITTFaxStream::getBlock() to speed up the CCITTFax decoder.
+Added resolution info to PNG files generated by pdftopng.
+The DCT decoder now ignores non-Adobe APP14 markers (rather than
+  generating an error).
+Regenerate appearance streams for FreeText annotations.
+The TextString class now handles UTF-16LE in addition to UTF-16BE.
+  (UTF-16LE isn't allowed by the PDF spec, but I've seen it used.)
+Added the simple one-column text extraction mode (pdftotext -simple).
+Added a -cmyk option to pdftoppm to write CMYK PAM files.
+Modified pdfinfo to output "static XFA" or "dynamic XFA", rather than
+  just "XFA".
+Added the "drawFormFields" xpdfrc command.
+Various performance optimizations in the rasterizer.
+Fixed the tiling pattern code to avoid stripes between pattern cells,
+  and to honor the TilingType flag.
+Always split axial shadings into 256 slices.
+Handle the case where an optional content block is left open at the
+  end of a page content stream.
+Accept code-to-CID-type CMaps as ToUnicode CMaps (to work around buggy
+  PDF generators).
+Unicode-to-unicode maps weren't working correctly with fonts that have
+  an "Identity" encoding.
+Modify the lexer to interpret "--123.4" as -123.4, "--123" as 0, and
+  "123-45" as "123" to match Adobe's behavior.
+The text extractor now reverses the order of side-by-side columns for
+  right-to-left scripts.
+Worked on right-to-left script support in pdftohtml (still not
+  complete).
+Switched the default display mode to continuous (from single page).
+Added the "-bom" switch to pdftotext.
+Share font objects when multiple font tags point to the same object.
+The transfer function goes before alpha blending in the rasterizer
+  pipeline.
+Added the "-allinvisible" switch to pdftohtml.
+Added overprint mode (OPM) support in Level 3 PostScript output.
+Added support for DeviceN color spaces in Level 3 PostScript output.
+Added the "-userunit" option to pdftops.
+Added the "-raw" and "-list" options to pdfimages.
+Pdfimages now writes PGM files (instead of PPM files) for DeviceGray
+  and CalGray images.
+Added grayscale PostScript output -- added the '-level2gray' and
+  '-level3gray' switches to pdftops.
+Fix up Type 1 fonts when embedding in PostScript.
+Added the "problematic" column to pdffonts output.
+Generate an appearance stream for signature fields in AcroForms.
+Correctly handle line dash patterns with zero-length segments when the
+  line cap is butt or project.
+Added the "strokeAdjust cad" xpdfrc option; fixed some issues with
+  stroke adjustment and projecting line caps.
+Added support for Code128B bar codes in XFA forms.
+Look at both the ON and OFF arrays in the default OCCD (optional
+  content config dict), to work around certain broken PDF files.
+Added support for XFA 'numericEdit' fields.
+Added support for 'picture' formatting in XFA forms.
+Added mouseClickN events (in addition to mousePressN and
+  mouseReleaseN).
+External CID OpenType CFF fonts should map through Unicode (similarly
+  to external CID TrueType fonts).
+Handle axial and radial shadings in SplashOutputDev, in device space.
+Use the PDF 2.0 inline image length field.
+Handle duplicate embedded Type 1 font names in PostScript output.
+Handle the case of a stroked closed path with a dash pattern -- if the
+  dash pattern is "on" at both the start and end of a subpath, it
+  should be joined.
+If the line dash phase is negative, add a multiple of 2x the total
+  dash length.
+Indexed color space indexes should be clipped to [0,hival].
+Fix the handling of the text render mode with Type 3 fonts, in the
+  rasterizer and in PostScript output.
+CID font metrics shouldn't be sorted - the PDF 2.0 spec says that, in
+  the case of duplicate entries, the first entry should be used.
+PSOutputDev now converts tiling patterns to PostScript tiling patterns
+  (in Level 2/3).
+Fixed a security hole in SecurityHandler.cc (uninitialized variables).
+  This vulnerability was discovered by Kushal Shah of Fortinet's
+  FortiGuard Labs.
+Fixed a security hole in Function.cc (write past end of array).
+Fixed a security hole with the use of d0/d1 operators outside of a
+  Type3 CharProc [CVE-2016-9027].
Index: main/trunk/greenstone2/bin/linux/xpdf-tools/COPYING
===================================================================
--- main/trunk/greenstone2/bin/linux/xpdf-tools/COPYING	(revision 32205)
+++ main/trunk/greenstone2/bin/linux/xpdf-tools/COPYING	(revision 32205)
@@ -0,0 +1,344 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+
+	Appendix: How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
Index: main/trunk/greenstone2/bin/linux/xpdf-tools/COPYING3
===================================================================
--- main/trunk/greenstone2/bin/linux/xpdf-tools/COPYING3	(revision 32205)
+++ main/trunk/greenstone2/bin/linux/xpdf-tools/COPYING3	(revision 32205)
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
Index: main/trunk/greenstone2/bin/linux/xpdf-tools/INSTALL
===================================================================
--- main/trunk/greenstone2/bin/linux/xpdf-tools/INSTALL	(revision 32205)
+++ main/trunk/greenstone2/bin/linux/xpdf-tools/INSTALL	(revision 32205)
@@ -0,0 +1,20 @@
+Xpdf - Unix binaries
+====================
+
+The Xpdf software and documentation are
+copyright 1996-2017 Glyph & Cog, LLC.
+
+Email: xpdf@xpdfreader.com
+WWW: http://www.xpdfreader.com/
+
+
+To install this binary package:
+
+1. Copy the executables (xpdf, pdftotext, etc.) to to /usr/local/bin.
+
+2. Copy the man pages (*.1 and *.5) to /usr/local/man/man1 and
+   /usr/local/man/man5.
+
+3. Copy the sample-xpdfrc file to /usr/local/etc/xpdfrc.  You'll
+   probably want to edit its contents (as distributed, everything is
+   commented out) -- see xpdfrc(5) for details.
Index: main/trunk/greenstone2/bin/linux/xpdf-tools/README
===================================================================
--- main/trunk/greenstone2/bin/linux/xpdf-tools/README	(revision 32205)
+++ main/trunk/greenstone2/bin/linux/xpdf-tools/README	(revision 32205)
@@ -0,0 +1,403 @@
+
+Xpdf
+====
+
+version 4.00
+2017-aug-10
+
+The Xpdf software and documentation are
+copyright 1996-2017 Glyph & Cog, LLC.
+
+Email: xpdf@xpdfreader.com
+WWW: http://www.xpdfreader.com/
+
+The PDF data structures, operators, and specification are
+documented in ISO 32000-1:2008.
+
+
+What is Xpdf?
+-------------
+
+Xpdf is an open source viewer for Portable Document Format (PDF)
+files.  (These are also sometimes also called 'Acrobat' files, from
+the name of Adobe's PDF software.)  The Xpdf project also includes a
+PDF text extractor, PDF-to-PostScript converter, and various other
+utilities.
+
+The Xpdf viewer uses the Qt cross-platform GUI toolkit.  The other
+command line utilties do not require Qt.
+
+
+License & Distribution
+----------------------
+
+Xpdf is licensed under the GNU General Public License (GPL), version 2
+or 3.  This means that you can distribute derivatives of Xpdf under
+any of the following:
+  - GPL v2 only
+  - GPL v3 only
+  - GPL v2 or v3
+
+The Xpdf source package includes the text of both GPL versions:
+COPYING for GPL v2, COPYING3 for GPL v3.
+
+Please note that Xpdf is NOT licensed under "any later version" of the
+GPL, as I have no idea what those versions will look like.
+
+If you are redistributing unmodified copies of Xpdf (or any of the
+Xpdf tools) in binary form, you need to include all of the
+documentation: README, man pages (or help files), COPYING, and
+COPYING3.
+
+If you want to incorporate the Xpdf source code into another program
+(or create a modified version of Xpdf), and you are distributing that
+program, you have two options: release your program under the GPL (v2
+and/or v3), or purchase a commercial Xpdf source license.
+
+If you're interested in commercial licensing, please see the Glyph &
+Cog web site:
+
+    http://www.glyphandcog.com/
+
+
+Compatibility
+-------------
+
+Xpdf uses the Qt toolkit and runs on Linux, Windows, and OS X -- and
+probably other systems that have a Qt port.
+
+The non-GUI command line utilities (pdftops, pdftotext, pdftohtml,
+pdfinfo, pdffonts, pdfdetach, pdftoppm, pdftopng, and pdfimages) run
+on Linux, Windows, and OS X -- and should run on pretty much any
+system with a decent C++ compiler.
+
+If you compile Xpdf for a system not listed on the web page, please
+let me know.  If you're willing to make your binary available by ftp
+or on the web, I'll be happy to add a link from the Xpdf web page.  I
+have decided not to host any binaries I didn't compile myself (for
+disk space and support reasons).
+
+If you can't get Xpdf to compile on your system, send me email and
+I'll try to help.
+
+
+Getting Xpdf
+------------
+
+The latest version is available from:
+
+  http://www.xpdfreader.com/
+
+Source code and several precompiled executables are available.
+
+Announcements of new versions are posted to comp.text.pdf and emailed
+to a list of people.  If you'd like to receive email notification of
+new versions, just let me know.
+
+
+Running Xpdf
+------------
+
+To run xpdf, simply type:
+
+  xpdf file.pdf
+
+To generate a PostScript file, run pdftops:
+
+  pdftops file.pdf
+
+To generate a plain text file, run pdftotext:
+
+  pdftotext file.pdf
+
+There are several additional utilities (which are fully described in
+their man pages):
+
+  pdftohtml -- converts a PDF file to HTML
+  pdfinfo -- dumps a PDF file's Info dictionary (plus some other
+             useful information)
+  pdffonts -- lists the fonts used in a PDF file along with various
+              information for each font
+  pdfdetach -- lists or extracts embedded files (attachments) from a
+               PDF file
+  pdftoppm -- converts a PDF file to a series of PPM/PGM/PBM-format
+              bitmaps
+  pdftopng -- converts a PDF file to a series of PNG image files
+  pdfimages -- extracts the images from a PDF file
+
+Command line options and many other details are described in the man
+pages: xpdf(1), etc.
+
+All of these utilities read an optional configuration file: see the
+xpdfrc(5) man page.
+
+
+Compiling Xpdf
+--------------
+
+See the separate file, INSTALL.
+
+
+Bugs
+----
+
+If you find a bug in Xpdf, i.e., if it prints an error message,
+crashes, or incorrectly displays a document, and you don't see that
+bug listed here, please send me email, with a pointer (URL, ftp site,
+etc.) to the PDF file.
+
+
+Third-Party Libraries
+---------------------
+
+Xpdf uses the following libraries:
+* FreeType [http://www.freetype.org/]
+* libpng [http://www.libpng.com/pub/png/libpng.html] (used by pdftohtml
+  and pdftopng)
+* zlib [http://zlib.net/] (used by pdftohtml)
+
+
+Acknowledgments
+---------------
+
+Thanks to:
+
+* Patrick Voigt for help with the remote server code.
+* Patrick Moreau, Martin P.J. Zinser, and David Mathog for the VMS
+  port.
+* David Boldt and Rick Rodgers for sample man pages.
+* Brendan Miller for the icon idea.
+* Olly Betts for help testing pdftotext.
+* Peter Ganten for the OS/2 port.
+* Michael Richmond for the Win32 port of pdftops and pdftotext and the
+  xpdf/cygwin/XFree86 build instructions.
+* Frank M. Siegert for improvements in the PostScript code.
+* Leo Smiers for the decryption patches.
+* Rainer Menzner for creating t1lib, and for helping me adapt it to
+  xpdf.
+* Pine Tree Systems A/S for funding the OPI and EPS support in
+  pdftops.
+* Easy Software Products for funding several improvements to the
+  PostScript output code.
+* Tom Kacvinsky for help with FreeType and for being my interface to
+  the FreeType team.
+* Theppitak Karoonboonyanan for help with Thai support.
+* Leonard Rosenthol for help and contributions on a bunch of things.
+* Alexandros Diamantidis and Maria Adaloglou for help with Greek
+  support.
+* Lawrence Lai for help with the CJK Unicode maps.
+
+Various people have contributed modifications made for use by the
+pdftex project:
+
+* Han The Thanh
+* Martin Schröder of ArtCom GmbH
+
+
+References
+----------
+
+Adobe Systems Inc., ISO 32000-1:2008, Document Management - Portable
+document format - Part 1: PDF 1.7.
+http://www.adobe.com/devnet/pdf/pdf_reference.html
+[The manual for PDF version 1.7.]
+
+Adobe Systems Inc., Adobe Supplement to the ISO 32000, BaseVersion
+1.7, ExtensionLevel 3, June 2008.
+http://www.adobe.com/devnet/pdf/pdf_reference.html
+[Additions to the PDF 1.7 spec.]
+
+Adobe Systems Inc., Adobe Supplement to ISO 32000-1, BaseVersion 1.7,
+ExtensionLevel 5, June 2009.
+http://www.adobe.com/devnet/pdf/pdf_reference.html
+[Additions to the PDF 1.7 spec.]
+
+Adobe Systems Inc., _PostScript Language Reference_, 3rd ed.
+Addison-Wesley, 1999, ISBN 0-201-37922-8.
+[The official PostScript manual.]
+
+Adobe Systems, Inc., _The Type 42 Font Format Specification_,
+Adobe Developer Support Technical Specification #5012.  1998.
+http://partners.adobe.com/asn/developer/pdfs/tn/5012.Type42_Spec.pdf
+[Type 42 is the format used to embed TrueType fonts in PostScript
+files.]
+
+Adobe Systems, Inc., _Adobe CMap and CIDFont Files Specification_,
+Adobe Developer Support Technical Specification #5014.  1995.
+http://www.adobe.com/supportservice/devrelations/PDFS/TN/5014.CIDFont_Spec.pdf
+[CMap file format needed for Japanese and Chinese font support.]
+
+Adobe Systems, Inc., _Adobe-Japan1-4 Character Collection for
+CID-Keyed Fonts_, Adobe Developer Support Technical Note #5078.
+2000.
+http://partners.adobe.com/asn/developer/PDFS/TN/5078.CID_Glyph.pdf
+[The Adobe Japanese character set.]
+
+Adobe Systems, Inc., _Adobe-GB1-4 Character Collection for
+CID-Keyed Fonts_, Adobe Developer Support Technical Note #5079.
+2000.
+http://partners.adobe.com/asn/developer/pdfs/tn/5079.Adobe-GB1-4.pdf
+[The Adobe Chinese GB (simplified) character set.]
+
+Adobe Systems, Inc., _Adobe-CNS1-3 Character Collection for
+CID-Keyed Fonts_, Adobe Developer Support Technical Note #5080.
+2000.
+http://partners.adobe.com/asn/developer/PDFS/TN/5080.CNS_CharColl.pdf
+[The Adobe Chinese CNS (traditional) character set.]
+
+Adobe Systems Inc., _Supporting the DCT Filters in PostScript Level
+2_, Adobe Developer Support Technical Note #5116.  1992.
+http://www.adobe.com/supportservice/devrelations/PDFS/TN/5116.PS2_DCT.PDF
+[Description of the DCTDecode filter parameters.]
+
+Adobe Systems Inc., _Open Prepress Interface (OPI) Specification -
+Version 2.0_, Adobe Developer Support Technical Note #5660.  2000.
+http://partners.adobe.com/asn/developer/PDFS/TN/5660.OPI_2.0.pdf
+
+Adobe Systems Inc., CMap files.
+ftp://ftp.oreilly.com/pub/examples/nutshell/cjkv/adobe/
+[The actual CMap files for the 16-bit CJK encodings.]
+
+Adobe Systems Inc., Unicode glyph lists.
+http://partners.adobe.com/asn/developer/type/unicodegn.html
+http://partners.adobe.com/asn/developer/type/glyphlist.txt
+http://partners.adobe.com/asn/developer/type/corporateuse.txt
+http://partners.adobe.com/asn/developer/type/zapfdingbats.txt
+[Mappings between character names to Unicode.]
+
+Adobe Systems Inc., OpenType Specification v. 1.4.
+http://partners.adobe.com/public/developer/opentype/index_spec.html
+[The OpenType font format spec.]
+
+Aldus Corp., _OPI: Open Prepress Interface Specification 1.3_.  1993.
+http://partners.adobe.com/asn/developer/PDFS/TN/OPI_13.pdf
+
+Anonymous, RC4 source code.
+ftp://ftp.ox.ac.uk/pub/crypto/misc/rc4.tar.gz
+ftp://idea.sec.dsi.unimi.it/pub/crypt/code/rc4.tar.gz
+[This is the algorithm used to encrypt PDF files.]
+
+T. Boutell, et al., "PNG (Portable Network Graphics) Specification,
+Version 1.0".  RFC 2083.
+[PDF uses the PNG filter algorithms.]
+
+CCITT, "Information Technology - Digital Compression and Coding of
+Continuous-tone Still Images - Requirements and Guidelines", CCITT
+Recommendation T.81.
+http://www.w3.org/Graphics/JPEG/
+[The official JPEG spec.]
+
+A. Chernov, "Registration of a Cyrillic Character Set".  RFC 1489.
+[Documentation for the KOI8-R Cyrillic encoding.]
+
+Roman Czyborra, "The ISO 8859 Alphabet Soup".
+http://czyborra.com/charsets/iso8859.html
+[Documentation on the various ISO 859 encodings.]
+
+L. Peter Deutsch, "ZLIB Compressed Data Format Specification version
+3.3".  RFC 1950.
+[Information on the general format used in FlateDecode streams.]
+
+L. Peter Deutsch, "DEFLATE Compressed Data Format Specification
+version 1.3".  RFC 1951.
+[The definition of the compression algorithm used in FlateDecode
+streams.]
+
+Morris Dworkin, "Recommendation for Block Cipher Modes of Operation",
+National Institute of Standards, NIST Special Publication 800-38A,
+2001.
+[The cipher block chaining (CBC) mode used with AES in PDF files.]
+
+Federal Information Processing Standards Publication 197 (FIPS PUBS
+197), "Advanced Encryption Standard (AES)", November 26, 2001.
+[AES encryption, used in PDF 1.6.]
+
+Jim Flowers, "X Logical Font Description Conventions", Version 1.5, X
+Consortium Standard, X Version 11, Release 6.1.
+ftp://ftp.x.org/pub/R6.1/xc/doc/hardcopy/XLFD/xlfd.PS.Z
+[The official specification of X font descriptors, including font
+transformation matrices.]
+
+Foley, van Dam, Feiner, and Hughes, _Computer Graphics: Principles and
+Practice_, 2nd ed.  Addison-Wesley, 1990, ISBN 0-201-12110-7.
+[Colorspace conversion functions, Bezier spline math.]
+
+Robert L. Hummel, _Programmer's Technical Reference: Data and Fax
+Communications_.  Ziff-Davis Press, 1993, ISBN 1-56276-077-7.
+[CCITT Group 3 and 4 fax decoding.]
+
+ISO/IEC, _Information technology -- Lossy/lossless coding of bi-level
+images_.  ISO/IEC 14492, First edition (2001-12-15).
+http://webstore.ansi.org/
+[The official JBIG2 standard.  The final draft of this spec is
+available from http://www.jpeg.org/jbighomepage.html.]
+
+ISO/IEC, _Information technology -- JPEG 2000 image coding system --
+Part 1: Core coding system_.  ISO/IEC 15444-1, First edition
+(2000-12-15).
+http://webstore.ansi.org/
+[The official JPEG 2000 standard.  The final committee draft of this
+spec is available from http://www.jpeg.org/JPEG2000.html, but there
+were changes made to the bitstream format between that draft and the
+published spec.]
+
+ITU, "Standardization of Group 3 facsimile terminals for document
+transmission", ITU-T Recommendation T.4, 1999.
+ITU, "Facsimile coding schemes and coding control functions for Group 4
+facsimile apparatus", ITU-T Recommendation T.6, 1993.
+http://www.itu.int/
+[The official Group 3 and 4 fax standards - used by the CCITTFaxDecode
+stream, as well as the JBIG2Decode stream.]
+
+B. Kaliski, "PKCS #5: Password-Based Cryptography Specification,
+Version 2.0".  RFC 2898.
+[Defines the padding scheme used with AES encryption in PDF files.]
+
+Christoph Loeffler, Adriaan Ligtenberg, George S. Moschytz, "Practical
+Fast 1-D DCT Algorithms with 11 Multiplications".  IEEE Intl. Conf. on
+Acoustics, Speech & Signal Processing, 1989, 988-991.
+[The fast IDCT algorithm used in the DCTDecode filter.]
+
+Microsoft, _TrueType 1.0 Font Files_, rev. 1.66.  1995.
+http://www.microsoft.com/typography/tt/tt.htm
+[The TrueType font spec (in MS Word format, naturally).]
+
+V. Ostromoukhov, R.D. Hersch, "Stochastic Clustered-Dot Dithering",
+Conf. Color Imaging: Device-Independent Color, Color Hardcopy, and
+Graphic Arts IV, 1999, SPIE Vol. 3648, 496-505.
+http://diwww.epfl.ch/w3lsp/publications/colour/scd.html
+[The stochastic dithering algorithm used in Xpdf.]
+
+P. Peterlin, "ISO 8859-2 (Latin 2) Resources".
+http://sizif.mf.uni-lj.si/linux/cee/iso8859-2.html
+[This is a web page with all sorts of useful Latin-2 character set and
+font information.]
+
+Charles Poynton, "Color FAQ".
+http://www.inforamp.net/~poynton/ColorFAQ.html
+[The mapping from the CIE 1931 (XYZ) color space to RGB.]
+
+R. Rivest, "The MD5 Message-Digest Algorithm".  RFC 1321.
+[MD5 is used in PDF document encryption.]
+
+Thai Industrial Standard, "Standard for Thai Character Codes for
+Computers", TIS-620-2533 (1990).
+http://www.nectec.or.th/it-standards/std620/std620.htm
+[The TIS-620 Thai encoding.]
+
+Unicode Consortium, "Unicode Home Page".
+http://www.unicode.org/
+[Online copy of the Unicode spec.]
+
+W3C Recommendation, "PNG (Portable Network Graphics) Specification
+Version 1.0".
+http://www.w3.org/Graphics/PNG/
+[Defines the PNG image predictor.]
+
+Gregory K. Wallace, "The JPEG Still Picture Compression Standard".
+ftp://ftp.uu.net/graphics/jpeg/wallace.ps.gz
+[Good description of the JPEG standard.  Also published in CACM, April
+1991, and submitted to IEEE Transactions on Consumer Electronics.]
+
+F. Yergeau, "UTF-8, a transformation format of ISO 10646".  RFC 2279.
+[A commonly used Unicode encoding.]
Index: main/trunk/greenstone2/bin/linux/xpdf-tools/doc/pdfdetach.1
===================================================================
--- main/trunk/greenstone2/bin/linux/xpdf-tools/doc/pdfdetach.1	(revision 32205)
+++ main/trunk/greenstone2/bin/linux/xpdf-tools/doc/pdfdetach.1	(revision 32205)
@@ -0,0 +1,107 @@
+.\" Copyright 2013-2017 Glyph & Cog, LLC
+.TH pdfdetach 1 "10 Aug 2017"
+.SH NAME
+pdfdetach \- Portable Document Format (PDF) document embedded file
+extractor (version 4.00)
+.SH SYNOPSIS
+.B pdfdetach
+[options]
+.RI [ PDF-file ]
+.SH DESCRIPTION
+.B Pdfdetach
+lists or extracts embedded files (attachments) from a Portable
+Document Format (PDF) file.
+.SH CONFIGURATION FILE
+Pdfdetach reads a configuration file at startup.  It first tries to
+find the user's private config file, ~/.xpdfrc.  If that doesn't
+exist, it looks for a system-wide config file, typically
+/usr/local/etc/xpdfrc (but this location can be changed when pdfinfo
+is built).  See the
+.BR xpdfrc (5)
+man page for details.
+.SH OPTIONS
+Some of the following options can be set with configuration file
+commands.  These are listed in square brackets with the description of
+the corresponding command line option.
+.TP
+.B \-list
+List all of the embedded files in the PDF file.  File names are
+converted to the text encoding specified by the "\-enc" switch.
+.TP
+.BI \-save " number"
+Save the specified embedded file.  By default, this uses the file name
+associated with the embedded file (as printed by the "\-list" switch);
+the file name can be changed with the "\-o" switch.
+.TP
+.BI \-saveall
+Save all of the embedded files.  This uses the file names associated
+with the embedded files (as printed by the "\-list" switch).  By
+default, the files are saved in the current directory; this can be
+changed with the "\-o" switch.
+.TP
+.BI \-o " path"
+Set the file name used when saving an embedded file with the "\-save"
+switch, or the directory used by "\-saveall".
+.TP
+.BI \-enc " encoding-name"
+Sets the encoding to use for text output (embedded file names).  The
+.I encoding\-name
+must be defined with the unicodeMap command (see
+.BR xpdfrc (5)).
+This defaults to "Latin1" (which is a built-in encoding).
+.RB "[config file: " textEncoding ]
+.TP
+.BI \-opw " password"
+Specify the owner password for the PDF file.  Providing this will
+bypass all security restrictions.
+.TP
+.BI \-upw " password"
+Specify the user password for the PDF file.
+.TP
+.BI \-cfg " config-file"
+Read
+.I config-file
+in place of ~/.xpdfrc or the system-wide config file.
+.TP
+.B \-v
+Print copyright and version information.
+.TP
+.B \-h
+Print usage information.
+.RB ( \-help
+and
+.B \-\-help
+are equivalent.)
+.SH EXIT CODES
+The Xpdf tools use the following exit codes:
+.TP
+0
+No error.
+.TP
+1
+Error opening a PDF file.
+.TP
+2
+Error opening an output file.
+.TP
+3
+Error related to PDF permissions.
+.TP
+99
+Other error.
+.SH AUTHOR
+The pdfinfo software and documentation are copyright 1996-2017 Glyph &
+Cog, LLC.
+.SH "SEE ALSO"
+.BR xpdf (1),
+.BR pdftops (1),
+.BR pdftotext (1),
+.BR pdftohtml (1),
+.BR pdfinfo (1),
+.BR pdffonts (1),
+.BR pdftoppm (1),
+.BR pdftopng (1),
+.BR pdfimages (1),
+.BR xpdfrc (5)
+.br
+.B http://www.xpdfreader.com/
Index: main/trunk/greenstone2/bin/linux/xpdf-tools/doc/pdffonts.1
===================================================================
--- main/trunk/greenstone2/bin/linux/xpdf-tools/doc/pdffonts.1	(revision 32205)
+++ main/trunk/greenstone2/bin/linux/xpdf-tools/doc/pdffonts.1	(revision 32205)
@@ -0,0 +1,164 @@
+.\" Copyright 1999-2017 Glyph & Cog, LLC
+.TH pdffonts 1 "10 Aug 2017"
+.SH NAME
+pdffonts \- Portable Document Format (PDF) font analyzer (version
+4.00)
+.SH SYNOPSIS
+.B pdffonts
+[options]
+.RI [ PDF-file ]
+.SH DESCRIPTION
+.B Pdffonts
+lists the fonts used in a Portable Document Format (PDF) file along
+with various information for each font.
+.PP
+The following information is listed for each font:
+.TP
+.B name
+the font name, exactly as given in the PDF file (potentially including
+a subset prefix)
+.TP
+.B type
+the font type -- see below for details
+.TP
+.B emb
+"yes" if the font is embedded in the PDF file
+.TP
+.B sub
+"yes" if the font is a subset
+.TP
+.B uni
+"yes" if there is an explicit "ToUnicode" map in the PDF file (the
+absence of a ToUnicode map doesn't necessarily mean that the text
+can't be converted to Unicode)
+.TP
+.B prob
+"X" if this font is likely to be problematic when converting text to
+Unicode
+.TP
+.B object ID
+the font dictionary object ID (number and generation)
+.TP
+.B location
+the font location (see the
+.B \-loc
+and
+.B \-locPS
+options).
+.PP
+PDF files can contain the following types of fonts:
+.PP
+.RS
+Type 1
+.RE
+.RS
+Type 1C -- aka Compact Font Format (CFF)
+.RE
+.RS
+Type 1C (OT) -- OpenType with 8-bit CFF data
+.RE
+.RS
+Type 3
+.RE
+.RS
+TrueType
+.RE
+.RS
+TrueType (OT) -- OpenType with 8-bit TrueType data
+.RE
+.RS
+CID Type 0 -- 16-bit font with no specified type
+.RE
+.RS
+CID Type 0C -- 16-bit PostScript CFF font
+.RE
+.RS
+CID Type 0C (OT) -- OpenType with CID CFF data
+.RE
+.RS
+CID TrueType -- 16-bit TrueType font
+.RE
+.RS
+CID TrueType (OT) -- OpenType with CID TrueType data
+.RE
+.SH CONFIGURATION FILE
+Pdffonts reads a configuration file at startup.  It first tries to
+find the user's private config file, ~/.xpdfrc.  If that doesn't
+exist, it looks for a system-wide config file, typically
+/usr/local/etc/xpdfrc (but this location can be changed when pdffonts
+is built).  See the
+.BR xpdfrc (5)
+man page for details.
+.SH OPTIONS
+Many of the following options can be set with configuration file
+commands.  These are listed in square brackets with the description of
+the corresponding command line option.
+.TP
+.BI \-f " number"
+Specifies the first page to analyze.
+.TP
+.B \-loc
+Shows additional information on the location of the font that will be
+used when the PDF file is rasterized (with xpdf, pdftoppm, etc.).
+.TP
+.B \-locPS
+Shows additional information on the location of the font that will be
+used when the PDF file is converted to PostScript (with pdftops).
+.TP
+.BI \-l " number"
+Specifies the last page to analyze.
+.TP
+.BI \-opw " password"
+Specify the owner password for the PDF file.  Providing this will
+bypass all security restrictions.
+.TP
+.BI \-upw " password"
+Specify the user password for the PDF file.
+.TP
+.BI \-cfg " config-file"
+Read
+.I config-file
+in place of ~/.xpdfrc or the system-wide config file.
+.TP
+.B \-v
+Print copyright and version information.
+.TP
+.B \-h
+Print usage information.
+.RB ( \-help
+and
+.B \-\-help
+are equivalent.)
+.SH EXIT CODES
+The Xpdf tools use the following exit codes:
+.TP
+0
+No error.
+.TP
+1
+Error opening a PDF file.
+.TP
+2
+Error opening an output file.
+.TP
+3
+Error related to PDF permissions.
+.TP
+99
+Other error.
+.SH AUTHOR
+The pdffonts software and documentation are copyright 1996-2017 Glyph
+& Cog, LLC.
+.SH "SEE ALSO"
+.BR xpdf (1),
+.BR pdftops (1),
+.BR pdftotext (1),
+.BR pdftohtml (1),
+.BR pdfinfo (1),
+.BR pdfdetach (1),
+.BR pdftoppm (1),
+.BR pdftopng (1),
+.BR pdfimages (1),
+.BR xpdfrc (5)
+.br
+.B http://www.xpdfreader.com/
Index: main/trunk/greenstone2/bin/linux/xpdf-tools/doc/pdfimages.1
===================================================================
--- main/trunk/greenstone2/bin/linux/xpdf-tools/doc/pdfimages.1	(revision 32205)
+++ main/trunk/greenstone2/bin/linux/xpdf-tools/doc/pdfimages.1	(revision 32205)
@@ -0,0 +1,119 @@
+.\" Copyright 1998-2017 Glyph & Cog, LLC
+.TH pdfimages 1 "10 Aug 2017"
+.SH NAME
+pdfimages \- Portable Document Format (PDF) image extractor
+(version 4.00)
+.SH SYNOPSIS
+.B pdfimages
+[options]
+.I PDF-file image-root
+.SH DESCRIPTION
+.B Pdfimages
+saves images from a Portable Document Format (PDF) file as Portable
+Pixmap (PPM), Portable Graymap (PGM), Portable Bitmap (PBM), or JPEG
+files.
+.PP
+Pdfimages reads the PDF file, scans one or more pages,
+.IR PDF-file ,
+and writes one PPM, PGM, PBM, or JPEG file for each image,
+.IR image-root - nnnn . xxx ,
+where
+.I nnnn
+is the image number and
+.I xxx
+is the image type (.ppm, .pgm, .pbm, .jpg).
+.PP
+NB: pdfimages extracts the raw image data from the PDF file, without
+performing any additional transforms.  Any rotation, clipping,
+color inversion, etc. done by the PDF content stream is ignored.
+.SH CONFIGURATION FILE
+Pdfimages reads a configuration file at startup.  It first tries to
+find the user's private config file, ~/.xpdfrc.  If that doesn't
+exist, it looks for a system-wide config file, typically
+/usr/local/etc/xpdfrc (but this location can be changed when pdfimages
+is built).  See the
+.BR xpdfrc (5)
+man page for details.
+.SH OPTIONS
+Many of the following options can be set with configuration file
+commands.  These are listed in square brackets with the description of
+the corresponding command line option.
+.TP
+.BI \-f " number"
+Specifies the first page to scan.
+.TP
+.BI \-l " number"
+Specifies the last page to scan.
+.TP
+.B \-j
+Normally, all images are written as PBM (for monochrome images), PGM
+(for grayscale images), or PPM (for color images) files.  With this
+option, images in DCT format are saved as JPEG files.  All non-DCT
+images are saved in PBM/PGM/PPM format as usual.  (Inline images are
+always saved in PBM/PGM/PPM format.)
+.TP
+.B \-raw
+Write all images in PDF-native formats.  Most of the formats are not
+standard image formats, so this option is primarily useful as input to
+a tool that generates PDF files.  (Inline images are always saved in
+PBM/PGM/PPM format.)
+.TP
+.B \-list
+Write a one-line summary to stdout for each image.  The summary
+provides the image file name, the page number, the image width and
+height, the horizontal and vertical resolution (DPI) as drawn, the
+color space type, and the number of bits per component (BPC).
+.TP
+.BI \-opw " password"
+Specify the owner password for the PDF file.  Providing this will
+bypass all security restrictions.
+.TP
+.BI \-upw " password"
+Specify the user password for the PDF file.
+.TP
+.B \-q
+Don't print any messages or errors.
+.RB "[config file: " errQuiet ]
+.TP
+.B \-v
+Print copyright and version information.
+.TP
+.B \-h
+Print usage information.
+.RB ( \-help
+and
+.B \-\-help
+are equivalent.)
+.SH EXIT CODES
+The Xpdf tools use the following exit codes:
+.TP
+0
+No error.
+.TP
+1
+Error opening a PDF file.
+.TP
+2
+Error opening an output file.
+.TP
+3
+Error related to PDF permissions.
+.TP
+99
+Other error.
+.SH AUTHOR
+The pdfimages software and documentation are copyright 1998-2017 Glyph
+& Cog, LLC.
+.SH "SEE ALSO"
+.BR xpdf (1),
+.BR pdftops (1),
+.BR pdftotext (1),
+.BR pdftohtml (1),
+.BR pdfinfo (1),
+.BR pdffonts (1),
+.BR pdfdetach (1),
+.BR pdftoppm (1),
+.BR pdftopng (1),
+.BR xpdfrc (5)
+.br
+.B http://www.xpdfreader.com/
Index: main/trunk/greenstone2/bin/linux/xpdf-tools/doc/pdfinfo.1
===================================================================
--- main/trunk/greenstone2/bin/linux/xpdf-tools/doc/pdfinfo.1	(revision 32205)
+++ main/trunk/greenstone2/bin/linux/xpdf-tools/doc/pdfinfo.1	(revision 32205)
@@ -0,0 +1,167 @@
+.\" Copyright 1999-2017 Glyph & Cog, LLC
+.TH pdfinfo 1 "10 Aug 2017"
+.SH NAME
+pdfinfo \- Portable Document Format (PDF) document information
+extractor (version 4.00)
+.SH SYNOPSIS
+.B pdfinfo
+[options]
+.RI [ PDF-file ]
+.SH DESCRIPTION
+.B Pdfinfo
+prints the contents of the \'Info' dictionary (plus some other useful
+information) from a Portable Document Format (PDF) file.
+.PP
+The \'Info' dictionary contains the following values:
+.PP
+.RS
+title
+.RE
+.RS
+subject
+.RE
+.RS
+keywords
+.RE
+.RS
+author
+.RE
+.RS
+creator
+.RE
+.RS
+producer
+.RE
+.RS
+creation date
+.RE
+.RS
+modification date
+.RE
+.PP
+In addition, the following information is printed:
+.PP
+.RS
+tagged (yes/no)
+.RE
+.RS
+form (AcroForm / static XFA / dynamic XFA / none)
+.RE
+.RS
+page count
+.RE
+.RS
+encrypted flag (yes/no)
+.RE
+.RS
+print and copy permissions (if encrypted)
+.RE
+.RS
+page size and rotation
+.RE
+.RS
+file size
+.RE
+.RS
+linearized (yes/no)
+.RE
+.RS
+PDF version
+.RE
+.RS
+metadata (only if requested)
+.RE
+.SH CONFIGURATION FILE
+Pdfinfo reads a configuration file at startup.  It first tries to find
+the user's private config file, ~/.xpdfrc.  If that doesn't exist, it
+looks for a system-wide config file, typically /usr/local/etc/xpdfrc
+(but this location can be changed when pdfinfo is built).  See the
+.BR xpdfrc (5)
+man page for details.
+.SH OPTIONS
+Many of the following options can be set with configuration file
+commands.  These are listed in square brackets with the description of
+the corresponding command line option.
+.TP
+.BI \-f " number"
+Specifies the first page to examine.  If multiple pages are requested
+using the "\-f" and "\-l" options, the size of each requested page
+(and, optionally, the bounding boxes for each requested page) are
+printed.  Otherwise, only page one is examined.
+.TP
+.BI \-l " number"
+Specifies the last page to examine.
+.TP
+.B \-box
+Prints the page box bounding boxes: MediaBox, CropBox, BleedBox,
+TrimBox, and ArtBox.
+.TP
+.B \-meta
+Prints document-level metadata.  (This is the "Metadata" stream from
+the PDF file's Catalog object.)
+.TP
+.B \-rawdates
+Prints the raw (undecoded) date strings, directly from the PDF file.
+.TP
+.BI \-enc " encoding-name"
+Sets the encoding to use for text output.  The
+.I encoding\-name
+must be defined with the unicodeMap command (see
+.BR xpdfrc (5)).
+This defaults to "Latin1" (which is a built-in encoding).
+.RB "[config file: " textEncoding ]
+.TP
+.BI \-opw " password"
+Specify the owner password for the PDF file.  Providing this will
+bypass all security restrictions.
+.TP
+.BI \-upw " password"
+Specify the user password for the PDF file.
+.TP
+.BI \-cfg " config-file"
+Read
+.I config-file
+in place of ~/.xpdfrc or the system-wide config file.
+.TP
+.B \-v
+Print copyright and version information.
+.TP
+.B \-h
+Print usage information.
+.RB ( \-help
+and
+.B \-\-help
+are equivalent.)
+.SH EXIT CODES
+The Xpdf tools use the following exit codes:
+.TP
+0
+No error.
+.TP
+1
+Error opening a PDF file.
+.TP
+2
+Error opening an output file.
+.TP
+3
+Error related to PDF permissions.
+.TP
+99
+Other error.
+.SH AUTHOR
+The pdfinfo software and documentation are copyright 1996-2017 Glyph &
+Cog, LLC.
+.SH "SEE ALSO"
+.BR xpdf (1),
+.BR pdftops (1),
+.BR pdftotext (1),
+.BR pdftohtml (1),
+.BR pdffonts (1),
+.BR pdfdetach (1),
+.BR pdftoppm (1),
+.BR pdftopng (1),
+.BR pdfimages (1),
+.BR xpdfrc (5)
+.br
+.B http://www.xpdfreader.com/
Index: main/trunk/greenstone2/bin/linux/xpdf-tools/doc/pdftohtml.1
===================================================================
--- main/trunk/greenstone2/bin/linux/xpdf-tools/doc/pdftohtml.1	(revision 32205)
+++ main/trunk/greenstone2/bin/linux/xpdf-tools/doc/pdftohtml.1	(revision 32205)
@@ -0,0 +1,126 @@
+.\" Copyright 1997-2017 Glyph & Cog, LLC
+.TH pdftohtml 1 "10 Aug 2017"
+.SH NAME
+pdftohtml \- Portable Document Format (PDF) to HTML converter
+(version 4.00)
+.SH SYNOPSIS
+.B pdftohtml
+[options]
+.I PDF-file
+.I HTML-dir
+.SH DESCRIPTION
+.B Pdftohtml
+converts Portable Document Format (PDF) files to HTML.
+.PP
+Pdftohtml reads the PDF file,
+.IR PDF-file ,
+and places an HTML file for each page, along with auxiliary images
+in the directory,
+.IR HTML-dir .
+The HTML directory will be created; if it already exists, pdftohtml
+will report an error.
+.SH CONFIGURATION FILE
+Pdftohtml reads a configuration file at startup.  It first tries to
+find the user's private config file, ~/.xpdfrc.  If that doesn't
+exist, it looks for a system-wide config file, typically
+/usr/local/etc/xpdfrc (but this location can be changed when pdftohtml
+is built).  See the
+.BR xpdfrc (5)
+man page for details.
+.SH OPTIONS
+Many of the following options can be set with configuration file
+commands.  These are listed in square brackets with the description of
+the corresponding command line option.
+.TP
+.BI \-f " number"
+Specifies the first page to convert.
+.TP
+.BI \-l " number"
+Specifies the last page to convert.
+.TP
+.BI \-z " number"
+Specifies the initial zoom level.  The default is 1.0, which means
+72dpi, i.e., 1 point in the PDF file will be 1 pixel in the HTML.
+Using \'-z 1.5', for example, will make the initial view 50% larger.
+.TP
+.BI \-r " number"
+Specifies the resolution, in DPI, for background images.  This
+controls the pixel size of the background image files.  The initial
+zoom level is controlled by the \'-z' option.  Specifying a larger
+\'-r' value will allow the viewer to zoom in farther without upscaling
+artifacts in the background.
+.TP
+.B \-skipinvisible
+Don't draw invisible text.  By default, invisible text (commonly used
+in OCR'ed PDF files) is drawn as transparent (alpha=0) HTML text.
+This option tells pdftohtml to discard invisible text entirely.
+.TP
+.B \-allinvisible
+Treat all text as invisible.  By default, regular (non-invisible) text
+is not drawn in the background image, and is instead drawn with HTML
+on top of the image.  This option tells pdftohtml to include the
+regular text in the background image, and then draw it as transparent
+(alpha=0) HTML text.
+.TP
+.BI \-opw " password"
+Specify the owner password for the PDF file.  Providing this will
+bypass all security restrictions.
+.TP
+.BI \-upw " password"
+Specify the user password for the PDF file.
+.TP
+.B \-q
+Don't print any messages or errors.
+.RB "[config file: " errQuiet ]
+.TP
+.BI \-cfg " config-file"
+Read
+.I config-file
+in place of ~/.xpdfrc or the system-wide config file.
+.TP
+.B \-v
+Print copyright and version information.
+.TP
+.B \-h
+Print usage information.
+.RB ( \-help
+and
+.B \-\-help
+are equivalent.)
+.SH BUGS
+Some PDF files contain fonts whose encodings have been mangled beyond
+recognition.  There is no way (short of OCR) to extract text from
+these files.
+.SH EXIT CODES
+The Xpdf tools use the following exit codes:
+.TP
+0
+No error.
+.TP
+1
+Error opening a PDF file.
+.TP
+2
+Error opening an output file.
+.TP
+3
+Error related to PDF permissions.
+.TP
+99
+Other error.
+.SH AUTHOR
+The pdftohtml software and documentation are copyright 1996-2017 Glyph
+& Cog, LLC.
+.SH "SEE ALSO"
+.BR xpdf (1),
+.BR pdftops (1),
+.BR pdftotext (1),
+.BR pdfinfo (1),
+.BR pdffonts (1),
+.BR pdfdetach (1),
+.BR pdftoppm (1),
+.BR pdftopng (1),
+.BR pdfimages (1),
+.BR xpdfrc (5)
+.br
+.B http://www.xpdfreader.com/
Index: main/trunk/greenstone2/bin/linux/xpdf-tools/doc/pdftopng.1
===================================================================
--- main/trunk/greenstone2/bin/linux/xpdf-tools/doc/pdftopng.1	(revision 32205)
+++ main/trunk/greenstone2/bin/linux/xpdf-tools/doc/pdftopng.1	(revision 32205)
@@ -0,0 +1,124 @@
+.\" Copyright 2017 Glyph & Cog, LLC
+.TH pdftopng 1 "10 Aug 2017"
+.SH NAME
+pdftopng \- Portable Document Format (PDF) to Portable Network Graphics
+(PNG) converter (version 4.00)
+.SH SYNOPSIS
+.B pdftopng
+[options]
+.I PDF-file PNG-root
+.SH DESCRIPTION
+.B Pdftopng
+converts Portable Document Format (PDF) files to color, grayscale, or
+monochrome image files in Portable Network Graphics (PNG) format.
+.PP
+Pdftopng reads the PDF file,
+.IR PDF-file ,
+and writes one PNG file for each page,
+.IR PNG-root - nnnnnn .png,
+where
+.I nnnnnn
+is the page number.
+If
+.I PNG-root
+is \'-', the image is sent to stdout (this is probably only useful
+when converting a single page).
+.SH CONFIGURATION FILE
+Pdftopng reads a configuration file at startup.  It first tries to
+find the user's private config file, ~/.xpdfrc.  If that doesn't
+exist, it looks for a system-wide config file, typically
+/usr/local/etc/xpdfrc (but this location can be changed when pdftopng
+is built).  See the
+.BR xpdfrc (5)
+man page for details.
+.SH OPTIONS
+Many of the following options can be set with configuration file
+commands.  These are listed in square brackets with the description of
+the corresponding command line option.
+.TP
+.BI \-f " number"
+Specifies the first page to convert.
+.TP
+.BI \-l " number"
+Specifies the last page to convert.
+.TP
+.BI \-r " number"
+Specifies the resolution, in DPI.  The default is 150 DPI.
+.TP
+.B \-mono
+Generate a monochrome image (instead of a color image).
+.TP
+.B \-gray
+Generate a grayscale image (instead of a color image).
+.TP
+.B \-alpha
+Generate an alpha channel in the PNG file.  This is only useful with
+PDF files that have been constructed with a transparent background.
+The \-alpha flag cannot be used with \-mono.
+.TP
+.BI \-freetype " yes | no"
+Enable or disable FreeType (a TrueType / Type 1 font rasterizer).
+This defaults to "yes".
+.RB "[config file: " enableFreeType ]
+.TP
+.BI \-aa " yes | no"
+Enable or disable font anti-aliasing.  This defaults to "yes".
+.RB "[config file: " antialias ]
+.TP
+.BI \-aaVector " yes | no"
+Enable or disable vector anti-aliasing.  This defaults to "yes".
+.RB "[config file: " vectorAntialias ]
+.TP
+.BI \-opw " password"
+Specify the owner password for the PDF file.  Providing this will
+bypass all security restrictions.
+.TP
+.BI \-upw " password"
+Specify the user password for the PDF file.
+.TP
+.B \-q
+Don't print any messages or errors.
+.RB "[config file: " errQuiet ]
+.TP
+.B \-v
+Print copyright and version information.
+.TP
+.B \-h
+Print usage information.
+.RB ( \-help
+and
+.B \-\-help
+are equivalent.)
+.SH EXIT CODES
+The Xpdf tools use the following exit codes:
+.TP
+0
+No error.
+.TP
+1
+Error opening a PDF file.
+.TP
+2
+Error opening an output file.
+.TP
+3
+Error related to PDF permissions.
+.TP
+99
+Other error.
+.SH AUTHOR
+The pdftopng software and documentation are copyright 1996-2017 Glyph
+& Cog, LLC.
+.SH "SEE ALSO"
+.BR xpdf (1),
+.BR pdftops (1),
+.BR pdftotext (1),
+.BR pdftohtml (1),
+.BR pdfinfo (1),
+.BR pdffonts (1),
+.BR pdfdetach (1),
+.BR pdftoppm (1),
+.BR pdfimages (1),
+.BR xpdfrc (5)
+.br
+.B http://www.xpdfreader.com/
Index: main/trunk/greenstone2/bin/linux/xpdf-tools/doc/pdftoppm.1
===================================================================
--- main/trunk/greenstone2/bin/linux/xpdf-tools/doc/pdftoppm.1	(revision 32205)
+++ main/trunk/greenstone2/bin/linux/xpdf-tools/doc/pdftoppm.1	(revision 32205)
@@ -0,0 +1,124 @@
+.\" Copyright 2005-2017 Glyph & Cog, LLC
+.TH pdftoppm 1 "10 Aug 2017"
+.SH NAME
+pdftoppm \- Portable Document Format (PDF) to Portable Pixmap (PPM)
+converter (version 4.00)
+.SH SYNOPSIS
+.B pdftoppm
+[options]
+.I PDF-file PPM-root
+.SH DESCRIPTION
+.B Pdftoppm
+converts Portable Document Format (PDF) files to color image files in
+Portable Pixmap (PPM) format, grayscale image files in Portable
+Graymap (PGM) format, or monochrome image files in Portable Bitmap
+(PBM) format.
+.PP
+Pdftoppm reads the PDF file,
+.IR PDF-file ,
+and writes one PPM file for each page,
+.IR PPM-root - nnnnnn .ppm,
+where
+.I nnnnnn
+is the page number.
+If
+.I PPM-root
+is \'-', the image is sent to stdout (this is probably only useful
+when converting a single page).
+.SH CONFIGURATION FILE
+Pdftoppm reads a configuration file at startup.  It first tries to
+find the user's private config file, ~/.xpdfrc.  If that doesn't
+exist, it looks for a system-wide config file, typically
+/usr/local/etc/xpdfrc (but this location can be changed when pdftoppm
+is built).  See the
+.BR xpdfrc (5)
+man page for details.
+.SH OPTIONS
+Many of the following options can be set with configuration file
+commands.  These are listed in square brackets with the description of
+the corresponding command line option.
+.TP
+.BI \-f " number"
+Specifies the first page to convert.
+.TP
+.BI \-l " number"
+Specifies the last page to convert.
+.TP
+.BI \-r " number"
+Specifies the resolution, in DPI.  The default is 150 DPI.
+.TP
+.B \-mono
+Generate a monochrome PBM file (instead of an RGB PPM file).
+.TP
+.B \-gray
+Generate a grayscale PGM file (instead of an RGB PPM file).
+.TP
+.B \-cmyk
+Generate a CMYK PAM file (instead of an RGB PPM file).
+.TP
+.BI \-freetype " yes | no"
+Enable or disable FreeType (a TrueType / Type 1 font rasterizer).
+This defaults to "yes".
+.RB "[config file: " enableFreeType ]
+.TP
+.BI \-aa " yes | no"
+Enable or disable font anti-aliasing.  This defaults to "yes".
+.RB "[config file: " antialias ]
+.TP
+.BI \-aaVector " yes | no"
+Enable or disable vector anti-aliasing.  This defaults to "yes".
+.RB "[config file: " vectorAntialias ]
+.TP
+.BI \-opw " password"
+Specify the owner password for the PDF file.  Providing this will
+bypass all security restrictions.
+.TP
+.BI \-upw " password"
+Specify the user password for the PDF file.
+.TP
+.B \-q
+Don't print any messages or errors.
+.RB "[config file: " errQuiet ]
+.TP
+.B \-v
+Print copyright and version information.
+.TP
+.B \-h
+Print usage information.
+.RB ( \-help
+and
+.B \-\-help
+are equivalent.)
+.SH EXIT CODES
+The Xpdf tools use the following exit codes:
+.TP
+0
+No error.
+.TP
+1
+Error opening a PDF file.
+.TP
+2
+Error opening an output file.
+.TP
+3
+Error related to PDF permissions.
+.TP
+99
+Other error.
+.SH AUTHOR
+The pdftoppm software and documentation are copyright 1996-2017 Glyph
+& Cog, LLC.
+.SH "SEE ALSO"
+.BR xpdf (1),
+.BR pdftops (1),
+.BR pdftotext (1),
+.BR pdftohtml (1),
+.BR pdfinfo (1),
+.BR pdffonts (1),
+.BR pdfdetach (1),
+.BR pdftopng (1),
+.BR pdfimages (1),
+.BR xpdfrc (5)
+.br
+.B http://www.xpdfreader.com/
Index: main/trunk/greenstone2/bin/linux/xpdf-tools/doc/pdftops.1
===================================================================
--- main/trunk/greenstone2/bin/linux/xpdf-tools/doc/pdftops.1	(revision 32205)
+++ main/trunk/greenstone2/bin/linux/xpdf-tools/doc/pdftops.1	(revision 32205)
@@ -0,0 +1,253 @@
+.\" Copyright 1996-2017 Glyph & Cog, LLC
+.TH pdftops 1 "10 Aug 2017"
+.SH NAME
+pdftops \- Portable Document Format (PDF) to PostScript converter
+(version 4.00)
+.SH SYNOPSIS
+.B pdftops
+[options]
+.RI [ PDF-file
+.RI [ PS-file ]]
+.SH DESCRIPTION
+.B Pdftops
+converts Portable Document Format (PDF) files to PostScript so they
+can be printed.
+.PP
+Pdftops reads the PDF file,
+.IR PDF-file ,
+and writes a PostScript file,
+.IR PS-file .
+If
+.I PS-file
+is not specified, pdftops converts
+.I file.pdf
+to
+.I file.ps
+(or
+.I file.eps
+with the \-eps option).  If 
+.I PS-file
+is \'-', the PostScript is sent to stdout.
+.SH CONFIGURATION FILE
+Pdftops reads a configuration file at startup.  It first tries to find
+the user's private config file, ~/.xpdfrc.  If that doesn't exist, it
+looks for a system-wide config file, typically /usr/local/etc/xpdfrc
+(but this location can be changed when pdftops is built).  See the
+.BR xpdfrc (5)
+man page for details.
+.SH OPTIONS
+Many of the following options can be set with configuration file
+commands.  These are listed in square brackets with the description of
+the corresponding command line option.
+.TP
+.BI \-f " number"
+Specifies the first page to print.
+.TP
+.BI \-l " number"
+Specifies the last page to print.
+.TP
+.B \-level1
+Generate Level 1 PostScript.  The resulting PostScript files will be
+significantly larger (if they contain images), but will print on Level
+1 printers.  This also converts all images to black and white.  No
+more than one of the PostScript level options (\-level1, \-level1sep,
+\-level2, \-level2sep, \-level3, \-level3Sep) may be given.
+.RB "[config file: " psLevel ]
+.TP
+.B \-level1sep
+Generate Level 1 separable PostScript.  All colors are converted to
+CMYK.  Images are written with separate stream data for the four
+components.
+.RB "[config file: " psLevel ]
+.TP
+.B \-level2
+Generate Level 2 PostScript.  Level 2 supports color images and image
+compression.  This is the default setting.
+.RB "[config file: " psLevel ]
+.TP
+.B \-level2gray
+Generate grayscale Level 2 PostScript.  All colors, including images,
+are converted to grayscale.
+.RB "[config file: " psLevel ]
+.TP
+.B \-level2sep
+Generate Level 2 separable PostScript.  All colors are converted to
+CMYK.  The PostScript separation convention operators are used to
+handle custom (spot) colors.
+.RB "[config file: " psLevel ]
+.TP
+.B \-level3
+Generate Level 3 PostScript.  This enables all Level 2 features plus
+CID font embedding and masked image generation.
+.RB "[config file: " psLevel ]
+.TP
+.B \-level3gray
+Generate grayscale Level 3 PostScript.  All colors, including images,
+are converted to grayscale.
+.RB "[config file: " psLevel ]
+.TP
+.B \-level3Sep
+Generate Level 3 separable PostScript.  The separation handling is the
+same as for \-level2Sep.
+.RB "[config file: " psLevel ]
+.TP
+.B \-eps
+Generate an Encapsulated PostScript (EPS) file.  An EPS file contains
+a single image, so if you use this option with a multi-page PDF file,
+you must use \-f and \-l to specify a single page.  No more than one of
+the mode options (\-eps, \-form) may be given.
+.TP
+.B \-form
+Generate a PostScript form which can be imported by software that
+understands forms.  A form contains a single page, so if you use this
+option with a multi-page PDF file, you must use \-f and \-l to specify a
+single page.  The \-level1 option cannot be used with \-form.
+.TP
+.B \-opi
+Generate OPI comments for all images and forms which have OPI
+information.  (This option is only available if pdftops was compiled
+with OPI support.)
+.RB "[config file: " psOPI ]
+.TP
+.B \-noembt1
+By default, any Type 1 fonts which are embedded in the PDF file are
+copied into the PostScript file.  This option causes pdftops to
+substitute base fonts instead.  Embedded fonts make PostScript files
+larger, but may be necessary for readable output.
+.RB "[config file: " psEmbedType1Fonts ]
+.TP
+.B \-noembtt
+By default, any TrueType fonts which are embedded in the PDF file are
+copied into the PostScript file.  This option causes pdftops to
+substitute base fonts instead.  Embedded fonts make PostScript files
+larger, but may be necessary for readable output.  Also, some
+PostScript interpreters do not have TrueType rasterizers.
+.RB "[config file: " psEmbedTrueTypeFonts ]
+.TP
+.B \-noembcidps
+By default, any CID PostScript fonts which are embedded in the PDF
+file are copied into the PostScript file.  This option disables that
+embedding.  No attempt is made to substitute for non-embedded CID
+PostScript fonts.
+.RB "[config file: " psEmbedCIDPostScriptFonts ]
+.TP
+.B \-noembcidtt
+By default, any CID TrueType fonts which are embedded in the PDF file
+are copied into the PostScript file.  This option disables that
+embedding.  No attempt is made to substitute for non-embedded CID
+TrueType fonts.
+.RB "[config file: " psEmbedCIDTrueTypeFonts ]
+.TP
+.B \-preload
+Convert PDF forms to PS procedures, and preload image data.  This uses
+more memory in the PostScript interpreter, but generates significantly
+smaller PS files in situations where, e.g., the same image is drawn on
+every page of a long document.
+.TP
+.BI \-paper " size"
+Set the paper size to one of "letter", "legal", "A4", or "A3".  This
+can also be set to "match", which will set the paper size to match the
+size specified in the PDF file.
+.RB "[config file: " psPaperSize ]
+.TP
+.BI \-paperw " size"
+Set the paper width, in points.
+.RB "[config file: " psPaperSize ]
+.TP
+.BI \-paperh " size"
+Set the paper height, in points.
+.RB "[config file: " psPaperSize ]
+.TP
+.B \-nocrop
+By default, output is cropped to the CropBox specified in the PDF
+file.  This option disables cropping.
+.RB "[config file: " psCrop ]
+.TP
+.B \-expand
+Expand PDF pages smaller than the paper to fill the paper.  By
+default, these pages are not scaled.
+.RB "[config file: " psExpandSmaller ]
+.TP
+.B \-noshrink
+Don't scale PDF pages which are larger than the paper.  By default,
+pages larger than the paper are shrunk to fit.
+.RB "[config file: " psShrinkLarger ]
+.TP
+.B \-nocenter
+By default, PDF pages smaller than the paper (after any scaling) are
+centered on the paper.  This option causes them to be aligned to the
+lower-left corner of the paper instead.
+.RB "[config file: " psCenter ]
+.TP
+.B \-pagecrop
+Treat the CropBox as the PDF page size.  By default, the MediaBox is
+used as the page size.
+.RB "[config file: " psUseCropBoxAsPage ]
+.TP
+.B \-userunit
+Honor the UserUnit settings on PDF pages when computing page/paper
+size.  By default, pdftops ignores UserUnit.
+.TP
+.B \-duplex
+Set the Duplex pagedevice entry in the PostScript file.  This tells
+duplex-capable printers to enable duplexing.
+.RB "[config file: " psDuplex ]
+.TP
+.BI \-opw " password"
+Specify the owner password for the PDF file.  Providing this will
+bypass all security restrictions.
+.TP
+.BI \-upw " password"
+Specify the user password for the PDF file.
+.TP
+.B \-q
+Don't print any messages or errors.
+.RB "[config file: " errQuiet ]
+.TP
+.BI \-cfg " config-file"
+Read
+.I config-file
+in place of ~/.xpdfrc or the system-wide config file.
+.TP
+.B \-v
+Print copyright and version information.
+.TP
+.B \-h
+Print usage information.
+.RB ( \-help
+and
+.B \-\-help
+are equivalent.)
+.SH EXIT CODES
+The Xpdf tools use the following exit codes:
+.TP
+0
+No error.
+.TP
+1
+Error opening a PDF file.
+.TP
+2
+Error opening an output file.
+.TP
+3
+Error related to PDF permissions.
+.TP
+99
+Other error.
+.SH AUTHOR
+The pdftops software and documentation are copyright 1996-2017 Glyph &
+Cog, LLC.
+.SH "SEE ALSO"
+.BR xpdf (1),
+.BR pdftotext (1),
+.BR pdftohtml (1),
+.BR pdfinfo (1),
+.BR pdffonts (1),
+.BR pdfdetach (1),
+.BR pdftoppm (1),
+.BR pdftopng (1),
+.BR pdfimages (1),
+.BR xpdfrc (5)
+.br
+.B http://www.xpdfreader.com/
Index: main/trunk/greenstone2/bin/linux/xpdf-tools/doc/pdftotext.1
===================================================================
--- main/trunk/greenstone2/bin/linux/xpdf-tools/doc/pdftotext.1	(revision 32205)
+++ main/trunk/greenstone2/bin/linux/xpdf-tools/doc/pdftotext.1	(revision 32205)
@@ -0,0 +1,189 @@
+.\" Copyright 1997-2017 Glyph & Cog, LLC
+.TH pdftotext 1 "10 Aug 2017"
+.SH NAME
+pdftotext \- Portable Document Format (PDF) to text converter
+(version 4.00)
+.SH SYNOPSIS
+.B pdftotext
+[options]
+.RI [ PDF-file
+.RI [ text-file ]]
+.SH DESCRIPTION
+.B Pdftotext
+converts Portable Document Format (PDF) files to plain text.
+.PP
+Pdftotext reads the PDF file,
+.IR PDF-file ,
+and writes a text file,
+.IR text-file .
+If
+.I text-file
+is not specified, pdftotext converts
+.I file.pdf
+to
+.IR file.txt .
+If 
+.I text-file
+is \'-', the text is sent to stdout.
+.SH CONFIGURATION FILE
+Pdftotext reads a configuration file at startup.  It first tries to
+find the user's private config file, ~/.xpdfrc.  If that doesn't
+exist, it looks for a system-wide config file, typically
+/usr/local/etc/xpdfrc (but this location can be changed when pdftotext
+is built).  See the
+.BR xpdfrc (5)
+man page for details.
+.SH OPTIONS
+Many of the following options can be set with configuration file
+commands.  These are listed in square brackets with the description of
+the corresponding command line option.
+.TP
+.BI \-f " number"
+Specifies the first page to convert.
+.TP
+.BI \-l " number"
+Specifies the last page to convert.
+.TP
+.B \-layout
+Maintain (as best as possible) the original physical layout of the
+text.  The default is to \'undo' physical layout (columns,
+hyphenation, etc.) and output the text in reading order.  If the
+.B \-fixed
+option is given, character spacing within each line will be determined
+by the specified character pitch.
+.TP
+.B \-simple
+Similar to
+.BR \-layout ,
+but optimized for simple one-column pages.  This mode will do a better
+job of maintaining horizontal spacing, but it will only work properly
+with a single column of text.
+.TP
+.B \-table
+Table mode is similar to physical layout mode, but optimized for
+tabular data, with the goal of keeping rows and columns aligned (at
+the expense of inserting extra whitespace).  If the
+.B \-fixed
+option is given, character spacing within each line will be determined
+by the specified character pitch.
+.TP
+.B \-lineprinter
+Line printer mode uses a strict fixed-character-pitch and -height
+layout.  That is, the page is broken into a grid, and characters are
+placed into that grid.  If the grid spacing is too small for the
+actual characters, the result is extra whitespace.  If the grid
+spacing is too large, the result is missing whitespace.  The grid
+spacing can be specified using the
+.B \-fixed
+and
+.B \-linespacing
+options.
+If one or both are not given on the command line, pdftotext will
+attempt to compute appropriate value(s).
+.TP
+.B \-raw
+Keep the text in content stream order.  Depending on how the PDF file
+was generated, this may or may not be useful.
+.TP
+.BI \-fixed " number"
+Specify the character pitch (character width), in points, for physical
+layout, table, or line printer mode.  This is ignored in all other
+modes.
+.TP
+.BI \-linespacing " number"
+Specify the line spacing, in points, for line printer mode.  This is
+ignored in all other modes.
+.TP
+.B \-clip
+Text which is hidden because of clipping is removed before doing
+layout, and then added back in.  This can be helpful for tables where
+clipped (invisible) text would overlap the next column.
+.TP
+.B \-nodiag
+Diagonal text, i.e., text that is not close to one of the 0, 90, 180,
+or 270 degree axes, is discarded.  This is useful to skip watermarks
+drawn on top of body text, etc.
+.TP
+.BI \-enc " encoding-name"
+Sets the encoding to use for text output.  The
+.I encoding\-name
+must be defined with the unicodeMap command (see
+.BR xpdfrc (5)).
+The encoding name is case-sensitive.  This defaults to "Latin1" (which
+is a built-in encoding).
+.RB "[config file: " textEncoding ]
+.TP
+.BI \-eol " unix | dos | mac"
+Sets the end-of-line convention to use for text output.
+.RB "[config file: " textEOL ]
+.TP
+.B \-nopgbrk
+Don't insert page breaks (form feed characters) between pages.
+.RB "[config file: " textPageBreaks ]
+.TP
+.B \-bom
+Insert a Unicode byte order marker (BOM) at the start of the text
+output.
+.TP
+.BI \-opw " password"
+Specify the owner password for the PDF file.  Providing this will
+bypass all security restrictions.
+.TP
+.BI \-upw " password"
+Specify the user password for the PDF file.
+.TP
+.B \-q
+Don't print any messages or errors.
+.RB "[config file: " errQuiet ]
+.TP
+.BI \-cfg " config-file"
+Read
+.I config-file
+in place of ~/.xpdfrc or the system-wide config file.
+.TP
+.B \-v
+Print copyright and version information.
+.TP
+.B \-h
+Print usage information.
+.RB ( \-help
+and
+.B \-\-help
+are equivalent.)
+.SH BUGS
+Some PDF files contain fonts whose encodings have been mangled beyond
+recognition.  There is no way (short of OCR) to extract text from
+these files.
+.SH EXIT CODES
+The Xpdf tools use the following exit codes:
+.TP
+0
+No error.
+.TP
+1
+Error opening a PDF file.
+.TP
+2
+Error opening an output file.
+.TP
+3
+Error related to PDF permissions.
+.TP
+99
+Other error.
+.SH AUTHOR
+The pdftotext software and documentation are copyright 1996-2017 Glyph
+& Cog, LLC.
+.SH "SEE ALSO"
+.BR xpdf (1),
+.BR pdftops (1),
+.BR pdftohtml (1),
+.BR pdfinfo (1),
+.BR pdffonts (1),
+.BR pdfdetach (1),
+.BR pdftoppm (1),
+.BR pdftopng (1),
+.BR pdfimages (1),
+.BR xpdfrc (5)
+.br
+.B http://www.xpdfreader.com/
Index: main/trunk/greenstone2/bin/linux/xpdf-tools/doc/sample-xpdfrc
===================================================================
--- main/trunk/greenstone2/bin/linux/xpdf-tools/doc/sample-xpdfrc	(revision 32205)
+++ main/trunk/greenstone2/bin/linux/xpdf-tools/doc/sample-xpdfrc	(revision 32205)
@@ -0,0 +1,91 @@
+#========================================================================
+#
+# Sample xpdfrc file
+#
+# The Xpdf tools look for a config file in two places:
+# 1. ~/.xpdfrc
+# 2. in a system-wide directory, typically /usr/local/etc/xpdfrc
+#
+# This sample config file demonstrates some of the more common
+# configuration options.  Everything here is commented out.  You
+# should edit things (especially the file/directory paths, since
+# they'll likely be different on your system), and uncomment whichever
+# options you want to use.  For complete details on config file syntax
+# and available options, please see the xpdfrc(5) man page.
+#
+# Also, the Xpdf language support packages each include a set of
+# options to be added to the xpdfrc file.
+#
+# http://www.xpdfreader.com/
+#
+#========================================================================
+
+#----- display fonts
+
+# These map the Base-14 fonts to the Type 1 fonts that ship with
+# ghostscript.  You'll almost certainly want to use something like
+# this, but you'll need to adjust this to point to wherever
+# ghostscript is installed on your system.  (But if the fonts are
+# installed in a "standard" location, xpdf will find them
+# automatically.)
+
+#fontFile Times-Roman		/usr/local/share/ghostscript/fonts/n021003l.pfb
+#fontFile Times-Italic		/usr/local/share/ghostscript/fonts/n021023l.pfb
+#fontFile Times-Bold		/usr/local/share/ghostscript/fonts/n021004l.pfb
+#fontFile Times-BoldItalic	/usr/local/share/ghostscript/fonts/n021024l.pfb
+#fontFile Helvetica		/usr/local/share/ghostscript/fonts/n019003l.pfb
+#fontFile Helvetica-Oblique	/usr/local/share/ghostscript/fonts/n019023l.pfb
+#fontFile Helvetica-Bold		/usr/local/share/ghostscript/fonts/n019004l.pfb
+#fontFile Helvetica-BoldOblique	/usr/local/share/ghostscript/fonts/n019024l.pfb
+#fontFile Courier		/usr/local/share/ghostscript/fonts/n022003l.pfb
+#fontFile Courier-Oblique	/usr/local/share/ghostscript/fonts/n022023l.pfb
+#fontFile Courier-Bold		/usr/local/share/ghostscript/fonts/n022004l.pfb
+#fontFile Courier-BoldOblique	/usr/local/share/ghostscript/fonts/n022024l.pfb
+#fontFile Symbol			/usr/local/share/ghostscript/fonts/s050000l.pfb
+#fontFile ZapfDingbats		/usr/local/share/ghostscript/fonts/d050000l.pfb
+
+# If you need to display PDF files that refer to non-embedded fonts,
+# you should add one or more fontDir options to point to the
+# directories containing the font files.  Xpdf will only look at .pfa,
+# .pfb, .ttf, and .ttc files in those directories (other files will
+# simply be ignored).
+
+#fontDir		/usr/local/fonts/bakoma
+
+#----- PostScript output control
+
+# Set the default PostScript file or command.
+
+#psFile			"|lpr -Pmyprinter"
+
+# Set the default PostScript paper size -- this can be letter, legal,
+# A4, or A3.  You can also specify a paper size as width and height
+# (in points).
+
+#psPaperSize		letter
+
+#----- text output control
+
+# Choose a text encoding for copy-and-paste and for pdftotext output.
+# The Latin1, ASCII7, and UTF-8 encodings are built into Xpdf.  Other
+# encodings are available in the language support packages.
+
+#textEncoding		UTF-8
+
+# Choose the end-of-line convention for multi-line copy-and-past and
+# for pdftotext output.  The available options are unix, mac, and dos.
+
+#textEOL		unix
+
+#----- misc settings
+
+# Enable FreeType, and anti-aliased text.
+
+#enableFreeType		yes
+#antialias		yes
+
+# Set the command used to run a web browser when a URL hyperlink is
+# clicked.
+
+#launchCommand  viewer-script
+#urlCommand	"netscape -remote 'openURL(%s)'"
Index: main/trunk/greenstone2/bin/linux/xpdf-tools/doc/xpdfrc.5
===================================================================
--- main/trunk/greenstone2/bin/linux/xpdf-tools/doc/xpdfrc.5	(revision 32205)
+++ main/trunk/greenstone2/bin/linux/xpdf-tools/doc/xpdfrc.5	(revision 32205)
@@ -0,0 +1,756 @@
+.\" Copyright 2002-2017 Glyph & Cog, LLC
+.TH xpdfrc 5 "10 Aug 2017"
+.SH NAME
+xpdfrc \- configuration file for Xpdf tools (version 4.00)
+.SH DESCRIPTION
+All of the Xpdf tools read a single configuration file.  If you have a
+.I .xpdfrc
+file in your home directory, it will be read.  Otherwise, a
+system-wide configuration file will be read from
+.IR /usr/local/etc/xpdfrc ,
+if it exists.  (This is its default location; depending on build
+options, it may be placed elsewhere.)  On Win32 systems, the
+.I xpdfrc
+file should be placed in the same directory as the executables.
+.PP
+The xpdfrc file consists of a series of configuration options, one
+per line.  Blank lines and lines starting with a \'#' (comments) are
+ignored.
+.PP
+Arguments may be quoted, using "double-quote" characters, e.g., for
+file names that contain spaces.
+.PP
+The following sections list all of the configuration options, sorted
+into functional groups.  There is an examples section at the end.
+.SH INCLUDE FILES
+.TP
+.BI include " config\-file"
+Includes the specified config file.  The effect of this is equivalent
+to inserting the contents of
+.I config\-file
+directly into the parent config file in place of the
+.I include
+command.  Config files can be nested arbitrarily deeply.
+.SH GENERAL FONT CONFIGURATION
+.TP
+.BI fontFile " PDF\-font\-name font\-file"
+Maps a PDF font,
+.IR PDF\-font\-name ,
+to a font for display or PostScript output.  The font file,
+.IR font\-file ,
+can be any type allowed in a PDF file.  This command can be used for
+8-bit or 16-bit (CID) fonts.
+.TP
+.BI fontDir " dir"
+Specifies a search directory for font files.  There can be multiple
+fontDir commands; all of the specified directories will be searched in
+order.  The font files can be Type 1 (.pfa or .pfb) or TrueType (.ttf
+or .ttc); other files in the directory will be ignored.  The font file
+name (not including the extension) must exactly match the PDF font
+name.  This search is performed if the font name doesn't match any of
+the fonts declared with the fontFile command.  There are no default
+fontDir directories.
+.TP
+.BI fontFileCC " registry\-ordering font\-file"
+Maps the
+.I registry\-ordering
+character collection to a font for display or PostScript output.  This
+mapping is used if the font name doesn't match any of the fonts
+declared with the fontFile, fontDir, psResidentFont16, or
+psResidentFontCC commands.
+.SH POSTSCRIPT FONT CONFIGURATION
+.TP
+.BR psFontPassthrough " yes | no"
+If set to "yes", pass 8-bit font names through to the PostScript
+output without substitution.  Fonts which are not embedded in the PDF
+file are expected to be available on the printer.  This defaults to
+"no".
+.TP
+.BI psResidentFont " PDF\-font\-name PS\-font\-name"
+When the 8-bit font
+.I PDF\-font\-name
+is used (without embedding) in a PDF file, it will be translated to
+the PostScript font
+.IR PS\-font\-name ,
+which is assumed to be resident in the printer.  Typically,
+.I PDF\-font\-name
+and
+.I PS\-font\-name
+are the same.  By default, only the Base-14 fonts are assumed to be
+resident.
+.TP
+.BI psResidentFont16 " PDF\-font\-name wMode PS\-font\-name encoding"
+When the 16-bit (CID) font
+.I PDF\-font\-name
+with writing mode
+.I wMode
+is used (without embedding) in a PDF file, it will be translated to
+the PostScript font
+.IR PS\-font\-name ,
+which is assumed to be resident in the printer.  The writing mode
+must be either \'H' for horizontal or \'V' for vertical.  The resident
+font is assumed to use the specified encoding (which must have been
+defined with the unicodeMap command).
+.TP
+.BI psResidentFontCC " registry\-ordering wMode PS\-font\-name encoding"
+When a 16-bit (CID) font using the
+.I registry\-ordering
+character collection and 
+.I wMode
+writing mode is used (without embedding) in a PDF file, the PostScript
+font,
+.IR PS\-font\-name ,
+is substituted for it.  The substituted font is assumed to be
+resident in the printer.  The writing mode must be either \'H' for
+horizontal or \'V' for vertical.  The resident font is assumed to use
+the specified encoding (which must have been defined with the
+unicodeMap command).
+.TP
+.BR psEmbedType1Fonts " yes | no"
+If set to "no", prevents embedding of Type 1 fonts in generated
+PostScript.  This defaults to "yes".
+.TP
+.BR psEmbedTrueTypeFonts " yes | no"
+If set to "no", prevents embedding of TrueType fonts in generated
+PostScript.  This defaults to "yes".
+.TP
+.BR psEmbedCIDTrueTypeFonts " yes | no"
+If set to "no", prevents embedding of CID TrueType fonts in generated
+PostScript.  For Level 3 PostScript, this generates a CID font, for
+lower levels it generates a non-CID composite font.  This defaults to
+"yes".
+.TP
+.BR psEmbedCIDPostScriptFonts " yes | no"
+If set to "no", prevents embedding of CID PostScript fonts in
+generated PostScript.  For Level 3 PostScript, this generates a CID
+font, for lower levels it generates a non-CID composite font.  This
+defaults to "yes".
+.SH POSTSCRIPT CONTROL
+.TP
+.BI psPaperSize " width(pts) height(pts)"
+Sets the paper size for PostScript output.  The
+.I width
+and
+.I height
+parameters give the paper size in PostScript points (1 point = 1/72
+inch).
+.TP
+.BR psPaperSize " letter | legal | A4 | A3 | match"
+Sets the paper size for PostScript output to a standard size.  The
+default paper size is set when xpdf and pdftops are built, typically
+to "letter" or "A4".  This can also be set to "match", which will set
+the paper size to match the size specified in the PDF file.
+.TP
+.BR psImageableArea " llx lly urx ury"
+Sets the imageable area for PostScript output.  The four integers are
+the coordinates of the lower-left and upper-right corners of the
+imageable region, specified in points (with the origin being the
+lower-left corner of the paper).  This defaults to the full paper
+size; the psPaperSize option will reset the imageable area
+coordinates.
+.TP
+.BR psCrop " yes | no"
+If set to "yes", PostScript output is cropped to the CropBox specified
+in the PDF file; otherwise no cropping is done.  This defaults to
+"yes".
+.TP
+.BR psUseCropBoxAsPage " yes | no"
+If set to "yes", PostScript output treats the CropBox as the page size.
+By default, this is "no", and the MediaBox is used as the page size.
+.TP
+.BR psExpandSmaller " yes | no"
+If set to "yes", PDF pages smaller than the PostScript imageable area
+are expanded to fill the imageable area.  Otherwise, no scaling is
+done on smaller pages.  This defaults to "no".
+.TP
+.BR psShrinkLarger " yes | no"
+If set to yes, PDF pages larger than the PostScript imageable area are
+shrunk to fit the imageable area.  Otherwise, no scaling is done on
+larger pages.  This defaults to "yes".
+.TP
+.BR psCenter " yes | no"
+If set to yes, PDF pages smaller than the PostScript imageable area
+(after any scaling) are centered in the imageable area.  Otherwise,
+they are aligned at the lower-left corner of the imageable area.  This
+defaults to "yes".
+.TP
+.BR psDuplex " yes | no"
+If set to "yes", the generated PostScript will set the "Duplex"
+pagedevice entry.  This tells duplex-capable printers to enable
+duplexing.  This defaults to "no".
+.TP
+.BR psLevel " level1 | level1sep | level2 | level2gray | level2sep | level3 | level3gray | level3Sep"
+Sets the PostScript level to generate.  This defaults to "level2".
+.TP
+.BR psPreload " yes | no"
+If set to "yes", PDF forms are converted to PS procedures, and image
+data is preloaded.  This uses more memory in the PostScript
+interpreter, but generates significantly smaller PS files in
+situations where, e.g., the same image is drawn on every page of a
+long document.  This defaults to "no".
+.TP
+.BR psOPI " yes | no"
+If set to "yes", generates PostScript OPI comments for all images and
+forms which have OPI information.  This option is only available if
+the Xpdf tools were compiled with OPI support.  This defaults to "no".
+.TP
+.BR psASCIIHex " yes | no"
+If set to "yes", the ASCIIHexEncode filter will be used instead of
+ASCII85Encode for binary data.  This defaults to "no".
+.TP
+.BR psLZW " yes | no"
+If set to "yes", the LZWEncode filter will be used for lossless
+compression in PostScript output; if set to "no", the RunLengthEncode
+filter will be used instead.  LZW generates better compression
+(smaller PS files), but may not be supported by some printers.  This
+defaults to "yes".
+.TP
+.BR psUncompressPreloadedImages " yes | no"
+If set to "yes", all preloaded images in PS files will uncompressed.
+If set to "no", the original compressed images will be used when
+possible.  The "yes" setting is useful to work around certain buggy
+PostScript interpreters.  This defaults to "no".
+.TP
+.BR psMinLineWidth " float"
+Set the minimum line width, in points, for PostScript output.  The
+default value is 0 (no minimum).
+.TP
+.BR psRasterResolution " float"
+Set the resolution (in dpi) for rasterized pages in PostScript output.
+(Pdftops will rasterize pages which use transparency.)  This defaults
+to 300.
+.TP
+.BR psRasterMono " yes | no"
+If set to "yes", rasterized pages in PS files will be monochrome
+(8-bit gray) instead of color.  This defaults to "no".
+.TP
+.BR psRasterSliceSize " pixels"
+When rasterizing pages, pdftops splits the page into horizontal
+"slices", to limit memory usage.  This option sets the maximum slice
+size, in pixels.  This defaults to 20000000 (20 million).
+.TP
+.BR psAlwaysRasterize " yes | no"
+If set to "yes", all PostScript output will be rasterized.  This
+defaults to "no".
+.TP
+.BR psNeverRasterize " yes | no"
+If set to "yes", PostScript output will never be rasterized.  This
+will likely result in incorrect output for PDF files that use
+transparency.  (If both psAlwaysRasterize and psNeverRasterize are set
+to "yes", the psAlwaysRasterize setting wins.)  This defaults to "no".
+.TP
+.BI psFile " file\-or\-command"
+Sets the default PostScript file or print command for xpdf.  Commands
+start with a \'|' character; anything else is a file.  If the file
+name or command contains spaces it must be quoted.  This defaults to
+unset, which tells xpdf to generate a name of the form <file>.ps for a
+PDF file <file>.pdf.
+.TP
+.BI fontDir " dir"
+See the description above, in the DISPLAY FONTS section.
+.SH TEXT CONTROL AND CHARACTER MAPPING
+.TP
+.BI textEncoding " encoding\-name"
+Sets the encoding to use for text output.  (This can be overridden
+with the "\-enc" switch on the command line.)  The
+.I encoding\-name
+must be defined with the unicodeMap command (see above).  This
+defaults to "Latin1".
+.TP
+.BR textEOL " unix | dos | mac"
+Sets the end-of-line convention to use for text output.  The options
+are:
+.nf
+
+    unix = LF
+    dos  = CR+LF
+    mac  = CR
+
+.fi
+(This can be overridden with the "\-eol" switch on the command line.)
+The default value is based on the OS where xpdf and pdftotext were
+built.
+.TP
+.BR textPageBreaks " yes | no"
+If set to "yes", text extraction will insert page breaks (form feed
+characters) between pages.  This defaults to "yes".
+.TP
+.BR textKeepTinyChars " yes | no"
+If set to "yes", text extraction will keep all characters.  If set to
+"no", text extraction will discard tiny (smaller than 3 point)
+characters after the first 50000 per page, avoiding extremely slow run
+times for PDF files that use special fonts to do shading or
+cross-hatching.  This defaults to "yes".
+.TP
+.BI nameToUnicode " map\-file"
+Specifies a file with the mapping from character names to Unicode.
+This is used to handle PDF fonts that have valid encodings but no
+ToUnicode entry.  Each line of a nameToUnicode file looks like this:
+
+.I "    " hex\-string name
+
+The
+.I hex\-string
+is the Unicode (UCS-2) character index, and
+.I name
+is the corresponding character name.  Multiple nameToUnicode files can
+be used; if a character name is given more than once, the code in the
+last specified file is used.  There is a built-in default
+nameToUnicode table with all of Adobe's standard character names.
+.TP
+.BI cidToUnicode " registry\-ordering map\-file"
+Specifies the file with the mapping from character collection to
+Unicode.  Each line of a cidToUnicode file represents one character:
+
+.I "    " hex\-string
+
+The
+.I hex\-string
+is the Unicode (UCS-2) index for that character.  The first line maps
+CID 0, the second line CID 1, etc.  File size is determined by size of
+the character collection.  Only one file is allowed per character
+collection; the last specified file is used.  There are no built-in
+cidToUnicode mappings.
+.TP
+.BI unicodeToUnicode " font\-name\-substring map\-file"
+This is used to work around PDF fonts which have incorrect Unicode
+information.  It specifies a file which maps from the given
+(incorrect) Unicode indexes to the correct ones.  The mapping will be
+used for any font whose name contains
+.IR font\-name\-substring .
+Each line of a unicodeToUnicode file represents one Unicode character:
+
+.RI "    " in\-hex " " out\-hex1 " " out\-hex2 " ..."
+
+The
+.I in\-hex
+field is an input (incorrect) Unicode index, and the rest of the
+fields are one or more output (correct) Unicode indexes.  Each
+occurrence of
+.I in\-hex
+will be converted to the specified output sequence.
+.TP
+.BI unicodeMap " encoding\-name map\-file"
+Specifies the file with mapping from Unicode to
+.IR encoding\-name .
+These encodings are used for text output (see below).  Each line of a
+unicodeMap file represents a range of one or more Unicode characters
+which maps linearly to a range in the output encoding:
+.nf
+
+.I "    " in\-start\-hex in\-end\-hex out\-start\-hex
+
+.fi
+Entries for single characters can be abbreviated to:
+.nf
+
+.I "    " in\-hex out\-hex
+
+.fi
+The
+.I in\-start\-hex
+and
+.I in\-end\-hex
+fields (or the single
+.I in\-hex
+field) specify the Unicode range.  The
+.I out\-start\-hex
+field (or the
+.I out\-hex
+field) specifies the start of the output encoding range.  The length
+of the
+.I out\-start\-hex
+(or
+.IR out\-hex )
+string determines the length of the output characters (e.g., UTF-8
+uses different numbers of bytes to represent characters in different
+ranges).  Entries must be given in increasing Unicode order.  Only one
+file is allowed per encoding; the last specified file is used.  The
+.IR Latin1 ,
+.IR ASCII7 ,
+.IR Symbol ,
+.IR ZapfDingbats ,
+.IR UTF-8 ,
+and
+.I UCS-2
+encodings are predefined.
+.TP
+.BI cMapDir " registry\-ordering dir"
+Specifies a search directory,
+.IR dir ,
+for CMaps for the
+.I registry\-ordering
+character collection.  There can be multiple directories for a
+particular collection.  There are no default CMap directories.
+.TP
+.BI toUnicodeDir " dir"
+Specifies a search directory,
+.IR dir ,
+for ToUnicode CMaps.  There can be multiple ToUnicode directories.
+There are no default ToUnicode directories.
+.TP
+.BI mapNumericCharNames " yes | no"
+If set to "yes", the Xpdf tools will attempt to map various numeric
+character names sometimes used in font subsets.  In some cases this
+leads to usable text, and in other cases it leads to gibberish --
+there is no way for Xpdf to tell.  This defaults to "yes".
+.TP
+.BI mapUnknownCharNames " yes | no"
+If set to "yes", and mapNumericCharNames is set to "no", the Xpdf
+tools will apply a simple pass-through mapping (Unicode index =
+character code) for all unrecognized glyph names.  (For CID fonts,
+setting mapNumericCharNames to "no" is unnecessary.)  In some cases,
+this leads to usable text, and in other cases it leads to gibberish --
+there is no way for Xpdf to tell.  This defaults to "no".
+.TP
+.BI mapExtTrueTypeFontsViaUnicode " yes | no"
+When rasterizing text using an external TrueType font, there are two
+options for handling character codes.  If
+mapExtTrueTypeFontsViaUnicode is set to "yes", Xpdf will use the font
+encoding/ToUnicode info to map character codes to Unicode, and then
+use the font's Unicode cmap to map Unicode to GIDs.  If
+mapExtTrueTypeFontsViaUnicode is set to "no", Xpdf will assume the
+character codes are GIDs (i.e., use an identity mapping).  This
+defaults to "yes".
+.SH RASTERIZER SETTINGS
+.TP
+.BR enableFreeType " yes | no"
+Enables or disables use of FreeType (a TrueType / Type 1 font
+rasterizer).  This is only relevant if the Xpdf tools were built with
+FreeType support.  ("enableFreeType" replaces the old
+"freetypeControl" option.)  This option defaults to "yes".
+.TP
+.BR disableFreeTypeHinting " yes | no"
+If this is set to "yes", FreeType hinting will be forced off.  This
+option defaults to "no".
+.TP
+.BR antialias " yes | no"
+Enables or disables font anti-aliasing in the PDF rasterizer.  This
+option affects all font rasterizers.  ("antialias" replaces the
+anti-aliasing control provided by the old "t1libControl" and
+"freetypeControl" options.)  This default to "yes".
+.TP
+.BR vectorAntialias " yes | no"
+Enables or disables anti-aliasing of vector graphics in the PDF
+rasterizer.  This defaults to "yes".
+.TP
+.BR antialiasPrinting " yes | no"
+If this is "yes", bitmaps sent to the printer will be antialiased
+(according to the "antialias" and "vectorAntialias" settings).  If
+this is "no", printed bitmaps will not be antialiased.  This defaults
+to "no".
+.TP
+.BR strokeAdjust " yes | no | cad"
+Sets the stroke adjustment mode.  If set to "no", no stroke adjustment
+will be done.  If set to "yes", normal stroke adjustment will be done:
+horizontal and vertical lines will be moved by up to half a pixel to
+make them look cleaner when vector anti-aliasing is enabled.  If set
+to "cad", a slightly different stroke adjustment algorithm will be
+used to ensure that lines of the same original width will always have
+the same adjusted width (at the expense of allowing gaps and overlaps
+between adjacent lines).  This defaults to "yes".
+.TP
+.BR screenType " dispersed | clustered | stochasticClustered"
+Sets the halftone screen type, which will be used when generating a
+monochrome (1-bit) bitmap.  The three options are dispersed-dot
+dithering, clustered-dot dithering (with a round dot and 45-degree
+screen angle), and stochastic clustered-dot dithering.  By default,
+"stochasticClustered" is used for resolutions of 300 dpi and higher,
+and "dispersed" is used for resolutions lower then 300 dpi.
+.TP
+.BI screenSize " integer"
+Sets the size of the (square) halftone screen threshold matrix.  By
+default, this is 4 for dispersed-dot dithering, 10 for clustered-dot
+dithering, and 100 for stochastic clustered-dot dithering.
+.TP
+.BI screenDotRadius " integer"
+Sets the halftone screen dot radius.  This is only used when
+screenType is set to stochasticClustered, and it defaults to 2.  In
+clustered-dot mode, the dot radius is half of the screen size.
+Dispersed-dot dithering doesn't have a dot radius.
+.TP
+.BI screenGamma " float"
+Sets the halftone screen gamma correction parameter.  Gamma values
+greater than 1 make the output brighter; gamma values less than 1 make
+it darker.  The default value is 1.
+.TP
+.BI screenBlackThreshold " float"
+When halftoning, all values below this threshold are forced to solid
+black.  This parameter is a floating point value between 0 (black) and
+1 (white).  The default value is 0.
+.TP
+.BI screenWhiteThreshold " float"
+When halftoning, all values above this threshold are forced to solid
+white.  This parameter is a floating point value between 0 (black) and
+1 (white).  The default value is 1.
+.TP
+.BI minLineWidth " float"
+Set the minimum line width, in device pixels.  This affects the
+rasterizer only, not the PostScript converter (except when it uses
+rasterization to handle transparency).  The default value is 0 (no
+minimum).
+.TP
+.BI enablePathSimplification " yes | no"
+If set to "yes", simplify paths by removing points where it won't make
+a significant difference to the shape.  The default value is "no".
+.TP
+.BI overprintPreview " yes | no"
+If set to "yes", generate overprint preview output, honoring the
+OP/op/OPM settings in the PDF file.  Ignored for non-CMYK output.  The
+default value is "no".
+.SH VIEWER SETTINGS
+These settings only apply to the Xpdf GUI PDF viewer.
+.TP
+.BR initialZoom " \fIpercentage\fR | page | width"
+Sets the initial zoom factor.  A number specifies a zoom percentage,
+where 100 means 72 dpi.  You may also specify \'page', to fit the page
+to the window size, or \'width', to fit the page width to the window
+width.
+.TP
+.BI defaultFitZoom " percentage"
+If xpdf is started with fit-page or fit-width zoom and no window
+geometry, it will calculate a desired window size based on the PDF
+page size and this defaultFitZoom value.  I.e., the window size will
+be chosen such that exactly one page will fit in the window at this
+zoom factor (which must be a percentage).  The default value is
+based on the screen resolution.
+.TP
+.BI initialSidebarState " yes | no"
+If set to "yes", xpdf opens with the sidebar (tabs, outline, etc.)
+visible.  If set to "no", xpdf opens with the sidebar collapsed.  The
+default is "no".
+.TP
+.BI paperColor " color"
+Set the "paper color", i.e., the background of the page display.  The
+color can be #RRGGBB (hexadecimal) or a named color.  This option will
+not work well with PDF files that do things like filling in white
+behind the text.
+.TP
+.BI matteColor " color"
+Set the matte color, i.e., the color used for background outside the
+actual page area.  The color can be #RRGGBB (hexadecimal) or a named
+color.
+.TP
+.BI fullScreenMatteColor " color"
+Set the matte color for full-screen mode.  The color can be #RRGGBB
+(hexadecimal) or a named color.
+.TP
+.BI popupMenuCmd " title command ..."
+Add a command to the popup menu.
+.I Title
+is the text to be displayed in the menu.
+.I Command
+is an Xpdf command (see the COMMANDS section of the
+.BR xpdf (1)
+man page for details).  Multiple commands are separated by whitespace.
+.TP
+.BI maxTileWidth " pixels"
+Set the maximum width of tiles to be used by xpdf when rasterizing
+pages.  This defaults to 1500.
+.TP
+.BI maxTileHeight " pixels"
+Set the maximum height of tiles to be used by xpdf when rasterizing
+pages.  This defaults to 1500.
+.TP
+.BI tileCacheSize " tiles"
+Set the maximum number of tiles to be cached by xpdf when rasterizing
+pages.  This defaults to 10.
+.TP
+.BI workerThreads " numThreads"
+Set the number of worker threads to be used by xpdf when rasterizing
+pages.  This defaults to 1.
+.TP
+.BI launchCommand " command"
+Sets the command executed when you click on a "launch"-type link.  The
+intent is for the command to be a program/script which determines the
+file type and runs the appropriate viewer.  The command line will
+consist of the file to be launched, followed by any parameters
+specified with the link.  Do not use "%s" in "command".  By default,
+this is unset, and Xpdf will simply try to execute the file (after
+prompting the user).
+.TP
+.BI movieCommand " command"
+Sets the command executed when you click on a movie annotation.  The
+string "%s" will be replaced with the movie file name.  This has no
+default value.
+.TP
+.BI bind " modifiers-key context command ..."
+Add a key or mouse button binding.
+.I Modifiers
+can be zero or more of:
+.nf
+
+    shift-
+    ctrl-
+    alt-
+
+.fi
+.I Key
+can be a regular ASCII character, or any one of:
+.nf
+
+    space
+    tab
+    return
+    enter
+    backspace
+    esc
+    insert
+    delete
+    home
+    end
+    pgup
+    pgdn
+    left / right / up / down        (arrow keys)
+    f1 .. f35                       (function keys)
+    mousePress1 .. mousePress7      (mouse buttons)
+    mouseRelease1 .. mouseRelease7  (mouse buttons)
+    mouseClick1 .. mouseClick7      (mouse buttons)
+
+.fi
+.I Context
+is either "any" or a comma-separated combination of:
+.nf
+
+    fullScreen / window       (full screen mode on/off)
+    continuous / singlePage   (continuous mode on/off)
+    overLink / offLink        (mouse over link or not)
+    scrLockOn / scrLockOff    (scroll lock on/off)
+
+.fi
+The context string can include only one of each pair in the above
+list.
+
+.I Command
+is an Xpdf command (see the COMMANDS section of the
+.BR xpdf (1)
+man page for details).  Multiple commands are separated by whitespace.
+
+The bind command replaces any existing binding, but only if it was
+defined for the exact same modifiers, key, and context.  All tokens
+(modifiers, key, context, commands) are case-sensitive.
+
+Example key bindings:
+.nf
+
+    # bind ctrl-a in any context to the nextPage
+    # command
+    bind ctrl-a any nextPage
+
+    # bind uppercase B, when in continuous mode
+    # with scroll lock on, to the reload command
+    # followed by the prevPage command
+    bind B continuous,scrLockOn reload prevPage
+
+.fi
+See the
+.BR xpdf (1)
+man page for more examples.
+.TP
+.BI unbind " modifiers-key context"
+Removes a key binding established with the bind command.  This is most
+useful to remove default key bindings before establishing new ones
+(e.g., if the default key binding is given for "any" context, and you
+want to create new key bindings for multiple contexts).
+.SH MISCELLANEOUS SETTINGS
+.TP
+.BI drawAnnotations " yes | no"
+If set to "no", annotations will not be drawn or printed.  The default
+value is "yes".
+.TP
+.BI drawFormFields " yes | no"
+If set to "no", form fields will not be drawn or printed.  The default
+value is "yes".
+.TP
+.BI enableXFA " yes | no"
+If set to "yes", an XFA form (if present) will be rendered in place of
+an AcroForm.  If "no", an XFA form will never be rendered.  This
+defaults to "yes".
+.TP
+.BI printCommands " yes | no"
+If set to "yes", drawing commands are printed as they're executed
+(useful for debugging).  This defaults to "no".
+.TP
+.BI errQuiet " yes | no"
+If set to "yes", this suppresses all error and warning messages from
+all of the Xpdf tools.  This defaults to "no".
+.SH EXAMPLES
+The following is a sample xpdfrc file.
+.nf
+
+# from the Thai support package
+nameToUnicode /usr/local/share/xpdf/Thai.nameToUnicode
+
+# from the Japanese support package
+cidToUnicode Adobe-Japan1 /usr/local/share/xpdf/Adobe-Japan1.cidToUnicode
+unicodeMap   JISX0208     /usr/local/share/xpdf/JISX0208.unicodeMap
+cMapDir      Adobe-Japan1 /usr/local/share/xpdf/cmap/Adobe-Japan1
+
+# use the Base-14 Type 1 fonts from ghostscript
+fontFile Times-Roman           /usr/local/share/ghostscript/fonts/n021003l.pfb
+fontFile Times-Italic          /usr/local/share/ghostscript/fonts/n021023l.pfb
+fontFile Times-Bold            /usr/local/share/ghostscript/fonts/n021004l.pfb
+fontFile Times-BoldItalic      /usr/local/share/ghostscript/fonts/n021024l.pfb
+fontFile Helvetica             /usr/local/share/ghostscript/fonts/n019003l.pfb
+fontFile Helvetica-Oblique     /usr/local/share/ghostscript/fonts/n019023l.pfb
+fontFile Helvetica-Bold        /usr/local/share/ghostscript/fonts/n019004l.pfb
+fontFile Helvetica-BoldOblique /usr/local/share/ghostscript/fonts/n019024l.pfb
+fontFile Courier               /usr/local/share/ghostscript/fonts/n022003l.pfb
+fontFile Courier-Oblique       /usr/local/share/ghostscript/fonts/n022023l.pfb
+fontFile Courier-Bold          /usr/local/share/ghostscript/fonts/n022004l.pfb
+fontFile Courier-BoldOblique   /usr/local/share/ghostscript/fonts/n022024l.pfb
+fontFile Symbol                /usr/local/share/ghostscript/fonts/s050000l.pfb
+fontFile ZapfDingbats          /usr/local/share/ghostscript/fonts/d050000l.pfb
+
+# use the Bakoma Type 1 fonts
+# (this assumes they happen to be installed in /usr/local/fonts/bakoma)
+fontDir /usr/local/fonts/bakoma
+
+# set some PostScript options
+psPaperSize          letter
+psDuplex             no
+psLevel              level2
+psEmbedType1Fonts    yes
+psEmbedTrueTypeFonts yes
+psFile               "| lpr \-Pprinter5"
+
+# assume that the PostScript printer has the Univers and
+# Univers-Bold fonts
+psResidentFont Univers      Univers
+psResidentFont Univers-Bold Univers-Bold
+
+# set the text output options
+textEncoding UTF-8
+textEOL      unix
+
+# misc options
+enableFreeType  yes
+launchCommand   viewer-script
+urlCommand      "netscape \-remote 'openURL(%s)'"
+
+.fi
+.SH FILES
+.TP
+.B /usr/local/etc/xpdfrc
+This is the default location for the system-wide configuration file.
+Depending on build options, it may be placed elsewhere.
+.TP
+.B $HOME/.xpdfrc
+This is the user's configuration file.  If it exists, it will be read
+in place of the system-wide file.
+.SH AUTHOR
+The Xpdf software and documentation are copyright 1996-2017 Glyph &
+Cog, LLC.
+.SH "SEE ALSO"
+.BR xpdf (1),
+.BR pdftops (1),
+.BR pdftotext (1),
+.BR pdftohtml (1),
+.BR pdfinfo (1),
+.BR pdffonts (1),
+.BR pdfdetach (1),
+.BR pdftoppm (1),
+.BR pdftopng (1),
+.BR pdfimages (1)
+.br
+.B http://www.xpdfreader.com/
Index: main/trunk/greenstone2/bin/script/gsConvert.pl
===================================================================
--- main/trunk/greenstone2/bin/script/gsConvert.pl	(revision 32204)
+++ main/trunk/greenstone2/bin/script/gsConvert.pl	(revision 32205)
@@ -323,8 +323,21 @@
 
     # Attempt conversion to HTML
-    if (!$output_type || ($output_type =~ m/html/i)) {
+    # Uses the old pdftohtml that doesn't work for newer PDF versions
+    #if ($output_type =~ m/^html/i) {
+    if (!$output_type || ($output_type =~ m/^html/i)) {
 	$success = &pdf_to_html($dirname, $input_filename, $output_filestem);
 	if ($success) {
 	    return "html";
+	}
+    }
+
+    # Attempt conversion to (paged) HTML using the newer pdftohtml of Xpdftools. This
+    # will be the new default for PDFs when output_type for PDF docs is not specified
+    # (once our use of xpdftools' pdftohtml has been implemented on win and mac).
+    if ($output_type =~ m/paged_html/i) {
+    #if (!$output_type || ($output_type =~ m/paged_html/i)) {
+	$success = &xpdf_to_html($dirname, $input_filename, $output_filestem);
+	if ($success) {
+	    return "paged_html";
 	}
     }
@@ -756,6 +769,6 @@
 
 
-# Convert a pdf file to html with the pdftohtml command
-
+# Convert a pdf file to html with the old pdftohtml command
+# which only works for older PDF versions
 sub pdf_to_html {
     my ($dirname, $input_filename, $output_filestem) = @_;
@@ -819,4 +832,111 @@
     return 1;
 }
+
+
+# Convert a pdf file to html with the newer Xpdftools' pdftohtml
+# This generates "paged HTML" where extracted, selectable text is positioned
+# over screenshots of each page.
+# Since xpdf's pdftohtml fails if the output dir already exists and for easier
+# naming, the output files are created in a "pages" subdirectory of the tmp
+# location parent of $output_filestem instead
+sub xpdf_to_html {
+    my ($dirname, $input_filename, $output_filestem) = @_;
+
+    my $cmd = "";
+
+    # build up the path to the doc-to-html conversion tool we're going to use
+    my $xpdf_pdftohtml = &FileUtils::filenameConcatenate($ENV{'GSDLHOME'}, "bin", $ENV{'GSDLOS'}, "xpdf-tools");
+
+    if ($ENV{'GSDLOS'} =~ m/^windows$/i) {
+	# TODO
+    } elsif ($ENV{'GSDLOS'} =~ m/^darwin$/i) {
+	# TODO
+    } else { # unix, use the appropriate bin folder for the bitness of the system
+
+	# Don't use $ENV{'GSDLARCH'}, use the new $ENV{'BITNESS'}, since
+	# $ENV{'GSDLARCH'} is only (meant to be) set when many other 32-bit or 64-bit
+	# specific subdirectories exist in a greenstone installation.
+	# None of those locations need exist when xpdf-tools is installed with GS.
+	# So don't depend on GSDLARCH as forcing that to be exported has side-effects
+	if($ENV{'BITNESS'}) {
+	    $xpdf_pdftohtml = &FileUtils::filenameConcatenate($xpdf_pdftohtml, "bin".$ENV{'BITNESS'});
+	} else { # what if $ENV{'BITNESS'} undefined, fallback on bin32? or 64?
+	    $xpdf_pdftohtml = &FileUtils::filenameConcatenate($xpdf_pdftohtml, "bin32");
+	}
+    }
+
+    # We'll create the file by name $output_filestem during post-conversion processing.
+    # Note that Xpdf tools will only create its conversion products in a dir that does
+    # not yet exist. So we'll create this location as a subdir of the output_filestem's
+    # parent directory. The parent dir is the already generated tmp area for conversion. So:
+    # - tmpdir gs2build/tmp/<random-num> already exists at this stage
+    # - We'll create gs2build/tmp/<rand>/output_filestem.html later, during post-processing
+    # - For now, XPdftools will create gs2build/tmp/<rand>/pages and put its products in there.
+    my ($tailname, $tmp_dirname, $suffix)
+	= &File::Basename::fileparse($output_filestem, "\\.[^\\.]+\$");
+    $tmp_dirname = &FileUtils::filenameConcatenate($tmp_dirname, "pages");
+
+    $xpdf_pdftohtml = &FileUtils::filenameConcatenate($xpdf_pdftohtml, "pdftohtml");
+    # xpdf's pdftohtml tool also takes a zoom factor, where a zoom of 1 is 100%
+    $cmd .= "\"$xpdf_pdftohtml\"";
+    $cmd .= " -z $pdf_zoom" if ($pdf_zoom);
+#    $cmd .= " -c" if ($pdf_complex);
+#    $cmd .= " -i" if ($pdf_ignore_images);
+#    $cmd .= " -a" if ($pdf_allow_images_only);
+#    $cmd .= " -hidden" unless ($pdf_nohidden);    
+    $cmd .= " \"$input_filename\" \"$tmp_dirname\"";
+    #$cmd .= " \"$input_filename\" \"$output_filestem\"";
+
+    if ($ENV{'GSDLOS'} !~ m/^windows$/i || $is_winnt_2000) {
+	$cmd .= " > \"$output_filestem.out\" 2> \"$output_filestem.err\"";
+    } else {
+	$cmd .= " > \"$output_filestem.err\"";
+    }
+
+    #print STDERR "@@@@ Running command: $cmd\n";
+
+    $!=0;
+    my $retval=system($cmd);
+    if ($retval!=0)
+    {
+	print STDERR "Error executing xpdf's pdftohtml tool";
+	if ($!) {print STDERR ": $!";} 
+	print STDERR "\n";
+    }
+
+    # make sure the converter made something
+    if ($retval!=0 || ! -s &FileUtils::filenameConcatenate($tmp_dirname,"index.html"))
+    {
+	&FileUtils::removeFiles("$output_filestem.out") if (-e "$output_filestem.out");
+	# print out the converter's std err, if any
+	if (-s "$output_filestem.err") {
+	    open (ERRLOG, "$output_filestem.err") || die "$!";
+	    print STDERR "pdftohtml error log:\n";
+	    while (<ERRLOG>) {
+		print STDERR "$_";
+	    }
+	    close ERRLOG;
+	}
+	#print STDERR "***********output filestem $output_filestem.html\n";
+	&FileUtils::removeFiles("$tmp_dirname") if (-d "$tmp_dirname");
+	if (-e "$output_filestem.err") {
+	    if ($faillogfile ne "" && defined(open(FAILLOG,">>$faillogfile")))
+	    {
+		open (ERRLOG, "$output_filestem.err");
+		while (<ERRLOG>) {print FAILLOG $_;}
+		close ERRLOG;
+		close FAILLOG;
+	    }	
+	    &FileUtils::removeFiles("$output_filestem.err");
+	}
+	return 0;
+    }
+
+    &FileUtils::removeFiles("$output_filestem.err") if (-e "$output_filestem.err");
+    &FileUtils::removeFiles("$output_filestem.out") if (-e "$output_filestem.out");
+    return 1;
+}
+
+
 
 # Convert a pdf file to various types of image with the convert command
Index: main/trunk/greenstone2/perllib/cpan/Mojo.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo.pm	(revision 32205)
@@ -0,0 +1,93 @@
+package Mojo;
+use Mojo::Base -strict;
+
+# "Professor: These old Doomsday devices are dangerously unstable. I'll rest
+#             easier not knowing where they are."
+use Mojo::Log;
+use Mojo::Transaction::HTTP;
+use Mojo::Util 'deprecated';
+
+# DEPRECATED!
+sub build_tx {
+  deprecated 'Mojo::build_tx is DEPRECATED in favor of Mojolicious::build_tx';
+  Mojo::Transaction::HTTP->new;
+}
+
+# DEPRECATED!
+sub config {
+  deprecated 'Mojo::config is DEPRECATED in favor of Mojolicious::config';
+  Mojo::Util::_stash(config => @_);
+}
+
+# DEPRECATED!
+sub log {
+  deprecated 'Mojo::log is DEPRECATED in favor of Mojolicious::log';
+  my $self = shift;
+  return $self->{log} unless @_;
+  $self->{log} = Mojo::Log->new // shift;
+  return $self;
+}
+
+# DEPRECATED!
+sub handler {
+  deprecated 'Mojo::handler is DEPRECATED in favor of Mojolicious::handler';
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo - Web development toolkit
+
+=head1 SYNOPSIS
+
+  # HTTP/WebSocket user agent
+  use Mojo::UserAgent;
+  my $ua = Mojo::UserAgent->new;
+  say $ua->get('www.mojolicious.org')->result->headers->server;
+
+  # HTML/XML DOM parser with CSS selectors
+  use Mojo::DOM;
+  my $dom = Mojo::DOM->new('<div><b>Hello Mojo!</b></div>');
+  say $dom->at('div > b')->text;
+
+  # Perl-ish templates
+  use Mojo::Template;
+  my $mt = Mojo::Template->new(vars => 1);
+  say $mt->render('Hello <%= $what %>!', {what => 'Mojo'});
+
+  # HTTP/WebSocket server
+  use Mojo::Server::Daemon;
+  my $daemon = Mojo::Server::Daemon->new(listen => ['http://*:8080']);
+  $daemon->unsubscribe('request')->on(request => sub {
+    my ($daemon, $tx) = @_;
+    $tx->res->code(200);
+    $tx->res->body('Hello Mojo!');
+    $tx->resume;
+  });
+  $daemon->run;
+
+  # Event loop
+  use Mojo::IOLoop;
+  for my $seconds (1 .. 5) {
+    Mojo::IOLoop->timer($seconds => sub { say $seconds });
+  }
+  Mojo::IOLoop->start;
+
+=head1 DESCRIPTION
+
+A powerful web development toolkit, with all the basic tools and helpers needed
+to write simple web applications and higher level web frameworks, such as
+L<Mojolicious>. Some of the most commonly used tools are L<Mojo::UserAgent>,
+L<Mojo::DOM>, L<Mojo::JSON>, L<Mojo::Server::Daemon>, L<Mojo::Server::Prefork>,
+L<Mojo::IOLoop> and L<Mojo::Template>.
+
+See L<Mojolicious::Guides> for more!
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/Asset.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/Asset.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/Asset.pm	(revision 32205)
@@ -0,0 +1,145 @@
+package Mojo::Asset;
+use Mojo::Base 'Mojo::EventEmitter';
+
+use Carp 'croak';
+
+has 'end_range';
+has start_range => 0;
+
+sub add_chunk { croak 'Method "add_chunk" not implemented by subclass' }
+sub contains  { croak 'Method "contains" not implemented by subclass' }
+sub get_chunk { croak 'Method "get_chunk" not implemented by subclass' }
+
+sub is_file {undef}
+
+sub is_range { !!($_[0]->end_range || $_[0]->start_range) }
+
+sub move_to { croak 'Method "move_to" not implemented by subclass' }
+sub mtime   { croak 'Method "mtime" not implemented by subclass' }
+sub size    { croak 'Method "size" not implemented by subclass' }
+sub slurp   { croak 'Method "slurp" not implemented by subclass' }
+sub to_file { croak 'Method "to_file" not implemented by subclass' }
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::Asset - HTTP content storage base class
+
+=head1 SYNOPSIS
+
+  package Mojo::Asset::MyAsset;
+  use Mojo::Base 'Mojo::Asset';
+
+  sub add_chunk {...}
+  sub contains  {...}
+  sub get_chunk {...}
+  sub move_to   {...}
+  sub mtime     {...}
+  sub size      {...}
+  sub slurp     {...}
+  sub to_file   {...}
+
+=head1 DESCRIPTION
+
+L<Mojo::Asset> is an abstract base class for HTTP content storage backends,
+like L<Mojo::Asset::File> and L<Mojo::Asset::Memory>.
+
+=head1 EVENTS
+
+L<Mojo::Asset> inherits all events from L<Mojo::EventEmitter>.
+
+=head1 ATTRIBUTES
+
+L<Mojo::Asset> implements the following attributes.
+
+=head2 end_range
+
+  my $end = $asset->end_range;
+  $asset  = $asset->end_range(8);
+
+Pretend file ends earlier.
+
+=head2 start_range
+
+  my $start = $asset->start_range;
+  $asset    = $asset->start_range(3);
+
+Pretend file starts later.
+
+=head1 METHODS
+
+L<Mojo::Asset> inherits all methods from L<Mojo::EventEmitter> and implements
+the following new ones.
+
+=head2 add_chunk
+
+  $asset = $asset->add_chunk('foo bar baz');
+
+Add chunk of data to asset. Meant to be overloaded in a subclass.
+
+=head2 contains
+
+  my $position = $asset->contains('bar');
+
+Check if asset contains a specific string. Meant to be overloaded in a
+subclass.
+
+=head2 get_chunk
+
+  my $bytes = $asset->get_chunk($offset);
+  my $bytes = $asset->get_chunk($offset, $max);
+
+Get chunk of data starting from a specific position, defaults to a maximum
+chunk size of C<131072> bytes (128KiB). Meant to be overloaded in a subclass.
+
+=head2 is_file
+
+  my $bool = $asset->is_file;
+
+False, this is not a L<Mojo::Asset::File> object.
+
+=head2 is_range
+
+  my $bool = $asset->is_range;
+
+Check if asset has a L</"start_range"> or L</"end_range">.
+
+=head2 move_to
+
+  $asset = $asset->move_to('/home/sri/foo.txt');
+
+Move asset data into a specific file. Meant to be overloaded in a subclass.
+
+=head2 mtime
+
+  my $mtime = $asset->mtime;
+
+Modification time of asset. Meant to be overloaded in a subclass.
+
+=head2 size
+
+  my $size = $asset->size;
+
+Size of asset data in bytes. Meant to be overloaded in a subclass.
+
+=head2 slurp
+
+  my $bytes = $asset->slurp;
+
+Read all asset data at once. Meant to be overloaded in a subclass.
+
+=head2 to_file
+
+  my $file = $asset->to_file;
+
+Convert asset to L<Mojo::Asset::File> object. Meant to be overloaded in a
+subclass.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/Asset/File.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/Asset/File.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/Asset/File.pm	(revision 32205)
@@ -0,0 +1,273 @@
+package Mojo::Asset::File;
+use Mojo::Base 'Mojo::Asset';
+
+use Carp 'croak';
+use Fcntl 'SEEK_SET';
+use File::Spec::Functions ();
+use Mojo::File 'tempfile';
+
+has [qw(cleanup path)];
+has handle => sub {
+  my $self = shift;
+
+  # Open existing file
+  my $path = $self->path;
+  return Mojo::File->new($path)->open('<') if defined $path && -e $path;
+
+  $self->cleanup(1) unless defined $self->cleanup;
+
+  # Create a specific file
+  return Mojo::File->new($path)->open('+>>') if defined $path;
+
+  # Create a temporary file
+  my $template = 'mojo.tmp.XXXXXXXXXXXXXXXX';
+  my $file = tempfile DIR => $self->tmpdir, TEMPLATE => $template, UNLINK => 0;
+  $self->path($file->to_string);
+  return $file->open('+>>');
+};
+has tmpdir => sub { $ENV{MOJO_TMPDIR} || File::Spec::Functions::tmpdir };
+
+sub DESTROY {
+  my $self = shift;
+
+  return unless $self->cleanup && defined(my $path = $self->path);
+  if (my $handle = $self->handle) { close $handle }
+
+  # Only the process that created the file is allowed to remove it
+  unlink $path if -w $path && ($self->{pid} // $$) == $$;
+}
+
+sub add_chunk {
+  my ($self, $chunk) = @_;
+  ($self->handle->syswrite($chunk) // -1) == length $chunk
+    or croak "Can't write to asset: $!";
+  return $self;
+}
+
+sub contains {
+  my ($self, $str) = @_;
+
+  my $handle = $self->handle;
+  $handle->sysseek($self->start_range, SEEK_SET);
+
+  # Calculate window size
+  my $end  = $self->end_range // $self->size;
+  my $len  = length $str;
+  my $size = $len > 131072 ? $len : 131072;
+  $size = $end - $self->start_range if $size > $end - $self->start_range;
+
+  # Sliding window search
+  my $offset = 0;
+  my $start = $handle->sysread(my $window, $len);
+  while ($offset < $end) {
+
+    # Read as much as possible
+    my $diff = $end - ($start + $offset);
+    my $read = $handle->sysread(my $buffer, $diff < $size ? $diff : $size);
+    $window .= $buffer;
+
+    # Search window
+    my $pos = index $window, $str;
+    return $offset + $pos if $pos >= 0;
+    return -1 if $read == 0 || ($offset += $read) == $end;
+
+    # Resize window
+    substr $window, 0, $read, '';
+  }
+
+  return -1;
+}
+
+sub get_chunk {
+  my ($self, $offset, $max) = @_;
+  $max //= 131072;
+
+  $offset += $self->start_range;
+  my $handle = $self->handle;
+  $handle->sysseek($offset, SEEK_SET);
+
+  my $buffer;
+  if (defined(my $end = $self->end_range)) {
+    return '' if (my $chunk = $end + 1 - $offset) <= 0;
+    $handle->sysread($buffer, $chunk > $max ? $max : $chunk);
+  }
+  else { $handle->sysread($buffer, $max) }
+
+  return $buffer;
+}
+
+sub is_file {1}
+
+sub move_to {
+  my ($self, $to) = @_;
+
+  # Windows requires that the handle is closed
+  close $self->handle;
+  delete $self->{handle};
+
+  # Move file and prevent clean up
+  Mojo::File->new($self->path)->move_to($to);
+  return $self->path($to)->cleanup(0);
+}
+
+sub mtime { (stat shift->handle)[9] }
+
+sub new {
+  my $file = shift->SUPER::new(@_);
+  $file->{pid} = $$;
+  return $file;
+}
+
+sub size { -s shift->handle }
+
+sub slurp {
+  my $handle = shift->handle;
+  $handle->sysseek(0, SEEK_SET);
+  my $ret = my $content = '';
+  while ($ret = $handle->sysread(my $buffer, 131072, 0)) { $content .= $buffer }
+  return defined $ret ? $content : croak "Can't read from asset: $!";
+}
+
+sub to_file {shift}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::Asset::File - File storage for HTTP content
+
+=head1 SYNOPSIS
+
+  use Mojo::Asset::File;
+
+  # Temporary file
+  my $file = Mojo::Asset::File->new;
+  $file->add_chunk('foo bar baz');
+  say 'File contains "bar"' if $file->contains('bar') >= 0;
+  say $file->slurp;
+
+  # Existing file
+  my $file = Mojo::Asset::File->new(path => '/home/sri/foo.txt');
+  $file->move_to('/yada.txt');
+  say $file->slurp;
+
+=head1 DESCRIPTION
+
+L<Mojo::Asset::File> is a file storage backend for HTTP content.
+
+=head1 EVENTS
+
+L<Mojo::Asset::File> inherits all events from L<Mojo::Asset>.
+
+=head1 ATTRIBUTES
+
+L<Mojo::Asset::File> inherits all attributes from L<Mojo::Asset> and implements
+the following new ones.
+
+=head2 cleanup
+
+  my $bool = $file->cleanup;
+  $file    = $file->cleanup($bool);
+
+Delete L</"path"> automatically once the file is not used anymore.
+
+=head2 handle
+
+  my $handle = $file->handle;
+  $file      = $file->handle(IO::File->new);
+
+Filehandle, created on demand for L</"path">, which can be generated
+automatically and safely based on L</"tmpdir">.
+
+=head2 path
+
+  my $path = $file->path;
+  $file    = $file->path('/home/sri/foo.txt');
+
+File path used to create L</"handle">.
+
+=head2 tmpdir
+
+  my $tmpdir = $file->tmpdir;
+  $file      = $file->tmpdir('/tmp');
+
+Temporary directory used to generate L</"path">, defaults to the value of the
+C<MOJO_TMPDIR> environment variable or auto-detection.
+
+=head1 METHODS
+
+L<Mojo::Asset::File> inherits all methods from L<Mojo::Asset> and implements
+the following new ones.
+
+=head2 add_chunk
+
+  $file = $file->add_chunk('foo bar baz');
+
+Add chunk of data.
+
+=head2 contains
+
+  my $position = $file->contains('bar');
+
+Check if asset contains a specific string.
+
+=head2 get_chunk
+
+  my $bytes = $file->get_chunk($offset);
+  my $bytes = $file->get_chunk($offset, $max);
+
+Get chunk of data starting from a specific position, defaults to a maximum
+chunk size of C<131072> bytes (128KiB).
+
+=head2 is_file
+
+  my $bool = $file->is_file;
+
+True, this is a L<Mojo::Asset::File> object.
+
+=head2 move_to
+
+  $file = $file->move_to('/home/sri/bar.txt');
+
+Move asset data into a specific file and disable L</"cleanup">.
+
+=head2 mtime
+
+  my $mtime = $file->mtime;
+
+Modification time of asset.
+
+=head2 new
+
+  my $file = Mojo::Asset::File->new;
+  my $file = Mojo::Asset::File->new(path => '/home/sri/test.txt');
+  my $file = Mojo::Asset::File->new({path => '/home/sri/test.txt'});
+
+Construct a new L<Mojo::Asset::File> object.
+
+=head2 size
+
+  my $size = $file->size;
+
+Size of asset data in bytes.
+
+=head2 slurp
+
+  my $bytes = $file->slurp;
+
+Read all asset data at once.
+
+=head2 to_file
+
+  $file = $file->to_file;
+
+Does nothing but return the invocant, since we already have a
+L<Mojo::Asset::File> object.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/Asset/Memory.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/Asset/Memory.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/Asset/Memory.pm	(revision 32205)
@@ -0,0 +1,174 @@
+package Mojo::Asset::Memory;
+use Mojo::Base 'Mojo::Asset';
+
+use Mojo::Asset::File;
+use Mojo::File 'path';
+
+has 'auto_upgrade';
+has max_memory_size => sub { $ENV{MOJO_MAX_MEMORY_SIZE} || 262144 };
+has mtime => sub {$^T};
+
+sub add_chunk {
+  my ($self, $chunk) = @_;
+
+  # Upgrade if necessary
+  $self->{content} .= $chunk;
+  return $self if !$self->auto_upgrade || $self->size <= $self->max_memory_size;
+  $self->emit(upgrade => my $file = $self->to_file);
+  return $file;
+}
+
+sub contains {
+  my ($self, $str) = @_;
+
+  my $start = $self->start_range;
+  my $pos = index $self->{content} // '', $str, $start;
+  $pos -= $start if $start && $pos >= 0;
+  my $end = $self->end_range;
+
+  return $end && ($pos + length $str) >= $end ? -1 : $pos;
+}
+
+sub get_chunk {
+  my ($self, $offset, $max) = @_;
+  $max //= 131072;
+
+  $offset += $self->start_range;
+  if (my $end = $self->end_range) {
+    $max = $end + 1 - $offset if ($offset + $max) > $end;
+  }
+
+  return substr shift->{content} // '', $offset, $max;
+}
+
+sub move_to { path($_[1])->spurt($_[0]{content} // '') and return $_[0] }
+
+sub size { length(shift->{content} // '') }
+
+sub slurp { shift->{content} // '' }
+
+sub to_file { Mojo::Asset::File->new->add_chunk(shift->slurp) }
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::Asset::Memory - In-memory storage for HTTP content
+
+=head1 SYNOPSIS
+
+  use Mojo::Asset::Memory;
+
+  my $mem = Mojo::Asset::Memory->new;
+  $mem->add_chunk('foo bar baz');
+  say $mem->slurp;
+
+=head1 DESCRIPTION
+
+L<Mojo::Asset::Memory> is an in-memory storage backend for HTTP content.
+
+=head1 EVENTS
+
+L<Mojo::Asset::Memory> inherits all events from L<Mojo::Asset> and can emit the
+following new ones.
+
+=head2 upgrade
+
+  $mem->on(upgrade => sub {
+    my ($mem, $file) = @_;
+    ...
+  });
+
+Emitted when asset gets upgraded to a L<Mojo::Asset::File> object.
+
+  $mem->on(upgrade => sub {
+    my ($mem, $file) = @_;
+    $file->tmpdir('/tmp');
+  });
+
+=head1 ATTRIBUTES
+
+L<Mojo::Asset::Memory> inherits all attributes from L<Mojo::Asset> and
+implements the following new ones.
+
+=head2 auto_upgrade
+
+  my $bool = $mem->auto_upgrade;
+  $mem     = $mem->auto_upgrade($bool);
+
+Try to detect if content size exceeds L</"max_memory_size"> limit and
+automatically upgrade to a L<Mojo::Asset::File> object.
+
+=head2 max_memory_size
+
+  my $size = $mem->max_memory_size;
+  $mem     = $mem->max_memory_size(1024);
+
+Maximum size in bytes of data to keep in memory before automatically upgrading
+to a L<Mojo::Asset::File> object, defaults to the value of the
+C<MOJO_MAX_MEMORY_SIZE> environment variable or C<262144> (256KiB).
+
+=head2 mtime
+
+  my $mtime = $mem->mtime;
+  $mem      = $mem->mtime(1408567500);
+
+Modification time of asset, defaults to the value of C<$^T>.
+
+=head1 METHODS
+
+L<Mojo::Asset::Memory> inherits all methods from L<Mojo::Asset> and implements
+the following new ones.
+
+=head2 add_chunk
+
+  $mem     = $mem->add_chunk('foo bar baz');
+  my $file = $mem->add_chunk('abc' x 262144);
+
+Add chunk of data and upgrade to L<Mojo::Asset::File> object if necessary.
+
+=head2 contains
+
+  my $position = $mem->contains('bar');
+
+Check if asset contains a specific string.
+
+=head2 get_chunk
+
+  my $bytes = $mem->get_chunk($offset);
+  my $bytes = $mem->get_chunk($offset, $max);
+
+Get chunk of data starting from a specific position, defaults to a maximum
+chunk size of C<131072> bytes (128KiB).
+
+=head2 move_to
+
+  $mem = $mem->move_to('/home/sri/foo.txt');
+
+Move asset data into a specific file.
+
+=head2 size
+
+  my $size = $mem->size;
+
+Size of asset data in bytes.
+
+=head2 slurp
+
+  my $bytes = mem->slurp;
+
+Read all asset data at once.
+
+=head2 to_file
+
+  my $file = $mem->to_file;
+
+Convert asset to L<Mojo::Asset::File> object.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/Base.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/Base.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/Base.pm	(revision 32205)
@@ -0,0 +1,326 @@
+package Mojo::Base;
+
+use strict;
+use warnings;
+use utf8;
+use feature ();
+
+# No imports because we get subclassed, a lot!
+use Carp         ();
+use Scalar::Util ();
+
+# Defer to runtime so Mojo::Util can use "-strict"
+require Mojo::Util;
+
+# Only Perl 5.14+ requires it on demand
+use IO::Handle ();
+
+# Role support requires Role::Tiny 2.000001+
+use constant ROLES =>
+  !!(eval { require Role::Tiny; Role::Tiny->VERSION('2.000001'); 1 });
+
+# Protect subclasses using AUTOLOAD
+sub DESTROY { }
+
+sub attr {
+  my ($self, $attrs, $value) = @_;
+  return unless (my $class = ref $self || $self) && $attrs;
+
+  Carp::croak 'Default has to be a code reference or constant value'
+    if ref $value && ref $value ne 'CODE';
+
+  for my $attr (@{ref $attrs eq 'ARRAY' ? $attrs : [$attrs]}) {
+    Carp::croak qq{Attribute "$attr" invalid} unless $attr =~ /^[a-zA-Z_]\w*$/;
+
+    # Very performance-sensitive code with lots of micro-optimizations
+    if (ref $value) {
+      my $sub = sub {
+        return
+          exists $_[0]{$attr} ? $_[0]{$attr} : ($_[0]{$attr} = $value->($_[0]))
+          if @_ == 1;
+        $_[0]{$attr} = $_[1];
+        $_[0];
+      };
+      Mojo::Util::monkey_patch($class, $attr, $sub);
+    }
+    elsif (defined $value) {
+      my $sub = sub {
+        return exists $_[0]{$attr} ? $_[0]{$attr} : ($_[0]{$attr} = $value)
+          if @_ == 1;
+        $_[0]{$attr} = $_[1];
+        $_[0];
+      };
+      Mojo::Util::monkey_patch($class, $attr, $sub);
+    }
+    else {
+      Mojo::Util::monkey_patch($class, $attr,
+        sub { return $_[0]{$attr} if @_ == 1; $_[0]{$attr} = $_[1]; $_[0] });
+    }
+  }
+}
+
+sub import {
+  my ($class, $caller) = (shift, caller);
+  return unless my @flags = @_;
+
+  # Base
+  if ($flags[0] eq '-base') { $flags[0] = $class }
+
+  # Role
+  if ($flags[0] eq '-role') {
+    Carp::croak 'Role::Tiny 2.000001+ is required for roles' unless ROLES;
+    Mojo::Util::monkey_patch($caller, 'has', sub { attr($caller, @_) });
+    eval "package $caller; use Role::Tiny; 1" or die $@;
+  }
+
+  # Module and not -strict
+  elsif ($flags[0] !~ /^-/) {
+    no strict 'refs';
+    require(Mojo::Util::class_to_path($flags[0])) unless $flags[0]->can('new');
+    push @{"${caller}::ISA"}, $flags[0];
+    Mojo::Util::monkey_patch($caller, 'has', sub { attr($caller, @_) });
+  }
+
+  # Mojo modules are strict!
+  $_->import for qw(strict warnings utf8);
+  feature->import(':5.10');
+
+  # Signatures (Perl 5.20+)
+  if (($flags[1] || '') eq '-signatures') {
+    Carp::croak 'Subroutine signatures require Perl 5.20+' if $] < 5.020;
+    require experimental;
+    experimental->import('signatures');
+  }
+}
+
+sub new {
+  my $class = shift;
+  bless @_ ? @_ > 1 ? {@_} : {%{$_[0]}} : {}, ref $class || $class;
+}
+
+sub tap {
+  my ($self, $cb) = (shift, shift);
+  $_->$cb(@_) for $self;
+  return $self;
+}
+
+sub with_roles {
+  Carp::croak 'Role::Tiny 2.000001+ is required for roles' unless ROLES;
+  my ($self, @roles) = @_;
+
+  return Role::Tiny->create_class_with_roles($self,
+    map { /^\+(.+)$/ ? "${self}::Role::$1" : $_ } @roles)
+    unless my $class = Scalar::Util::blessed $self;
+
+  return Role::Tiny->apply_roles_to_object($self,
+    map { /^\+(.+)$/ ? "${class}::Role::$1" : $_ } @roles);
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::Base - Minimal base class for Mojo projects
+
+=head1 SYNOPSIS
+
+  package Cat;
+  use Mojo::Base -base;
+
+  has name => 'Nyan';
+  has ['age', 'weight'] => 4;
+
+  package Tiger;
+  use Mojo::Base 'Cat';
+
+  has friend  => sub { Cat->new };
+  has stripes => 42;
+
+  package main;
+  use Mojo::Base -strict;
+
+  my $mew = Cat->new(name => 'Longcat');
+  say $mew->age;
+  say $mew->age(3)->weight(5)->age;
+
+  my $rawr = Tiger->new(stripes => 38, weight => 250);
+  say $rawr->tap(sub { $_->friend->name('Tacgnol') })->weight;
+
+=head1 DESCRIPTION
+
+L<Mojo::Base> is a simple base class for L<Mojo> projects with fluent
+interfaces.
+
+  # Automatically enables "strict", "warnings", "utf8" and Perl 5.10 features
+  use Mojo::Base -strict;
+  use Mojo::Base -base;
+  use Mojo::Base 'SomeBaseClass';
+  use Mojo::Base -role;
+
+All four forms save a lot of typing. Note that role support depends on
+L<Role::Tiny> (2.000001+).
+
+  # use Mojo::Base -strict;
+  use strict;
+  use warnings;
+  use utf8;
+  use feature ':5.10';
+  use IO::Handle ();
+
+  # use Mojo::Base -base;
+  use strict;
+  use warnings;
+  use utf8;
+  use feature ':5.10';
+  use IO::Handle ();
+  push @ISA, 'Mojo::Base';
+  sub has { Mojo::Base::attr(__PACKAGE__, @_) }
+
+  # use Mojo::Base 'SomeBaseClass';
+  use strict;
+  use warnings;
+  use utf8;
+  use feature ':5.10';
+  use IO::Handle ();
+  require SomeBaseClass;
+  push @ISA, 'SomeBaseClass';
+  sub has { Mojo::Base::attr(__PACKAGE__, @_) }
+
+  # use Mojo::Base -role;
+  use strict;
+  use warnings;
+  use utf8;
+  use feature ':5.10';
+  use IO::Handle ();
+  use Role::Tiny;
+  sub has { Mojo::Base::attr(__PACKAGE__, @_) }
+
+On Perl 5.20+ you can also append a C<-signatures> flag to all three forms and
+enable support for L<subroutine signatures|perlsub/"Signatures">.
+
+  # Also enable signatures
+  use Mojo::Base -strict, -signatures;
+  use Mojo::Base -base, -signatures;
+  use Mojo::Base 'SomeBaseClass', -signatures;
+  use Mojo::Base -role, -signatures;
+
+This will also disable experimental warnings on versions of Perl where this
+feature was still experimental.
+
+=head1 FLUENT INTERFACES
+
+Fluent interfaces are a way to design object-oriented APIs around method
+chaining to create domain-specific languages, with the goal of making the
+readablity of the source code close to written prose.
+
+  package Duck;
+  use Mojo::Base -base;
+
+  has 'name';
+
+  sub quack {
+    my $self = shift;
+    my $name = $self->name;
+    say "$name: Quack!"
+  }
+
+L<Mojo::Base> will help you with this by having all attribute accessors created
+with L</"has"> (or L</"attr">) return their invocant (C<$self>) whenever they
+are used to assign a new attribute value.
+
+  Duck->new->name('Donald')->quack;
+
+In this case the C<name> attribute accessor is called on the object created by
+C<Duck-E<gt>new>. It assigns a new attribute value and then returns the C<Duck>
+object, so the C<quack> method can be called on it afterwards. These method
+chains can continue until one of the methods called does not return the C<Duck>
+object.
+
+=head1 FUNCTIONS
+
+L<Mojo::Base> implements the following functions, which can be imported with
+the C<-base> flag or by setting a base class.
+
+=head2 has
+
+  has 'name';
+  has ['name1', 'name2', 'name3'];
+  has name => 'foo';
+  has name => sub {...};
+  has ['name1', 'name2', 'name3'] => 'foo';
+  has ['name1', 'name2', 'name3'] => sub {...};
+
+Create attributes for hash-based objects, just like the L</"attr"> method.
+
+=head1 METHODS
+
+L<Mojo::Base> implements the following methods.
+
+=head2 attr
+
+  $object->attr('name');
+  SubClass->attr('name');
+  SubClass->attr(['name1', 'name2', 'name3']);
+  SubClass->attr(name => 'foo');
+  SubClass->attr(name => sub {...});
+  SubClass->attr(['name1', 'name2', 'name3'] => 'foo');
+  SubClass->attr(['name1', 'name2', 'name3'] => sub {...});
+
+Create attribute accessors for hash-based objects, an array reference can be
+used to create more than one at a time. Pass an optional second argument to set
+a default value, it should be a constant or a callback. The callback will be
+executed at accessor read time if there's no set value, and gets passed the
+current instance of the object as first argument. Accessors can be chained, that
+means they return their invocant when they are called with an argument.
+
+=head2 new
+
+  my $object = SubClass->new;
+  my $object = SubClass->new(name => 'value');
+  my $object = SubClass->new({name => 'value'});
+
+This base class provides a basic constructor for hash-based objects. You can
+pass it either a hash or a hash reference with attribute values.
+
+=head2 tap
+
+  $object = $object->tap(sub {...});
+  $object = $object->tap('some_method');
+  $object = $object->tap('some_method', @args);
+
+Tap into a method chain to perform operations on an object within the chain
+(also known as a K combinator or Kestrel). The object will be the first argument
+passed to the callback, and is also available as C<$_>. The callback's return
+value will be ignored; instead, the object (the callback's first argument) will
+be the return value. In this way, arbitrary code can be used within (i.e.,
+spliced or tapped into) a chained set of object method calls.
+
+  # Longer version
+  $object = $object->tap(sub { $_->some_method(@args) });
+
+  # Inject side effects into a method chain
+  $object->foo('A')->tap(sub { say $_->foo })->foo('B');
+
+=head2 with_roles
+
+  my $new_class = SubClass->with_roles('SubClass::Role::One');
+  my $new_class = SubClass->with_roles('+One', '+Two');
+  $object       = $object->with_roles('+One', '+Two');
+
+Create a new class with one or more L<Role::Tiny> roles. If called on a class
+returns the new class, or if called on an object reblesses the object into the
+new class. For roles following the naming scheme C<MyClass::Role::RoleName> you
+can use the shorthand C<+RoleName>. Note that role support depends on
+L<Role::Tiny> (2.000001+).
+
+  # Create a new class with the role "SubClass::Role::Foo" and instantiate it
+  my $new_class = SubClass->with_roles('+Foo');
+  my $object    = $new_class->new;
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/ByteStream.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/ByteStream.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/ByteStream.pm	(revision 32205)
@@ -0,0 +1,377 @@
+package Mojo::ByteStream;
+use Mojo::Base -strict;
+use overload bool => sub {1}, '""' => sub { ${$_[0]} }, fallback => 1;
+
+use Exporter 'import';
+use Mojo::Collection;
+use Mojo::Util;
+
+our @EXPORT_OK = ('b');
+
+# Turn most functions from Mojo::Util into methods
+my @UTILS = (
+  qw(b64_decode b64_encode camelize decamelize hmac_sha1_sum html_unescape),
+  qw(md5_bytes md5_sum punycode_decode punycode_encode quote sha1_bytes),
+  qw(sha1_sum slugify term_escape trim unindent unquote url_escape),
+  qw(url_unescape xml_escape xor_encode)
+);
+for my $name (@UTILS) {
+  my $sub = Mojo::Util->can($name);
+  Mojo::Util::monkey_patch __PACKAGE__, $name, sub {
+    my $self = shift;
+    $$self = $sub->($$self, @_);
+    return $self;
+  };
+}
+
+sub b { __PACKAGE__->new(@_) }
+
+sub clone { $_[0]->new(${$_[0]}) }
+
+sub decode { shift->_delegate(\&Mojo::Util::decode, @_) }
+sub encode { shift->_delegate(\&Mojo::Util::encode, @_) }
+
+sub new {
+  my $class = shift;
+  return bless \(my $dummy = join '', @_), ref $class || $class;
+}
+
+sub say {
+  my ($self, $handle) = @_;
+  $handle ||= \*STDOUT;
+  say $handle $$self;
+  return $self;
+}
+
+sub secure_compare { Mojo::Util::secure_compare ${shift()}, shift }
+
+sub size { length ${$_[0]} }
+
+sub split {
+  my ($self, $pattern) = @_;
+  return Mojo::Collection->new(map { $self->new($_) } split $pattern, $$self);
+}
+
+sub tap { shift->Mojo::Base::tap(@_) }
+
+sub to_string { ${$_[0]} }
+
+sub with_roles { shift->Mojo::Base::with_roles(@_) }
+
+sub _delegate {
+  my ($self, $sub) = (shift, shift);
+  $$self = $sub->(shift || 'UTF-8', $$self);
+  return $self;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::ByteStream - ByteStream
+
+=head1 SYNOPSIS
+
+  use Mojo::ByteStream;
+
+  # Manipulate bytestream
+  my $stream = Mojo::ByteStream->new('foo_bar_baz');
+  say $stream->camelize;
+
+  # Chain methods
+  my $stream = Mojo::ByteStream->new('foo bar baz')->quote;
+  $stream = $stream->unquote->encode('UTF-8')->b64_encode('');
+  say "$stream";
+
+  # Use the alternative constructor
+  use Mojo::ByteStream 'b';
+  my $stream = b('foobarbaz')->b64_encode('')->say;
+
+=head1 DESCRIPTION
+
+L<Mojo::ByteStream> is a scalar-based container for bytestreams that provides a
+more friendly API for many of the functions in L<Mojo::Util>.
+
+  # Access scalar directly to manipulate bytestream
+  my $stream = Mojo::ByteStream->new('foo');
+  $$stream .= 'bar';
+
+=head1 FUNCTIONS
+
+L<Mojo::ByteStream> implements the following functions, which can be imported
+individually.
+
+=head2 b
+
+  my $stream = b('test123');
+
+Construct a new scalar-based L<Mojo::ByteStream> object.
+
+=head1 METHODS
+
+L<Mojo::ByteStream> implements the following methods.
+
+=head2 b64_decode
+
+  $stream = $stream->b64_decode;
+
+Base64 decode bytestream with L<Mojo::Util/"b64_decode">.
+
+=head2 b64_encode
+
+  $stream = $stream->b64_encode;
+  $stream = $stream->b64_encode("\n");
+
+Base64 encode bytestream with L<Mojo::Util/"b64_encode">.
+
+  # "Zm9vIGJhciBiYXo="
+  b('foo bar baz')->b64_encode('');
+
+=head2 camelize
+
+  $stream = $stream->camelize;
+
+Camelize bytestream with L<Mojo::Util/"camelize">.
+
+=head2 clone
+
+  my $stream2 = $stream->clone;
+
+Return a new L<Mojo::ByteStream> object cloned from this bytestream.
+
+=head2 decamelize
+
+  $stream = $stream->decamelize;
+
+Decamelize bytestream with L<Mojo::Util/"decamelize">.
+
+=head2 decode
+
+  $stream = $stream->decode;
+  $stream = $stream->decode('iso-8859-1');
+
+Decode bytestream with L<Mojo::Util/"decode">, defaults to using C<UTF-8>.
+
+  # "â¥"
+  b('%E2%99%A5')->url_unescape->decode;
+
+=head2 encode
+
+  $stream = $stream->encode;
+  $stream = $stream->encode('iso-8859-1');
+
+Encode bytestream with L<Mojo::Util/"encode">, defaults to using C<UTF-8>.
+
+  # "%E2%99%A5"
+  b('â¥')->encode->url_escape;
+
+=head2 hmac_sha1_sum
+
+  $stream = $stream->hmac_sha1_sum('passw0rd');
+
+Generate HMAC-SHA1 checksum for bytestream with L<Mojo::Util/"hmac_sha1_sum">.
+
+  # "7fbdc89263974a89210ea71f171c77d3f8c21471"
+  b('foo bar baz')->hmac_sha1_sum('secr3t');
+
+=head2 html_unescape
+
+  $stream = $stream->html_unescape;
+
+Unescape all HTML entities in bytestream with L<Mojo::Util/"html_unescape">.
+
+  # "%3Chtml%3E"
+  b('&lt;html&gt;')->html_unescape->url_escape;
+
+=head2 md5_bytes
+
+  $stream = $stream->md5_bytes;
+
+Generate binary MD5 checksum for bytestream with L<Mojo::Util/"md5_bytes">.
+
+=head2 md5_sum
+
+  $stream = $stream->md5_sum;
+
+Generate MD5 checksum for bytestream with L<Mojo::Util/"md5_sum">.
+
+=head2 new
+
+  my $stream = Mojo::ByteStream->new('test123');
+
+Construct a new scalar-based L<Mojo::ByteStream> object.
+
+=head2 punycode_decode
+
+  $stream = $stream->punycode_decode;
+
+Punycode decode bytestream with L<Mojo::Util/"punycode_decode">.
+
+=head2 punycode_encode
+
+  $stream = $stream->punycode_encode;
+
+Punycode encode bytestream with L<Mojo::Util/"punycode_encode">.
+
+=head2 quote
+
+  $stream = $stream->quote;
+
+Quote bytestream with L<Mojo::Util/"quote">.
+
+=head2 say
+
+  $stream = $stream->say;
+  $stream = $stream->say(*STDERR);
+
+Print bytestream to handle and append a newline, defaults to using C<STDOUT>.
+
+=head2 secure_compare
+
+  my $bool = $stream->secure_compare($str);
+
+Compare bytestream with L<Mojo::Util/"secure_compare">.
+
+=head2 sha1_bytes
+
+  $stream = $stream->sha1_bytes;
+
+Generate binary SHA1 checksum for bytestream with L<Mojo::Util/"sha1_bytes">.
+
+=head2 sha1_sum
+
+  $stream = $stream->sha1_sum;
+
+Generate SHA1 checksum for bytestream with L<Mojo::Util/"sha1_sum">.
+
+=head2 size
+
+  my $size = $stream->size;
+
+Size of bytestream.
+
+=head2 slugify
+
+  $stream = $stream->slugify;
+  $stream = $stream->slugify($bool);
+
+Generate URL slug for bytestream with L<Mojo::Util/"slugify">.
+
+=head2 split
+
+  my $collection = $stream->split(',');
+
+Turn bytestream into L<Mojo::Collection> object containing L<Mojo::ByteStream>
+objects.
+
+  # "One,Two,Three"
+  b("one,two,three")->split(',')->map('camelize')->join(',');
+
+=head2 tap
+
+  $stream = $stream->tap(sub {...});
+
+Alias for L<Mojo::Base/"tap">.
+
+=head2 term_escape
+
+  $stream = $stream->term_escape;
+
+Escape POSIX control characters in bytestream with L<Mojo::Util/"term_escape">.
+
+  # Print binary checksum to terminal
+  b('foo')->sha1_bytes->term_escape->say;
+
+=head2 to_string
+
+  my $str = $stream->to_string;
+
+Stringify bytestream.
+
+=head2 trim
+
+  $stream = $stream->trim;
+
+Trim whitespace characters from both ends of bytestream with
+L<Mojo::Util/"trim">.
+
+=head2 unindent
+
+  $stream = $stream->unindent;
+
+Unindent bytestream with L<Mojo::Util/"unindent">.
+
+=head2 unquote
+
+  $stream = $stream->unquote;
+
+Unquote bytestream with L<Mojo::Util/"unquote">.
+
+=head2 url_escape
+
+  $stream = $stream->url_escape;
+  $stream = $stream->url_escape('^A-Za-z0-9\-._~');
+
+Percent encode all unsafe characters in bytestream with
+L<Mojo::Util/"url_escape">.
+
+  # "%E2%98%83"
+  b('â')->encode->url_escape;
+
+=head2 url_unescape
+
+  $stream = $stream->url_unescape;
+
+Decode percent encoded characters in bytestream with
+L<Mojo::Util/"url_unescape">.
+
+  # "&lt;html&gt;"
+  b('%3Chtml%3E')->url_unescape->xml_escape;
+
+=head2 with_roles
+
+  my $new_class = Mojo::ByteStream->with_roles('Mojo::ByteStream::Role::One');
+  my $new_class = Mojo::ByteStream->with_roles('+One', '+Two');
+  $stream       = $stream->with_roles('+One', '+Two');
+
+Alias for L<Mojo::Base/"with_roles">.
+
+=head2 xml_escape
+
+  $stream = $stream->xml_escape;
+
+Escape only the characters C<&>, C<E<lt>>, C<E<gt>>, C<"> and C<'> in
+bytestream with L<Mojo::Util/"xml_escape">.
+
+=head2 xor_encode
+
+  $stream = $stream->xor_encode($key);
+
+XOR encode bytestream with L<Mojo::Util/"xor_encode">.
+
+  # "%04%0E%15B%03%1B%10"
+  b('foo bar')->xor_encode('baz')->url_escape;
+
+=head1 OPERATORS
+
+L<Mojo::ByteStream> overloads the following operators.
+
+=head2 bool
+
+  my $bool = !!$bytestream;
+
+Always true.
+
+=head2 stringify
+
+  my $str = "$bytestream";
+
+Alias for L</"to_string">.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/Cache.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/Cache.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/Cache.pm	(revision 32205)
@@ -0,0 +1,75 @@
+package Mojo::Cache;
+use Mojo::Base -base;
+
+has 'max_keys' => 100;
+
+sub get { (shift->{cache} || {})->{shift()} }
+
+sub set {
+  my ($self, $key, $value) = @_;
+
+  return $self unless (my $max = $self->max_keys) > 0;
+
+  my $cache = $self->{cache} ||= {};
+  my $queue = $self->{queue} ||= [];
+  delete $cache->{shift @$queue} while @$queue >= $max;
+  push @$queue, $key unless exists $cache->{$key};
+  $cache->{$key} = $value;
+
+  return $self;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::Cache - Naive in-memory cache
+
+=head1 SYNOPSIS
+
+  use Mojo::Cache;
+
+  my $cache = Mojo::Cache->new(max_keys => 50);
+  $cache->set(foo => 'bar');
+  my $foo = $cache->get('foo');
+
+=head1 DESCRIPTION
+
+L<Mojo::Cache> is a naive in-memory cache with size limits.
+
+=head1 ATTRIBUTES
+
+L<Mojo::Cache> implements the following attributes.
+
+=head2 max_keys
+
+  my $max = $cache->max_keys;
+  $cache  = $cache->max_keys(50);
+
+Maximum number of cache keys, defaults to C<100>. Setting the value to C<0>
+will disable caching.
+
+=head1 METHODS
+
+L<Mojo::Cache> inherits all methods from L<Mojo::Base> and implements the
+following new ones.
+
+=head2 get
+
+  my $value = $cache->get('foo');
+
+Get cached value.
+
+=head2 set
+
+  $cache = $cache->set(foo => 'bar');
+
+Set cached value.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/Collection.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/Collection.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/Collection.pm	(revision 32205)
@@ -0,0 +1,385 @@
+package Mojo::Collection;
+use Mojo::Base -strict;
+
+use Carp 'croak';
+use Exporter 'import';
+use List::Util;
+use Mojo::ByteStream;
+use Scalar::Util 'blessed';
+
+our @EXPORT_OK = ('c');
+
+sub TO_JSON { [@{shift()}] }
+
+sub c { __PACKAGE__->new(@_) }
+
+sub compact {
+  my $self = shift;
+  return $self->new(grep { defined && (ref || length) } @$self);
+}
+
+sub each {
+  my ($self, $cb) = @_;
+  return @$self unless $cb;
+  my $i = 1;
+  $_->$cb($i++) for @$self;
+  return $self;
+}
+
+sub first {
+  my ($self, $cb) = (shift, shift);
+  return $self->[0] unless $cb;
+  return List::Util::first { $_ =~ $cb } @$self if ref $cb eq 'Regexp';
+  return List::Util::first { $_->$cb(@_) } @$self;
+}
+
+sub flatten { $_[0]->new(_flatten(@{$_[0]})) }
+
+sub grep {
+  my ($self, $cb) = (shift, shift);
+  return $self->new(grep { $_ =~ $cb } @$self) if ref $cb eq 'Regexp';
+  return $self->new(grep { $_->$cb(@_) } @$self);
+}
+
+sub join {
+  Mojo::ByteStream->new(join $_[1] // '', map {"$_"} @{$_[0]});
+}
+
+sub last { shift->[-1] }
+
+sub map {
+  my ($self, $cb) = (shift, shift);
+  return $self->new(map { $_->$cb(@_) } @$self);
+}
+
+sub new {
+  my $class = shift;
+  return bless [@_], ref $class || $class;
+}
+
+sub reduce {
+  my $self = shift;
+  @_ = (@_, @$self);
+  goto &List::Util::reduce;
+}
+
+sub reverse { $_[0]->new(reverse @{$_[0]}) }
+
+sub shuffle { $_[0]->new(List::Util::shuffle @{$_[0]}) }
+
+sub size { scalar @{$_[0]} }
+
+sub slice {
+  my $self = shift;
+  return $self->new(@$self[@_]);
+}
+
+sub sort {
+  my ($self, $cb) = @_;
+
+  return $self->new(sort @$self) unless $cb;
+
+  my $caller = caller;
+  no strict 'refs';
+  my @sorted = sort {
+    local (*{"${caller}::a"}, *{"${caller}::b"}) = (\$a, \$b);
+    $a->$cb($b);
+  } @$self;
+  return $self->new(@sorted);
+}
+
+sub tap { shift->Mojo::Base::tap(@_) }
+
+sub to_array { [@{shift()}] }
+
+sub uniq {
+  my ($self, $cb) = (shift, shift);
+  my %seen;
+  return $self->new(grep { !$seen{$_->$cb(@_) // ''}++ } @$self) if $cb;
+  return $self->new(grep { !$seen{$_ // ''}++ } @$self);
+}
+
+sub with_roles { shift->Mojo::Base::with_roles(@_) }
+
+sub _flatten {
+  map { _ref($_) ? _flatten(@$_) : $_ } @_;
+}
+
+sub _ref { ref $_[0] eq 'ARRAY' || blessed $_[0] && $_[0]->isa(__PACKAGE__) }
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::Collection - Collection
+
+=head1 SYNOPSIS
+
+  use Mojo::Collection;
+
+  # Manipulate collection
+  my $collection = Mojo::Collection->new(qw(just works));
+  unshift @$collection, 'it';
+  say $collection->join("\n");
+
+  # Chain methods
+  $collection->map(sub { ucfirst })->shuffle->each(sub {
+    my ($word, $num) = @_;
+    say "$num: $word";
+  });
+
+  # Use the alternative constructor
+  use Mojo::Collection 'c';
+  c(qw(a b c))->join('/')->url_escape->say;
+
+=head1 DESCRIPTION
+
+L<Mojo::Collection> is an array-based container for collections.
+
+  # Access array directly to manipulate collection
+  my $collection = Mojo::Collection->new(1 .. 25);
+  $collection->[23] += 100;
+  say for @$collection;
+
+=head1 FUNCTIONS
+
+L<Mojo::Collection> implements the following functions, which can be imported
+individually.
+
+=head2 c
+
+  my $collection = c(1, 2, 3);
+
+Construct a new array-based L<Mojo::Collection> object.
+
+=head1 METHODS
+
+L<Mojo::Collection> implements the following methods.
+
+=head2 TO_JSON
+
+  my $array = $collection->TO_JSON;
+
+Alias for L</"to_array">.
+
+=head2 compact
+
+  my $new = $collection->compact;
+
+Create a new collection with all elements that are defined and not an empty
+string.
+
+  # "0, 1, 2, 3"
+  c(0, 1, undef, 2, '', 3)->compact->join(', ');
+
+=head2 each
+
+  my @elements = $collection->each;
+  $collection  = $collection->each(sub {...});
+
+Evaluate callback for each element in collection, or return all elements as a
+list if none has been provided. The element will be the first argument passed
+to the callback, and is also available as C<$_>.
+
+  # Make a numbered list
+  $collection->each(sub {
+    my ($e, $num) = @_;
+    say "$num: $e";
+  });
+
+=head2 first
+
+  my $first = $collection->first;
+  my $first = $collection->first(qr/foo/);
+  my $first = $collection->first(sub {...});
+  my $first = $collection->first('some_method');
+  my $first = $collection->first('some_method', @args);
+
+Evaluate regular expression/callback for, or call method on, each element in
+collection and return the first one that matched the regular expression, or for
+which the callback/method returned true. The element will be the first argument
+passed to the callback, and is also available as C<$_>.
+
+  # Longer version
+  my $first = $collection->first(sub { $_->some_method(@args) });
+
+  # Find first value that contains the word "mojo"
+  my $interesting = $collection->first(qr/mojo/i);
+
+  # Find first value that is greater than 5
+  my $greater = $collection->first(sub { $_ > 5 });
+
+=head2 flatten
+
+  my $new = $collection->flatten;
+
+Flatten nested collections/arrays recursively and create a new collection with
+all elements.
+
+  # "1, 2, 3, 4, 5, 6, 7"
+  c(1, [2, [3, 4], 5, [6]], 7)->flatten->join(', ');
+
+=head2 grep
+
+  my $new = $collection->grep(qr/foo/);
+  my $new = $collection->grep(sub {...});
+  my $new = $collection->grep('some_method');
+  my $new = $collection->grep('some_method', @args);
+
+Evaluate regular expression/callback for, or call method on, each element in
+collection and create a new collection with all elements that matched the
+regular expression, or for which the callback/method returned true. The element
+will be the first argument passed to the callback, and is also available as
+C<$_>.
+
+  # Longer version
+  my $new = $collection->grep(sub { $_->some_method(@args) });
+
+  # Find all values that contain the word "mojo"
+  my $interesting = $collection->grep(qr/mojo/i);
+
+  # Find all values that are greater than 5
+  my $greater = $collection->grep(sub { $_ > 5 });
+
+=head2 join
+
+  my $stream = $collection->join;
+  my $stream = $collection->join("\n");
+
+Turn collection into L<Mojo::ByteStream>.
+
+  # Join all values with commas
+  $collection->join(', ')->say;
+
+=head2 last
+
+  my $last = $collection->last;
+
+Return the last element in collection.
+
+=head2 map
+
+  my $new = $collection->map(sub {...});
+  my $new = $collection->map('some_method');
+  my $new = $collection->map('some_method', @args);
+
+Evaluate callback for, or call method on, each element in collection and create
+a new collection from the results. The element will be the first argument
+passed to the callback, and is also available as C<$_>.
+
+  # Longer version
+  my $new = $collection->map(sub { $_->some_method(@args) });
+
+  # Append the word "mojo" to all values
+  my $mojoified = $collection->map(sub { $_ . 'mojo' });
+
+=head2 new
+
+  my $collection = Mojo::Collection->new(1, 2, 3);
+
+Construct a new array-based L<Mojo::Collection> object.
+
+=head2 reduce
+
+  my $result = $collection->reduce(sub {...});
+  my $result = $collection->reduce(sub {...}, $initial);
+
+Reduce elements in collection with a callback and return its final result,
+setting C<$a> and C<$b> each time the callback is executed. The first time C<$a>
+will be set to an optional initial value or the first element in the collection.
+And from then on C<$a> will be set to the return value of the callback, while
+C<$b> will always be set to the next element in the collection.
+
+  # Calculate the sum of all values
+  my $sum = $collection->reduce(sub { $a + $b });
+
+  # Count how often each value occurs in collection
+  my $hash = $collection->reduce(sub { $a->{$b}++; $a }, {});
+
+=head2 reverse
+
+  my $new = $collection->reverse;
+
+Create a new collection with all elements in reverse order.
+
+=head2 slice
+
+  my $new = $collection->slice(4 .. 7);
+
+Create a new collection with all selected elements.
+
+  # "B C E"
+  c('A', 'B', 'C', 'D', 'E')->slice(1, 2, 4)->join(' ');
+
+=head2 shuffle
+
+  my $new = $collection->shuffle;
+
+Create a new collection with all elements in random order.
+
+=head2 size
+
+  my $size = $collection->size;
+
+Number of elements in collection.
+
+=head2 sort
+
+  my $new = $collection->sort;
+  my $new = $collection->sort(sub {...});
+
+Sort elements based on return value of a callback and create a new collection
+from the results, setting C<$a> and C<$b> to the elements being compared, each
+time the callback is executed.
+
+  # Sort values case-insensitive
+  my $case_insensitive = $collection->sort(sub { uc($a) cmp uc($b) });
+
+=head2 tap
+
+  $collection = $collection->tap(sub {...});
+
+Alias for L<Mojo::Base/"tap">.
+
+=head2 to_array
+
+  my $array = $collection->to_array;
+
+Turn collection into array reference.
+
+=head2 uniq
+
+  my $new = $collection->uniq;
+  my $new = $collection->uniq(sub {...});
+  my $new = $collection->uniq('some_method');
+  my $new = $collection->uniq('some_method', @args);
+
+Create a new collection without duplicate elements, using the string
+representation of either the elements or the return value of the
+callback/method to decide uniqueness. Note that C<undef> and empty string are
+treated the same.
+
+  # Longer version
+  my $new = $collection->uniq(sub { $_->some_method(@args) });
+
+  # "foo bar baz"
+  c('foo', 'bar', 'bar', 'baz')->uniq->join(' ');
+
+  # "[[1, 2], [2, 1]]"
+  c([1, 2], [2, 1], [3, 2])->uniq(sub{ $_->[1] })->to_array;
+
+=head2 with_roles
+
+  my $new_class = Mojo::Collection->with_roles('Mojo::Collection::Role::One');
+  my $new_class = Mojo::Collection->with_roles('+One', '+Two');
+  $collection   = $collection->with_roles('+One', '+Two');
+
+Alias for L<Mojo::Base/"with_roles">.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/Content.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/Content.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/Content.pm	(revision 32205)
@@ -0,0 +1,599 @@
+package Mojo::Content;
+use Mojo::Base 'Mojo::EventEmitter';
+
+use Carp 'croak';
+use Compress::Raw::Zlib qw(WANT_GZIP Z_STREAM_END);
+use Mojo::Headers;
+use Scalar::Util 'looks_like_number';
+
+has [qw(auto_decompress auto_relax expect_close relaxed skip_body)];
+has headers           => sub { Mojo::Headers->new };
+has max_buffer_size   => sub { $ENV{MOJO_MAX_BUFFER_SIZE} || 262144 };
+has max_leftover_size => sub { $ENV{MOJO_MAX_LEFTOVER_SIZE} || 262144 };
+
+my $BOUNDARY_RE
+  = qr!multipart.*boundary\s*=\s*(?:"([^"]+)"|([\w'(),.:?\-+/]+))!i;
+
+sub body_contains {
+  croak 'Method "body_contains" not implemented by subclass';
+}
+
+sub body_size { croak 'Method "body_size" not implemented by subclass' }
+
+sub boundary {
+  (shift->headers->content_type // '') =~ $BOUNDARY_RE ? $1 // $2 : undef;
+}
+
+sub charset {
+  my $type = shift->headers->content_type // '';
+  return $type =~ /charset\s*=\s*"?([^"\s;]+)"?/i ? $1 : undef;
+}
+
+sub clone {
+  my $self = shift;
+  return undef if $self->is_dynamic;
+  return $self->new(headers => $self->headers->clone);
+}
+
+sub generate_body_chunk {
+  my ($self, $offset) = @_;
+
+  $self->emit(drain => $offset) unless length($self->{body_buffer} //= '');
+  my $len = $self->headers->content_length;
+  return '' if looks_like_number $len && $len == $offset;
+  my $chunk = delete $self->{body_buffer};
+  return $self->{eof} ? '' : undef unless length $chunk;
+
+  return $chunk;
+}
+
+sub get_body_chunk {
+  croak 'Method "get_body_chunk" not implemented by subclass';
+}
+
+sub get_header_chunk { substr shift->_headers->{header_buffer}, shift, 131072 }
+
+sub header_size { length shift->_headers->{header_buffer} }
+
+sub headers_contain { index(shift->_headers->{header_buffer}, shift) >= 0 }
+
+sub is_chunked { !!shift->headers->transfer_encoding }
+
+sub is_compressed { lc(shift->headers->content_encoding // '') eq 'gzip' }
+
+sub is_dynamic { !!$_[0]{dynamic} }
+
+sub is_finished { (shift->{state} // '') eq 'finished' }
+
+sub is_limit_exceeded { !!shift->{limit} }
+
+sub is_multipart {undef}
+
+sub is_parsing_body { (shift->{state} // '') eq 'body' }
+
+sub leftovers { shift->{buffer} }
+
+sub parse {
+  my $self = shift;
+
+  # Headers
+  $self->_parse_until_body(@_);
+  return $self if $self->{state} eq 'headers';
+
+  # Chunked content
+  $self->{real_size} //= 0;
+  if ($self->is_chunked && $self->{state} ne 'headers') {
+    $self->_parse_chunked;
+    $self->{state} = 'finished' if ($self->{chunk_state} // '') eq 'finished';
+  }
+
+  # Not chunked, pass through to second buffer
+  else {
+    $self->{real_size} += length $self->{pre_buffer};
+    my $limit = $self->is_finished
+      && length($self->{buffer}) > $self->max_leftover_size;
+    $self->{buffer} .= $self->{pre_buffer} unless $limit;
+    $self->{pre_buffer} = '';
+  }
+
+  # No content
+  if ($self->skip_body) {
+    $self->{state} = 'finished';
+    return $self;
+  }
+
+  # Relaxed parsing
+  my $headers = $self->headers;
+  my $len     = $headers->content_length // '';
+  if ($self->auto_relax && !length $len) {
+    my $connection = lc($headers->connection // '');
+    $self->relaxed(1)
+      if $connection eq 'close' || (!$connection && $self->expect_close);
+  }
+
+  # Chunked or relaxed content
+  if ($self->is_chunked || $self->relaxed) {
+    $self->_decompress($self->{buffer} //= '');
+    $self->{size} += length $self->{buffer};
+    $self->{buffer} = '';
+    return $self;
+  }
+
+  # Normal content
+  $len = 0 unless looks_like_number $len;
+  if ((my $need = $len - ($self->{size} ||= 0)) > 0) {
+    my $len = length $self->{buffer};
+    my $chunk = substr $self->{buffer}, 0, $need > $len ? $len : $need, '';
+    $self->_decompress($chunk);
+    $self->{size} += length $chunk;
+  }
+  $self->{state} = 'finished' if $len <= $self->progress;
+
+  return $self;
+}
+
+sub parse_body {
+  my $self = shift;
+  $self->{state} = 'body';
+  return $self->parse(@_);
+}
+
+sub progress {
+  my $self = shift;
+  return 0 unless my $state = $self->{state};
+  return 0 unless $state eq 'body' || $state eq 'finished';
+  return $self->{raw_size} - ($self->{header_size} || 0);
+}
+
+sub write {
+  my ($self, $chunk, $cb) = @_;
+
+  $self->{dynamic} = 1;
+  $self->{body_buffer} .= $chunk if defined $chunk;
+  $self->once(drain => $cb) if $cb;
+  $self->{eof} = 1 if defined $chunk && !length $chunk;
+
+  return $self;
+}
+
+sub write_chunk {
+  my ($self, $chunk, $cb) = @_;
+  $self->headers->transfer_encoding('chunked') unless $self->is_chunked;
+  $self->write(defined $chunk ? $self->_build_chunk($chunk) : $chunk, $cb);
+  $self->{eof} = 1 if defined $chunk && !length $chunk;
+  return $self;
+}
+
+sub _build_chunk {
+  my ($self, $chunk) = @_;
+
+  # End
+  return "\x0d\x0a0\x0d\x0a\x0d\x0a" unless length $chunk;
+
+  # First chunk has no leading CRLF
+  my $crlf = $self->{chunks}++ ? "\x0d\x0a" : '';
+  return $crlf . sprintf('%x', length $chunk) . "\x0d\x0a$chunk";
+}
+
+sub _decompress {
+  my ($self, $chunk) = @_;
+
+  # No compression
+  return $self->emit(read => $chunk)
+    unless $self->auto_decompress && $self->is_compressed;
+
+  # Decompress
+  $self->{post_buffer} .= $chunk;
+  my $gz = $self->{gz}
+    //= Compress::Raw::Zlib::Inflate->new(WindowBits => WANT_GZIP);
+  my $status = $gz->inflate(\$self->{post_buffer}, my $out);
+  $self->emit(read => $out) if defined $out;
+
+  # Replace Content-Encoding with Content-Length
+  $self->headers->content_length($gz->total_out)->remove('Content-Encoding')
+    if $status == Z_STREAM_END;
+
+  # Check buffer size
+  @$self{qw(state limit)} = ('finished', 1)
+    if length($self->{post_buffer} // '') > $self->max_buffer_size;
+}
+
+sub _headers {
+  my $self = shift;
+  return $self if defined $self->{header_buffer};
+  my $headers = $self->headers->to_string;
+  $self->{header_buffer} = $headers ? "$headers\x0d\x0a\x0d\x0a" : "\x0d\x0a";
+  return $self;
+}
+
+sub _parse_chunked {
+  my $self = shift;
+
+  # Trailing headers
+  return $self->_parse_chunked_trailing_headers
+    if ($self->{chunk_state} // '') eq 'trailing_headers';
+
+  while (my $len = length $self->{pre_buffer}) {
+
+    # Start new chunk (ignore the chunk extension)
+    unless ($self->{chunk_len}) {
+      last
+        unless $self->{pre_buffer} =~ s/^(?:\x0d?\x0a)?([0-9a-fA-F]+).*\x0a//;
+      next if $self->{chunk_len} = hex $1;
+
+      # Last chunk
+      $self->{chunk_state} = 'trailing_headers';
+      last;
+    }
+
+    # Remove as much as possible from payload
+    $len = $self->{chunk_len} if $self->{chunk_len} < $len;
+    $self->{buffer} .= substr $self->{pre_buffer}, 0, $len, '';
+    $self->{real_size} += $len;
+    $self->{chunk_len} -= $len;
+  }
+
+  # Trailing headers
+  $self->_parse_chunked_trailing_headers
+    if ($self->{chunk_state} // '') eq 'trailing_headers';
+
+  # Check buffer size
+  @$self{qw(state limit)} = ('finished', 1)
+    if length($self->{pre_buffer} // '') > $self->max_buffer_size;
+}
+
+sub _parse_chunked_trailing_headers {
+  my $self = shift;
+
+  my $headers = $self->headers->parse(delete $self->{pre_buffer});
+  return unless $headers->is_finished;
+  $self->{chunk_state} = 'finished';
+
+  # Take care of leftover and replace Transfer-Encoding with Content-Length
+  $self->{buffer} .= $headers->leftovers;
+  $headers->remove('Transfer-Encoding');
+  $headers->content_length($self->{real_size}) unless $headers->content_length;
+}
+
+sub _parse_headers {
+  my $self = shift;
+
+  my $headers = $self->headers->parse(delete $self->{pre_buffer});
+  return unless $headers->is_finished;
+  $self->{state} = 'body';
+
+  # Take care of leftovers
+  my $leftovers = $self->{pre_buffer} = $headers->leftovers;
+  $self->{header_size} = $self->{raw_size} - length $leftovers;
+}
+
+sub _parse_until_body {
+  my ($self, $chunk) = @_;
+
+  $self->{raw_size} += length($chunk //= '');
+  $self->{pre_buffer} .= $chunk;
+  $self->_parse_headers if ($self->{state} ||= 'headers') eq 'headers';
+  $self->emit('body') if $self->{state} ne 'headers' && !$self->{body}++;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::Content - HTTP content base class
+
+=head1 SYNOPSIS
+
+  package Mojo::Content::MyContent;
+  use Mojo::Base 'Mojo::Content';
+
+  sub body_contains  {...}
+  sub body_size      {...}
+  sub get_body_chunk {...}
+
+=head1 DESCRIPTION
+
+L<Mojo::Content> is an abstract base class for HTTP content containers, based on
+L<RFC 7230|http://tools.ietf.org/html/rfc7230> and
+L<RFC 7231|http://tools.ietf.org/html/rfc7231>, like
+L<Mojo::Content::MultiPart> and L<Mojo::Content::Single>.
+
+=head1 EVENTS
+
+L<Mojo::Content> inherits all events from L<Mojo::EventEmitter> and can emit
+the following new ones.
+
+=head2 body
+
+  $content->on(body => sub {
+    my $content = shift;
+    ...
+  });
+
+Emitted once all headers have been parsed and the body starts.
+
+  $content->on(body => sub {
+    my $content = shift;
+    $content->auto_upgrade(0) if $content->headers->header('X-No-MultiPart');
+  });
+
+=head2 drain
+
+  $content->on(drain => sub {
+    my ($content, $offset) = @_;
+    ...
+  });
+
+Emitted once all data has been written.
+
+  $content->on(drain => sub {
+    my $content = shift;
+    $content->write_chunk(time);
+  });
+
+=head2 read
+
+  $content->on(read => sub {
+    my ($content, $bytes) = @_;
+    ...
+  });
+
+Emitted when a new chunk of content arrives.
+
+  $content->on(read => sub {
+    my ($content, $bytes) = @_;
+    say "Streaming: $bytes";
+  });
+
+=head1 ATTRIBUTES
+
+L<Mojo::Content> implements the following attributes.
+
+=head2 auto_decompress
+
+  my $bool = $content->auto_decompress;
+  $content = $content->auto_decompress($bool);
+
+Decompress content automatically if L</"is_compressed"> is true.
+
+=head2 auto_relax
+
+  my $bool = $content->auto_relax;
+  $content = $content->auto_relax($bool);
+
+Try to detect when relaxed parsing is necessary.
+
+=head2 expect_close
+
+  my $bool = $content->expect_close;
+  $content = $content->expect_close($bool);
+
+Expect a response that is terminated with a connection close.
+
+=head2 headers
+
+  my $headers = $content->headers;
+  $content    = $content->headers(Mojo::Headers->new);
+
+Content headers, defaults to a L<Mojo::Headers> object.
+
+=head2 max_buffer_size
+
+  my $size = $content->max_buffer_size;
+  $content = $content->max_buffer_size(1024);
+
+Maximum size in bytes of buffer for content parser, defaults to the value of
+the C<MOJO_MAX_BUFFER_SIZE> environment variable or C<262144> (256KiB).
+
+=head2 max_leftover_size
+
+  my $size = $content->max_leftover_size;
+  $content = $content->max_leftover_size(1024);
+
+Maximum size in bytes of buffer for pipelined HTTP requests, defaults to the
+value of the C<MOJO_MAX_LEFTOVER_SIZE> environment variable or C<262144>
+(256KiB).
+
+=head2 relaxed
+
+  my $bool = $content->relaxed;
+  $content = $content->relaxed($bool);
+
+Activate relaxed parsing for responses that are terminated with a connection
+close.
+
+=head2 skip_body
+
+  my $bool = $content->skip_body;
+  $content = $content->skip_body($bool);
+
+Skip body parsing and finish after headers.
+
+=head1 METHODS
+
+L<Mojo::Content> inherits all methods from L<Mojo::EventEmitter> and implements
+the following new ones.
+
+=head2 body_contains
+
+  my $bool = $content->body_contains('foo bar baz');
+
+Check if content contains a specific string. Meant to be overloaded in a
+subclass.
+
+=head2 body_size
+
+  my $size = $content->body_size;
+
+Content size in bytes. Meant to be overloaded in a subclass.
+
+=head2 boundary
+
+  my $boundary = $content->boundary;
+
+Extract multipart boundary from C<Content-Type> header.
+
+=head2 charset
+
+  my $charset = $content->charset;
+
+Extract charset from C<Content-Type> header.
+
+=head2 clone
+
+  my $clone = $content->clone;
+
+Return a new L<Mojo::Content> object cloned from this content if possible,
+otherwise return C<undef>.
+
+=head2 generate_body_chunk
+
+  my $bytes = $content->generate_body_chunk(0);
+
+Generate dynamic content.
+
+=head2 get_body_chunk
+
+  my $bytes = $content->get_body_chunk(0);
+
+Get a chunk of content starting from a specific position. Meant to be
+overloaded in a subclass.
+
+=head2 get_header_chunk
+
+  my $bytes = $content->get_header_chunk(13);
+
+Get a chunk of the headers starting from a specific position. Note that this
+method finalizes the content.
+
+=head2 header_size
+
+  my $size = $content->header_size;
+
+Size of headers in bytes. Note that this method finalizes the content.
+
+=head2 headers_contain
+
+  my $bool = $content->headers_contain('foo bar baz');
+
+Check if headers contain a specific string. Note that this method finalizes the
+content.
+
+=head2 is_chunked
+
+  my $bool = $content->is_chunked;
+
+Check if C<Transfer-Encoding> header indicates chunked transfer encoding.
+
+=head2 is_compressed
+
+  my $bool = $content->is_compressed;
+
+Check C<Content-Encoding> header for C<gzip> value.
+
+=head2 is_dynamic
+
+  my $bool = $content->is_dynamic;
+
+Check if content will be dynamically generated, which prevents L</"clone"> from
+working.
+
+=head2 is_finished
+
+  my $bool = $content->is_finished;
+
+Check if parser is finished.
+
+=head2 is_limit_exceeded
+
+  my $bool = $content->is_limit_exceeded;
+
+Check if buffer has exceeded L</"max_buffer_size">.
+
+=head2 is_multipart
+
+  my $bool = $content->is_multipart;
+
+False, this is not a L<Mojo::Content::MultiPart> object.
+
+=head2 is_parsing_body
+
+  my $bool = $content->is_parsing_body;
+
+Check if body parsing started yet.
+
+=head2 leftovers
+
+  my $bytes = $content->leftovers;
+
+Get leftover data from content parser.
+
+=head2 parse
+
+  $content
+    = $content->parse("Content-Length: 12\x0d\x0a\x0d\x0aHello World!");
+
+Parse content chunk.
+
+=head2 parse_body
+
+  $content = $content->parse_body('Hi!');
+
+Parse body chunk and skip headers.
+
+=head2 progress
+
+  my $size = $content->progress;
+
+Size of content already received from message in bytes.
+
+=head2 write
+
+  $content = $content->write;
+  $content = $content->write('');
+  $content = $content->write($bytes);
+  $content = $content->write($bytes => sub {...});
+
+Write dynamic content non-blocking, the optional drain callback will be executed
+once all data has been written. Calling this method without a chunk of data
+will finalize the L</"headers"> and allow for dynamic content to be written
+later. You can write an empty chunk of data at any time to end the stream.
+
+  # Make sure previous chunk of data has been written before continuing
+  $content->write('He' => sub {
+    my $content = shift;
+    $content->write('llo!' => sub {
+      my $content = shift;
+      $content->write('');
+    });
+  });
+
+=head2 write_chunk
+
+  $content = $content->write_chunk;
+  $content = $content->write_chunk('');
+  $content = $content->write_chunk($bytes);
+  $content = $content->write_chunk($bytes => sub {...});
+
+Write dynamic content non-blocking with chunked transfer encoding, the optional
+drain callback will be executed once all data has been written. Calling this
+method without a chunk of data will finalize the L</"headers"> and allow for
+dynamic content to be written later. You can write an empty chunk of data at any
+time to end the stream.
+
+  # Make sure previous chunk of data has been written before continuing
+  $content->write_chunk('He' => sub {
+    my $content = shift;
+    $content->write_chunk('llo!' => sub {
+      my $content = shift;
+      $content->write_chunk('');
+    });
+  });
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/Content/MultiPart.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/Content/MultiPart.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/Content/MultiPart.pm	(revision 32205)
@@ -0,0 +1,314 @@
+package Mojo::Content::MultiPart;
+use Mojo::Base 'Mojo::Content';
+
+use Mojo::Util 'b64_encode';
+
+has parts => sub { [] };
+
+sub body_contains {
+  my ($self, $chunk) = @_;
+  ($_->headers_contain($chunk) or $_->body_contains($chunk)) and return 1
+    for @{$self->parts};
+  return undef;
+}
+
+sub body_size {
+  my $self = shift;
+
+  # Check for existing Content-Lenght header
+  if (my $len = $self->headers->content_length) { return $len }
+
+  # Calculate length of whole body
+  my $len = my $boundary_len = length($self->build_boundary) + 6;
+  $len += $_->header_size + $_->body_size + $boundary_len for @{$self->parts};
+
+  return $len;
+}
+
+sub build_boundary {
+  my $self = shift;
+
+  # Check for existing boundary
+  my $boundary;
+  return $boundary if defined($boundary = $self->boundary);
+
+  # Generate and check boundary
+  my $size = 1;
+  do {
+    $boundary = b64_encode join('', map chr(rand 256), 1 .. $size++ * 3);
+    $boundary =~ s/\W/X/g;
+  } while $self->body_contains($boundary);
+
+  # Add boundary to Content-Type header
+  my $headers = $self->headers;
+  ($headers->content_type // '') =~ m!^(.*multipart/[^;]+)(.*)$!;
+  my $before = $1 || 'multipart/mixed';
+  my $after  = $2 || '';
+  $headers->content_type("$before; boundary=$boundary$after");
+
+  return $boundary;
+}
+
+sub clone {
+  my $self = shift;
+  return undef unless my $clone = $self->SUPER::clone();
+  return $clone->parts($self->parts);
+}
+
+sub get_body_chunk {
+  my ($self, $offset) = @_;
+
+  # Body generator
+  return $self->generate_body_chunk($offset) if $self->is_dynamic;
+
+  # First boundary
+  my $boundary     = $self->{boundary} //= $self->build_boundary;
+  my $boundary_len = length($boundary) + 6;
+  my $len          = $boundary_len - 2;
+  return substr "--$boundary\x0d\x0a", $offset if $len > $offset;
+
+  # Skip parts that have already been processed
+  my $start = 0;
+  ($len, $start) = ($self->{last_len}, $self->{last_part} + 1)
+    if $self->{offset} && $offset > $self->{offset};
+
+  # Prepare content part by part
+  my $parts = $self->parts;
+  for (my $i = $start; $i < @$parts; $i++) {
+    my $part = $parts->[$i];
+
+    # Headers
+    my $header_len = $part->header_size;
+    return $part->get_header_chunk($offset - $len)
+      if ($len + $header_len) > $offset;
+    $len += $header_len;
+
+    # Content
+    my $content_len = $part->body_size;
+    return $part->get_body_chunk($offset - $len)
+      if ($len + $content_len) > $offset;
+    $len += $content_len;
+
+    # Boundary
+    if ($#$parts == $i) {
+      $boundary .= '--';
+      $boundary_len += 2;
+    }
+    return substr "\x0d\x0a--$boundary\x0d\x0a", $offset - $len
+      if ($len + $boundary_len) > $offset;
+    $len += $boundary_len;
+
+    @{$self}{qw(last_len last_part offset)} = ($len, $i, $offset);
+  }
+}
+
+sub is_multipart {1}
+
+sub new {
+  my $self = shift->SUPER::new(@_);
+  $self->on(read => \&_read);
+  return $self;
+}
+
+sub _parse_multipart_body {
+  my ($self, $boundary) = @_;
+
+  # Whole part in buffer
+  my $pos = index $self->{multipart}, "\x0d\x0a--$boundary";
+  if ($pos < 0) {
+    my $len = length($self->{multipart}) - (length($boundary) + 8);
+    return undef unless $len > 0;
+
+    # Store chunk
+    my $chunk = substr $self->{multipart}, 0, $len, '';
+    $self->parts->[-1] = $self->parts->[-1]->parse($chunk);
+    return undef;
+  }
+
+  # Store chunk
+  my $chunk = substr $self->{multipart}, 0, $pos, '';
+  $self->parts->[-1] = $self->parts->[-1]->parse($chunk);
+  return !!($self->{multi_state} = 'multipart_boundary');
+}
+
+sub _parse_multipart_boundary {
+  my ($self, $boundary) = @_;
+
+  # Boundary begins
+  if ((index $self->{multipart}, "\x0d\x0a--$boundary\x0d\x0a") == 0) {
+    substr $self->{multipart}, 0, length($boundary) + 6, '';
+
+    # New part
+    my $part = Mojo::Content::Single->new(relaxed => 1);
+    $self->emit(part => $part);
+    push @{$self->parts}, $part;
+    return !!($self->{multi_state} = 'multipart_body');
+  }
+
+  # Boundary ends
+  my $end = "\x0d\x0a--$boundary--";
+  if ((index $self->{multipart}, $end) == 0) {
+    substr $self->{multipart}, 0, length $end, '';
+    $self->{multi_state} = 'finished';
+  }
+
+  return undef;
+}
+
+sub _parse_multipart_preamble {
+  my ($self, $boundary) = @_;
+
+  # No boundary yet
+  return undef if (my $pos = index $self->{multipart}, "--$boundary") < 0;
+
+  # Replace preamble with carriage return and line feed
+  substr $self->{multipart}, 0, $pos, "\x0d\x0a";
+
+  # Parse boundary
+  return !!($self->{multi_state} = 'multipart_boundary');
+}
+
+sub _read {
+  my ($self, $chunk) = @_;
+
+  $self->{multipart} .= $chunk;
+  my $boundary = $self->boundary;
+  until (($self->{multi_state} //= 'multipart_preamble') eq 'finished') {
+
+    # Preamble
+    if ($self->{multi_state} eq 'multipart_preamble') {
+      last unless $self->_parse_multipart_preamble($boundary);
+    }
+
+    # Boundary
+    elsif ($self->{multi_state} eq 'multipart_boundary') {
+      last unless $self->_parse_multipart_boundary($boundary);
+    }
+
+    # Body
+    elsif ($self->{multi_state} eq 'multipart_body') {
+      last unless $self->_parse_multipart_body($boundary);
+    }
+  }
+
+  # Check buffer size
+  @$self{qw(state limit)} = ('finished', 1)
+    if length($self->{multipart} // '') > $self->max_buffer_size;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::Content::MultiPart - HTTP multipart content
+
+=head1 SYNOPSIS
+
+  use Mojo::Content::MultiPart;
+
+  my $multi = Mojo::Content::MultiPart->new;
+  $multi->parse('Content-Type: multipart/mixed; boundary=---foobar');
+  my $single = $multi->parts->[4];
+
+=head1 DESCRIPTION
+
+L<Mojo::Content::MultiPart> is a container for HTTP multipart content, based on
+L<RFC 7230|http://tools.ietf.org/html/rfc7230>,
+L<RFC 7231|http://tools.ietf.org/html/rfc7231> and
+L<RFC 2388|http://tools.ietf.org/html/rfc2388>.
+
+=head1 EVENTS
+
+L<Mojo::Content::Multipart> inherits all events from L<Mojo::Content> and can
+emit the following new ones.
+
+=head2 part
+
+  $multi->on(part => sub {
+    my ($multi, $single) = @_;
+    ...
+  });
+
+Emitted when a new L<Mojo::Content::Single> part starts.
+
+  $multi->on(part => sub {
+    my ($multi, $single) = @_;
+    return unless $single->headers->content_disposition =~ /name="([^"]+)"/;
+    say "Field: $1";
+  });
+
+=head1 ATTRIBUTES
+
+L<Mojo::Content::MultiPart> inherits all attributes from L<Mojo::Content> and
+implements the following new ones.
+
+=head2 parts
+
+  my $parts = $multi->parts;
+  $multi    = $multi->parts([Mojo::Content::Single->new]);
+
+Content parts embedded in this multipart content, usually
+L<Mojo::Content::Single> objects.
+
+=head1 METHODS
+
+L<Mojo::Content::MultiPart> inherits all methods from L<Mojo::Content> and
+implements the following new ones.
+
+=head2 body_contains
+
+  my $bool = $multi->body_contains('foobarbaz');
+
+Check if content parts contain a specific string.
+
+=head2 body_size
+
+  my $size = $multi->body_size;
+
+Content size in bytes.
+
+=head2 build_boundary
+
+  my $boundary = $multi->build_boundary;
+
+Generate a suitable boundary for content and add it to C<Content-Type> header.
+
+=head2 clone
+
+  my $clone = $multi->clone;
+
+Return a new L<Mojo::Content::MultiPart> object cloned from this content if
+possible, otherwise return C<undef>.
+
+=head2 get_body_chunk
+
+  my $bytes = $multi->get_body_chunk(0);
+
+Get a chunk of content starting from a specific position. Note that it might
+not be possible to get the same chunk twice if content was generated
+dynamically.
+
+=head2 is_multipart
+
+  my $bool = $multi->is_multipart;
+
+True, this is a L<Mojo::Content::MultiPart> object.
+
+=head2 new
+
+  my $multi = Mojo::Content::MultiPart->new;
+  my $multi
+    = Mojo::Content::MultiPart->new(parts => [Mojo::Content::Single->new]);
+  my $multi
+    = Mojo::Content::MultiPart->new({parts => [Mojo::Content::Single->new]});
+
+Construct a new L<Mojo::Content::MultiPart> object and subscribe to L</"read">
+event with default content parser.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/Content/Single.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/Content/Single.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/Content/Single.pm	(revision 32205)
@@ -0,0 +1,171 @@
+package Mojo::Content::Single;
+use Mojo::Base 'Mojo::Content';
+
+use Mojo::Asset::Memory;
+use Mojo::Content::MultiPart;
+
+has asset => sub { Mojo::Asset::Memory->new(auto_upgrade => 1) };
+has auto_upgrade => 1;
+
+sub body_contains { shift->asset->contains(shift) >= 0 }
+
+sub body_size {
+  my $self = shift;
+  return ($self->headers->content_length || 0) if $self->is_dynamic;
+  return $self->{body_size} //= $self->asset->size;
+}
+
+sub clone {
+  my $self = shift;
+  return undef unless my $clone = $self->SUPER::clone();
+  return $clone->asset($self->asset);
+}
+
+sub get_body_chunk {
+  my ($self, $offset) = @_;
+  return $self->generate_body_chunk($offset) if $self->is_dynamic;
+  return $self->asset->get_chunk($offset);
+}
+
+sub new {
+  my $self = shift->SUPER::new(@_);
+  $self->{read}
+    = $self->on(read => sub { $_[0]->asset($_[0]->asset->add_chunk($_[1])) });
+  return $self;
+}
+
+sub parse {
+  my $self = shift;
+
+  # Parse headers
+  $self->_parse_until_body(@_);
+
+  # Parse body
+  return $self->SUPER::parse
+    unless $self->auto_upgrade && defined $self->boundary;
+
+  # Content needs to be upgraded to multipart
+  $self->unsubscribe(read => $self->{read});
+  my $multi = Mojo::Content::MultiPart->new(%$self);
+  $self->emit(upgrade => $multi);
+  return $multi->parse;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::Content::Single - HTTP content
+
+=head1 SYNOPSIS
+
+  use Mojo::Content::Single;
+
+  my $single = Mojo::Content::Single->new;
+  $single->parse("Content-Length: 12\x0d\x0a\x0d\x0aHello World!");
+  say $single->headers->content_length;
+
+=head1 DESCRIPTION
+
+L<Mojo::Content::Single> is a container for HTTP content, based on
+L<RFC 7230|http://tools.ietf.org/html/rfc7230> and
+L<RFC 7231|http://tools.ietf.org/html/rfc7231>.
+
+=head1 EVENTS
+
+L<Mojo::Content::Single> inherits all events from L<Mojo::Content> and can emit
+the following new ones.
+
+=head2 upgrade
+
+  $single->on(upgrade => sub {
+    my ($single, $multi) = @_;
+    ...
+  });
+
+Emitted when content gets upgraded to a L<Mojo::Content::MultiPart> object.
+
+  $single->on(upgrade => sub {
+    my ($single, $multi) = @_;
+    return unless $multi->headers->content_type =~ /multipart\/([^;]+)/i;
+    say "Multipart: $1";
+  });
+
+=head1 ATTRIBUTES
+
+L<Mojo::Content::Single> inherits all attributes from L<Mojo::Content> and
+implements the following new ones.
+
+=head2 asset
+
+  my $asset = $single->asset;
+  $single   = $single->asset(Mojo::Asset::Memory->new);
+
+The actual content, defaults to a L<Mojo::Asset::Memory> object with
+L<Mojo::Asset::Memory/"auto_upgrade"> enabled.
+
+=head2 auto_upgrade
+
+  my $bool = $single->auto_upgrade;
+  $single  = $single->auto_upgrade($bool);
+
+Try to detect multipart content and automatically upgrade to a
+L<Mojo::Content::MultiPart> object, defaults to a true value.
+
+=head1 METHODS
+
+L<Mojo::Content::Single> inherits all methods from L<Mojo::Content> and
+implements the following new ones.
+
+=head2 body_contains
+
+  my $bool = $single->body_contains('1234567');
+
+Check if content contains a specific string.
+
+=head2 body_size
+
+  my $size = $single->body_size;
+
+Content size in bytes.
+
+=head2 clone
+
+  my $clone = $single->clone;
+
+Return a new L<Mojo::Content::Single> object cloned from this content if
+possible, otherwise return C<undef>.
+
+=head2 get_body_chunk
+
+  my $bytes = $single->get_body_chunk(0);
+
+Get a chunk of content starting from a specific position. Note that it might
+not be possible to get the same chunk twice if content was generated
+dynamically.
+
+=head2 new
+
+  my $single = Mojo::Content::Single->new;
+  my $single = Mojo::Content::Single->new(asset => Mojo::Asset::File->new);
+  my $single = Mojo::Content::Single->new({asset => Mojo::Asset::File->new});
+
+Construct a new L<Mojo::Content::Single> object and subscribe to L</"read">
+event with default content parser.
+
+=head2 parse
+
+  $single = $single->parse("Content-Length: 12\x0d\x0a\x0d\x0aHello World!");
+  my $multi
+    = $single->parse("Content-Type: multipart/form-data\x0d\x0a\x0d\x0a");
+
+Parse content chunk and upgrade to L<Mojo::Content::MultiPart> object if
+necessary.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/Cookie.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/Cookie.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/Cookie.pm	(revision 32205)
@@ -0,0 +1,89 @@
+package Mojo::Cookie;
+use Mojo::Base -base;
+use overload bool => sub {1}, '""' => sub { shift->to_string }, fallback => 1;
+
+use Carp 'croak';
+
+has [qw(name value)];
+
+sub parse     { croak 'Method "parse" not implemented by subclass' }
+sub to_string { croak 'Method "to_string" not implemented by subclass' }
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::Cookie - HTTP cookie base class
+
+=head1 SYNOPSIS
+
+  package Mojo::Cookie::MyCookie;
+  use Mojo::Base 'Mojo::Cookie';
+
+  sub parse     {...}
+  sub to_string {...}
+
+=head1 DESCRIPTION
+
+L<Mojo::Cookie> is an abstract base class for HTTP cookie containers, based on
+L<RFC 6265|http://tools.ietf.org/html/rfc6265>, like L<Mojo::Cookie::Request>
+and L<Mojo::Cookie::Response>.
+
+=head1 ATTRIBUTES
+
+L<Mojo::Cookie> implements the following attributes.
+
+=head2 name
+
+  my $name = $cookie->name;
+  $cookie  = $cookie->name('foo');
+
+Cookie name.
+
+=head2 value
+
+  my $value = $cookie->value;
+  $cookie   = $cookie->value('/test');
+
+Cookie value.
+
+=head1 METHODS
+
+L<Mojo::Cookie> inherits all methods from L<Mojo::Base> and implements the
+following new ones.
+
+=head2 parse
+
+  my $cookies = $cookie->parse($str);
+
+Parse cookies. Meant to be overloaded in a subclass.
+
+=head2 to_string
+
+  my $str = $cookie->to_string;
+
+Render cookie. Meant to be overloaded in a subclass.
+
+=head1 OPERATORS
+
+L<Mojo::Cookie> overloads the following operators.
+
+=head2 bool
+
+  my $bool = !!$cookie;
+
+Always true.
+
+=head2 stringify
+
+  my $str = "$cookie";
+
+Alias for L</"to_string">.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/Cookie/Request.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/Cookie/Request.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/Cookie/Request.pm	(revision 32205)
@@ -0,0 +1,73 @@
+package Mojo::Cookie::Request;
+use Mojo::Base 'Mojo::Cookie';
+
+use Mojo::Util qw(quote split_header);
+
+sub parse {
+  my ($self, $str) = @_;
+
+  my @cookies;
+  my @pairs = map {@$_} @{split_header $str // ''};
+  while (my ($name, $value) = splice @pairs, 0, 2) {
+    next if $name =~ /^\$/;
+    push @cookies, $self->new(name => $name, value => $value // '');
+  }
+
+  return \@cookies;
+}
+
+sub to_string {
+  my $self = shift;
+  return '' unless length(my $name = $self->name // '');
+  my $value = $self->value // '';
+  return join '=', $name, $value =~ /[,;" ]/ ? quote $value : $value;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::Cookie::Request - HTTP request cookie
+
+=head1 SYNOPSIS
+
+  use Mojo::Cookie::Request;
+
+  my $cookie = Mojo::Cookie::Request->new;
+  $cookie->name('foo');
+  $cookie->value('bar');
+  say "$cookie";
+
+=head1 DESCRIPTION
+
+L<Mojo::Cookie::Request> is a container for HTTP request cookies, based on
+L<RFC 6265|http://tools.ietf.org/html/rfc6265>.
+
+=head1 ATTRIBUTES
+
+L<Mojo::Cookie::Request> inherits all attributes from L<Mojo::Cookie>.
+
+=head1 METHODS
+
+L<Mojo::Cookie::Request> inherits all methods from L<Mojo::Cookie> and
+implements the following new ones.
+
+=head2 parse
+
+  my $cookies = Mojo::Cookie::Request->parse('f=b; g=a');
+
+Parse cookies.
+
+=head2 to_string
+
+  my $str = $cookie->to_string;
+
+Render cookie.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/Cookie/Response.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/Cookie/Response.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/Cookie/Response.pm	(revision 32205)
@@ -0,0 +1,162 @@
+package Mojo::Cookie::Response;
+use Mojo::Base 'Mojo::Cookie';
+
+use Mojo::Date;
+use Mojo::Util qw(quote split_cookie_header);
+
+has [qw(domain expires host_only httponly max_age path secure)];
+
+my %ATTRS = map { $_ => 1 } qw(domain expires httponly max-age path secure);
+
+sub parse {
+  my ($self, $str) = @_;
+
+  my @cookies;
+  my $tree = split_cookie_header $str // '';
+  while (my $pairs = shift @$tree) {
+    my ($name, $value) = splice @$pairs, 0, 2;
+    push @cookies, $self->new(name => $name, value => $value // '');
+
+    while (my ($name, $value) = splice @$pairs, 0, 2) {
+      next unless $ATTRS{my $attr = lc $name};
+      $value =~ s/^\.// if $attr eq 'domain' && defined $value;
+      $value = Mojo::Date->new($value // '')->epoch if $attr eq 'expires';
+      $value = 1 if $attr eq 'secure' || $attr eq 'httponly';
+      $cookies[-1]{$attr eq 'max-age' ? 'max_age' : $attr} = $value;
+    }
+  }
+
+  return \@cookies;
+}
+
+sub to_string {
+  my $self = shift;
+
+  # Name and value
+  return '' unless length(my $name = $self->name // '');
+  my $value = $self->value // '';
+  my $cookie = join '=', $name, $value =~ /[,;" ]/ ? quote $value : $value;
+
+  # "expires"
+  my $expires = $self->expires;
+  $cookie .= '; expires=' . Mojo::Date->new($expires) if defined $expires;
+
+  # "domain"
+  if (my $domain = $self->domain) { $cookie .= "; domain=$domain" }
+
+  # "path"
+  if (my $path = $self->path) { $cookie .= "; path=$path" }
+
+  # "secure"
+  $cookie .= "; secure" if $self->secure;
+
+  # "HttpOnly"
+  $cookie .= "; HttpOnly" if $self->httponly;
+
+  # "Max-Age"
+  if (defined(my $max = $self->max_age)) { $cookie .= "; Max-Age=$max" }
+
+  return $cookie;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::Cookie::Response - HTTP response cookie
+
+=head1 SYNOPSIS
+
+  use Mojo::Cookie::Response;
+
+  my $cookie = Mojo::Cookie::Response->new;
+  $cookie->name('foo');
+  $cookie->value('bar');
+  say "$cookie";
+
+=head1 DESCRIPTION
+
+L<Mojo::Cookie::Response> is a container for HTTP response cookies, based on
+L<RFC 6265|http://tools.ietf.org/html/rfc6265>.
+
+=head1 ATTRIBUTES
+
+L<Mojo::Cookie::Response> inherits all attributes from L<Mojo::Cookie> and
+implements the following new ones.
+
+=head2 domain
+
+  my $domain = $cookie->domain;
+  $cookie    = $cookie->domain('localhost');
+
+Cookie domain.
+
+=head2 expires
+
+  my $expires = $cookie->expires;
+  $cookie     = $cookie->expires(time + 60);
+
+Expiration for cookie.
+
+=head2 host_only
+
+  my $bool = $cookie->host_only;
+  $cookie  = $cookie->host_only($bool);
+
+Host-only flag, indicating that the canonicalized request-host is identical to
+the cookie's L</"domain">.
+
+=head2 httponly
+
+  my $bool = $cookie->httponly;
+  $cookie  = $cookie->httponly($bool);
+
+HttpOnly flag, which can prevent client-side scripts from accessing this
+cookie.
+
+=head2 max_age
+
+  my $max_age = $cookie->max_age;
+  $cookie     = $cookie->max_age(60);
+
+Max age for cookie.
+
+=head2 path
+
+  my $path = $cookie->path;
+  $cookie  = $cookie->path('/test');
+
+Cookie path.
+
+=head2 secure
+
+  my $bool = $cookie->secure;
+  $cookie  = $cookie->secure($bool);
+
+Secure flag, which instructs browsers to only send this cookie over HTTPS
+connections.
+
+=head1 METHODS
+
+L<Mojo::Cookie::Response> inherits all methods from L<Mojo::Cookie> and
+implements the following new ones.
+
+=head2 parse
+
+  my $cookies = Mojo::Cookie::Response->parse('f=b; path=/');
+
+Parse cookies.
+
+=head2 to_string
+
+  my $str = $cookie->to_string;
+
+Render cookie.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/DOM.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/DOM.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/DOM.pm	(revision 32205)
@@ -0,0 +1,1137 @@
+package Mojo::DOM;
+use Mojo::Base -strict;
+use overload
+  '@{}'    => sub { shift->child_nodes },
+  '%{}'    => sub { shift->attr },
+  bool     => sub {1},
+  '""'     => sub { shift->to_string },
+  fallback => 1;
+
+# "Fry: This snow is beautiful. I'm glad global warming never happened.
+#  Leela: Actually, it did. But thank God nuclear winter canceled it out."
+use Mojo::Collection;
+use Mojo::DOM::CSS;
+use Mojo::DOM::HTML;
+use Scalar::Util qw(blessed weaken);
+use Storable 'dclone';
+
+sub all_text { _text(_nodes(shift->tree), 1) }
+
+sub ancestors { _select($_[0]->_collect([_ancestors($_[0]->tree)]), $_[1]) }
+
+sub append { shift->_add(1, @_) }
+sub append_content { shift->_content(1, 0, @_) }
+
+sub at {
+  my $self = shift;
+  return undef unless my $result = $self->_css->select_one(@_);
+  return $self->_build($result, $self->xml);
+}
+
+sub attr {
+  my $self = shift;
+
+  # Hash
+  my $tree = $self->tree;
+  my $attrs = $tree->[0] ne 'tag' ? {} : $tree->[2];
+  return $attrs unless @_;
+
+  # Get
+  return $attrs->{$_[0]} unless @_ > 1 || ref $_[0];
+
+  # Set
+  my $values = ref $_[0] ? $_[0] : {@_};
+  @$attrs{keys %$values} = values %$values;
+
+  return $self;
+}
+
+sub child_nodes { $_[0]->_collect(_nodes($_[0]->tree)) }
+
+sub children { _select($_[0]->_collect(_nodes($_[0]->tree, 1)), $_[1]) }
+
+sub content {
+  my $self = shift;
+
+  my $type = $self->type;
+  if ($type eq 'root' || $type eq 'tag') {
+    return $self->_content(0, 1, @_) if @_;
+    my $html = Mojo::DOM::HTML->new(xml => $self->xml);
+    return join '', map { $html->tree($_)->render } @{_nodes($self->tree)};
+  }
+
+  return $self->tree->[1] unless @_;
+  $self->tree->[1] = shift;
+  return $self;
+}
+
+sub descendant_nodes { $_[0]->_collect(_all(_nodes($_[0]->tree))) }
+
+sub find {
+  my $self = shift;
+  return $self->_collect($self->_css->select(@_));
+}
+
+sub following { _select($_[0]->_collect(_siblings($_[0]->tree, 1, 1)), $_[1]) }
+sub following_nodes { $_[0]->_collect(_siblings($_[0]->tree, 0, 1)) }
+
+sub matches { shift->_css->matches(@_) }
+
+sub namespace {
+  my $self = shift;
+
+  return undef if (my $tree = $self->tree)->[0] ne 'tag';
+
+  # Extract namespace prefix and search parents
+  my $ns = $tree->[1] =~ /^(.*?):/ ? "xmlns:$1" : undef;
+  for my $node ($tree, _ancestors($tree)) {
+
+    # Namespace for prefix
+    my $attrs = $node->[2];
+    if ($ns) { $_ eq $ns and return $attrs->{$_} for keys %$attrs }
+
+    # Namespace attribute
+    elsif (defined $attrs->{xmlns}) { return $attrs->{xmlns} }
+  }
+
+  return undef;
+}
+
+sub new {
+  my $class = shift;
+  my $self = bless \Mojo::DOM::HTML->new, ref $class || $class;
+  return @_ ? $self->parse(@_) : $self;
+}
+
+sub new_tag {
+  my $self = shift;
+  my $new  = $self->new;
+  $$new->tag(@_);
+  $$new->xml($$self->xml) if ref $self;
+  return $new;
+}
+
+sub next      { $_[0]->_maybe(_siblings($_[0]->tree, 1, 1, 0)) }
+sub next_node { $_[0]->_maybe(_siblings($_[0]->tree, 0, 1, 0)) }
+
+sub parent {
+  my $self = shift;
+  return undef if (my $tree = $self->tree)->[0] eq 'root';
+  return $self->_build(_parent($tree), $self->xml);
+}
+
+sub parse { ${$_[0]}->parse($_[1]) and return $_[0] }
+
+sub preceding { _select($_[0]->_collect(_siblings($_[0]->tree, 1, 0)), $_[1]) }
+sub preceding_nodes { $_[0]->_collect(_siblings($_[0]->tree, 0)) }
+
+sub prepend { shift->_add(0, @_) }
+sub prepend_content { shift->_content(0, 0, @_) }
+
+sub previous      { $_[0]->_maybe(_siblings($_[0]->tree, 1, 0, -1)) }
+sub previous_node { $_[0]->_maybe(_siblings($_[0]->tree, 0, 0, -1)) }
+
+sub remove { shift->replace('') }
+
+sub replace {
+  my ($self, $new) = @_;
+  return $self->parse($new) if (my $tree = $self->tree)->[0] eq 'root';
+  return $self->_replace(_parent($tree), $tree, _nodes($self->_parse($new)));
+}
+
+sub root {
+  my $self = shift;
+  return $self unless my $tree = _ancestors($self->tree, 1);
+  return $self->_build($tree, $self->xml);
+}
+
+sub selector {
+  return undef unless (my $tree = shift->tree)->[0] eq 'tag';
+  return join ' > ',
+    reverse map { $_->[1] . ':nth-child(' . (@{_siblings($_, 1)} + 1) . ')' }
+    $tree, _ancestors($tree);
+}
+
+sub strip {
+  my $self = shift;
+  return $self if (my $tree = $self->tree)->[0] ne 'tag';
+  return $self->_replace($tree->[3], $tree, _nodes($tree));
+}
+
+sub tag {
+  my ($self, $tag) = @_;
+  return undef if (my $tree = $self->tree)->[0] ne 'tag';
+  return $tree->[1] unless $tag;
+  $tree->[1] = $tag;
+  return $self;
+}
+
+sub tap { shift->Mojo::Base::tap(@_) }
+
+sub text { _text(_nodes(shift->tree), 0) }
+
+sub to_string { ${shift()}->render }
+
+sub tree { @_ > 1 ? (${$_[0]}->tree($_[1]) and return $_[0]) : ${$_[0]}->tree }
+
+sub type { shift->tree->[0] }
+
+sub val {
+  my $self = shift;
+
+  # "option"
+  return $self->{value} // $self->text if (my $tag = $self->tag) eq 'option';
+
+  # "input" ("type=checkbox" and "type=radio")
+  my $type = $self->{type} // '';
+  return $self->{value} // 'on'
+    if $tag eq 'input' && ($type eq 'radio' || $type eq 'checkbox');
+
+  # "textarea", "input" or "button"
+  return $tag eq 'textarea' ? $self->text : $self->{value} if $tag ne 'select';
+
+  # "select"
+  my $v = $self->find('option:checked:not([disabled])')
+    ->grep(sub { !$_->ancestors('optgroup[disabled]')->size })->map('val');
+  return exists $self->{multiple} ? $v->size ? $v->to_array : undef : $v->last;
+}
+
+sub with_roles { shift->Mojo::Base::with_roles(@_) }
+
+sub wrap         { shift->_wrap(0, @_) }
+sub wrap_content { shift->_wrap(1, @_) }
+
+sub xml { @_ > 1 ? (${$_[0]}->xml($_[1]) and return $_[0]) : ${$_[0]}->xml }
+
+sub _add {
+  my ($self, $offset, $new) = @_;
+
+  return $self if (my $tree = $self->tree)->[0] eq 'root';
+
+  my $parent = _parent($tree);
+  splice @$parent, _offset($parent, $tree) + $offset, 0,
+    @{_link($parent, _nodes($self->_parse($new)))};
+
+  return $self;
+}
+
+sub _all {
+  my $nodes = shift;
+  @$nodes = map { $_->[0] eq 'tag' ? ($_, @{_all(_nodes($_))}) : ($_) } @$nodes;
+  return $nodes;
+}
+
+sub _ancestors {
+  my ($tree, $root) = @_;
+
+  return () unless $tree = _parent($tree);
+  my @ancestors;
+  do { push @ancestors, $tree }
+    while ($tree->[0] eq 'tag') && ($tree = $tree->[3]);
+  return $root ? $ancestors[-1] : @ancestors[0 .. $#ancestors - 1];
+}
+
+sub _build { shift->new->tree(shift)->xml(shift) }
+
+sub _collect {
+  my ($self, $nodes) = (shift, shift // []);
+  my $xml = $self->xml;
+  return Mojo::Collection->new(map { $self->_build($_, $xml) } @$nodes);
+}
+
+sub _content {
+  my ($self, $start, $offset, $new) = @_;
+
+  my $tree = $self->tree;
+  unless ($tree->[0] eq 'root' || $tree->[0] eq 'tag') {
+    my $old = $self->content;
+    return $self->content($start ? $old . $new : $new . $old);
+  }
+
+  $start  = $start  ? ($#$tree + 1) : _start($tree);
+  $offset = $offset ? $#$tree       : 0;
+  splice @$tree, $start, $offset, @{_link($tree, _nodes($self->_parse($new)))};
+
+  return $self;
+}
+
+sub _css { Mojo::DOM::CSS->new(tree => shift->tree) }
+
+sub _fragment { _link(my $r = ['root', @_], [@_]); $r }
+
+sub _link {
+  my ($parent, $children) = @_;
+
+  # Link parent to children
+  for my $node (@$children) {
+    my $offset = $node->[0] eq 'tag' ? 3 : 2;
+    $node->[$offset] = $parent;
+    weaken $node->[$offset];
+  }
+
+  return $children;
+}
+
+sub _maybe { $_[1] ? $_[0]->_build($_[1], $_[0]->xml) : undef }
+
+sub _nodes {
+  return () unless my $tree = shift;
+  my @nodes = @$tree[_start($tree) .. $#$tree];
+  return shift() ? [grep { $_->[0] eq 'tag' } @nodes] : \@nodes;
+}
+
+sub _offset {
+  my ($parent, $child) = @_;
+  my $i = _start($parent);
+  $_ eq $child ? last : $i++ for @$parent[$i .. $#$parent];
+  return $i;
+}
+
+sub _parent { $_[0]->[$_[0][0] eq 'tag' ? 3 : 2] }
+
+sub _parse {
+  my ($self, $input) = @_;
+  return Mojo::DOM::HTML->new(xml => $self->xml)->parse($input)->tree
+    unless blessed $input && $input->isa('Mojo::DOM');
+  my $tree = dclone $input->tree;
+  return $tree->[0] eq 'root' ? $tree : _fragment($tree);
+}
+
+sub _replace {
+  my ($self, $parent, $child, $nodes) = @_;
+  splice @$parent, _offset($parent, $child), 1, @{_link($parent, $nodes)};
+  return $self->parent;
+}
+
+sub _select { $_[1] ? $_[0]->grep(matches => $_[1]) : $_[0] }
+
+sub _siblings {
+  my ($tree, $tags, $tail, $i) = @_;
+
+  return defined $i ? undef : [] if $tree->[0] eq 'root';
+
+  my $nodes = _nodes(_parent($tree));
+  my $match = -1;
+  defined($match++) and $_ eq $tree and last for @$nodes;
+
+  if ($tail) { splice @$nodes, 0, $match + 1 }
+  else       { splice @$nodes, $match, ($#$nodes + 1) - $match }
+
+  @$nodes = grep { $_->[0] eq 'tag' } @$nodes if $tags;
+
+  return defined $i ? $i == -1 && !@$nodes ? undef : $nodes->[$i] : $nodes;
+}
+
+sub _start { $_[0][0] eq 'root' ? 1 : 4 }
+
+sub _text {
+  my ($nodes, $all) = @_;
+
+  my $text = '';
+  while (my $node = shift @$nodes) {
+    my $type = $node->[0];
+
+    # Text
+    if ($type eq 'text' || $type eq 'cdata' || $type eq 'raw') {
+      $text .= $node->[1];
+    }
+
+    # Nested tag
+    elsif ($type eq 'tag' && $all) { unshift @$nodes, @{_nodes($node)} }
+  }
+
+  return $text;
+}
+
+sub _wrap {
+  my ($self, $content, $new) = @_;
+
+  return $self if (my $tree = $self->tree)->[0] eq 'root' && !$content;
+  return $self if $tree->[0] ne 'root' && $tree->[0] ne 'tag' && $content;
+
+  # Find innermost tag
+  my $current;
+  my $first = $new = $self->_parse($new);
+  $current = $first while $first = _nodes($first, 1)->[0];
+  return $self unless $current;
+
+  # Wrap content
+  if ($content) {
+    push @$current, @{_link($current, _nodes($tree))};
+    splice @$tree, _start($tree), $#$tree, @{_link($tree, _nodes($new))};
+    return $self;
+  }
+
+  # Wrap element
+  $self->_replace(_parent($tree), $tree, _nodes($new));
+  push @$current, @{_link($current, [$tree])};
+  return $self;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::DOM - Minimalistic HTML/XML DOM parser with CSS selectors
+
+=head1 SYNOPSIS
+
+  use Mojo::DOM;
+
+  # Parse
+  my $dom = Mojo::DOM->new('<div><p id="a">Test</p><p id="b">123</p></div>');
+
+  # Find
+  say $dom->at('#b')->text;
+  say $dom->find('p')->map('text')->join("\n");
+  say $dom->find('[id]')->map(attr => 'id')->join("\n");
+
+  # Iterate
+  $dom->find('p[id]')->reverse->each(sub { say $_->{id} });
+
+  # Loop
+  for my $e ($dom->find('p[id]')->each) {
+    say $e->{id}, ':', $e->text;
+  }
+
+  # Modify
+  $dom->find('div p')->last->append('<p id="c">456</p>');
+  $dom->at('#c')->prepend($dom->new_tag('p', id => 'd', '789'));
+  $dom->find(':not(p)')->map('strip');
+
+  # Render
+  say "$dom";
+
+=head1 DESCRIPTION
+
+L<Mojo::DOM> is a minimalistic and relaxed HTML/XML DOM parser with CSS
+selector support. It will even try to interpret broken HTML and XML, so you
+should not use it for validation.
+
+=head1 NODES AND ELEMENTS
+
+When we parse an HTML/XML fragment, it gets turned into a tree of nodes.
+
+  <!DOCTYPE html>
+  <html>
+    <head><title>Hello</title></head>
+    <body>World!</body>
+  </html>
+
+There are currently eight different kinds of nodes, C<cdata>, C<comment>,
+C<doctype>, C<pi>, C<raw>, C<root>, C<tag> and C<text>. Elements are nodes of
+the type C<tag>.
+
+  root
+  |- doctype (html)
+  +- tag (html)
+     |- tag (head)
+     |  +- tag (title)
+     |     +- raw (Hello)
+     +- tag (body)
+        +- text (World!)
+
+While all node types are represented as L<Mojo::DOM> objects, some methods like
+L</"attr"> and L</"namespace"> only apply to elements.
+
+=head1 CASE-SENSITIVITY
+
+L<Mojo::DOM> defaults to HTML semantics, that means all tags and attribute
+names are lowercased and selectors need to be lowercase as well.
+
+  # HTML semantics
+  my $dom = Mojo::DOM->new('<P ID="greeting">Hi!</P>');
+  say $dom->at('p[id]')->text;
+
+If an XML declaration is found, the parser will automatically switch into XML
+mode and everything becomes case-sensitive.
+
+  # XML semantics
+  my $dom = Mojo::DOM->new('<?xml version="1.0"?><P ID="greeting">Hi!</P>');
+  say $dom->at('P[ID]')->text;
+
+HTML or XML semantics can also be forced with the L</"xml"> method.
+
+  # Force HTML semantics
+  my $dom = Mojo::DOM->new->xml(0)->parse('<P ID="greeting">Hi!</P>');
+  say $dom->at('p[id]')->text;
+
+  # Force XML semantics
+  my $dom = Mojo::DOM->new->xml(1)->parse('<P ID="greeting">Hi!</P>');
+  say $dom->at('P[ID]')->text;
+
+=head1 METHODS
+
+L<Mojo::DOM> implements the following methods.
+
+=head2 all_text
+
+  my $text = $dom->all_text;
+
+Extract text content from all descendant nodes of this element.
+
+  # "foo\nbarbaz\n"
+  $dom->parse("<div>foo\n<p>bar</p>baz\n</div>")->at('div')->all_text;
+
+=head2 ancestors
+
+  my $collection = $dom->ancestors;
+  my $collection = $dom->ancestors('div ~ p');
+
+Find all ancestor elements of this node matching the CSS selector and return a
+L<Mojo::Collection> object containing these elements as L<Mojo::DOM> objects.
+All selectors from L<Mojo::DOM::CSS/"SELECTORS"> are supported.
+
+  # List tag names of ancestor elements
+  say $dom->ancestors->map('tag')->join("\n");
+
+=head2 append
+
+  $dom = $dom->append('<p>I â¥ Mojolicious!</p>');
+  $dom = $dom->append(Mojo::DOM->new);
+
+Append HTML/XML fragment to this node (for all node types other than C<root>).
+
+  # "<div><h1>Test</h1><h2>123</h2></div>"
+  $dom->parse('<div><h1>Test</h1></div>')
+    ->at('h1')->append('<h2>123</h2>')->root;
+
+  # "<p>Test 123</p>"
+  $dom->parse('<p>Test</p>')->at('p')
+    ->child_nodes->first->append(' 123')->root;
+
+=head2 append_content
+
+  $dom = $dom->append_content('<p>I â¥ Mojolicious!</p>');
+  $dom = $dom->append_content(Mojo::DOM->new);
+
+Append HTML/XML fragment (for C<root> and C<tag> nodes) or raw content to this
+node's content.
+
+  # "<div><h1>Test123</h1></div>"
+  $dom->parse('<div><h1>Test</h1></div>')
+    ->at('h1')->append_content('123')->root;
+
+  # "<!-- Test 123 --><br>"
+  $dom->parse('<!-- Test --><br>')
+    ->child_nodes->first->append_content('123 ')->root;
+
+  # "<p>Test<i>123</i></p>"
+  $dom->parse('<p>Test</p>')->at('p')->append_content('<i>123</i>')->root;
+
+=head2 at
+
+  my $result = $dom->at('div ~ p');
+  my $result = $dom->at('svg|line', svg => 'http://www.w3.org/2000/svg');
+
+Find first descendant element of this element matching the CSS selector and
+return it as a L<Mojo::DOM> object, or C<undef> if none could be found. All
+selectors from L<Mojo::DOM::CSS/"SELECTORS"> are supported.
+
+  # Find first element with "svg" namespace definition
+  my $namespace = $dom->at('[xmlns\:svg]')->{'xmlns:svg'};
+
+Trailing key/value pairs can be used to declare xml namespace aliases.
+
+  # "<rect />"
+  $dom->parse('<svg xmlns="http://www.w3.org/2000/svg"><rect /></svg>')
+    ->at('svg|rect', svg => 'http://www.w3.org/2000/svg');
+
+=head2 attr
+
+  my $hash = $dom->attr;
+  my $foo  = $dom->attr('foo');
+  $dom     = $dom->attr({foo => 'bar'});
+  $dom     = $dom->attr(foo => 'bar');
+
+This element's attributes.
+
+  # Remove an attribute
+  delete $dom->attr->{id};
+
+  # Attribute without value
+  $dom->attr(selected => undef);
+
+  # List id attributes
+  say $dom->find('*')->map(attr => 'id')->compact->join("\n");
+
+=head2 child_nodes
+
+  my $collection = $dom->child_nodes;
+
+Return a L<Mojo::Collection> object containing all child nodes of this element
+as L<Mojo::DOM> objects.
+
+  # "<p><b>123</b></p>"
+  $dom->parse('<p>Test<b>123</b></p>')->at('p')->child_nodes->first->remove;
+
+  # "<!DOCTYPE html>"
+  $dom->parse('<!DOCTYPE html><b>123</b>')->child_nodes->first;
+
+  # " Test "
+  $dom->parse('<b>123</b><!-- Test -->')->child_nodes->last->content;
+
+=head2 children
+
+  my $collection = $dom->children;
+  my $collection = $dom->children('div ~ p');
+
+Find all child elements of this element matching the CSS selector and return a
+L<Mojo::Collection> object containing these elements as L<Mojo::DOM> objects.
+All selectors from L<Mojo::DOM::CSS/"SELECTORS"> are supported.
+
+  # Show tag name of random child element
+  say $dom->children->shuffle->first->tag;
+
+=head2 content
+
+  my $str = $dom->content;
+  $dom    = $dom->content('<p>I â¥ Mojolicious!</p>');
+  $dom    = $dom->content(Mojo::DOM->new);
+
+Return this node's content or replace it with HTML/XML fragment (for C<root>
+and C<tag> nodes) or raw content.
+
+  # "<b>Test</b>"
+  $dom->parse('<div><b>Test</b></div>')->at('div')->content;
+
+  # "<div><h1>123</h1></div>"
+  $dom->parse('<div><h1>Test</h1></div>')->at('h1')->content('123')->root;
+
+  # "<p><i>123</i></p>"
+  $dom->parse('<p>Test</p>')->at('p')->content('<i>123</i>')->root;
+
+  # "<div><h1></h1></div>"
+  $dom->parse('<div><h1>Test</h1></div>')->at('h1')->content('')->root;
+
+  # " Test "
+  $dom->parse('<!-- Test --><br>')->child_nodes->first->content;
+
+  # "<div><!-- 123 -->456</div>"
+  $dom->parse('<div><!-- Test -->456</div>')
+    ->at('div')->child_nodes->first->content(' 123 ')->root;
+
+=head2 descendant_nodes
+
+  my $collection = $dom->descendant_nodes;
+
+Return a L<Mojo::Collection> object containing all descendant nodes of this
+element as L<Mojo::DOM> objects.
+
+  # "<p><b>123</b></p>"
+  $dom->parse('<p><!-- Test --><b>123<!-- 456 --></b></p>')
+    ->descendant_nodes->grep(sub { $_->type eq 'comment' })
+    ->map('remove')->first;
+
+  # "<p><b>test</b>test</p>"
+  $dom->parse('<p><b>123</b>456</p>')
+    ->at('p')->descendant_nodes->grep(sub { $_->type eq 'text' })
+    ->map(content => 'test')->first->root;
+
+=head2 find
+
+  my $collection = $dom->find('div ~ p');
+  my $collection = $dom->find('svg|line', svg => 'http://www.w3.org/2000/svg');
+
+Find all descendant elements of this element matching the CSS selector and
+return a L<Mojo::Collection> object containing these elements as L<Mojo::DOM>
+objects. All selectors from L<Mojo::DOM::CSS/"SELECTORS"> are supported.
+
+  # Find a specific element and extract information
+  my $id = $dom->find('div')->[23]{id};
+
+  # Extract information from multiple elements
+  my @headers = $dom->find('h1, h2, h3')->map('text')->each;
+
+  # Count all the different tags
+  my $hash = $dom->find('*')->reduce(sub { $a->{$b->tag}++; $a }, {});
+
+  # Find elements with a class that contains dots
+  my @divs = $dom->find('div.foo\.bar')->each;
+
+Trailing key/value pairs can be used to declare xml namespace aliases.
+
+  # "<rect />"
+  $dom->parse('<svg xmlns="http://www.w3.org/2000/svg"><rect /></svg>')
+    ->find('svg|rect', svg => 'http://www.w3.org/2000/svg')->first;
+
+=head2 following
+
+  my $collection = $dom->following;
+  my $collection = $dom->following('div ~ p');
+
+Find all sibling elements after this node matching the CSS selector and return
+a L<Mojo::Collection> object containing these elements as L<Mojo::DOM> objects.
+All selectors from L<Mojo::DOM::CSS/"SELECTORS"> are supported.
+
+  # List tags of sibling elements after this node
+  say $dom->following->map('tag')->join("\n");
+
+=head2 following_nodes
+
+  my $collection = $dom->following_nodes;
+
+Return a L<Mojo::Collection> object containing all sibling nodes after this
+node as L<Mojo::DOM> objects.
+
+  # "C"
+  $dom->parse('<p>A</p><!-- B -->C')->at('p')->following_nodes->last->content;
+
+=head2 matches
+
+  my $bool = $dom->matches('div ~ p');
+  my $bool = $dom->matches('svg|line', svg => 'http://www.w3.org/2000/svg');
+
+Check if this element matches the CSS selector. All selectors from
+L<Mojo::DOM::CSS/"SELECTORS"> are supported.
+
+  # True
+  $dom->parse('<p class="a">A</p>')->at('p')->matches('.a');
+  $dom->parse('<p class="a">A</p>')->at('p')->matches('p[class]');
+
+  # False
+  $dom->parse('<p class="a">A</p>')->at('p')->matches('.b');
+  $dom->parse('<p class="a">A</p>')->at('p')->matches('p[id]');
+
+Trailing key/value pairs can be used to declare xml namespace aliases.
+
+  # True
+  $dom->parse('<svg xmlns="http://www.w3.org/2000/svg"><rect /></svg>')
+    ->matches('svg|rect', svg => 'http://www.w3.org/2000/svg');
+
+=head2 namespace
+
+  my $namespace = $dom->namespace;
+
+Find this element's namespace, or return C<undef> if none could be found.
+
+  # Find namespace for an element with namespace prefix
+  my $namespace = $dom->at('svg > svg\:circle')->namespace;
+
+  # Find namespace for an element that may or may not have a namespace prefix
+  my $namespace = $dom->at('svg > circle')->namespace;
+
+=head2 new
+
+  my $dom = Mojo::DOM->new;
+  my $dom = Mojo::DOM->new('<foo bar="baz">I â¥ Mojolicious!</foo>');
+
+Construct a new scalar-based L<Mojo::DOM> object and L</"parse"> HTML/XML
+fragment if necessary.
+
+=head2 new_tag
+
+  my $tag = Mojo::DOM->new_tag('div');
+  my $tag = $dom->new_tag('div');
+  my $tag = $dom->new_tag('div', id => 'foo', hidden => undef);
+  my $tag = $dom->new_tag('div', 'safe content');
+  my $tag = $dom->new_tag('div', id => 'foo', 'safe content');
+  my $tag = $dom->new_tag('div', data => {mojo => 'rocks'}, 'safe content');
+  my $tag = $dom->new_tag('div', id => 'foo', sub { 'unsafe content' });
+
+Construct a new L<Mojo::DOM> object for an HTML/XML tag with or without
+attributes and content. The C<data> attribute may contain a hash reference with
+key/value pairs to generate attributes from.
+
+  # "<br>"
+  $dom->new_tag('br');
+
+  # "<div></div>"
+  $dom->new_tag('div');
+
+  # "<div id="foo" hidden></div>"
+  $dom->new_tag('div', id => 'foo', hidden => undef);
+
+  # "<div>test &amp; 123</div>"
+  $dom->new_tag('div', 'test & 123');
+
+  # "<div id="foo">test &amp; 123</div>"
+  $dom->new_tag('div', id => 'foo', 'test & 123');
+
+  # "<div data-foo="1" data-bar="test">test &amp; 123</div>""
+  $dom->new_tag('div', data => {foo => 1, Bar => 'test'}, 'test & 123');
+
+  # "<div id="foo">test & 123</div>"
+  $dom->new_tag('div', id => 'foo', sub { 'test & 123' });
+
+  # "<div>Hello<b>Mojo!</b></div>"
+  $dom->parse('<div>Hello</div>')->at('div')
+    ->append_content($dom->new_tag('b', 'Mojo!'))->root;
+
+=head2 next
+
+  my $sibling = $dom->next;
+
+Return L<Mojo::DOM> object for next sibling element, or C<undef> if there are no
+more siblings.
+
+  # "<h2>123</h2>"
+  $dom->parse('<div><h1>Test</h1><h2>123</h2></div>')->at('h1')->next;
+
+=head2 next_node
+
+  my $sibling = $dom->next_node;
+
+Return L<Mojo::DOM> object for next sibling node, or C<undef> if there are no
+more siblings.
+
+  # "456"
+  $dom->parse('<p><b>123</b><!-- Test -->456</p>')
+    ->at('b')->next_node->next_node;
+
+  # " Test "
+  $dom->parse('<p><b>123</b><!-- Test -->456</p>')
+    ->at('b')->next_node->content;
+
+=head2 parent
+
+  my $parent = $dom->parent;
+
+Return L<Mojo::DOM> object for parent of this node, or C<undef> if this node has
+no parent.
+
+  # "<b><i>Test</i></b>"
+  $dom->parse('<p><b><i>Test</i></b></p>')->at('i')->parent;
+
+=head2 parse
+
+  $dom = $dom->parse('<foo bar="baz">I â¥ Mojolicious!</foo>');
+
+Parse HTML/XML fragment with L<Mojo::DOM::HTML>.
+
+  # Parse XML
+  my $dom = Mojo::DOM->new->xml(1)->parse('<foo>I â¥ Mojolicious!</foo>');
+
+=head2 preceding
+
+  my $collection = $dom->preceding;
+  my $collection = $dom->preceding('div ~ p');
+
+Find all sibling elements before this node matching the CSS selector and return
+a L<Mojo::Collection> object containing these elements as L<Mojo::DOM> objects.
+All selectors from L<Mojo::DOM::CSS/"SELECTORS"> are supported.
+
+  # List tags of sibling elements before this node
+  say $dom->preceding->map('tag')->join("\n");
+
+=head2 preceding_nodes
+
+  my $collection = $dom->preceding_nodes;
+
+Return a L<Mojo::Collection> object containing all sibling nodes before this
+node as L<Mojo::DOM> objects.
+
+  # "A"
+  $dom->parse('A<!-- B --><p>C</p>')->at('p')->preceding_nodes->first->content;
+
+=head2 prepend
+
+  $dom = $dom->prepend('<p>I â¥ Mojolicious!</p>');
+  $dom = $dom->prepend(Mojo::DOM->new);
+
+Prepend HTML/XML fragment to this node (for all node types other than C<root>).
+
+  # "<div><h1>Test</h1><h2>123</h2></div>"
+  $dom->parse('<div><h2>123</h2></div>')
+    ->at('h2')->prepend('<h1>Test</h1>')->root;
+
+  # "<p>Test 123</p>"
+  $dom->parse('<p>123</p>')
+    ->at('p')->child_nodes->first->prepend('Test ')->root;
+
+=head2 prepend_content
+
+  $dom = $dom->prepend_content('<p>I â¥ Mojolicious!</p>');
+  $dom = $dom->prepend_content(Mojo::DOM->new);
+
+Prepend HTML/XML fragment (for C<root> and C<tag> nodes) or raw content to this
+node's content.
+
+  # "<div><h2>Test123</h2></div>"
+  $dom->parse('<div><h2>123</h2></div>')
+    ->at('h2')->prepend_content('Test')->root;
+
+  # "<!-- Test 123 --><br>"
+  $dom->parse('<!-- 123 --><br>')
+    ->child_nodes->first->prepend_content(' Test')->root;
+
+  # "<p><i>123</i>Test</p>"
+  $dom->parse('<p>Test</p>')->at('p')->prepend_content('<i>123</i>')->root;
+
+=head2 previous
+
+  my $sibling = $dom->previous;
+
+Return L<Mojo::DOM> object for previous sibling element, or C<undef> if there
+are no more siblings.
+
+  # "<h1>Test</h1>"
+  $dom->parse('<div><h1>Test</h1><h2>123</h2></div>')->at('h2')->previous;
+
+=head2 previous_node
+
+  my $sibling = $dom->previous_node;
+
+Return L<Mojo::DOM> object for previous sibling node, or C<undef> if there are
+no more siblings.
+
+  # "123"
+  $dom->parse('<p>123<!-- Test --><b>456</b></p>')
+    ->at('b')->previous_node->previous_node;
+
+  # " Test "
+  $dom->parse('<p>123<!-- Test --><b>456</b></p>')
+    ->at('b')->previous_node->content;
+
+=head2 remove
+
+  my $parent = $dom->remove;
+
+Remove this node and return L</"root"> (for C<root> nodes) or L</"parent">.
+
+  # "<div></div>"
+  $dom->parse('<div><h1>Test</h1></div>')->at('h1')->remove;
+
+  # "<p><b>456</b></p>"
+  $dom->parse('<p>123<b>456</b></p>')
+    ->at('p')->child_nodes->first->remove->root;
+
+=head2 replace
+
+  my $parent = $dom->replace('<div>I â¥ Mojolicious!</div>');
+  my $parent = $dom->replace(Mojo::DOM->new);
+
+Replace this node with HTML/XML fragment and return L</"root"> (for C<root>
+nodes) or L</"parent">.
+
+  # "<div><h2>123</h2></div>"
+  $dom->parse('<div><h1>Test</h1></div>')->at('h1')->replace('<h2>123</h2>');
+
+  # "<p><b>123</b></p>"
+  $dom->parse('<p>Test</p>')
+    ->at('p')->child_nodes->[0]->replace('<b>123</b>')->root;
+
+=head2 root
+
+  my $root = $dom->root;
+
+Return L<Mojo::DOM> object for C<root> node.
+
+=head2 selector
+
+  my $selector = $dom->selector;
+
+Get a unique CSS selector for this element.
+
+  # "ul:nth-child(1) > li:nth-child(2)"
+  $dom->parse('<ul><li>Test</li><li>123</li></ul>')->find('li')->last->selector;
+
+  # "p:nth-child(1) > b:nth-child(1) > i:nth-child(1)"
+  $dom->parse('<p><b><i>Test</i></b></p>')->at('i')->selector;
+
+=head2 strip
+
+  my $parent = $dom->strip;
+
+Remove this element while preserving its content and return L</"parent">.
+
+  # "<div>Test</div>"
+  $dom->parse('<div><h1>Test</h1></div>')->at('h1')->strip;
+
+=head2 tag
+
+  my $tag = $dom->tag;
+  $dom    = $dom->tag('div');
+
+This element's tag name.
+
+  # List tag names of child elements
+  say $dom->children->map('tag')->join("\n");
+
+=head2 tap
+
+  $dom = $dom->tap(sub {...});
+
+Alias for L<Mojo::Base/"tap">.
+
+=head2 text
+
+  my $text = $dom->text;
+
+Extract text content from this element only (not including child elements).
+
+  # "bar"
+  $dom->parse("<div>foo<p>bar</p>baz</div>")->at('p')->text;
+
+  # "foo\nbaz\n"
+  $dom->parse("<div>foo\n<p>bar</p>baz\n</div>")->at('div')->text;
+
+=head2 to_string
+
+  my $str = $dom->to_string;
+
+Render this node and its content to HTML/XML.
+
+  # "<b>Test</b>"
+  $dom->parse('<div><b>Test</b></div>')->at('div b')->to_string;
+
+=head2 tree
+
+  my $tree = $dom->tree;
+  $dom     = $dom->tree(['root']);
+
+Document Object Model. Note that this structure should only be used very
+carefully since it is very dynamic.
+
+=head2 type
+
+  my $type = $dom->type;
+
+This node's type, usually C<cdata>, C<comment>, C<doctype>, C<pi>, C<raw>,
+C<root>, C<tag> or C<text>.
+
+  # "cdata"
+  $dom->parse('<![CDATA[Test]]>')->child_nodes->first->type;
+
+  # "comment"
+  $dom->parse('<!-- Test -->')->child_nodes->first->type;
+
+  # "doctype"
+  $dom->parse('<!DOCTYPE html>')->child_nodes->first->type;
+
+  # "pi"
+  $dom->parse('<?xml version="1.0"?>')->child_nodes->first->type;
+
+  # "raw"
+  $dom->parse('<title>Test</title>')->at('title')->child_nodes->first->type;
+
+  # "root"
+  $dom->parse('<p>Test</p>')->type;
+
+  # "tag"
+  $dom->parse('<p>Test</p>')->at('p')->type;
+
+  # "text"
+  $dom->parse('<p>Test</p>')->at('p')->child_nodes->first->type;
+
+=head2 val
+
+  my $value = $dom->val;
+
+Extract value from form element (such as C<button>, C<input>, C<option>,
+C<select> and C<textarea>), or return C<undef> if this element has no value. In
+the case of C<select> with C<multiple> attribute, find C<option> elements with
+C<selected> attribute and return an array reference with all values, or C<undef>
+if none could be found.
+
+  # "a"
+  $dom->parse('<input name=test value=a>')->at('input')->val;
+
+  # "b"
+  $dom->parse('<textarea>b</textarea>')->at('textarea')->val;
+
+  # "c"
+  $dom->parse('<option value="c">Test</option>')->at('option')->val;
+
+  # "d"
+  $dom->parse('<select><option selected>d</option></select>')
+    ->at('select')->val;
+
+  # "e"
+  $dom->parse('<select multiple><option selected>e</option></select>')
+    ->at('select')->val->[0];
+
+  # "on"
+  $dom->parse('<input name=test type=checkbox>')->at('input')->val;
+
+=head2 with_roles
+
+  my $new_class = Mojo::DOM->with_roles('Mojo::DOM::Role::One');
+  my $new_class = Mojo::DOM->with_roles('+One', '+Two');
+  $dom          = $dom->with_roles('+One', '+Two');
+
+Alias for L<Mojo::Base/"with_roles">.
+
+=head2 wrap
+
+  $dom = $dom->wrap('<div></div>');
+  $dom = $dom->wrap(Mojo::DOM->new);
+
+Wrap HTML/XML fragment around this node (for all node types other than C<root>),
+placing it as the last child of the first innermost element.
+
+  # "<p>123<b>Test</b></p>"
+  $dom->parse('<b>Test</b>')->at('b')->wrap('<p>123</p>')->root;
+
+  # "<div><p><b>Test</b></p>123</div>"
+  $dom->parse('<b>Test</b>')->at('b')->wrap('<div><p></p>123</div>')->root;
+
+  # "<p><b>Test</b></p><p>123</p>"
+  $dom->parse('<b>Test</b>')->at('b')->wrap('<p></p><p>123</p>')->root;
+
+  # "<p><b>Test</b></p>"
+  $dom->parse('<p>Test</p>')->at('p')->child_nodes->first->wrap('<b>')->root;
+
+=head2 wrap_content
+
+  $dom = $dom->wrap_content('<div></div>');
+  $dom = $dom->wrap_content(Mojo::DOM->new);
+
+Wrap HTML/XML fragment around this node's content (for C<root> and C<tag>
+nodes), placing it as the last children of the first innermost element.
+
+  # "<p><b>123Test</b></p>"
+  $dom->parse('<p>Test<p>')->at('p')->wrap_content('<b>123</b>')->root;
+
+  # "<p><b>Test</b></p><p>123</p>"
+  $dom->parse('<b>Test</b>')->wrap_content('<p></p><p>123</p>');
+
+=head2 xml
+
+  my $bool = $dom->xml;
+  $dom     = $dom->xml($bool);
+
+Disable HTML semantics in parser and activate case-sensitivity, defaults to
+auto-detection based on XML declarations.
+
+=head1 OPERATORS
+
+L<Mojo::DOM> overloads the following operators.
+
+=head2 array
+
+  my @nodes = @$dom;
+
+Alias for L</"child_nodes">.
+
+  # "<!-- Test -->"
+  $dom->parse('<!-- Test --><b>123</b>')->[0];
+
+=head2 bool
+
+  my $bool = !!$dom;
+
+Always true.
+
+=head2 hash
+
+  my %attrs = %$dom;
+
+Alias for L</"attr">.
+
+  # "test"
+  $dom->parse('<div id="test">Test</div>')->at('div')->{id};
+
+=head2 stringify
+
+  my $str = "$dom";
+
+Alias for L</"to_string">.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/DOM/CSS.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/DOM/CSS.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/DOM/CSS.pm	(revision 32205)
@@ -0,0 +1,673 @@
+package Mojo::DOM::CSS;
+use Mojo::Base -base;
+
+use Mojo::Util 'trim';
+
+has 'tree';
+
+my $ESCAPE_RE = qr/\\[^0-9a-fA-F]|\\[0-9a-fA-F]{1,6}/;
+my $ATTR_RE   = qr/
+  \[
+  ((?:$ESCAPE_RE|[\w\-])+)                              # Key
+  (?:
+    (\W)?=                                              # Operator
+    (?:"((?:\\"|[^"])*)"|'((?:\\'|[^'])*)'|([^\]]+?))   # Value
+    (?:\s+(i))?                                         # Case-sensitivity
+  )?
+  \]
+/x;
+
+sub matches {
+  my $tree = shift->tree;
+  return $tree->[0] ne 'tag' ? undef : _match(_compile(@_), $tree, $tree);
+}
+
+sub select     { _select(0, shift->tree, _compile(@_)) }
+sub select_one { _select(1, shift->tree, _compile(@_)) }
+
+sub _ancestor {
+  my ($selectors, $current, $tree, $one, $pos) = @_;
+
+  while ($current = $current->[3]) {
+    return undef if $current->[0] eq 'root' || $current eq $tree;
+    return 1 if _combinator($selectors, $current, $tree, $pos);
+    last if $one;
+  }
+
+  return undef;
+}
+
+sub _attr {
+  my ($name_re, $value_re, $current) = @_;
+
+  my $attrs = $current->[2];
+  for my $name (keys %$attrs) {
+    my $value = $attrs->{$name};
+    next if $name !~ $name_re || (!defined $value && defined $value_re);
+    return 1 if !(defined $value && defined $value_re) || $value =~ $value_re;
+  }
+
+  return undef;
+}
+
+sub _combinator {
+  my ($selectors, $current, $tree, $pos) = @_;
+
+  # Selector
+  return undef unless my $c = $selectors->[$pos];
+  if (ref $c) {
+    return undef unless _selector($c, $current);
+    return 1 unless $c = $selectors->[++$pos];
+  }
+
+  # ">" (parent only)
+  return _ancestor($selectors, $current, $tree, 1, ++$pos) if $c eq '>';
+
+  # "~" (preceding siblings)
+  return _sibling($selectors, $current, $tree, 0, ++$pos) if $c eq '~';
+
+  # "+" (immediately preceding siblings)
+  return _sibling($selectors, $current, $tree, 1, ++$pos) if $c eq '+';
+
+  # " " (ancestor)
+  return _ancestor($selectors, $current, $tree, 0, ++$pos);
+}
+
+sub _compile {
+  my ($css, %ns) = (trim('' . shift), @_);
+
+  my $group = [[]];
+  while (my $selectors = $group->[-1]) {
+    push @$selectors, [] unless @$selectors && ref $selectors->[-1];
+    my $last = $selectors->[-1];
+
+    # Separator
+    if ($css =~ /\G\s*,\s*/gc) { push @$group, [] }
+
+    # Combinator
+    elsif ($css =~ /\G\s*([ >+~])\s*/gc) { push @$selectors, $1 }
+
+    # Class or ID
+    elsif ($css =~ /\G([.#])((?:$ESCAPE_RE\s|\\.|[^,.#:[ >~+])+)/gco) {
+      my ($name, $op) = $1 eq '.' ? ('class', '~') : ('id', '');
+      push @$last, ['attr', _name($name), _value($op, $2)];
+    }
+
+    # Attributes
+    elsif ($css =~ /\G$ATTR_RE/gco) {
+      push @$last, ['attr', _name($1), _value($2 // '', $3 // $4 // $5, $6)];
+    }
+
+    # Pseudo-class
+    elsif ($css =~ /\G:([\w\-]+)(?:\(((?:\([^)]+\)|[^)])+)\))?/gcs) {
+      my ($name, $args) = (lc $1, $2);
+
+      # ":matches" and ":not" (contains more selectors)
+      $args = _compile($args, %ns) if $name eq 'matches' || $name eq 'not';
+
+      # ":nth-*" (with An+B notation)
+      $args = _equation($args) if $name =~ /^nth-/;
+
+      # ":first-*" (rewrite to ":nth-*")
+      ($name, $args) = ("nth-$1", [0, 1]) if $name =~ /^first-(.+)$/;
+
+      # ":last-*" (rewrite to ":nth-*")
+      ($name, $args) = ("nth-$name", [-1, 1]) if $name =~ /^last-/;
+
+      push @$last, ['pc', $name, $args];
+    }
+
+    # Tag
+    elsif ($css =~ /\G((?:$ESCAPE_RE\s|\\.|[^,.#:[ >~+])+)/gco) {
+      my $alias = (my $name = $1) =~ s/^([^|]*)\|// && $1 ne '*' ? $1 : undef;
+      my $ns = length $alias ? $ns{$alias} // return [['invalid']] : $alias;
+      push @$last, ['tag', $name eq '*' ? undef : _name($name), _unescape($ns)];
+    }
+
+    else {last}
+  }
+
+  return $group;
+}
+
+sub _empty { $_[0][0] eq 'comment' || $_[0][0] eq 'pi' }
+
+sub _equation {
+  return [0, 0] unless my $equation = shift;
+
+  # "even"
+  return [2, 2] if $equation =~ /^\s*even\s*$/i;
+
+  # "odd"
+  return [2, 1] if $equation =~ /^\s*odd\s*$/i;
+
+  # "4", "+4" or "-4"
+  return [0, $1] if $equation =~ /^\s*((?:\+|-)?\d+)\s*$/;
+
+  # "n", "4n", "+4n", "-4n", "n+1", "4n-1", "+4n-1" (and other variations)
+  return [0, 0]
+    unless $equation =~ /^\s*((?:\+|-)?(?:\d+)?)?n\s*((?:\+|-)\s*\d+)?\s*$/i;
+  return [$1 eq '-' ? -1 : !length $1 ? 1 : $1, join('', split(' ', $2 // 0))];
+}
+
+sub _match {
+  my ($group, $current, $tree) = @_;
+  _combinator([reverse @$_], $current, $tree, 0) and return 1 for @$group;
+  return undef;
+}
+
+sub _name {qr/(?:^|:)\Q@{[_unescape(shift)]}\E$/}
+
+sub _namespace {
+  my ($ns, $current) = @_;
+
+  my $attr = $current->[1] =~ /^([^:]+):/ ? "xmlns:$1" : 'xmlns';
+  while ($current) {
+    last if $current->[0] eq 'root';
+    return $current->[2]{$attr} eq $ns if exists $current->[2]{$attr};
+
+    $current = $current->[3];
+  }
+
+  # Failing to match yields true if searching for no namespace, false otherwise
+  return !length $ns;
+}
+
+sub _pc {
+  my ($class, $args, $current) = @_;
+
+  # ":checked"
+  return exists $current->[2]{checked} || exists $current->[2]{selected}
+    if $class eq 'checked';
+
+  # ":not"
+  return !_match($args, $current, $current) if $class eq 'not';
+
+  # ":matches"
+  return !!_match($args, $current, $current) if $class eq 'matches';
+
+  # ":empty"
+  return !grep { !_empty($_) } @$current[4 .. $#$current] if $class eq 'empty';
+
+  # ":root"
+  return $current->[3] && $current->[3][0] eq 'root' if $class eq 'root';
+
+  # ":link" and ":visited"
+  if ($class eq 'link' || $class eq 'visited') {
+    return undef unless $current->[0] eq 'tag' && exists $current->[2]{href};
+    return !!grep { $current->[1] eq $_ } qw(a area link);
+  }
+
+  # ":only-child" or ":only-of-type"
+  if ($class eq 'only-child' || $class eq 'only-of-type') {
+    my $type = $class eq 'only-of-type' ? $current->[1] : undef;
+    $_ ne $current and return undef for @{_siblings($current, $type)};
+    return 1;
+  }
+
+  # ":nth-child", ":nth-last-child", ":nth-of-type" or ":nth-last-of-type"
+  if (ref $args) {
+    my $type = $class eq 'nth-of-type'
+      || $class eq 'nth-last-of-type' ? $current->[1] : undef;
+    my @siblings = @{_siblings($current, $type)};
+    @siblings = reverse @siblings
+      if $class eq 'nth-last-child' || $class eq 'nth-last-of-type';
+
+    for my $i (0 .. $#siblings) {
+      next if (my $result = $args->[0] * $i + $args->[1]) < 1;
+      return undef unless my $sibling = $siblings[$result - 1];
+      return 1 if $sibling eq $current;
+    }
+  }
+
+  # Everything else
+  return undef;
+}
+
+sub _select {
+  my ($one, $tree, $group) = @_;
+
+  my @results;
+  my @queue = @$tree[($tree->[0] eq 'root' ? 1 : 4) .. $#$tree];
+  while (my $current = shift @queue) {
+    next unless $current->[0] eq 'tag';
+
+    unshift @queue, @$current[4 .. $#$current];
+    next unless _match($group, $current, $tree);
+    $one ? return $current : push @results, $current;
+  }
+
+  return $one ? undef : \@results;
+}
+
+sub _selector {
+  my ($selector, $current) = @_;
+
+  for my $s (@$selector) {
+    my $type = $s->[0];
+
+    # Tag
+    if ($type eq 'tag') {
+      return undef if defined $s->[1] && $current->[1] !~ $s->[1];
+      return undef if defined $s->[2] && !_namespace($s->[2], $current);
+    }
+
+    # Attribute
+    elsif ($type eq 'attr') { return undef unless _attr(@$s[1, 2], $current) }
+
+    # Pseudo-class
+    elsif ($type eq 'pc') { return undef unless _pc(@$s[1, 2], $current) }
+
+    # Invalid selector
+    else { return undef }
+  }
+
+  return 1;
+}
+
+sub _sibling {
+  my ($selectors, $current, $tree, $immediate, $pos) = @_;
+
+  my $found;
+  for my $sibling (@{_siblings($current)}) {
+    return $found if $sibling eq $current;
+
+    # "+" (immediately preceding sibling)
+    if ($immediate) { $found = _combinator($selectors, $sibling, $tree, $pos) }
+
+    # "~" (preceding sibling)
+    else { return 1 if _combinator($selectors, $sibling, $tree, $pos) }
+  }
+
+  return undef;
+}
+
+sub _siblings {
+  my ($current, $type) = @_;
+
+  my $parent = $current->[3];
+  my @siblings = grep { $_->[0] eq 'tag' }
+    @$parent[($parent->[0] eq 'root' ? 1 : 4) .. $#$parent];
+  @siblings = grep { $type eq $_->[1] } @siblings if defined $type;
+
+  return \@siblings;
+}
+
+sub _unescape {
+  return undef unless defined(my $value = shift);
+
+  # Remove escaped newlines
+  $value =~ s/\\\n//g;
+
+  # Unescape Unicode characters
+  $value =~ s/\\([0-9a-fA-F]{1,6})\s?/pack 'U', hex $1/ge;
+
+  # Remove backslash
+  $value =~ s/\\//g;
+
+  return $value;
+}
+
+sub _value {
+  my ($op, $value, $insensitive) = @_;
+  return undef unless defined $value;
+  $value = ($insensitive ? '(?i)' : '') . quotemeta _unescape($value);
+
+  # "~=" (word)
+  return qr/(?:^|\s+)$value(?:\s+|$)/ if $op eq '~';
+
+  # "|=" (hyphen-separated)
+  return qr/^$value(?:-|$)/ if $op eq '|';
+
+  # "*=" (contains)
+  return qr/$value/ if $op eq '*';
+
+  # "^=" (begins with)
+  return qr/^$value/ if $op eq '^';
+
+  # "$=" (ends with)
+  return qr/$value$/ if $op eq '$';
+
+  # Everything else
+  return qr/^$value$/;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::DOM::CSS - CSS selector engine
+
+=head1 SYNOPSIS
+
+  use Mojo::DOM::CSS;
+
+  # Select elements from DOM tree
+  my $css = Mojo::DOM::CSS->new(tree => $tree);
+  my $elements = $css->select('h1, h2, h3');
+
+=head1 DESCRIPTION
+
+L<Mojo::DOM::CSS> is the CSS selector engine used by L<Mojo::DOM>, based on the
+L<HTML Living Standard|https://html.spec.whatwg.org> and
+L<Selectors Level 3|http://www.w3.org/TR/css3-selectors/>.
+
+=head1 SELECTORS
+
+All CSS selectors that make sense for a standalone parser are supported.
+
+=head2 *
+
+Any element.
+
+  my $all = $css->select('*');
+
+=head2 E
+
+An element of type C<E>.
+
+  my $title = $css->select('title');
+
+=head2 E[foo]
+
+An C<E> element with a C<foo> attribute.
+
+  my $links = $css->select('a[href]');
+
+=head2 E[foo="bar"]
+
+An C<E> element whose C<foo> attribute value is exactly equal to C<bar>.
+
+  my $case_sensitive = $css->select('input[type="hidden"]');
+  my $case_sensitive = $css->select('input[type=hidden]');
+
+=head2 E[foo="bar" i]
+
+An C<E> element whose C<foo> attribute value is exactly equal to any
+(ASCII-range) case-permutation of C<bar>. Note that this selector is
+EXPERIMENTAL and might change without warning!
+
+  my $case_insensitive = $css->select('input[type="hidden" i]');
+  my $case_insensitive = $css->select('input[type=hidden i]');
+  my $case_insensitive = $css->select('input[class~="foo" i]');
+
+This selector is part of
+L<Selectors Level 4|http://dev.w3.org/csswg/selectors-4>, which is still a work
+in progress.
+
+=head2 E[foo~="bar"]
+
+An C<E> element whose C<foo> attribute value is a list of whitespace-separated
+values, one of which is exactly equal to C<bar>.
+
+  my $foo = $css->select('input[class~="foo"]');
+  my $foo = $css->select('input[class~=foo]');
+
+=head2 E[foo^="bar"]
+
+An C<E> element whose C<foo> attribute value begins exactly with the string
+C<bar>.
+
+  my $begins_with = $css->select('input[name^="f"]');
+  my $begins_with = $css->select('input[name^=f]');
+
+=head2 E[foo$="bar"]
+
+An C<E> element whose C<foo> attribute value ends exactly with the string
+C<bar>.
+
+  my $ends_with = $css->select('input[name$="o"]');
+  my $ends_with = $css->select('input[name$=o]');
+
+=head2 E[foo*="bar"]
+
+An C<E> element whose C<foo> attribute value contains the substring C<bar>.
+
+  my $contains = $css->select('input[name*="fo"]');
+  my $contains = $css->select('input[name*=fo]');
+
+=head2 E[foo|="en"]
+
+An C<E> element whose C<foo> attribute has a hyphen-separated list of values
+beginning (from the left) with C<en>.
+
+  my $english = $css->select('link[hreflang|=en]');
+
+=head2 E:root
+
+An C<E> element, root of the document.
+
+  my $root = $css->select(':root');
+
+=head2 E:nth-child(n)
+
+An C<E> element, the C<n-th> child of its parent.
+
+  my $third = $css->select('div:nth-child(3)');
+  my $odd   = $css->select('div:nth-child(odd)');
+  my $even  = $css->select('div:nth-child(even)');
+  my $top3  = $css->select('div:nth-child(-n+3)');
+
+=head2 E:nth-last-child(n)
+
+An C<E> element, the C<n-th> child of its parent, counting from the last one.
+
+  my $third    = $css->select('div:nth-last-child(3)');
+  my $odd      = $css->select('div:nth-last-child(odd)');
+  my $even     = $css->select('div:nth-last-child(even)');
+  my $bottom3  = $css->select('div:nth-last-child(-n+3)');
+
+=head2 E:nth-of-type(n)
+
+An C<E> element, the C<n-th> sibling of its type.
+
+  my $third = $css->select('div:nth-of-type(3)');
+  my $odd   = $css->select('div:nth-of-type(odd)');
+  my $even  = $css->select('div:nth-of-type(even)');
+  my $top3  = $css->select('div:nth-of-type(-n+3)');
+
+=head2 E:nth-last-of-type(n)
+
+An C<E> element, the C<n-th> sibling of its type, counting from the last one.
+
+  my $third    = $css->select('div:nth-last-of-type(3)');
+  my $odd      = $css->select('div:nth-last-of-type(odd)');
+  my $even     = $css->select('div:nth-last-of-type(even)');
+  my $bottom3  = $css->select('div:nth-last-of-type(-n+3)');
+
+=head2 E:first-child
+
+An C<E> element, first child of its parent.
+
+  my $first = $css->select('div p:first-child');
+
+=head2 E:last-child
+
+An C<E> element, last child of its parent.
+
+  my $last = $css->select('div p:last-child');
+
+=head2 E:first-of-type
+
+An C<E> element, first sibling of its type.
+
+  my $first = $css->select('div p:first-of-type');
+
+=head2 E:last-of-type
+
+An C<E> element, last sibling of its type.
+
+  my $last = $css->select('div p:last-of-type');
+
+=head2 E:only-child
+
+An C<E> element, only child of its parent.
+
+  my $lonely = $css->select('div p:only-child');
+
+=head2 E:only-of-type
+
+An C<E> element, only sibling of its type.
+
+  my $lonely = $css->select('div p:only-of-type');
+
+=head2 E:empty
+
+An C<E> element that has no children (including text nodes).
+
+  my $empty = $css->select(':empty');
+
+=head2 E:link
+
+An C<E> element being the source anchor of a hyperlink of which the target is
+not yet visited (C<:link>) or already visited (C<:visited>). Note that
+L<Mojo::DOM::CSS> is not stateful, therefore C<:link> and C<:visited> yield
+exactly the same results.
+
+  my $links = $css->select(':link');
+  my $links = $css->select(':visited');
+
+=head2 E:visited
+
+Alias for L</"E:link">.
+
+=head2 E:checked
+
+A user interface element C<E> which is checked (for instance a radio-button or
+checkbox).
+
+  my $input = $css->select(':checked');
+
+=head2 E.warning
+
+An C<E> element whose class is "warning".
+
+  my $warning = $css->select('div.warning');
+
+=head2 E#myid
+
+An C<E> element with C<ID> equal to "myid".
+
+  my $foo = $css->select('div#foo');
+
+=head2 E:not(s1, s2)
+
+An C<E> element that does not match either compound selector C<s1> or compound
+selector C<s2>. Note that support for compound selectors is EXPERIMENTAL and
+might change without warning!
+
+  my $others = $css->select('div p:not(:first-child, :last-child)');
+
+Support for compound selectors was added as part of
+L<Selectors Level 4|http://dev.w3.org/csswg/selectors-4>, which is still a work
+in progress.
+
+=head2 E:matches(s1, s2)
+
+An C<E> element that matches compound selector C<s1> and/or compound selector
+C<s2>. Note that this selector is EXPERIMENTAL and might change without warning!
+
+  my $headers = $css->select(':matches(section, article, aside, nav) h1');
+
+This selector is part of
+L<Selectors Level 4|http://dev.w3.org/csswg/selectors-4>, which is still a work
+in progress.
+
+=head2 A|E
+
+An C<E> element that belongs to the namespace alias C<A> from
+L<CSS Namespaces Module Level 3|https://www.w3.org/TR/css-namespaces-3/>.
+Key/value pairs passed to selector methods are used to declare namespace
+aliases.
+
+  my $elem = $css->select('lq|elem', lq => 'http://example.com/q-markup');
+
+Using an empty alias searches for an element that belongs to no namespace.
+
+  my $div = $c->select('|div');
+
+=head2 E F
+
+An C<F> element descendant of an C<E> element.
+
+  my $headlines = $css->select('div h1');
+
+=head2 E E<gt> F
+
+An C<F> element child of an C<E> element.
+
+  my $headlines = $css->select('html > body > div > h1');
+
+=head2 E + F
+
+An C<F> element immediately preceded by an C<E> element.
+
+  my $second = $css->select('h1 + h2');
+
+=head2 E ~ F
+
+An C<F> element preceded by an C<E> element.
+
+  my $second = $css->select('h1 ~ h2');
+
+=head2 E, F, G
+
+Elements of type C<E>, C<F> and C<G>.
+
+  my $headlines = $css->select('h1, h2, h3');
+
+=head2 E[foo=bar][bar=baz]
+
+An C<E> element whose attributes match all following attribute selectors.
+
+  my $links = $css->select('a[foo^=b][foo$=ar]');
+
+=head1 ATTRIBUTES
+
+L<Mojo::DOM::CSS> implements the following attributes.
+
+=head2 tree
+
+  my $tree = $css->tree;
+  $css     = $css->tree(['root']);
+
+Document Object Model. Note that this structure should only be used very
+carefully since it is very dynamic.
+
+=head1 METHODS
+
+L<Mojo::DOM::CSS> inherits all methods from L<Mojo::Base> and implements the
+following new ones.
+
+=head2 matches
+
+  my $bool = $css->matches('head > title');
+  my $bool = $css->matches('svg|line', svg => 'http://www.w3.org/2000/svg');
+
+Check if first node in L</"tree"> matches the CSS selector. Trailing key/value
+pairs can be used to declare xml namespace aliases.
+
+=head2 select
+
+  my $results = $css->select('head > title');
+  my $results = $css->select('svg|line', svg => 'http://www.w3.org/2000/svg');
+
+Run CSS selector against L</"tree">. Trailing key/value pairs can be used to
+declare xml namespace aliases.
+
+=head2 select_one
+
+  my $result = $css->select_one('head > title');
+  my $result =
+    $css->select_one('svg|line', svg => 'http://www.w3.org/2000/svg');
+
+Run CSS selector against L</"tree"> and stop as soon as the first node matched.
+Trailing key/value pairs can be used to declare xml namespace aliases.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/DOM/HTML.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/DOM/HTML.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/DOM/HTML.pm	(revision 32205)
@@ -0,0 +1,382 @@
+package Mojo::DOM::HTML;
+use Mojo::Base -base;
+
+use Exporter 'import';
+use Mojo::Util qw(html_attr_unescape html_unescape xml_escape);
+use Scalar::Util 'weaken';
+
+our @EXPORT_OK = ('tag_to_html');
+
+has tree => sub { ['root'] };
+has 'xml';
+
+my $ATTR_RE = qr/
+  ([^<>=\s\/]+|\/)                     # Key
+  (?:
+    \s*=\s*
+    (?s:(["'])(.*?)\g{-2}|([^>\s]*))   # Value
+  )?
+  \s*
+/x;
+my $TOKEN_RE = qr/
+  ([^<]+)?                                            # Text
+  (?:
+    <(?:
+      !(?:
+        DOCTYPE(
+        \s+\w+                                        # Doctype
+        (?:(?:\s+\w+)?(?:\s+(?:"[^"]*"|'[^']*'))+)?   # External ID
+        (?:\s+\[.+?\])?                               # Int Subset
+        \s*)
+      |
+        --(.*?)--\s*                                  # Comment
+      |
+        \[CDATA\[(.*?)\]\]                            # CDATA
+      )
+    |
+      \?(.*?)\?                                       # Processing Instruction
+    |
+      \s*([^<>\s]+\s*(?:(?:$ATTR_RE){0,32766})*+)     # Tag
+    )>
+  |
+    (<)                                               # Runaway "<"
+  )??
+/xis;
+
+# HTML elements that only contain raw text
+my %RAW = map { $_ => 1 } qw(script style);
+
+# HTML elements that only contain raw text and entities
+my %RCDATA = map { $_ => 1 } qw(title textarea);
+
+# HTML elements with optional end tags
+my %END = (body => 'head', optgroup => 'optgroup', option => 'option');
+
+# HTML elements that break paragraphs
+map { $END{$_} = 'p' } (
+  qw(address article aside blockquote details div dl fieldset figcaption),
+  qw(figure footer form h1 h2 h3 h4 h5 h6 header hgroup hr main menu nav ol p),
+  qw(pre section table ul)
+);
+
+# HTML table elements with optional end tags
+my %TABLE = map { $_ => 1 } qw(colgroup tbody td tfoot th thead tr);
+
+# HTML elements with optional end tags and scoping rules
+my %CLOSE
+  = (li => [{li => 1}, {ul => 1, ol => 1}], tr => [{tr => 1}, {table => 1}]);
+$CLOSE{$_} = [\%TABLE, {table => 1}] for qw(colgroup tbody tfoot thead);
+$CLOSE{$_} = [{dd => 1, dt => 1}, {dl    => 1}] for qw(dd dt);
+$CLOSE{$_} = [{rp => 1, rt => 1}, {ruby  => 1}] for qw(rp rt);
+$CLOSE{$_} = [{th => 1, td => 1}, {table => 1}] for qw(td th);
+
+# HTML elements without end tags
+my %EMPTY = map { $_ => 1 } (
+  qw(area base br col embed hr img input keygen link menuitem meta param),
+  qw(source track wbr)
+);
+
+# HTML elements categorized as phrasing content (and obsolete inline elements)
+my @PHRASING = (
+  qw(a abbr area audio b bdi bdo br button canvas cite code data datalist),
+  qw(del dfn em embed i iframe img input ins kbd keygen label link map mark),
+  qw(math meta meter noscript object output picture progress q ruby s samp),
+  qw(script select slot small span strong sub sup svg template textarea time u),
+  qw(var video wbr)
+);
+my @OBSOLETE = qw(acronym applet basefont big font strike tt);
+my %PHRASING = map { $_ => 1 } @OBSOLETE, @PHRASING;
+
+# HTML elements that don't get their self-closing flag acknowledged
+my %BLOCK = map { $_ => 1 } (
+  qw(a address applet article aside b big blockquote body button caption),
+  qw(center code col colgroup dd details dialog dir div dl dt em fieldset),
+  qw(figcaption figure font footer form frameset h1 h2 h3 h4 h5 h6 head),
+  qw(header hgroup html i iframe li listing main marquee menu nav nobr),
+  qw(noembed noframes noscript object ol optgroup option p plaintext pre rp),
+  qw(rt s script section select small strike strong style summary table),
+  qw(tbody td template textarea tfoot th thead title tr tt u ul xmp)
+);
+
+sub parse {
+  my ($self, $html) = (shift, "$_[0]");
+
+  my $xml = $self->xml;
+  my $current = my $tree = ['root'];
+  while ($html =~ /\G$TOKEN_RE/gcso) {
+    my ($text, $doctype, $comment, $cdata, $pi, $tag, $runaway)
+      = ($1, $2, $3, $4, $5, $6, $11);
+
+    # Text (and runaway "<")
+    $text .= '<' if defined $runaway;
+    _node($current, 'text', html_unescape $text) if defined $text;
+
+    # Tag
+    if (defined $tag) {
+
+      # End
+      if ($tag =~ /^\/\s*(\S+)/) { _end($xml ? $1 : lc $1, $xml, \$current) }
+
+      # Start
+      elsif ($tag =~ m!^([^\s/]+)([\s\S]*)!) {
+        my ($start, $attr) = ($xml ? $1 : lc $1, $2);
+
+        # Attributes
+        my (%attrs, $closing);
+        while ($attr =~ /$ATTR_RE/go) {
+          my ($key, $value) = ($xml ? $1 : lc $1, $3 // $4);
+
+          # Empty tag
+          ++$closing and next if $key eq '/';
+
+          $attrs{$key} = defined $value ? html_attr_unescape $value : $value;
+        }
+
+        # "image" is an alias for "img"
+        $start = 'img' if !$xml && $start eq 'image';
+        _start($start, \%attrs, $xml, \$current);
+
+        # Element without end tag (self-closing)
+        _end($start, $xml, \$current)
+          if !$xml && $EMPTY{$start} || ($xml || !$BLOCK{$start}) && $closing;
+
+        # Raw text elements
+        next if $xml || !$RAW{$start} && !$RCDATA{$start};
+        next unless $html =~ m!\G(.*?)<\s*/\s*\Q$start\E\s*>!gcsi;
+        _node($current, 'raw', $RCDATA{$start} ? html_unescape $1 : $1);
+        _end($start, 0, \$current);
+      }
+    }
+
+    # DOCTYPE
+    elsif (defined $doctype) { _node($current, 'doctype', $doctype) }
+
+    # Comment
+    elsif (defined $comment) { _node($current, 'comment', $comment) }
+
+    # CDATA
+    elsif (defined $cdata) { _node($current, 'cdata', $cdata) }
+
+    # Processing instruction (try to detect XML)
+    elsif (defined $pi) {
+      $self->xml($xml = 1) if !exists $self->{xml} && $pi =~ /xml/i;
+      _node($current, 'pi', $pi);
+    }
+  }
+
+  return $self->tree($tree);
+}
+
+sub render { _render($_[0]->tree, $_[0]->xml) }
+
+sub tag { shift->tree(['root', _tag(@_)]) }
+
+sub tag_to_html { _render(_tag(@_), undef) }
+
+sub _end {
+  my ($end, $xml, $current) = @_;
+
+  # Search stack for start tag
+  my $next = $$current;
+  do {
+
+    # Ignore useless end tag
+    return if $next->[0] eq 'root';
+
+    # Right tag
+    return $$current = $next->[3] if $next->[1] eq $end;
+
+    # Phrasing content can only cross phrasing content
+    return if !$xml && $PHRASING{$end} && !$PHRASING{$next->[1]};
+
+  } while $next = $next->[3];
+}
+
+sub _node {
+  my ($current, $type, $content) = @_;
+  push @$current, my $new = [$type, $content, $current];
+  weaken $new->[2];
+}
+
+sub _render {
+  my ($tree, $xml) = @_;
+
+  # Tag
+  my $type = $tree->[0];
+  if ($type eq 'tag') {
+
+    # Start tag
+    my $tag    = $tree->[1];
+    my $result = "<$tag";
+
+    # Attributes
+    for my $key (sort keys %{$tree->[2]}) {
+      my $value = $tree->[2]{$key};
+      $result .= $xml ? qq{ $key="$key"} : " $key" and next
+        unless defined $value;
+      $result .= qq{ $key="} . xml_escape($value) . '"';
+    }
+
+    # No children
+    return $xml ? "$result />" : $EMPTY{$tag} ? "$result>" : "$result></$tag>"
+      unless $tree->[4];
+
+    # Children
+    no warnings 'recursion';
+    $result .= '>' . join '', map { _render($_, $xml) } @$tree[4 .. $#$tree];
+
+    # End tag
+    return "$result</$tag>";
+  }
+
+  # Text (escaped)
+  return xml_escape $tree->[1] if $type eq 'text';
+
+  # Raw text
+  return $tree->[1] if $type eq 'raw';
+
+  # Root
+  return join '', map { _render($_, $xml) } @$tree[1 .. $#$tree]
+    if $type eq 'root';
+
+  # DOCTYPE
+  return '<!DOCTYPE' . $tree->[1] . '>' if $type eq 'doctype';
+
+  # Comment
+  return '<!--' . $tree->[1] . '-->' if $type eq 'comment';
+
+  # CDATA
+  return '<![CDATA[' . $tree->[1] . ']]>' if $type eq 'cdata';
+
+  # Processing instruction
+  return '<?' . $tree->[1] . '?>' if $type eq 'pi';
+
+  # Everything else
+  return '';
+}
+
+sub _start {
+  my ($start, $attrs, $xml, $current) = @_;
+
+  # Autoclose optional HTML elements
+  if (!$xml && $$current->[0] ne 'root') {
+    if (my $end = $END{$start}) { _end($end, 0, $current) }
+
+    elsif (my $close = $CLOSE{$start}) {
+      my ($allowed, $scope) = @$close;
+
+      # Close allowed parent elements in scope
+      my $parent = $$current;
+      while ($parent->[0] ne 'root' && !$scope->{$parent->[1]}) {
+        _end($parent->[1], 0, $current) if $allowed->{$parent->[1]};
+        $parent = $parent->[3];
+      }
+    }
+  }
+
+  # New tag
+  push @$$current, my $new = ['tag', $start, $attrs, $$current];
+  weaken $new->[3];
+  $$current = $new;
+}
+
+sub _tag {
+  my $tree = ['tag', shift, undef, undef];
+
+  # Content
+  push @$tree, ref $_[-1] eq 'CODE' ? ['raw', pop->()] : ['text', pop]
+    if @_ % 2;
+
+  # Attributes
+  my $attrs = $tree->[2] = {@_};
+  return $tree unless exists $attrs->{data} && ref $attrs->{data} eq 'HASH';
+  my $data = delete $attrs->{data};
+  @$attrs{map { y/_/-/; lc "data-$_" } keys %$data} = values %$data;
+  return $tree;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::DOM::HTML - HTML/XML engine
+
+=head1 SYNOPSIS
+
+  use Mojo::DOM::HTML;
+
+  # Turn HTML into DOM tree
+  my $html = Mojo::DOM::HTML->new;
+  $html->parse('<div><p id="a">Test</p><p id="b">123</p></div>');
+  my $tree = $html->tree;
+
+=head1 DESCRIPTION
+
+L<Mojo::DOM::HTML> is the HTML/XML engine used by L<Mojo::DOM>, based on the
+L<HTML Living Standard|https://html.spec.whatwg.org> and the
+L<Extensible Markup Language (XML) 1.0|http://www.w3.org/TR/xml/>.
+
+=head1 FUNCTIONS
+
+L<Mojo::DOM::HTML> implements the following functions, which can be imported
+individually.
+
+=head2 tag_to_html
+
+  my $str = tag_to_html 'div', id => 'foo', 'safe content';
+
+Generate HTML/XML tag and render it right away. This is a significantly faster
+alternative to L</"tag"> for template systems that have to generate a lot of
+tags. Note that this function is EXPERIMENTAL and might change without warning!
+
+=head1 ATTRIBUTES
+
+L<Mojo::DOM::HTML> implements the following attributes.
+
+=head2 tree
+
+  my $tree = $html->tree;
+  $html    = $html->tree(['root']);
+
+Document Object Model. Note that this structure should only be used very
+carefully since it is very dynamic.
+
+=head2 xml
+
+  my $bool = $html->xml;
+  $html    = $html->xml($bool);
+
+Disable HTML semantics in parser and activate case-sensitivity, defaults to
+auto-detection based on XML declarations.
+
+=head1 METHODS
+
+L<Mojo::DOM::HTML> inherits all methods from L<Mojo::Base> and implements the
+following new ones.
+
+=head2 parse
+
+  $html = $html->parse('<foo bar="baz">I â¥ Mojolicious!</foo>');
+
+Parse HTML/XML fragment.
+
+=head2 render
+
+  my $str = $html->render;
+
+Render DOM to HTML/XML.
+
+=head2 tag
+
+  $html = $html->tag('div', id => 'foo', 'safe content');
+
+Generate HTML/XML tag. Note that this method is EXPERIMENTAL and might change
+without warning!
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/Date.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/Date.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/Date.pm	(revision 32205)
@@ -0,0 +1,188 @@
+package Mojo::Date;
+use Mojo::Base -base;
+use overload bool => sub {1}, '""' => sub { shift->to_string }, fallback => 1;
+
+use Time::Local 'timegm';
+
+has epoch => sub {time};
+
+my $RFC3339_RE = qr/
+  ^(\d+)-(\d+)-(\d+)\D+(\d+):(\d+):(\d+(?:\.\d+)?)   # Date and time
+  (?:Z|([+-])(\d+):(\d+))?$                          # Offset
+/xi;
+
+my @DAYS   = qw(Sun Mon Tue Wed Thu Fri Sat);
+my @MONTHS = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
+my %MONTHS;
+@MONTHS{@MONTHS} = (0 .. 11);
+
+sub new { @_ > 1 ? shift->SUPER::new->parse(@_) : shift->SUPER::new }
+
+sub parse {
+  my ($self, $date) = @_;
+
+  # epoch (784111777)
+  return $self->epoch($date) if $date =~ /^\d+$|^\d+\.\d+$/;
+
+  # RFC 822/1123 (Sun, 06 Nov 1994 08:49:37 GMT)
+  # RFC 850/1036 (Sunday, 06-Nov-94 08:49:37 GMT)
+  my $offset = 0;
+  my ($day, $month, $year, $h, $m, $s);
+  if ($date =~ /^\w+\W+(\d+)\W+(\w+)\W+(\d+)\W+(\d+):(\d+):(\d+)\W*\w+$/) {
+    ($day, $month, $year, $h, $m, $s) = ($1, $MONTHS{$2}, $3, $4, $5, $6);
+  }
+
+  # RFC 3339 (1994-11-06T08:49:37Z)
+  elsif ($date =~ $RFC3339_RE) {
+    ($year, $month, $day, $h, $m, $s) = ($1, $2 - 1, $3, $4, $5, $6);
+    $offset = (($8 * 3600) + ($9 * 60)) * ($7 eq '+' ? -1 : 1) if $7;
+  }
+
+  # ANSI C asctime() (Sun Nov  6 08:49:37 1994)
+  elsif ($date =~ /^\w+\s+(\w+)\s+(\d+)\s+(\d+):(\d+):(\d+)\s+(\d+)$/) {
+    ($month, $day, $h, $m, $s, $year) = ($MONTHS{$1}, $2, $3, $4, $5, $6);
+  }
+
+  # Invalid
+  else { return $self->epoch(undef) }
+
+  my $epoch = eval { timegm $s, $m, $h, $day, $month, $year };
+  return $self->epoch(
+    (defined $epoch && ($epoch += $offset) >= 0) ? $epoch : undef);
+}
+
+sub to_datetime {
+
+  # RFC 3339 (1994-11-06T08:49:37Z)
+  my ($s, $m, $h, $day, $month, $year) = gmtime(my $epoch = shift->epoch);
+  my $str = sprintf '%04d-%02d-%02dT%02d:%02d:%02d', $year + 1900, $month + 1,
+    $day, $h, $m, $s;
+  return $str . ($epoch =~ /(\.\d+)$/ ? $1 : '') . 'Z';
+}
+
+sub to_string {
+
+  # RFC 7231 (Sun, 06 Nov 1994 08:49:37 GMT)
+  my ($s, $m, $h, $mday, $month, $year, $wday) = gmtime shift->epoch;
+  return sprintf '%s, %02d %s %04d %02d:%02d:%02d GMT', $DAYS[$wday], $mday,
+    $MONTHS[$month], $year + 1900, $h, $m, $s;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::Date - HTTP date
+
+=head1 SYNOPSIS
+
+  use Mojo::Date;
+
+  # Parse
+  my $date = Mojo::Date->new('Sun, 06 Nov 1994 08:49:37 GMT');
+  say $date->epoch;
+
+  # Build
+  my $date = Mojo::Date->new(time + 60);
+  say "$date";
+
+=head1 DESCRIPTION
+
+L<Mojo::Date> implements HTTP date and time functions, based on
+L<RFC 7230|http://tools.ietf.org/html/rfc7230>,
+L<RFC 7231|http://tools.ietf.org/html/rfc7231> and
+L<RFC 3339|http://tools.ietf.org/html/rfc3339>.
+
+=head1 ATTRIBUTES
+
+L<Mojo::Date> implements the following attributes.
+
+=head2 epoch
+
+  my $epoch = $date->epoch;
+  $date     = $date->epoch(784111777);
+
+Epoch seconds, defaults to the current time.
+
+=head1 METHODS
+
+L<Mojo::Date> inherits all methods from L<Mojo::Base> and implements the
+following new ones.
+
+=head2 new
+
+  my $date = Mojo::Date->new;
+  my $date = Mojo::Date->new('Sun Nov  6 08:49:37 1994');
+
+Construct a new L<Mojo::Date> object and L</"parse"> date if necessary.
+
+=head2 parse
+
+  $date = $date->parse('Sun Nov  6 08:49:37 1994');
+
+Parse date.
+
+  # Epoch
+  say Mojo::Date->new('784111777')->epoch;
+  say Mojo::Date->new('784111777.21')->epoch;
+
+  # RFC 822/1123
+  say Mojo::Date->new('Sun, 06 Nov 1994 08:49:37 GMT')->epoch;
+
+  # RFC 850/1036
+  say Mojo::Date->new('Sunday, 06-Nov-94 08:49:37 GMT')->epoch;
+
+  # Ansi C asctime()
+  say Mojo::Date->new('Sun Nov  6 08:49:37 1994')->epoch;
+
+  # RFC 3339
+  say Mojo::Date->new('1994-11-06T08:49:37Z')->epoch;
+  say Mojo::Date->new('1994-11-06T08:49:37')->epoch;
+  say Mojo::Date->new('1994-11-06T08:49:37.21Z')->epoch;
+  say Mojo::Date->new('1994-11-06T08:49:37+01:00')->epoch;
+  say Mojo::Date->new('1994-11-06T08:49:37-01:00')->epoch;
+
+=head2 to_datetime
+
+  my $str = $date->to_datetime;
+
+Render L<RFC 3339|http://tools.ietf.org/html/rfc3339> date and time.
+
+  # "1994-11-06T08:49:37Z"
+  Mojo::Date->new(784111777)->to_datetime;
+
+  # "1994-11-06T08:49:37.21Z"
+  Mojo::Date->new(784111777.21)->to_datetime;
+
+=head2 to_string
+
+  my $str = $date->to_string;
+
+Render date suitable for HTTP messages.
+
+  # "Sun, 06 Nov 1994 08:49:37 GMT"
+  Mojo::Date->new(784111777)->to_string;
+
+=head1 OPERATORS
+
+L<Mojo::Date> overloads the following operators.
+
+=head2 bool
+
+  my $bool = !!$date;
+
+Always true.
+
+=head2 stringify
+
+  my $str = "$date";
+
+Alias for L</"to_string">.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/EventEmitter.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/EventEmitter.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/EventEmitter.pm	(revision 32205)
@@ -0,0 +1,193 @@
+package Mojo::EventEmitter;
+use Mojo::Base -base;
+
+use Scalar::Util qw(blessed weaken);
+
+use constant DEBUG => $ENV{MOJO_EVENTEMITTER_DEBUG} || 0;
+
+sub catch { $_[0]->on(error => $_[1]) and return $_[0] }
+
+sub emit {
+  my ($self, $name) = (shift, shift);
+
+  if (my $s = $self->{events}{$name}) {
+    warn "-- Emit $name in @{[blessed $self]} (@{[scalar @$s]})\n" if DEBUG;
+    for my $cb (@$s) { $self->$cb(@_) }
+  }
+  else {
+    warn "-- Emit $name in @{[blessed $self]} (0)\n" if DEBUG;
+    die "@{[blessed $self]}: $_[0]" if $name eq 'error';
+  }
+
+  return $self;
+}
+
+sub has_subscribers { !!shift->{events}{shift()} }
+
+sub on { push @{$_[0]{events}{$_[1]}}, $_[2] and return $_[2] }
+
+sub once {
+  my ($self, $name, $cb) = @_;
+
+  weaken $self;
+  my $wrapper;
+  $wrapper = sub {
+    $self->unsubscribe($name => $wrapper);
+    $cb->(@_);
+  };
+  $self->on($name => $wrapper);
+  weaken $wrapper;
+
+  return $wrapper;
+}
+
+sub subscribers { shift->{events}{shift()} ||= [] }
+
+sub unsubscribe {
+  my ($self, $name, $cb) = @_;
+
+  # One
+  if ($cb) {
+    $self->{events}{$name} = [grep { $cb ne $_ } @{$self->{events}{$name}}];
+    delete $self->{events}{$name} unless @{$self->{events}{$name}};
+  }
+
+  # All
+  else { delete $self->{events}{$name} }
+
+  return $self;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::EventEmitter - Event emitter base class
+
+=head1 SYNOPSIS
+
+  package Cat;
+  use Mojo::Base 'Mojo::EventEmitter';
+
+  # Emit events
+  sub poke {
+    my $self = shift;
+    $self->emit(roar => 3);
+  }
+
+  package main;
+
+  # Subscribe to events
+  my $tiger = Cat->new;
+  $tiger->on(roar => sub {
+    my ($tiger, $times) = @_;
+    say 'RAWR!' for 1 .. $times;
+  });
+  $tiger->poke;
+
+=head1 DESCRIPTION
+
+L<Mojo::EventEmitter> is a simple base class for event emitting objects.
+
+=head1 EVENTS
+
+L<Mojo::EventEmitter> can emit the following events.
+
+=head2 error
+
+  $e->on(error => sub {
+    my ($e, $err) = @_;
+    ...
+  });
+
+This is a special event for errors, it will not be emitted directly by this
+class, but is fatal if unhandled. Subclasses may choose to emit it, but are not
+required to do so.
+
+  $e->on(error => sub {
+    my ($e, $err) = @_;
+    say "This looks bad: $err";
+  });
+
+=head1 METHODS
+
+L<Mojo::EventEmitter> inherits all methods from L<Mojo::Base> and implements
+the following new ones.
+
+=head2 catch
+
+  $e = $e->catch(sub {...});
+
+Subscribe to L</"error"> event.
+
+  # Longer version
+  $e->on(error => sub {...});
+
+=head2 emit
+
+  $e = $e->emit('foo');
+  $e = $e->emit('foo', 123);
+
+Emit event.
+
+=head2 has_subscribers
+
+  my $bool = $e->has_subscribers('foo');
+
+Check if event has subscribers.
+
+=head2 on
+
+  my $cb = $e->on(foo => sub {...});
+
+Subscribe to event.
+
+  $e->on(foo => sub {
+    my ($e, @args) = @_;
+    ...
+  });
+
+=head2 once
+
+  my $cb = $e->once(foo => sub {...});
+
+Subscribe to event and unsubscribe again after it has been emitted once.
+
+  $e->once(foo => sub {
+    my ($e, @args) = @_;
+    ...
+  });
+
+=head2 subscribers
+
+  my $subscribers = $e->subscribers('foo');
+
+All subscribers for event.
+
+  # Unsubscribe last subscriber
+  $e->unsubscribe(foo => $e->subscribers('foo')->[-1]);
+
+  # Change order of subscribers
+  @{$e->subscribers('foo')} = reverse @{$e->subscribers('foo')};
+
+=head2 unsubscribe
+
+  $e = $e->unsubscribe('foo');
+  $e = $e->unsubscribe(foo => $cb);
+
+Unsubscribe from event.
+
+=head1 DEBUGGING
+
+You can set the C<MOJO_EVENTEMITTER_DEBUG> environment variable to get some
+advanced diagnostics information printed to C<STDERR>.
+
+  MOJO_EVENTEMITTER_DEBUG=1
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/Exception.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/Exception.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/Exception.pm	(revision 32205)
@@ -0,0 +1,240 @@
+package Mojo::Exception;
+use Mojo::Base -base;
+use overload bool => sub {1}, '""' => sub { shift->to_string }, fallback => 1;
+
+use Mojo::Util 'decode';
+
+has [qw(frames line lines_after lines_before)] => sub { [] };
+has message => 'Exception!';
+has 'verbose';
+
+sub inspect {
+  my ($self, @sources) = @_;
+
+  # Extract file and line from message
+  my @files;
+  my $msg = $self->lines_before([])->line([])->lines_after([])->message;
+  while ($msg =~ /at\s+(.+?)\s+line\s+(\d+)/g) { unshift @files, [$1, $2] }
+
+  # Extract file and line from stack trace
+  if (my $zero = $self->frames->[0]) { push @files, [$zero->[1], $zero->[2]] }
+
+  # Search for context in files
+  for my $file (@files) {
+    next unless -r $file->[0] && open my $handle, '<', $file->[0];
+    $self->_context($file->[1], [[<$handle>]]);
+    return $self;
+  }
+
+  # Search for context in sources
+  $self->_context($files[-1][1], [map { [split "\n"] } @sources]) if @sources;
+
+  return $self;
+}
+
+sub new { @_ > 1 ? shift->SUPER::new(message => shift) : shift->SUPER::new }
+
+sub to_string {
+  my $self = shift;
+
+  my $str = $self->message;
+  return $str unless $self->verbose;
+
+  $str .= "\n" unless $str =~ /\n$/;
+  $str .= $_->[0] . ': ' . $_->[1] . "\n" for @{$self->lines_before};
+  $str .= $self->line->[0] . ': ' . $self->line->[1] . "\n" if $self->line->[0];
+  $str .= $_->[0] . ': ' . $_->[1] . "\n" for @{$self->lines_after};
+
+  return $str;
+}
+
+sub throw { CORE::die shift->new(shift)->trace(2)->inspect }
+
+sub trace {
+  my ($self, $start) = (shift, shift // 1);
+  my @frames;
+  while (my @trace = caller($start++)) { push @frames, \@trace }
+  return $self->frames(\@frames);
+}
+
+sub _append {
+  my ($stack, $line) = @_;
+  $line = decode('UTF-8', $line) // $line;
+  chomp $line;
+  push @$stack, $line;
+}
+
+sub _context {
+  my ($self, $num, $sources) = @_;
+
+  # Line
+  return unless defined $sources->[0][$num - 1];
+  $self->line([$num]);
+  _append($self->line, $_->[$num - 1]) for @$sources;
+
+  # Before
+  for my $i (2 .. 6) {
+    last if ((my $previous = $num - $i) < 0);
+    unshift @{$self->lines_before}, [$previous + 1];
+    _append($self->lines_before->[0], $_->[$previous]) for @$sources;
+  }
+
+  # After
+  for my $i (0 .. 4) {
+    next if ((my $next = $num + $i) < 0);
+    next unless defined $sources->[0][$next];
+    push @{$self->lines_after}, [$next + 1];
+    _append($self->lines_after->[-1], $_->[$next]) for @$sources;
+  }
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::Exception - Exceptions with context
+
+=head1 SYNOPSIS
+
+  use Mojo::Exception;
+
+  # Throw exception and show stack trace
+  eval { Mojo::Exception->throw('Something went wrong!') };
+  say "$_->[1]:$_->[2]" for @{$@->frames};
+
+  # Customize exception
+  eval {
+    my $e = Mojo::Exception->new('Died at test.pl line 3.');
+    die $e->trace(2)->inspect->verbose(1);
+  };
+  say $@;
+
+=head1 DESCRIPTION
+
+L<Mojo::Exception> is a container for exceptions with context information.
+
+=head1 ATTRIBUTES
+
+L<Mojo::Exception> implements the following attributes.
+
+=head2 frames
+
+  my $frames = $e->frames;
+  $e         = $e->frames([$frame1, $frame2]);
+
+Stack trace if available.
+
+  # Extract information from the last frame
+  my ($package, $filename, $line, $subroutine, $hasargs, $wantarray, $evaltext,
+      $is_require, $hints, $bitmask, $hinthash) = @{$e->frames->[-1]};
+
+=head2 line
+
+  my $line = $e->line;
+  $e       = $e->line([3, 'die;']);
+
+The line where the exception occurred if available.
+
+=head2 lines_after
+
+  my $lines = $e->lines_after;
+  $e        = $e->lines_after([[4, 'say $foo;'], [5, 'say $bar;']]);
+
+Lines after the line where the exception occurred if available.
+
+=head2 lines_before
+
+  my $lines = $e->lines_before;
+  $e        = $e->lines_before([[1, 'my $foo = 23;'], [2, 'my $bar = 24;']]);
+
+Lines before the line where the exception occurred if available.
+
+=head2 message
+
+  my $msg = $e->message;
+  $e      = $e->message('Died at test.pl line 3.');
+
+Exception message, defaults to C<Exception!>.
+
+=head2 verbose
+
+  my $bool = $e->verbose;
+  $e       = $e->verbose($bool);
+
+Enable context information for L</"to_string">.
+
+=head1 METHODS
+
+L<Mojo::Exception> inherits all methods from L<Mojo::Base> and implements the
+following new ones.
+
+=head2 inspect
+
+  $e = $e->inspect;
+  $e = $e->inspect($source1, $source2);
+
+Inspect L</"message">, L</"frames"> and optional additional sources to fill
+L</"lines_before">, L</"line"> and L</"lines_after"> with context information.
+
+=head2 new
+
+  my $e = Mojo::Exception->new;
+  my $e = Mojo::Exception->new('Died at test.pl line 3.');
+
+Construct a new L<Mojo::Exception> object and assign L</"message"> if necessary.
+
+=head2 to_string
+
+  my $str = $e->to_string;
+
+Render exception.
+
+  # Render exception with context
+  say $e->verbose(1)->to_string;
+
+=head2 throw
+
+  Mojo::Exception->throw('Something went wrong!');
+
+Throw exception from the current execution context.
+
+  # Longer version
+  die Mojo::Exception->new('Something went wrong!')->trace->inspect;
+
+=head2 trace
+
+  $e = $e->trace;
+  $e = $e->trace($skip);
+
+Generate stack trace and store all L</"frames">, defaults to skipping C<1> call
+frame.
+
+  # Skip 3 call frames
+  $e->trace(3);
+
+  # Skip no call frames
+  $e->trace(0);
+
+=head1 OPERATORS
+
+L<Mojo::Exception> overloads the following operators.
+
+=head2 bool
+
+  my $bool = !!$e;
+
+Always true.
+
+=head2 stringify
+
+  my $str = "$e";
+
+Alias for L</"to_string">.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/File.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/File.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/File.pm	(revision 32205)
@@ -0,0 +1,494 @@
+package Mojo::File;
+use Mojo::Base -strict;
+use overload
+  '@{}'    => sub { shift->to_array },
+  bool     => sub {1},
+  '""'     => sub { ${$_[0]} },
+  fallback => 1;
+
+use Carp 'croak';
+use Cwd 'getcwd';
+use Exporter 'import';
+use File::Basename ();
+use File::Copy qw(copy move);
+use File::Find 'find';
+use File::Path ();
+use File::Spec::Functions
+  qw(abs2rel canonpath catfile file_name_is_absolute rel2abs splitdir);
+use File::Temp ();
+use IO::File   ();
+use Mojo::Collection;
+
+our @EXPORT_OK = ('path', 'tempdir', 'tempfile');
+
+sub basename { File::Basename::basename ${shift()}, @_ }
+
+sub child { $_[0]->new(@_) }
+
+sub copy_to {
+  my ($self, $to) = @_;
+  copy($$self, $to) or croak qq{Can't copy file "$$self" to "$to": $!};
+  return $self->new(-d $to ? ($to, File::Basename::basename $self) : $to);
+}
+
+sub dirname { $_[0]->new(scalar File::Basename::dirname ${$_[0]}) }
+
+sub is_abs { file_name_is_absolute ${shift()} }
+
+sub list {
+  my ($self, $options) = (shift, shift // {});
+
+  return Mojo::Collection->new unless -d $$self;
+  opendir(my $dir, $$self) or croak qq{Can't open directory "$$self": $!};
+  my @files = grep { $_ ne '.' && $_ ne '..' } readdir $dir;
+  @files = grep { !/^\./ } @files unless $options->{hidden};
+  @files = map { catfile $$self, $_ } @files;
+  @files = grep { !-d } @files unless $options->{dir};
+
+  return Mojo::Collection->new(map { $self->new($_) } sort @files);
+}
+
+sub list_tree {
+  my ($self, $options) = (shift, shift // {});
+
+  # This may break in the future, but is worth it for performance
+  local $File::Find::skip_pattern = qr/^\./ unless $options->{hidden};
+
+  # The File::Find documentation lies, this is needed for CIFS
+  local $File::Find::dont_use_nlink = 1 if $options->{dont_use_nlink};
+
+  my %all;
+  my $wanted = {wanted => sub { $all{$File::Find::name}++ }, no_chdir => 1};
+  $wanted->{postprocess} = sub { delete $all{$File::Find::dir} }
+    unless $options->{dir};
+  find $wanted, $$self if -d $$self;
+  delete $all{$$self};
+
+  return Mojo::Collection->new(map { $self->new(canonpath $_) } sort keys %all);
+}
+
+sub make_path {
+  my $self = shift;
+  File::Path::make_path $$self, @_;
+  return $self;
+}
+
+sub move_to {
+  my ($self, $to) = @_;
+  move($$self, $to) or croak qq{Can't move file "$$self" to "$to": $!};
+  return $self->new(-d $to ? ($to, File::Basename::basename $self) : $to);
+}
+
+sub new {
+  my $class = shift;
+  my $value = @_ == 1 ? $_[0] : @_ > 1 ? catfile @_ : canonpath getcwd;
+  return bless \$value, ref $class || $class;
+}
+
+sub open {
+  my $self   = shift;
+  my $handle = IO::File->new;
+  $handle->open($$self, @_) or croak qq{Can't open file "$$self": $!};
+  return $handle;
+}
+
+sub path { __PACKAGE__->new(@_) }
+
+sub realpath { $_[0]->new(Cwd::realpath ${$_[0]}) }
+
+sub remove_tree {
+  my $self = shift;
+  File::Path::remove_tree $$self, @_;
+  return $self;
+}
+
+sub sibling {
+  my $self = shift;
+  return $self->new(scalar File::Basename::dirname($self), @_);
+}
+
+sub slurp {
+  my $self = shift;
+
+  CORE::open my $file, '<', $$self or croak qq{Can't open file "$$self": $!};
+  my $ret = my $content = '';
+  while ($ret = $file->sysread(my $buffer, 131072, 0)) { $content .= $buffer }
+  croak qq{Can't read from file "$$self": $!} unless defined $ret;
+
+  return $content;
+}
+
+sub spurt {
+  my ($self, $content) = (shift, join '', @_);
+  CORE::open my $file, '>', $$self or croak qq{Can't open file "$$self": $!};
+  ($file->syswrite($content) // -1) == length $content
+    or croak qq{Can't write to file "$$self": $!};
+  return $self;
+}
+
+sub tap { shift->Mojo::Base::tap(@_) }
+
+sub tempdir { __PACKAGE__->new(File::Temp->newdir(@_)) }
+
+sub tempfile { __PACKAGE__->new(File::Temp->new(@_)) }
+
+sub to_abs { $_[0]->new(rel2abs ${$_[0]}) }
+
+sub to_array { [splitdir ${shift()}] }
+
+sub to_rel { $_[0]->new(abs2rel(${$_[0]}, $_[1])) }
+
+sub to_string {"${$_[0]}"}
+
+sub with_roles { shift->Mojo::Base::with_roles(@_) }
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::File - File system paths
+
+=head1 SYNOPSIS
+
+  use Mojo::File;
+
+  # Portably deal with file system paths
+  my $path = Mojo::File->new('/home/sri/.vimrc');
+  say $path->slurp;
+  say $path->dirname;
+  say $path->basename;
+  say $path->sibling('.bashrc');
+
+  # Use the alternative constructor
+  use Mojo::File 'path';
+  my $path = path('/tmp/foo/bar')->make_path;
+  $path->child('test.txt')->spurt('Hello Mojo!');
+
+=head1 DESCRIPTION
+
+L<Mojo::File> is a scalar-based container for file system paths that provides a
+friendly API for dealing with different operating systems.
+
+  # Access scalar directly to manipulate path
+  my $path = Mojo::File->new('/home/sri/test');
+  $$path .= '.txt';
+
+=head1 FUNCTIONS
+
+L<Mojo::File> implements the following functions, which can be imported
+individually.
+
+=head2 path
+
+  my $path = path;
+  my $path = path('/home/sri/.vimrc');
+  my $path = path('/home', 'sri', '.vimrc');
+  my $path = path(File::Temp->newdir);
+
+Construct a new scalar-based L<Mojo::File> object, defaults to using the current
+working directory.
+
+  # "foo/bar/baz.txt" (on UNIX)
+  path('foo', 'bar', 'baz.txt');
+
+=head2 tempdir
+
+  my $path = tempdir;
+  my $path = tempdir('tempXXXXX');
+
+Construct a new scalar-based L<Mojo::File> object for a temporary directory with
+L<File::Temp>.
+
+  # Longer version
+  my $path = path(File::Temp->newdir('tempXXXXX'));
+
+=head2 tempfile
+
+  my $path = tempfile;
+  my $path = tempfile(DIR => '/tmp');
+
+Construct a new scalar-based L<Mojo::File> object for a temporary file with
+L<File::Temp>.
+
+  # Longer version
+  my $path = path(File::Temp->new(DIR => '/tmp'));
+
+=head1 METHODS
+
+L<Mojo::File> implements the following methods.
+
+=head2 basename
+
+  my $name = $path->basename;
+  my $name = $path->basename('.txt');
+
+Return the last level of the path with L<File::Basename>.
+
+  # ".vimrc" (on UNIX)
+  path('/home/sri/.vimrc')->basename;
+
+  # "test" (on UNIX)
+  path('/home/sri/test.txt')->basename('.txt');
+
+=head2 child
+
+  my $child = $path->child('.vimrc');
+
+Return a new L<Mojo::File> object relative to the path.
+
+  # "/home/sri/.vimrc" (on UNIX)
+  path('/home')->child('sri', '.vimrc');
+
+=head2 copy_to
+
+  my $destination = $path->copy_to('/home/sri');
+  my $destination = $path->copy_to('/home/sri/.vimrc.backup');
+
+Copy file with L<File::Copy> and return the destination as a L<Mojo::File>
+object.
+
+=head2 dirname
+
+  my $name = $path->dirname;
+
+Return all but the last level of the path with L<File::Basename> as a
+L<Mojo::File> object.
+
+  # "/home/sri" (on UNIX)
+  path('/home/sri/.vimrc')->dirname;
+
+=head2 is_abs
+
+  my $bool = $path->is_abs;
+
+Check if the path is absolute.
+
+  # True (on UNIX)
+  path('/home/sri/.vimrc')->is_abs;
+
+  # False (on UNIX)
+  path('.vimrc')->is_abs;
+
+=head2 list
+
+  my $collection = $path->list;
+  my $collection = $path->list({hidden => 1});
+
+List all files in the directory and return a L<Mojo::Collection> object
+containing the results as L<Mojo::File> objects. The list does not include C<.>
+and C<..>.
+
+  # List files
+  say for path('/home/sri/myapp')->list->each;
+
+These options are currently available:
+
+=over 2
+
+=item dir
+
+  dir => 1
+
+Include directories.
+
+=item hidden
+
+  hidden => 1
+
+Include hidden files.
+
+=back
+
+=head2 list_tree
+
+  my $collection = $path->list_tree;
+  my $collection = $path->list_tree({hidden => 1});
+
+List all files recursively in the directory and return a L<Mojo::Collection>
+object containing the results as L<Mojo::File> objects. The list does not
+include C<.> and C<..>.
+
+  # List all templates
+  say for path('/home/sri/myapp/templates')->list_tree->each;
+
+These options are currently available:
+
+=over 2
+
+=item dir
+
+  dir => 1
+
+Include directories.
+
+=item dont_use_nlink
+
+  dont_use_nlink => 1
+
+Force L<File::Find> to always stat directories.
+
+=item hidden
+
+  hidden => 1
+
+Include hidden files and directories.
+
+=back
+
+=head2 make_path
+
+  $path = $path->make_path;
+  $path = $path->make_path({mode => 0711});
+
+Create the directories if they don't already exist, any additional arguments are
+passed through to L<File::Path>.
+
+=head2 move_to
+
+  my $destination = $path->move_to('/home/sri');
+  my $destination = $path->move_to('/home/sri/.vimrc.backup');
+
+Move file with L<File::Copy> and return the destination as a L<Mojo::File>
+object.
+
+=head2 new
+
+  my $path = Mojo::File->new;
+  my $path = Mojo::File->new('/home/sri/.vimrc');
+  my $path = Mojo::File->new('/home', 'sri', '.vimrc');
+  my $path = Mojo::File->new(File::Temp->new);
+  my $path = Mojo::File->new(File::Temp->newdir);
+
+Construct a new L<Mojo::File> object, defaults to using the current working
+directory.
+
+  # "foo/bar/baz.txt" (on UNIX)
+  Mojo::File->new('foo', 'bar', 'baz.txt');
+
+=head2 open
+
+  my $handle = $path->open('+<');
+  my $handle = $path->open('r+');
+  my $handle = $path->open(O_RDWR);
+  my $handle = $path->open('<:encoding(UTF-8)');
+
+Open file with L<IO::File>.
+
+  # Combine "fcntl.h" constants
+  use Fcntl qw(O_CREAT O_EXCL O_RDWR);
+  my $handle = path('/home/sri/test.pl')->open(O_RDWR | O_CREAT | O_EXCL);
+
+=head2 realpath
+
+  my $realpath = $path->realpath;
+
+Resolve the path with L<Cwd> and return the result as a L<Mojo::File> object.
+
+=head2 remove_tree
+
+  $path = $path->remove_tree;
+  $path = $path->remove_tree({keep_root => 1});
+
+Delete this directory and any files and subdirectories it may contain, any
+additional arguments are passed through to L<File::Path>.
+
+=head2 sibling
+
+  my $sibling = $path->sibling('.vimrc');
+
+Return a new L<Mojo::File> object relative to the directory part of the path.
+
+  # "/home/sri/.vimrc" (on UNIX)
+  path('/home/sri/.bashrc')->sibling('.vimrc');
+
+  # "/home/sri/.ssh/known_hosts" (on UNIX)
+  path('/home/sri/.bashrc')->sibling('.ssh', 'known_hosts');
+
+=head2 slurp
+
+  my $bytes = $path->slurp;
+
+Read all data at once from the file.
+
+=head2 spurt
+
+  $path = $path->spurt($bytes);
+  $path = $path->spurt(@chunks_of_bytes);
+
+Write all data at once to the file.
+
+=head2 tap
+
+  $path = $path->tap(sub {...});
+
+Alias for L<Mojo::Base/"tap">.
+
+=head2 to_abs
+
+  my $absolute = $path->to_abs;
+
+Return absolute path as a L<Mojo::File> object, the path does not need to exist
+on the file system.
+
+=head2 to_array
+
+  my $parts = $path->to_array;
+
+Split the path on directory separators.
+
+  # "home:sri:.vimrc" (on UNIX)
+  join ':', @{path('/home/sri/.vimrc')->to_array};
+
+=head2 to_rel
+
+  my $relative = $path->to_rel('/some/base/path');
+
+Return a relative path from the original path to the destination path as a
+L<Mojo::File> object.
+
+  # "sri/.vimrc" (on UNIX)
+  path('/home/sri/.vimrc')->to_rel('/home');
+
+=head2 to_string
+
+  my $str = $path->to_string;
+
+Stringify the path.
+
+=head2 with_roles
+
+  my $new_class = Mojo::File->with_roles('Mojo::File::Role::One');
+  my $new_class = Mojo::File->with_roles('+One', '+Two');
+  $path         = $path->with_roles('+One', '+Two');
+
+Alias for L<Mojo::Base/"with_roles">.
+
+=head1 OPERATORS
+
+L<Mojo::File> overloads the following operators.
+
+=head2 array
+
+  my @parts = @$path;
+
+Alias for L</"to_array">.
+
+=head2 bool
+
+  my $bool = !!$path;
+
+Always true.
+
+=head2 stringify
+
+  my $str = "$path";
+
+Alias for L</"to_string">.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/Headers.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/Headers.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/Headers.pm	(revision 32205)
@@ -0,0 +1,717 @@
+package Mojo::Headers;
+use Mojo::Base -base;
+
+use Mojo::Util 'monkey_patch';
+
+has max_line_size => sub { $ENV{MOJO_MAX_LINE_SIZE} || 8192 };
+has max_lines     => sub { $ENV{MOJO_MAX_LINES}     || 100 };
+
+# Common headers
+my %NAMES = map { lc() => $_ } (
+  qw(Accept Accept-Charset Accept-Encoding Accept-Language Accept-Ranges),
+  qw(Access-Control-Allow-Origin Allow Authorization Cache-Control Connection),
+  qw(Content-Disposition Content-Encoding Content-Language Content-Length),
+  qw(Content-Location Content-Range Content-Security-Policy Content-Type),
+  qw(Cookie DNT Date ETag Expect Expires Host If-Modified-Since If-None-Match),
+  qw(Last-Modified Link Location Origin Proxy-Authenticate),
+  qw(Proxy-Authorization Range Sec-WebSocket-Accept Sec-WebSocket-Extensions),
+  qw(Sec-WebSocket-Key Sec-WebSocket-Protocol Sec-WebSocket-Version Server),
+  qw(Server-Timing Set-Cookie Status Strict-Transport-Security TE Trailer),
+  qw(Transfer-Encoding Upgrade User-Agent Vary WWW-Authenticate)
+);
+for my $header (keys %NAMES) {
+  my $name = $header;
+  $name =~ y/-/_/;
+  monkey_patch __PACKAGE__, $name, sub {
+    my $self = shift;
+    $self->{headers}{$header} = [@_] and return $self if @_;
+    return undef unless my $headers = $self->{headers}{$header};
+    return join ', ', @$headers;
+  };
+}
+
+sub add {
+  my ($self, $name) = (shift, shift);
+
+  # Make sure we have a normal case entry for name
+  my $key = lc $name;
+  $self->{names}{$key} //= $name unless $NAMES{$key};
+  push @{$self->{headers}{$key}}, @_;
+
+  return $self;
+}
+
+sub append {
+  my ($self, $name, $value) = @_;
+  my $old = $self->header($name);
+  return $self->header($name => defined $old ? "$old, $value" : $value);
+}
+
+sub clone { $_[0]->new->from_hash($_[0]->to_hash(1)) }
+
+sub every_header { shift->{headers}{lc shift} || [] }
+
+sub from_hash {
+  my ($self, $hash) = @_;
+
+  # Empty hash deletes all headers
+  delete $self->{headers} if keys %{$hash} == 0;
+
+  # Merge
+  for my $header (keys %$hash) {
+    my $value = $hash->{$header};
+    $self->add($header => ref $value eq 'ARRAY' ? @$value : $value);
+  }
+
+  return $self;
+}
+
+sub header {
+  my ($self, $name) = (shift, shift);
+
+  # Replace
+  return $self->remove($name)->add($name, @_) if @_;
+
+  return undef unless my $headers = $self->{headers}{lc $name};
+  return join ', ', @$headers;
+}
+
+sub is_finished { (shift->{state} // '') eq 'finished' }
+
+sub is_limit_exceeded { !!shift->{limit} }
+
+sub leftovers { delete shift->{buffer} }
+
+sub names {
+  my $self = shift;
+  return [map { $NAMES{$_} || $self->{names}{$_} } keys %{$self->{headers}}];
+}
+
+sub parse {
+  my ($self, $chunk) = @_;
+
+  $self->{state} = 'headers';
+  $self->{buffer} .= $chunk;
+  my $headers = $self->{cache} ||= [];
+  my $size    = $self->max_line_size;
+  my $lines   = $self->max_lines;
+  while ($self->{buffer} =~ s/^(.*?)\x0d?\x0a//) {
+    my $line = $1;
+
+    # Check line size limit
+    if ($+[0] > $size || @$headers >= $lines) {
+      @$self{qw(state limit)} = ('finished', 1);
+      return $self;
+    }
+
+    # New header
+    if ($line =~ /^(\S[^:]*)\s*:\s*(.*)$/) { push @$headers, [$1, $2] }
+
+    # Multi-line
+    elsif ($line =~ s/^\s+// && @$headers) { $headers->[-1][1] .= " $line" }
+
+    # Empty line
+    else {
+      $self->add(@$_) for @$headers;
+      @$self{qw(state cache)} = ('finished', []);
+      return $self;
+    }
+  }
+
+  # Check line size limit
+  @$self{qw(state limit)} = ('finished', 1) if length $self->{buffer} > $size;
+
+  return $self;
+}
+
+sub referrer { shift->header(Referer => @_) }
+
+sub remove {
+  my ($self, $name) = @_;
+  delete $self->{headers}{lc $name};
+  return $self;
+}
+
+sub to_hash {
+  my ($self, $multi) = @_;
+  return {map { $_ => $self->{headers}{lc $_} } @{$self->names}} if $multi;
+  return {map { $_ => $self->header($_) } @{$self->names}};
+}
+
+sub to_string {
+  my $self = shift;
+
+  # Make sure multi-line values are formatted correctly
+  my @headers;
+  for my $name (@{$self->names}) {
+    push @headers, "$name: $_" for @{$self->{headers}{lc $name}};
+  }
+
+  return join "\x0d\x0a", @headers;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::Headers - HTTP headers
+
+=head1 SYNOPSIS
+
+  use Mojo::Headers;
+
+  # Parse
+  my $headers = Mojo::Headers->new;
+  $headers->parse("Content-Length: 42\x0d\x0a");
+  $headers->parse("Content-Type: text/html\x0d\x0a\x0d\x0a");
+  say $headers->content_length;
+  say $headers->content_type;
+
+  # Build
+  my $headers = Mojo::Headers->new;
+  $headers->content_length(42);
+  $headers->content_type('text/plain');
+  say $headers->to_string;
+
+=head1 DESCRIPTION
+
+L<Mojo::Headers> is a container for HTTP headers, based on
+L<RFC 7230|http://tools.ietf.org/html/rfc7230> and
+L<RFC 7231|http://tools.ietf.org/html/rfc7231>.
+
+=head1 ATTRIBUTES
+
+L<Mojo::Headers> implements the following attributes.
+
+=head2 max_line_size
+
+  my $size = $headers->max_line_size;
+  $headers = $headers->max_line_size(1024);
+
+Maximum header line size in bytes, defaults to the value of the
+C<MOJO_MAX_LINE_SIZE> environment variable or C<8192> (8KiB).
+
+=head2 max_lines
+
+  my $num  = $headers->max_lines;
+  $headers = $headers->max_lines(200);
+
+Maximum number of header lines, defaults to the value of the C<MOJO_MAX_LINES>
+environment variable or C<100>.
+
+=head1 METHODS
+
+L<Mojo::Headers> inherits all methods from L<Mojo::Base> and implements the
+following new ones.
+
+=head2 accept
+
+  my $accept = $headers->accept;
+  $headers   = $headers->accept('application/json');
+
+Get or replace current header value, shortcut for the C<Accept> header.
+
+=head2 accept_charset
+
+  my $charset = $headers->accept_charset;
+  $headers    = $headers->accept_charset('UTF-8');
+
+Get or replace current header value, shortcut for the C<Accept-Charset> header.
+
+=head2 accept_encoding
+
+  my $encoding = $headers->accept_encoding;
+  $headers     = $headers->accept_encoding('gzip');
+
+Get or replace current header value, shortcut for the C<Accept-Encoding> header.
+
+=head2 accept_language
+
+  my $language = $headers->accept_language;
+  $headers     = $headers->accept_language('de, en');
+
+Get or replace current header value, shortcut for the C<Accept-Language> header.
+
+=head2 accept_ranges
+
+  my $ranges = $headers->accept_ranges;
+  $headers   = $headers->accept_ranges('bytes');
+
+Get or replace current header value, shortcut for the C<Accept-Ranges> header.
+
+=head2 access_control_allow_origin
+
+  my $origin = $headers->access_control_allow_origin;
+  $headers   = $headers->access_control_allow_origin('*');
+
+Get or replace current header value, shortcut for the
+C<Access-Control-Allow-Origin> header from
+L<Cross-Origin Resource Sharing|http://www.w3.org/TR/cors/>.
+
+=head2 add
+
+  $headers = $headers->add(Foo => 'one value');
+  $headers = $headers->add(Foo => 'first value', 'second value');
+
+Add header with one or more lines.
+
+  # "Vary: Accept
+  #  Vary: Accept-Encoding"
+  $headers->add(Vary => 'Accept')->add(Vary => 'Accept-Encoding')->to_string;
+
+=head2 allow
+
+  my $allow = $headers->allow;
+  $headers  = $headers->allow('GET, POST');
+
+Get or replace current header value, shortcut for the C<Allow> header.
+
+=head2 append
+
+  $headers = $headers->append(Vary => 'Accept-Encoding');
+
+Append value to header and flatten it if necessary.
+
+  # "Vary: Accept"
+  $headers->append(Vary => 'Accept')->to_string;
+
+  # "Vary: Accept, Accept-Encoding"
+  $headers->vary('Accept')->append(Vary => 'Accept-Encoding')->to_string;
+
+=head2 authorization
+
+  my $authorization = $headers->authorization;
+  $headers          = $headers->authorization('Basic Zm9vOmJhcg==');
+
+Get or replace current header value, shortcut for the C<Authorization> header.
+
+=head2 cache_control
+
+  my $cache_control = $headers->cache_control;
+  $headers          = $headers->cache_control('max-age=1, no-cache');
+
+Get or replace current header value, shortcut for the C<Cache-Control> header.
+
+=head2 clone
+
+  my $clone = $headers->clone;
+
+Return a new L<Mojo::Headers> object cloned from these headers.
+
+=head2 connection
+
+  my $connection = $headers->connection;
+  $headers       = $headers->connection('close');
+
+Get or replace current header value, shortcut for the C<Connection> header.
+
+=head2 content_disposition
+
+  my $disposition = $headers->content_disposition;
+  $headers        = $headers->content_disposition('foo');
+
+Get or replace current header value, shortcut for the C<Content-Disposition>
+header.
+
+=head2 content_encoding
+
+  my $encoding = $headers->content_encoding;
+  $headers     = $headers->content_encoding('gzip');
+
+Get or replace current header value, shortcut for the C<Content-Encoding>
+header.
+
+=head2 content_language
+
+  my $language = $headers->content_language;
+  $headers     = $headers->content_language('en');
+
+Get or replace current header value, shortcut for the C<Content-Language>
+header.
+
+=head2 content_length
+
+  my $len  = $headers->content_length;
+  $headers = $headers->content_length(4000);
+
+Get or replace current header value, shortcut for the C<Content-Length> header.
+
+=head2 content_location
+
+  my $location = $headers->content_location;
+  $headers     = $headers->content_location('http://127.0.0.1/foo');
+
+Get or replace current header value, shortcut for the C<Content-Location>
+header.
+
+=head2 content_range
+
+  my $range = $headers->content_range;
+  $headers  = $headers->content_range('bytes 2-8/100');
+
+Get or replace current header value, shortcut for the C<Content-Range> header.
+
+=head2 content_security_policy
+
+  my $policy = $headers->content_security_policy;
+  $headers   = $headers->content_security_policy('default-src https:');
+
+Get or replace current header value, shortcut for the C<Content-Security-Policy>
+header from L<Content Security Policy 1.0|http://www.w3.org/TR/CSP/>.
+
+=head2 content_type
+
+  my $type = $headers->content_type;
+  $headers = $headers->content_type('text/plain');
+
+Get or replace current header value, shortcut for the C<Content-Type> header.
+
+=head2 cookie
+
+  my $cookie = $headers->cookie;
+  $headers   = $headers->cookie('f=b');
+
+Get or replace current header value, shortcut for the C<Cookie> header from
+L<RFC 6265|http://tools.ietf.org/html/rfc6265>.
+
+=head2 date
+
+  my $date = $headers->date;
+  $headers = $headers->date('Sun, 17 Aug 2008 16:27:35 GMT');
+
+Get or replace current header value, shortcut for the C<Date> header.
+
+=head2 dnt
+
+  my $dnt  = $headers->dnt;
+  $headers = $headers->dnt(1);
+
+Get or replace current header value, shortcut for the C<DNT> (Do Not Track)
+header, which has no specification yet, but is very commonly used.
+
+=head2 etag
+
+  my $etag = $headers->etag;
+  $headers = $headers->etag('"abc321"');
+
+Get or replace current header value, shortcut for the C<ETag> header.
+
+=head2 every_header
+
+  my $all = $headers->every_header('Location');
+
+Similar to L</"header">, but returns all headers sharing the same name as an
+array reference.
+
+  # Get first header value
+  say $headers->every_header('Location')->[0];
+
+=head2 expect
+
+  my $expect = $headers->expect;
+  $headers   = $headers->expect('100-continue');
+
+Get or replace current header value, shortcut for the C<Expect> header.
+
+=head2 expires
+
+  my $expires = $headers->expires;
+  $headers    = $headers->expires('Thu, 01 Dec 1994 16:00:00 GMT');
+
+Get or replace current header value, shortcut for the C<Expires> header.
+
+=head2 from_hash
+
+  $headers = $headers->from_hash({'Cookie' => 'a=b'});
+  $headers = $headers->from_hash({'Cookie' => ['a=b', 'c=d']});
+  $headers = $headers->from_hash({});
+
+Parse headers from a hash reference, an empty hash removes all headers.
+
+=head2 header
+
+  my $value = $headers->header('Foo');
+  $headers  = $headers->header(Foo => 'one value');
+  $headers  = $headers->header(Foo => 'first value', 'second value');
+
+Get or replace the current header values.
+
+=head2 host
+
+  my $host = $headers->host;
+  $headers = $headers->host('127.0.0.1');
+
+Get or replace current header value, shortcut for the C<Host> header.
+
+=head2 if_modified_since
+
+  my $date = $headers->if_modified_since;
+  $headers = $headers->if_modified_since('Sun, 17 Aug 2008 16:27:35 GMT');
+
+Get or replace current header value, shortcut for the C<If-Modified-Since>
+header.
+
+=head2 if_none_match
+
+  my $etag = $headers->if_none_match;
+  $headers = $headers->if_none_match('"abc321"');
+
+Get or replace current header value, shortcut for the C<If-None-Match> header.
+
+=head2 is_finished
+
+  my $bool = $headers->is_finished;
+
+Check if header parser is finished.
+
+=head2 is_limit_exceeded
+
+  my $bool = $headers->is_limit_exceeded;
+
+Check if headers have exceeded L</"max_line_size"> or L</"max_lines">.
+
+=head2 last_modified
+
+  my $date = $headers->last_modified;
+  $headers = $headers->last_modified('Sun, 17 Aug 2008 16:27:35 GMT');
+
+Get or replace current header value, shortcut for the C<Last-Modified> header.
+
+=head2 leftovers
+
+  my $bytes = $headers->leftovers;
+
+Get and remove leftover data from header parser.
+
+=head2 link
+
+  my $link = $headers->link;
+  $headers = $headers->link('<http://127.0.0.1/foo/3>; rel="next"');
+
+Get or replace current header value, shortcut for the C<Link> header from
+L<RFC 5988|http://tools.ietf.org/html/rfc5988>.
+
+=head2 location
+
+  my $location = $headers->location;
+  $headers     = $headers->location('http://127.0.0.1/foo');
+
+Get or replace current header value, shortcut for the C<Location> header.
+
+=head2 names
+
+  my $names = $headers->names;
+
+Return an array reference with all currently defined headers.
+
+  # Names of all headers
+  say for @{$headers->names};
+
+=head2 origin
+
+  my $origin = $headers->origin;
+  $headers   = $headers->origin('http://example.com');
+
+Get or replace current header value, shortcut for the C<Origin> header from
+L<RFC 6454|http://tools.ietf.org/html/rfc6454>.
+
+=head2 parse
+
+  $headers = $headers->parse("Content-Type: text/plain\x0d\x0a\x0d\x0a");
+
+Parse formatted headers.
+
+=head2 proxy_authenticate
+
+  my $authenticate = $headers->proxy_authenticate;
+  $headers         = $headers->proxy_authenticate('Basic "realm"');
+
+Get or replace current header value, shortcut for the C<Proxy-Authenticate>
+header.
+
+=head2 proxy_authorization
+
+  my $authorization = $headers->proxy_authorization;
+  $headers          = $headers->proxy_authorization('Basic Zm9vOmJhcg==');
+
+Get or replace current header value, shortcut for the C<Proxy-Authorization>
+header.
+
+=head2 range
+
+  my $range = $headers->range;
+  $headers  = $headers->range('bytes=2-8');
+
+Get or replace current header value, shortcut for the C<Range> header.
+
+=head2 referrer
+
+  my $referrer = $headers->referrer;
+  $headers     = $headers->referrer('http://example.com');
+
+Get or replace current header value, shortcut for the C<Referer> header, there
+was a typo in L<RFC 2068|http://tools.ietf.org/html/rfc2068> which resulted in
+C<Referer> becoming an official header.
+
+=head2 remove
+
+  $headers = $headers->remove('Foo');
+
+Remove a header.
+
+=head2 sec_websocket_accept
+
+  my $accept = $headers->sec_websocket_accept;
+  $headers   = $headers->sec_websocket_accept('s3pPLMBiTxaQ9kYGzzhZRbK+xOo=');
+
+Get or replace current header value, shortcut for the C<Sec-WebSocket-Accept>
+header from L<RFC 6455|http://tools.ietf.org/html/rfc6455>.
+
+=head2 sec_websocket_extensions
+
+  my $extensions = $headers->sec_websocket_extensions;
+  $headers       = $headers->sec_websocket_extensions('foo');
+
+Get or replace current header value, shortcut for the
+C<Sec-WebSocket-Extensions> header from
+L<RFC 6455|http://tools.ietf.org/html/rfc6455>.
+
+=head2 sec_websocket_key
+
+  my $key  = $headers->sec_websocket_key;
+  $headers = $headers->sec_websocket_key('dGhlIHNhbXBsZSBub25jZQ==');
+
+Get or replace current header value, shortcut for the C<Sec-WebSocket-Key>
+header from L<RFC 6455|http://tools.ietf.org/html/rfc6455>.
+
+=head2 sec_websocket_protocol
+
+  my $proto = $headers->sec_websocket_protocol;
+  $headers  = $headers->sec_websocket_protocol('sample');
+
+Get or replace current header value, shortcut for the C<Sec-WebSocket-Protocol>
+header from L<RFC 6455|http://tools.ietf.org/html/rfc6455>.
+
+=head2 sec_websocket_version
+
+  my $version = $headers->sec_websocket_version;
+  $headers    = $headers->sec_websocket_version(13);
+
+Get or replace current header value, shortcut for the C<Sec-WebSocket-Version>
+header from L<RFC 6455|http://tools.ietf.org/html/rfc6455>.
+
+=head2 server
+
+  my $server = $headers->server;
+  $headers   = $headers->server('Mojo');
+
+Get or replace current header value, shortcut for the C<Server> header.
+
+=head2 server_timing
+
+  my $timing = $headers->server_timing;
+  $headers   = $headers->server_timing('app;desc=Mojolicious;dur=0.0001');
+
+Get or replace current header value, shortcut for the C<Server-Timing> header
+from L<Server Timing|https://www.w3.org/TR/server-timing/>.
+Note that this method is EXPERIMENTAL and might change without warning!
+
+=head2 set_cookie
+
+  my $cookie = $headers->set_cookie;
+  $headers   = $headers->set_cookie('f=b; path=/');
+
+Get or replace current header value, shortcut for the C<Set-Cookie> header from
+L<RFC 6265|http://tools.ietf.org/html/rfc6265>.
+
+=head2 status
+
+  my $status = $headers->status;
+  $headers   = $headers->status('200 OK');
+
+Get or replace current header value, shortcut for the C<Status> header from
+L<RFC 3875|http://tools.ietf.org/html/rfc3875>.
+
+=head2 strict_transport_security
+
+  my $policy = $headers->strict_transport_security;
+  $headers   = $headers->strict_transport_security('max-age=31536000');
+
+Get or replace current header value, shortcut for the
+C<Strict-Transport-Security> header from
+L<RFC 6797|http://tools.ietf.org/html/rfc6797>.
+
+=head2 te
+
+  my $te   = $headers->te;
+  $headers = $headers->te('chunked');
+
+Get or replace current header value, shortcut for the C<TE> header.
+
+=head2 to_hash
+
+  my $single = $headers->to_hash;
+  my $multi  = $headers->to_hash(1);
+
+Turn headers into hash reference, array references to represent multiple
+headers with the same name are disabled by default.
+
+  say $headers->to_hash->{DNT};
+
+=head2 to_string
+
+  my $str = $headers->to_string;
+
+Turn headers into a string, suitable for HTTP messages.
+
+=head2 trailer
+
+  my $trailer = $headers->trailer;
+  $headers    = $headers->trailer('X-Foo');
+
+Get or replace current header value, shortcut for the C<Trailer> header.
+
+=head2 transfer_encoding
+
+  my $encoding = $headers->transfer_encoding;
+  $headers     = $headers->transfer_encoding('chunked');
+
+Get or replace current header value, shortcut for the C<Transfer-Encoding>
+header.
+
+=head2 upgrade
+
+  my $upgrade = $headers->upgrade;
+  $headers    = $headers->upgrade('websocket');
+
+Get or replace current header value, shortcut for the C<Upgrade> header.
+
+=head2 user_agent
+
+  my $agent = $headers->user_agent;
+  $headers  = $headers->user_agent('Mojo/1.0');
+
+Get or replace current header value, shortcut for the C<User-Agent> header.
+
+=head2 vary
+
+  my $vary = $headers->vary;
+  $headers = $headers->vary('*');
+
+Get or replace current header value, shortcut for the C<Vary> header.
+
+=head2 www_authenticate
+
+  my $authenticate = $headers->www_authenticate;
+  $headers         = $headers->www_authenticate('Basic realm="realm"');
+
+Get or replace current header value, shortcut for the C<WWW-Authenticate>
+header.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/HelloWorld.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/HelloWorld.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/HelloWorld.pm	(revision 32205)
@@ -0,0 +1,50 @@
+package Mojo::HelloWorld;
+use Mojo::Base 'Mojolicious';
+
+sub startup {
+  my $self = shift;
+  $self->log->level('error')->path(undef);
+  $self->routes->any(
+    '/*whatever' => {whatever => '', text => 'Your Mojo is working!'});
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::HelloWorld - Hello World!
+
+=head1 SYNOPSIS
+
+  use Mojo::HelloWorld;
+
+  my $hello = Mojo::HelloWorld->new;
+  $hello->start;
+
+=head1 DESCRIPTION
+
+L<Mojo::HelloWorld> is the default L<Mojolicious> application, used mostly for
+testing.
+
+=head1 ATTRIBUTES
+
+L<Mojo::HelloWorld> inherits all attributes from L<Mojolicious>.
+
+=head1 METHODS
+
+L<Mojo::HelloWorld> inherits all methods from L<Mojolicious> and implements the
+following new ones.
+
+=head2 startup
+
+  $hello->startup;
+
+Creates a catch-all route that renders a text message.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/Home.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/Home.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/Home.pm	(revision 32205)
@@ -0,0 +1,84 @@
+package Mojo::Home;
+use Mojo::Base 'Mojo::File';
+
+use Mojo::Util 'class_to_path';
+
+sub detect {
+  my ($self, $class) = @_;
+
+  # Environment variable
+  my $home;
+  if ($ENV{MOJO_HOME}) { $home = Mojo::File->new($ENV{MOJO_HOME})->to_array }
+
+  # Location of the application class (Windows mixes backslash and slash)
+  elsif ($class && (my $path = $INC{my $file = class_to_path $class})) {
+    $home = Mojo::File->new($path)->to_array;
+    splice @$home, (my @dummy = split('/', $file)) * -1;
+    @$home && $home->[-1] eq $_ && pop @$home for qw(lib blib);
+  }
+
+  $$self = Mojo::File->new(@$home)->to_abs->to_string if $home;
+  return $self;
+}
+
+sub mojo_lib_dir { shift->new(__FILE__)->sibling('..') }
+
+sub rel_file { shift->child(split('/', shift)) }
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::Home - Home sweet home
+
+=head1 SYNOPSIS
+
+  use Mojo::Home;
+
+  # Find and manage the project root directory
+  my $home = Mojo::Home->new;
+  $home->detect;
+  say $home->child('templates', 'layouts', 'default.html.ep');
+  say "$home";
+
+=head1 DESCRIPTION
+
+L<Mojo::Home> is a container for home directories based on L<Mojo::File>.
+
+=head1 METHODS
+
+L<Mojo::Home> inherits all methods from L<Mojo::File> and implements the
+following new ones.
+
+=head2 detect
+
+  $home = $home->detect;
+  $home = $home->detect('My::App');
+
+Detect home directory from the value of the C<MOJO_HOME> environment variable or
+the location of the application class.
+
+=head2 mojo_lib_dir
+
+  my $path = $home->mojo_lib_dir;
+
+Path to C<lib> directory in which L<Mojolicious> is installed as a L<Mojo::Home>
+object.
+
+=head2 rel_file
+
+  my $path = $home->rel_file('foo/bar.html');
+
+Return a new L<Mojo::Home> object relative to the home directory.
+
+=head1 OPERATORS
+
+L<Mojo::Home> inherits all overloaded operators from L<Mojo::File>.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/IOLoop.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/IOLoop.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/IOLoop.pm	(revision 32205)
@@ -0,0 +1,681 @@
+package Mojo::IOLoop;
+use Mojo::Base 'Mojo::EventEmitter';
+
+# "Professor: Amy, technology isn't intrinsically good or evil. It's how it's
+#             used. Like the death ray."
+use Carp 'croak';
+use Mojo::IOLoop::Client;
+use Mojo::IOLoop::Delay;
+use Mojo::IOLoop::Server;
+use Mojo::IOLoop::Stream;
+use Mojo::IOLoop::Subprocess;
+use Mojo::Reactor::Poll;
+use Mojo::Util qw(md5_sum steady_time);
+use Scalar::Util qw(blessed weaken);
+
+use constant DEBUG => $ENV{MOJO_IOLOOP_DEBUG} || 0;
+
+has max_accepts     => 0;
+has max_connections => 1000;
+has reactor         => sub {
+  my $class = Mojo::Reactor::Poll->detect;
+  warn "-- Reactor initialized ($class)\n" if DEBUG;
+  return $class->new->catch(sub { warn "@{[blessed $_[0]]}: $_[1]" });
+};
+
+# Ignore PIPE signal
+$SIG{PIPE} = 'IGNORE';
+
+# Initialize singleton reactor early
+__PACKAGE__->singleton->reactor;
+
+sub acceptor {
+  my ($self, $acceptor) = (_instance(shift), @_);
+
+  # Find acceptor for id
+  return $self->{acceptors}{$acceptor} unless ref $acceptor;
+
+  # Connect acceptor with reactor
+  $self->{acceptors}{my $id = $self->_id} = $acceptor;
+  weaken $acceptor->reactor($self->reactor)->{reactor};
+
+  # Allow new acceptor to get picked up
+  $self->_not_accepting->_maybe_accepting;
+
+  return $id;
+}
+
+sub client {
+  my ($self, $cb) = (_instance(shift), pop);
+  my $args = ref $_[0] ? $_[0] : {@_};
+
+  my $id = $self->_id;
+  my $client = $self->{out}{$id}{client} = Mojo::IOLoop::Client->new;
+  weaken $client->reactor($self->reactor)->{reactor};
+  my $class = delete $args->{stream_class} || 'Mojo::IOLoop::Stream';
+
+  weaken $self;
+  $client->on(
+    connect => sub {
+      delete $self->{out}{$id}{client};
+      my $stream = $class->new(pop);
+      $self->_stream($stream => $id);
+      $self->$cb(undef, $stream);
+    }
+  );
+  $client->on(error => sub { $self->_remove($id); $self->$cb(pop, undef) });
+  $client->connect($args);
+
+  return $id;
+}
+
+sub delay {
+  my $delay = Mojo::IOLoop::Delay->new;
+  weaken $delay->ioloop(_instance(shift))->{ioloop};
+  return @_ ? $delay->steps(@_) : $delay;
+}
+
+sub is_running { _instance(shift)->reactor->is_running }
+
+sub next_tick {
+  my ($self, $cb) = (_instance(shift), @_);
+  weaken $self;
+  return $self->reactor->next_tick(sub { $self->$cb });
+}
+
+sub one_tick {
+  my $self = _instance(shift);
+  croak 'Mojo::IOLoop already running' if $self->is_running;
+  $self->reactor->one_tick;
+}
+
+sub recurring { shift->_timer(recurring => @_) }
+
+sub remove {
+  my ($self, $id) = (_instance(shift), @_);
+  my $c = $self->{in}{$id} || $self->{out}{$id};
+  if ($c && (my $stream = $c->{stream})) { return $stream->close_gracefully }
+  $self->_remove($id);
+}
+
+sub reset {
+  my $self = _instance(shift);
+  delete @$self{qw(accepting acceptors events in out stop)};
+  $self->reactor->reset;
+  $self->stop;
+}
+
+sub server {
+  my ($self, $cb) = (_instance(shift), pop);
+  my $args = ref $_[0] ? $_[0] : {@_};
+
+  my $server = Mojo::IOLoop::Server->new;
+  my $class = delete $args->{stream_class} || 'Mojo::IOLoop::Stream';
+  weaken $self;
+  $server->on(
+    accept => sub {
+      my $stream = $class->new(pop);
+      $self->$cb($stream, $self->_stream($stream, $self->_id, 1));
+
+      # Enforce connection limit (randomize to improve load balancing)
+      if (my $max = $self->max_accepts) {
+        $self->{accepts} //= $max - int rand $max / 2;
+        $self->stop_gracefully if ($self->{accepts} -= 1) <= 0;
+      }
+
+      # Stop accepting if connection limit has been reached
+      $self->_not_accepting if $self->_limit;
+    }
+  );
+  $server->listen($args);
+
+  return $self->acceptor($server);
+}
+
+sub singleton { state $loop = shift->SUPER::new }
+
+sub start {
+  my $self = _instance(shift);
+  croak 'Mojo::IOLoop already running' if $self->is_running;
+  $self->reactor->start;
+}
+
+sub stop { _instance(shift)->reactor->stop }
+
+sub stop_gracefully {
+  my $self = _instance(shift)->_not_accepting;
+  ++$self->{stop} and !$self->emit('finish')->_in and $self->stop;
+}
+
+sub stream {
+  my ($self, $stream) = (_instance(shift), @_);
+  return $self->_stream($stream => $self->_id) if ref $stream;
+  my $c = $self->{in}{$stream} || $self->{out}{$stream} || {};
+  return $c->{stream};
+}
+
+sub subprocess {
+  my $subprocess = Mojo::IOLoop::Subprocess->new;
+  weaken $subprocess->ioloop(_instance(shift))->{ioloop};
+  return @_ ? $subprocess->run(@_) : $subprocess;
+}
+
+sub timer { shift->_timer(timer => @_) }
+
+sub transition {
+  my ($self, $id, $class) = (_instance(shift), @_);
+  my $new = $class->new($self->stream($id)->steal_handle);
+  $self->_stream($new, $id, !!$self->{in}{$id});
+  return $new;
+}
+
+sub _id {
+  my $self = shift;
+  my $id;
+  do { $id = md5_sum 'c' . steady_time . rand }
+    while $self->{in}{$id} || $self->{out}{$id} || $self->{acceptors}{$id};
+  return $id;
+}
+
+sub _in { scalar keys %{shift->{in} || {}} }
+
+sub _instance { ref $_[0] ? $_[0] : $_[0]->singleton }
+
+sub _limit { $_[0]{stop} ? 1 : $_[0]->_in >= $_[0]->max_connections }
+
+sub _maybe_accepting {
+  my $self = shift;
+  return if $self->{accepting} || $self->_limit;
+  $_->start for values %{$self->{acceptors} || {}};
+  $self->{accepting} = 1;
+}
+
+sub _not_accepting {
+  my $self = shift;
+  return $self unless delete $self->{accepting};
+  $_->stop for values %{$self->{acceptors} || {}};
+  return $self;
+}
+
+sub _out { scalar keys %{shift->{out} || {}} }
+
+sub _remove {
+  my ($self, $id) = @_;
+
+  # Timer
+  return unless my $reactor = $self->reactor;
+  return if $reactor->remove($id);
+
+  # Acceptor
+  return $self->_not_accepting->_maybe_accepting
+    if delete $self->{acceptors}{$id};
+
+  # Connection
+  return unless delete $self->{in}{$id} || delete $self->{out}{$id};
+  return $self->stop if $self->{stop} && !$self->_in;
+  $self->_maybe_accepting;
+  warn "-- $id <<< $$ (@{[$self->_in]}:@{[$self->_out]})\n" if DEBUG;
+}
+
+sub _stream {
+  my ($self, $stream, $id, $server) = @_;
+
+  # Connect stream with reactor
+  $self->{$server ? 'in' : 'out'}{$id}{stream} = $stream;
+  warn "-- $id >>> $$ (@{[$self->_in]}:@{[$self->_out]})\n" if DEBUG;
+  weaken $stream->reactor($self->reactor)->{reactor};
+  weaken $self;
+  $stream->on(close => sub { $self && $self->_remove($id) });
+  $stream->start;
+
+  return $id;
+}
+
+sub _timer {
+  my ($self, $method, $after, $cb) = (_instance(shift), @_);
+  weaken $self;
+  return $self->reactor->$method($after => sub { $self->$cb });
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::IOLoop - Minimalistic event loop
+
+=head1 SYNOPSIS
+
+  use Mojo::IOLoop;
+
+  # Listen on port 3000
+  Mojo::IOLoop->server({port => 3000} => sub {
+    my ($loop, $stream) = @_;
+
+    $stream->on(read => sub {
+      my ($stream, $bytes) = @_;
+
+      # Process input chunk
+      say $bytes;
+
+      # Write response
+      $stream->write('HTTP/1.1 200 OK');
+    });
+  });
+
+  # Connect to port 3000
+  my $id = Mojo::IOLoop->client({port => 3000} => sub {
+    my ($loop, $err, $stream) = @_;
+
+    $stream->on(read => sub {
+      my ($stream, $bytes) = @_;
+
+      # Process input
+      say "Input: $bytes";
+    });
+
+    # Write request
+    $stream->write("GET / HTTP/1.1\x0d\x0a\x0d\x0a");
+  });
+
+  # Add a timer
+  Mojo::IOLoop->timer(5 => sub {
+    my $loop = shift;
+    $loop->remove($id);
+  });
+
+  # Start event loop if necessary
+  Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
+
+=head1 DESCRIPTION
+
+L<Mojo::IOLoop> is a very minimalistic event loop based on L<Mojo::Reactor>, it
+has been reduced to the absolute minimal feature set required to build solid
+and scalable non-blocking clients and servers.
+
+Depending on operating system, the default per-process and system-wide file
+descriptor limits are often very low and need to be tuned for better
+scalability. The C<LIBEV_FLAGS> environment variable should also be used to
+select the best possible L<EV> backend, which usually defaults to the not very
+scalable C<select>.
+
+  LIBEV_FLAGS=1   # select
+  LIBEV_FLAGS=2   # poll
+  LIBEV_FLAGS=4   # epoll (Linux)
+  LIBEV_FLAGS=8   # kqueue (*BSD, OS X)
+
+The event loop will be resilient to time jumps if a monotonic clock is
+available through L<Time::HiRes>. A TLS certificate and key are also built
+right in, to make writing test servers as easy as possible. Also note that for
+convenience the C<PIPE> signal will be set to C<IGNORE> when L<Mojo::IOLoop> is
+loaded.
+
+For better scalability (epoll, kqueue) and to provide non-blocking name
+resolution, SOCKS5 as well as TLS support, the optional modules L<EV> (4.0+),
+L<Net::DNS::Native> (0.15+), L<IO::Socket::Socks> (0.64+) and
+L<IO::Socket::SSL> (2.009+) will be used automatically if possible. Individual
+features can also be disabled with the C<MOJO_NO_NNR>, C<MOJO_NO_SOCKS> and
+C<MOJO_NO_TLS> environment variables.
+
+See L<Mojolicious::Guides::Cookbook/"REAL-TIME WEB"> for more.
+
+=head1 EVENTS
+
+L<Mojo::IOLoop> inherits all events from L<Mojo::EventEmitter> and can emit the
+following new ones.
+
+=head2 finish
+
+  $loop->on(finish => sub {
+    my $loop = shift;
+    ...
+  });
+
+Emitted when the event loop wants to shut down gracefully and is just waiting
+for all existing connections to be closed.
+
+=head1 ATTRIBUTES
+
+L<Mojo::IOLoop> implements the following attributes.
+
+=head2 max_accepts
+
+  my $max = $loop->max_accepts;
+  $loop   = $loop->max_accepts(1000);
+
+The maximum number of connections this event loop is allowed to accept, before
+shutting down gracefully without interrupting existing connections, defaults to
+C<0>. Setting the value to C<0> will allow this event loop to accept new
+connections indefinitely. Note that up to half of this value can be subtracted
+randomly to improve load balancing between multiple server processes, and to
+make sure that not all of them restart at the same time.
+
+=head2 max_connections
+
+  my $max = $loop->max_connections;
+  $loop   = $loop->max_connections(100);
+
+The maximum number of accepted connections this event loop is allowed to handle
+concurrently, before stopping to accept new incoming connections, defaults to
+C<1000>.
+
+=head2 reactor
+
+  my $reactor = $loop->reactor;
+  $loop       = $loop->reactor(Mojo::Reactor->new);
+
+Low-level event reactor, usually a L<Mojo::Reactor::Poll> or
+L<Mojo::Reactor::EV> object with a default subscriber to the event
+L<Mojo::Reactor/"error">.
+
+  # Watch if handle becomes readable or writable
+  Mojo::IOLoop->singleton->reactor->io($handle => sub {
+    my ($reactor, $writable) = @_;
+    say $writable ? 'Handle is writable' : 'Handle is readable';
+  });
+
+  # Change to watching only if handle becomes writable
+  Mojo::IOLoop->singleton->reactor->watch($handle, 0, 1);
+
+  # Remove handle again
+  Mojo::IOLoop->singleton->reactor->remove($handle);
+
+=head1 METHODS
+
+L<Mojo::IOLoop> inherits all methods from L<Mojo::EventEmitter> and implements
+the following new ones.
+
+=head2 acceptor
+
+  my $server = Mojo::IOLoop->acceptor($id);
+  my $server = $loop->acceptor($id);
+  my $id     = $loop->acceptor(Mojo::IOLoop::Server->new);
+
+Get L<Mojo::IOLoop::Server> object for id or turn object into an acceptor.
+
+=head2 client
+
+  my $id
+    = Mojo::IOLoop->client(address => '127.0.0.1', port => 3000, sub {...});
+  my $id = $loop->client(address => '127.0.0.1', port => 3000, sub {...});
+  my $id = $loop->client({address => '127.0.0.1', port => 3000} => sub {...});
+
+Open a TCP/IP or UNIX domain socket connection with L<Mojo::IOLoop::Client> and
+create a stream object (usually L<Mojo::IOLoop::Stream>), takes the same
+arguments as L<Mojo::IOLoop::Client/"connect"> in addition to C<stream_class>.
+
+  # Connect to 127.0.0.1 on port 3000 with a custom stream class
+  my $class = 'Mojo::IOLoop::Stream::HTTPClient';
+  Mojo::IOLoop->client({port => 3000, stream_class => $class} => sub {
+    my ($loop, $err, $stream) = @_;
+    ...
+  });
+
+=head2 delay
+
+  my $delay = Mojo::IOLoop->delay;
+  my $delay = $loop->delay;
+  my $delay = $loop->delay(sub {...});
+  my $delay = $loop->delay(sub {...}, sub {...});
+
+Build L<Mojo::IOLoop::Delay> object to use as a promise and/or for flow-control.
+Callbacks will be passed along to L<Mojo::IOLoop::Delay/"steps">.
+
+  # Wrap continuation-passing style APIs with promises
+  my $ua = Mojo::UserAgent->new;
+  sub get {
+    my $promise = Mojo::IOLoop->delay;
+    $ua->get(@_ => sub {
+      my ($ua, $tx) = @_;
+      my $err = $tx->error;
+      $promise->resolve($tx) if !$err || $err->{code};
+      $promise->reject($err->{message});
+    });
+    return $promise;
+  }
+  my $mojo = get('https://mojolicious.org');
+  my $cpan = get('https://metacpan.org');
+  Mojo::Promise->race($mojo, $cpan)->then(sub { say shift->req->url })->wait;
+
+  # Synchronize multiple non-blocking operations
+  my $delay = Mojo::IOLoop->delay(sub { say 'BOOM!' });
+  for my $i (1 .. 10) {
+    my $end = $delay->begin;
+    Mojo::IOLoop->timer($i => sub {
+      say 10 - $i;
+      $end->();
+    });
+  }
+  $delay->wait;
+
+  # Sequentialize multiple non-blocking operations
+  Mojo::IOLoop->delay(
+
+    # First step (simple timer)
+    sub {
+      my $delay = shift;
+      Mojo::IOLoop->timer(2 => $delay->begin);
+      say 'Second step in 2 seconds.';
+    },
+
+    # Second step (concurrent timers)
+    sub {
+      my $delay = shift;
+      Mojo::IOLoop->timer(1 => $delay->begin);
+      Mojo::IOLoop->timer(3 => $delay->begin);
+      say 'Third step in 3 seconds.';
+    },
+
+    # Third step (the end)
+    sub { say 'And done after 5 seconds total.' }
+  )->wait;
+
+=head2 is_running
+
+  my $bool = Mojo::IOLoop->is_running;
+  my $bool = $loop->is_running;
+
+Check if event loop is running.
+
+  exit unless Mojo::IOLoop->is_running;
+
+=head2 next_tick
+
+  my $undef = Mojo::IOLoop->next_tick(sub {...});
+  my $undef = $loop->next_tick(sub {...});
+
+Execute callback as soon as possible, but not before returning or other
+callbacks that have been registered with this method, always returns C<undef>.
+
+  # Perform operation on next reactor tick
+  Mojo::IOLoop->next_tick(sub {
+    my $loop = shift;
+    ...
+  });
+
+=head2 one_tick
+
+  Mojo::IOLoop->one_tick;
+  $loop->one_tick;
+
+Run event loop until an event occurs.
+
+  # Don't block longer than 0.5 seconds
+  my $id = Mojo::IOLoop->timer(0.5 => sub {});
+  Mojo::IOLoop->one_tick;
+  Mojo::IOLoop->remove($id);
+
+=head2 recurring
+
+  my $id = Mojo::IOLoop->recurring(3 => sub {...});
+  my $id = $loop->recurring(0 => sub {...});
+  my $id = $loop->recurring(0.25 => sub {...});
+
+Create a new recurring timer, invoking the callback repeatedly after a given
+amount of time in seconds.
+
+  # Perform operation every 5 seconds
+  Mojo::IOLoop->recurring(5 => sub {
+    my $loop = shift;
+    ...
+  });
+
+=head2 remove
+
+  Mojo::IOLoop->remove($id);
+  $loop->remove($id);
+
+Remove anything with an id, connections will be dropped gracefully by allowing
+them to finish writing all data in their write buffers.
+
+=head2 reset
+
+  Mojo::IOLoop->reset;
+  $loop->reset;
+
+Remove everything and stop the event loop.
+
+=head2 server
+
+  my $id = Mojo::IOLoop->server(port => 3000, sub {...});
+  my $id = $loop->server(port => 3000, sub {...});
+  my $id = $loop->server({port => 3000} => sub {...});
+
+Accept TCP/IP and UNIX domain socket connections with L<Mojo::IOLoop::Server>
+and create stream objects (usually L<Mojo::IOLoop::Stream>, takes the same
+arguments as L<Mojo::IOLoop::Server/"listen"> in addition to C<stream_class>.
+
+  # Listen on port 3000 with a custom stream class
+  my $class = 'Mojo::IOLoop::Stream::HTTPServer';
+  Mojo::IOLoop->server({port => 3000, stream_class => $class} => sub {
+    my ($loop, $stream, $id) = @_;
+    ...
+  });
+
+  # Listen on random port
+  my $id = Mojo::IOLoop->server({address => '127.0.0.1'} => sub {
+    my ($loop, $stream, $id) = @_;
+    ...
+  });
+  my $port = Mojo::IOLoop->acceptor($id)->port;
+
+=head2 singleton
+
+  my $loop = Mojo::IOLoop->singleton;
+
+The global L<Mojo::IOLoop> singleton, used to access a single shared event loop
+object from everywhere inside the process.
+
+  # Many methods also allow you to take shortcuts
+  Mojo::IOLoop->timer(2 => sub { Mojo::IOLoop->stop });
+  Mojo::IOLoop->start;
+
+  # Restart active timer
+  my $id = Mojo::IOLoop->timer(3 => sub { say 'Timeout!' });
+  Mojo::IOLoop->singleton->reactor->again($id);
+
+  # Turn file descriptor into handle and watch if it becomes readable
+  my $handle = IO::Handle->new_from_fd($fd, 'r');
+  Mojo::IOLoop->singleton->reactor->io($handle => sub {
+    my ($reactor, $writable) = @_;
+    say $writable ? 'Handle is writable' : 'Handle is readable';
+  })->watch($handle, 1, 0);
+
+=head2 start
+
+  Mojo::IOLoop->start;
+  $loop->start;
+
+Start the event loop, this will block until L</"stop"> is called. Note that
+some reactors stop automatically if there are no events being watched anymore.
+
+  # Start event loop only if it is not running already
+  Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
+
+=head2 stop
+
+  Mojo::IOLoop->stop;
+  $loop->stop;
+
+Stop the event loop, this will not interrupt any existing connections and the
+event loop can be restarted by running L</"start"> again.
+
+=head2 stop_gracefully
+
+  Mojo::IOLoop->stop_gracefully;
+  $loop->stop_gracefully;
+
+Stop accepting new connections and wait for already accepted connections to be
+closed, before stopping the event loop.
+
+=head2 stream
+
+  my $stream = Mojo::IOLoop->stream($id);
+  my $stream = $loop->stream($id);
+  my $id     = $loop->stream(Mojo::IOLoop::Stream->new);
+
+Get L<Mojo::IOLoop::Stream> object for id or turn object into a connection.
+
+  # Increase inactivity timeout for connection to 300 seconds
+  Mojo::IOLoop->stream($id)->timeout(300);
+
+=head2 subprocess
+
+  my $subprocess = Mojo::IOLoop->subprocess(sub {...}, sub {...});
+  my $subprocess = $loop->subprocess;
+  my $subprocess = $loop->subprocess(sub {...}, sub {...});
+
+Build L<Mojo::IOLoop::Subprocess> object to perform computationally expensive
+operations in subprocesses, without blocking the event loop. Callbacks will be
+passed along to L<Mojo::IOLoop::Subprocess/"run">.
+
+  # Operation that would block the event loop for 5 seconds
+  Mojo::IOLoop->subprocess(
+    sub {
+      my $subprocess = shift;
+      sleep 5;
+      return 'â¥', 'Mojolicious';
+    },
+    sub {
+      my ($subprocess, $err, @results) = @_;
+      say "Subprocess error: $err" and return if $err;
+      say "I $results[0] $results[1]!";
+    }
+  );
+
+=head2 timer
+
+  my $id = Mojo::IOLoop->timer(3 => sub {...});
+  my $id = $loop->timer(0 => sub {...});
+  my $id = $loop->timer(0.25 => sub {...});
+
+Create a new timer, invoking the callback after a given amount of time in
+seconds.
+
+  # Perform operation in 5 seconds
+  Mojo::IOLoop->timer(5 => sub {
+    my $loop = shift;
+    ...
+  });
+
+=head2 transition
+
+  my $stream =
+    Mojo::IOLoop->transition($id => 'Mojo::IOLoop::Stream::HTTPClient');
+  my $stream = $loop->transition($id => 'Mojo::IOLoop::Stream::HTTPClient');
+
+Transition stream to a different class.
+
+=head1 DEBUGGING
+
+You can set the C<MOJO_IOLOOP_DEBUG> environment variable to get some advanced
+diagnostics information printed to C<STDERR>.
+
+  MOJO_IOLOOP_DEBUG=1
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/IOLoop/Client.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/IOLoop/Client.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/IOLoop/Client.pm	(revision 32205)
@@ -0,0 +1,384 @@
+package Mojo::IOLoop::Client;
+use Mojo::Base 'Mojo::EventEmitter';
+
+use Errno 'EINPROGRESS';
+use IO::Socket::IP;
+use IO::Socket::UNIX;
+use Mojo::IOLoop;
+use Mojo::IOLoop::TLS;
+use Scalar::Util 'weaken';
+use Socket qw(IPPROTO_TCP SOCK_STREAM TCP_NODELAY);
+
+# Non-blocking name resolution requires Net::DNS::Native
+use constant NNR => $ENV{MOJO_NO_NNR}
+  ? 0
+  : eval { require Net::DNS::Native; Net::DNS::Native->VERSION('0.15'); 1 };
+my $NDN;
+
+# SOCKS support requires IO::Socket::Socks
+use constant SOCKS => $ENV{MOJO_NO_SOCKS}
+  ? 0
+  : eval { require IO::Socket::Socks; IO::Socket::Socks->VERSION('0.64'); 1 };
+use constant READ  => SOCKS ? IO::Socket::Socks::SOCKS_WANT_READ()  : 0;
+use constant WRITE => SOCKS ? IO::Socket::Socks::SOCKS_WANT_WRITE() : 0;
+
+has reactor => sub { Mojo::IOLoop->singleton->reactor };
+
+sub DESTROY { shift->_cleanup }
+
+sub can_nnr   {NNR}
+sub can_socks {SOCKS}
+
+sub connect {
+  my ($self, $args) = (shift, ref $_[0] ? $_[0] : {@_});
+
+  # Timeout
+  weaken $self;
+  my $reactor = $self->reactor;
+  $self->{timer} = $reactor->timer($args->{timeout} || 10,
+    sub { $self->emit(error => 'Connect timeout') });
+
+  # Blocking name resolution
+  $_ && s/[[\]]//g for @$args{qw(address socks_address)};
+  my $address = $args->{socks_address} || ($args->{address} ||= '127.0.0.1');
+  return $reactor->next_tick(sub { $self && $self->_connect($args) })
+    if !NNR || $args->{handle} || $args->{path};
+
+  # Non-blocking name resolution
+  $NDN //= Net::DNS::Native->new(pool => 5, extra_thread => 1);
+  my $handle = $self->{dns} = $NDN->getaddrinfo($address, _port($args),
+    {protocol => IPPROTO_TCP, socktype => SOCK_STREAM});
+  $reactor->io(
+    $handle => sub {
+      my $reactor = shift;
+
+      $reactor->remove($self->{dns});
+      my ($err, @res) = $NDN->get_result(delete $self->{dns});
+      return $self->emit(error => "Can't resolve: $err") if $err;
+
+      $args->{addr_info} = \@res;
+      $self->_connect($args);
+    }
+  )->watch($handle, 1, 0);
+}
+
+sub _cleanup {
+  my $self = shift;
+  $NDN->timedout($self->{dns}) if $NDN && $self->{dns};
+  return unless my $reactor = $self->reactor;
+  $self->{$_} && $reactor->remove(delete $self->{$_}) for qw(dns timer handle);
+  return $self;
+}
+
+sub _connect {
+  my ($self, $args) = @_;
+
+  my $path = $args->{path};
+  my $handle = $self->{handle} = $args->{handle};
+
+  unless ($handle) {
+    my $class = $path ? 'IO::Socket::UNIX' : 'IO::Socket::IP';
+    my %options = (Blocking => 0);
+
+    # UNIX domain socket
+    if ($path) { $options{Peer} = $path }
+
+    # IP socket
+    else {
+      if (my $info = $args->{addr_info}) { $options{PeerAddrInfo} = $info }
+      else {
+        $options{PeerAddr} = $args->{socks_address} || $args->{address};
+        $options{PeerPort} = _port($args);
+      }
+      $options{LocalAddr} = $args->{local_address} if $args->{local_address};
+    }
+
+    return $self->emit(error => "Can't connect: $@")
+      unless $self->{handle} = $handle = $class->new(%options);
+  }
+  $handle->blocking(0);
+
+  $path ? $self->_try_socks($args) : $self->_wait('_ready', $handle, $args);
+}
+
+sub _port { $_[0]{socks_port} || $_[0]{port} || ($_[0]{tls} ? 443 : 80) }
+
+sub _ready {
+  my ($self, $args) = @_;
+
+  # Socket changes in between attempts and needs to be re-added for epoll/kqueue
+  my $handle = $self->{handle};
+  unless ($handle->connect) {
+    return $self->emit(error => $!) unless $! == EINPROGRESS;
+    $self->reactor->remove($handle);
+    return $self->_wait('_ready', $handle, $args);
+  }
+
+  return $self->emit(error => $! || 'Not connected') unless $handle->connected;
+
+  # Disable Nagle's algorithm
+  setsockopt $handle, IPPROTO_TCP, TCP_NODELAY, 1;
+
+  $self->_try_socks($args);
+}
+
+sub _socks {
+  my ($self, $args) = @_;
+
+  # Connected
+  my $handle = $self->{handle};
+  return $self->_try_tls($args) if $handle->ready;
+
+  # Switch between reading and writing
+  my $err = $IO::Socket::Socks::SOCKS_ERROR;
+  if    ($err == READ)  { $self->reactor->watch($handle, 1, 0) }
+  elsif ($err == WRITE) { $self->reactor->watch($handle, 1, 1) }
+  else                  { $self->emit(error => $err) }
+}
+
+sub _try_socks {
+  my ($self, $args) = @_;
+
+  my $handle = $self->{handle};
+  return $self->_try_tls($args) unless $args->{socks_address};
+  return $self->emit(
+    error => 'IO::Socket::Socks 0.64+ required for SOCKS support')
+    unless SOCKS;
+
+  my %options = (ConnectAddr => $args->{address}, ConnectPort => $args->{port});
+  @options{qw(AuthType Username Password)}
+    = ('userpass', @$args{qw(socks_user socks_pass)})
+    if $args->{socks_user};
+  my $reactor = $self->reactor;
+  $reactor->remove($handle);
+  return $self->emit(error => 'SOCKS upgrade failed')
+    unless IO::Socket::Socks->start_SOCKS($handle, %options);
+
+  $self->_wait('_socks', $handle, $args);
+}
+
+sub _try_tls {
+  my ($self, $args) = @_;
+
+  my $handle = $self->{handle};
+  return $self->_cleanup->emit(connect => $handle) unless $args->{tls};
+  my $reactor = $self->reactor;
+  $reactor->remove($handle);
+
+  # Start TLS handshake
+  weaken $self;
+  my $tls = Mojo::IOLoop::TLS->new($handle)->reactor($self->reactor);
+  $tls->on(upgrade => sub { $self->_cleanup->emit(connect => pop) });
+  $tls->on(error => sub { $self->emit(error => pop) });
+  $tls->negotiate(%$args);
+}
+
+sub _wait {
+  my ($self, $next, $handle, $args) = @_;
+  weaken $self;
+  $self->reactor->io($handle => sub { $self->$next($args) })
+    ->watch($handle, 0, 1);
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::IOLoop::Client - Non-blocking TCP/IP and UNIX domain socket client
+
+=head1 SYNOPSIS
+
+  use Mojo::IOLoop::Client;
+
+  # Create socket connection
+  my $client = Mojo::IOLoop::Client->new;
+  $client->on(connect => sub {
+    my ($client, $handle) = @_;
+    ...
+  });
+  $client->on(error => sub {
+    my ($client, $err) = @_;
+    ...
+  });
+  $client->connect(address => 'example.com', port => 80);
+
+  # Start reactor if necessary
+  $client->reactor->start unless $client->reactor->is_running;
+
+=head1 DESCRIPTION
+
+L<Mojo::IOLoop::Client> opens TCP/IP and UNIX domain socket connections for
+L<Mojo::IOLoop>.
+
+=head1 EVENTS
+
+L<Mojo::IOLoop::Client> inherits all events from L<Mojo::EventEmitter> and can
+emit the following new ones.
+
+=head2 connect
+
+  $client->on(connect => sub {
+    my ($client, $handle) = @_;
+    ...
+  });
+
+Emitted once the connection is established.
+
+=head2 error
+
+  $client->on(error => sub {
+    my ($client, $err) = @_;
+    ...
+  });
+
+Emitted if an error occurs on the connection, fatal if unhandled.
+
+=head1 ATTRIBUTES
+
+L<Mojo::IOLoop::Client> implements the following attributes.
+
+=head2 reactor
+
+  my $reactor = $client->reactor;
+  $client     = $client->reactor(Mojo::Reactor::Poll->new);
+
+Low-level event reactor, defaults to the C<reactor> attribute value of the
+global L<Mojo::IOLoop> singleton.
+
+=head1 METHODS
+
+L<Mojo::IOLoop::Client> inherits all methods from L<Mojo::EventEmitter> and
+implements the following new ones.
+
+=head2 can_nnr
+
+  my $bool = Mojo::IOLoop::Client->can_nnr;
+
+True if L<Net::DNS::Native> 0.15+ is installed and non-blocking name resolution
+support enabled.
+
+=head2 can_socks
+
+  my $bool = Mojo::IOLoop::Client->can_socks;
+
+True if L<IO::Socket::SOCKS> 0.64+ is installed and SOCKS5 support enabled.
+
+=head2 connect
+
+  $client->connect(address => '127.0.0.1', port => 3000);
+  $client->connect({address => '127.0.0.1', port => 3000});
+
+Open a socket connection to a remote host. Note that non-blocking name
+resolution depends on L<Net::DNS::Native> (0.15+), SOCKS5 support on
+L<IO::Socket::Socks> (0.64), and TLS support on L<IO::Socket::SSL> (2.009+).
+
+These options are currently available:
+
+=over 2
+
+=item address
+
+  address => 'mojolicious.org'
+
+Address or host name of the peer to connect to, defaults to C<127.0.0.1>.
+
+=item handle
+
+  handle => $handle
+
+Use an already prepared L<IO::Socket::IP> object.
+
+=item local_address
+
+  local_address => '127.0.0.1'
+
+Local address to bind to.
+
+=item path
+
+  path => '/tmp/myapp.sock'
+
+Path of UNIX domain socket to connect to.
+
+=item port
+
+  port => 80
+
+Port to connect to, defaults to C<80> or C<443> with C<tls> option.
+
+=item socks_address
+
+  socks_address => '127.0.0.1'
+
+Address or host name of SOCKS5 proxy server to use for connection.
+
+=item socks_pass
+
+  socks_pass => 'secr3t'
+
+Password to use for SOCKS5 authentication.
+
+=item socks_port
+
+  socks_port => 9050
+
+Port of SOCKS5 proxy server to use for connection.
+
+=item socks_user
+
+  socks_user => 'sri'
+
+Username to use for SOCKS5 authentication.
+
+=item timeout
+
+  timeout => 15
+
+Maximum amount of time in seconds establishing connection may take before
+getting canceled, defaults to C<10>.
+
+=item tls
+
+  tls => 1
+
+Enable TLS.
+
+=item tls_ca
+
+  tls_ca => '/etc/tls/ca.crt'
+
+Path to TLS certificate authority file.
+
+=item tls_cert
+
+  tls_cert => '/etc/tls/client.crt'
+
+Path to the TLS certificate file.
+
+=item tls_key
+
+  tls_key => '/etc/tls/client.key'
+
+Path to the TLS key file.
+
+=item tls_protocols
+
+  tls_protocols => ['foo', 'bar']
+
+ALPN protocols to negotiate.
+
+=item tls_verify
+
+  tls_verify => 0x00
+
+TLS verification mode.
+
+=back
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/IOLoop/Delay.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/IOLoop/Delay.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/IOLoop/Delay.pm	(revision 32205)
@@ -0,0 +1,249 @@
+package Mojo::IOLoop::Delay;
+use Mojo::Base 'Mojo::Promise';
+
+# DEPRECATED!
+use base 'Mojo::EventEmitter';
+
+sub begin {
+  my ($self, $offset, $len) = @_;
+  $self->{pending}++;
+  my $id = $self->{counter}++;
+  return sub { $self->_step($id, $offset // 1, $len, @_) };
+}
+
+sub pass { $_[0]->begin->(@_) }
+
+sub steps {
+  my ($self, @steps) = @_;
+  $self->{steps} = \@steps;
+  $self->ioloop->next_tick($self->begin);
+
+  # DEPRECATED!
+  $self->{deprecated} ||= $self->on(error => sub { });
+
+  return $self;
+}
+
+sub _step {
+  my ($self, $id, $offset, $len) = (shift, shift, shift, shift);
+
+  $self->{args}[$id]
+    = [@_ ? defined $len ? splice @_, $offset, $len : splice @_, $offset : ()];
+  return $self if $self->{fail} || --$self->{pending} || $self->{lock};
+  local $self->{lock} = 1;
+  my @args = map {@$_} @{delete $self->{args}};
+
+  $self->{counter} = 0;
+  if (my $cb = shift @{$self->{steps}}) {
+    unless (eval { $self->$cb(@args); 1 }) {
+      my $err = $@;
+      @{$self}{qw(fail steps)} = (1, []);
+      return $self->reject($err)->emit(error => $err);
+    }
+  }
+
+  ($self->{steps} = []) and return $self->resolve(@args)->emit(finish => @args)
+    unless $self->{counter};
+  $self->ioloop->next_tick($self->begin) unless $self->{pending};
+  return $self;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::IOLoop::Delay - Promises/A+ and flow-control helpers
+
+=head1 SYNOPSIS
+
+  use Mojo::IOLoop::Delay;
+
+  # Synchronize multiple non-blocking operations
+  my $delay = Mojo::IOLoop::Delay->new;
+  $delay->steps(sub { say 'BOOM!' });
+  for my $i (1 .. 10) {
+    my $end = $delay->begin;
+    Mojo::IOLoop->timer($i => sub {
+      say 10 - $i;
+      $end->();
+    });
+  }
+  $delay->wait;
+
+  # Sequentialize multiple non-blocking operations
+  Mojo::IOLoop::Delay->new->steps(
+
+    # First step (simple timer)
+    sub {
+      my $delay = shift;
+      Mojo::IOLoop->timer(2 => $delay->begin);
+      say 'Second step in 2 seconds.';
+    },
+
+    # Second step (concurrent timers)
+    sub {
+      my ($delay, @args) = @_;
+      Mojo::IOLoop->timer(1 => $delay->begin);
+      Mojo::IOLoop->timer(3 => $delay->begin);
+      say 'Third step in 3 seconds.';
+    },
+
+    # Third step (the end)
+    sub {
+      my ($delay, @args) = @_;
+      say 'And done after 5 seconds total.';
+    }
+  )->wait;
+
+=head1 DESCRIPTION
+
+L<Mojo::IOLoop::Delay> adds flow-control helpers to L<Mojo::Promise>, which can
+help you avoid deep nested closures that often result from continuation-passing
+style.
+
+  use Mojo::IOLoop;
+
+  # These deep nested closures are often referred to as "Callback Hell"
+  Mojo::IOLoop->timer(3 => sub {
+    my loop = shift;
+
+    say '3 seconds';
+    Mojo::IOLoop->timer(3 => sub {
+      my $loop = shift;
+
+      say '6 seconds';
+      Mojo::IOLoop->timer(3 => sub {
+        my $loop = shift;
+
+        say '9 seconds';
+        Mojo::IOLoop->stop;
+      });
+    });
+  });
+
+  Mojo::IOLoop->start;
+
+The idea behind L<Mojo::IOLoop::Delay> is to turn the nested closures above into
+a flat series of closures. In the example below, the call to L</"begin"> creates
+a code reference that we can pass to L<Mojo::IOLoop/"timer"> as a callback, and
+that leads to the next closure in the series when executed.
+
+  use Mojo::IOLoop;
+
+  # Instead of nested closures we now have a simple chain of steps
+  my $delay = Mojo::IOLoop->delay(
+    sub {
+      my $delay = shift;
+      Mojo::IOLoop->timer(3 => $delay->begin);
+    },
+    sub {
+      my $delay = shift;
+      say '3 seconds';
+      Mojo::IOLoop->timer(3 => $delay->begin);
+    },
+    sub {
+      my $delay = shift;
+      say '6 seconds';
+      Mojo::IOLoop->timer(3 => $delay->begin);
+    },
+    sub {
+      my $delay = shift;
+      say '9 seconds';
+    }
+  );
+  $delay->wait;
+
+Another positive side effect of this pattern is that we do not need to call
+L<Mojo::IOLoop/"start"> and L<Mojo::IOLoop/"stop"> manually, because we know
+exactly when our chain of L</"steps"> has reached the end. So
+L<Mojo::Promise/"wait"> can stop the event loop automatically if it had to be
+started at all in the first place.
+
+=head1 ATTRIBUTES
+
+L<Mojo::IOLoop::Delay> inherits all attributes from L<Mojo::Promise>.
+
+=head1 METHODS
+
+L<Mojo::IOLoop::Delay> inherits all methods from L<Mojo::Promise> and implements
+the following new ones.
+
+=head2 begin
+
+  my $cb = $delay->begin;
+  my $cb = $delay->begin($offset);
+  my $cb = $delay->begin($offset, $len);
+
+Indicate an active event by incrementing the event counter, the returned
+code reference can be used as a callback, and needs to be executed when the
+event has completed to decrement the event counter again. When all code
+references generated by this method have been executed and the event counter has
+reached zero, L</"steps"> will continue.
+
+  # Capture all arguments except for the first one (invocant)
+  my $delay = Mojo::IOLoop->delay(sub {
+    my ($delay, $err, $stream) = @_;
+    ...
+  });
+  Mojo::IOLoop->client({port => 3000} => $delay->begin);
+  $delay->wait;
+
+Arguments passed to the returned code reference are spliced with the given
+offset and length, defaulting to an offset of C<1> with no default length. The
+arguments are then combined in the same order L</"begin"> was called, and passed
+together to the next step.
+
+  # Capture all arguments
+  my $delay = Mojo::IOLoop->delay(sub {
+    my ($delay, $loop, $err, $stream) = @_;
+    ...
+  });
+  Mojo::IOLoop->client({port => 3000} => $delay->begin(0));
+  $delay->wait;
+
+  # Capture only the second argument
+  my $delay = Mojo::IOLoop->delay(sub {
+    my ($delay, $err) = @_;
+    ...
+  });
+  Mojo::IOLoop->client({port => 3000} => $delay->begin(1, 1));
+  $delay->wait;
+
+  # Capture and combine arguments
+  my $delay = Mojo::IOLoop->delay(sub {
+    my ($delay, $three_err, $three_stream, $four_err, $four_stream) = @_;
+    ...
+  });
+  Mojo::IOLoop->client({port => 3000} => $delay->begin);
+  Mojo::IOLoop->client({port => 4000} => $delay->begin);
+  $delay->wait;
+
+=head2 pass
+
+  $delay = $delay->pass;
+  $delay = $delay->pass(@args);
+
+Shortcut for passing values between L</"steps">.
+
+  # Longer version
+  $delay->begin(0)->(@args);
+
+=head2 steps
+
+  $delay = $delay->steps(sub {...}, sub {...});
+
+Sequentialize multiple events, every time the event counter reaches zero a
+callback will run, the first one automatically runs during the next reactor tick
+unless it is delayed by incrementing the event counter. This chain will continue
+until there are no remaining callbacks, a callback does not increment the event
+counter or an exception gets thrown in a callback. Finishing the chain will also
+result in the promise being fulfilled, or if an exception got thrown it will be
+rejected.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/IOLoop/Server.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/IOLoop/Server.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/IOLoop/Server.pm	(revision 32205)
@@ -0,0 +1,332 @@
+package Mojo::IOLoop::Server;
+use Mojo::Base 'Mojo::EventEmitter';
+
+use Carp 'croak';
+use IO::Socket::IP;
+use IO::Socket::UNIX;
+use Mojo::IOLoop;
+use Mojo::IOLoop::TLS;
+use Scalar::Util 'weaken';
+use Socket qw(IPPROTO_TCP TCP_NODELAY);
+
+has reactor => sub { Mojo::IOLoop->singleton->reactor };
+
+sub DESTROY {
+  my $self = shift;
+  $ENV{MOJO_REUSE} =~ s/(?:^|\,)\Q$self->{reuse}\E// if $self->{reuse};
+  $self->stop if $self->{handle} && $self->reactor;
+}
+
+sub generate_port {
+  IO::Socket::IP->new(Listen => 5, LocalAddr => '127.0.0.1')->sockport;
+}
+
+sub handle { shift->{handle} }
+
+sub is_accepting { !!shift->{active} }
+
+sub listen {
+  my ($self, $args) = (shift, ref $_[0] ? $_[0] : {@_});
+
+  # Look for reusable file descriptor
+  my $path    = $args->{path};
+  my $address = $args->{address} || '0.0.0.0';
+  my $port    = $args->{port};
+  $ENV{MOJO_REUSE} ||= '';
+  my $fd
+    = ($path && $ENV{MOJO_REUSE} =~ /(?:^|\,)unix:\Q$path\E:(\d+)/)
+    || ($port && $ENV{MOJO_REUSE} =~ /(?:^|\,)\Q$address:$port\E:(\d+)/)
+    ? $1
+    : undef;
+
+  # Allow file descriptor inheritance
+  local $^F = 1023;
+
+  # Reuse file descriptor
+  my $handle;
+  my $class = $path ? 'IO::Socket::UNIX' : 'IO::Socket::IP';
+  if (defined($fd //= $args->{fd})) {
+    $handle = $class->new_from_fd($fd, 'r')
+      or croak "Can't open file descriptor $fd: $!";
+  }
+
+  else {
+    my %options
+      = (Listen => $args->{backlog} // SOMAXCONN, Type => SOCK_STREAM);
+
+    # UNIX domain socket
+    my $reuse;
+    if ($path) {
+      unlink $path if -S $path;
+      $options{Local} = $path;
+      $handle = $class->new(%options) or croak "Can't create listen socket: $!";
+      $reuse = $self->{reuse} = join ':', 'unix', $path, fileno $handle;
+    }
+
+    # IP socket
+    else {
+      $options{LocalAddr} = $address;
+      $options{LocalAddr} =~ s/[\[\]]//g;
+      $options{LocalPort} = $port if $port;
+      $options{ReuseAddr} = 1;
+      $options{ReusePort} = $args->{reuse};
+      $handle = $class->new(%options) or croak "Can't create listen socket: $@";
+      $fd     = fileno $handle;
+      $reuse  = $self->{reuse} = join ':', $address, $handle->sockport, $fd;
+    }
+
+    $ENV{MOJO_REUSE} .= length $ENV{MOJO_REUSE} ? ",$reuse" : "$reuse";
+  }
+  $handle->blocking(0);
+  @$self{qw(args handle)} = ($args, $handle);
+
+  croak 'IO::Socket::SSL 2.009+ required for TLS support'
+    if !Mojo::IOLoop::TLS->can_tls && $args->{tls};
+}
+
+sub port { shift->{handle}->sockport }
+
+sub start {
+  my $self = shift;
+  weaken $self;
+  ++$self->{active}
+    and $self->reactor->io($self->{handle} => sub { $self->_accept });
+}
+
+sub stop { delete($_[0]{active}) and $_[0]->reactor->remove($_[0]{handle}) }
+
+sub _accept {
+  my $self = shift;
+
+  # Greedy accept
+  my $args     = $self->{args};
+  my $accepted = 0;
+  while ($self->{active} && !($args->{single_accept} && $accepted++)) {
+    return unless my $handle = $self->{handle}->accept;
+    $handle->blocking(0);
+
+    # Disable Nagle's algorithm
+    setsockopt $handle, IPPROTO_TCP, TCP_NODELAY, 1;
+
+    $self->emit(accept => $handle) and next unless $args->{tls};
+
+    # Start TLS handshake
+    my $tls = Mojo::IOLoop::TLS->new($handle)->reactor($self->reactor);
+    $tls->on(upgrade => sub { $self->emit(accept => pop) });
+    $tls->on(error => sub { });
+    $tls->negotiate(%$args, server => 1);
+  }
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::IOLoop::Server - Non-blocking TCP and UNIX domain socket server
+
+=head1 SYNOPSIS
+
+  use Mojo::IOLoop::Server;
+
+  # Create listen socket
+  my $server = Mojo::IOLoop::Server->new;
+  $server->on(accept => sub {
+    my ($server, $handle) = @_;
+    ...
+  });
+  $server->listen(port => 3000);
+
+  # Start and stop accepting connections
+  $server->start;
+  $server->stop;
+
+  # Start reactor if necessary
+  $server->reactor->start unless $server->reactor->is_running;
+
+=head1 DESCRIPTION
+
+L<Mojo::IOLoop::Server> accepts TCP/IP and UNIX domain socket connections for
+L<Mojo::IOLoop>.
+
+=head1 EVENTS
+
+L<Mojo::IOLoop::Server> inherits all events from L<Mojo::EventEmitter> and can
+emit the following new ones.
+
+=head2 accept
+
+  $server->on(accept => sub {
+    my ($server, $handle) = @_;
+    ...
+  });
+
+Emitted for each accepted connection.
+
+=head1 ATTRIBUTES
+
+L<Mojo::IOLoop::Server> implements the following attributes.
+
+=head2 reactor
+
+  my $reactor = $server->reactor;
+  $server     = $server->reactor(Mojo::Reactor::Poll->new);
+
+Low-level event reactor, defaults to the C<reactor> attribute value of the
+global L<Mojo::IOLoop> singleton.
+
+=head1 METHODS
+
+L<Mojo::IOLoop::Server> inherits all methods from L<Mojo::EventEmitter> and
+implements the following new ones.
+
+=head2 generate_port
+
+  my $port = Mojo::IOLoop::Server->generate_port;
+
+Find a free TCP port, primarily used for tests.
+
+=head2 handle
+
+  my $handle = $server->handle;
+
+Get handle for server, usually an L<IO::Socket::IP> object.
+
+=head2 is_accepting
+
+  my $bool = $server->is_accepting;
+
+Check if connections are currently being accepted.
+
+=head2 listen
+
+  $server->listen(port => 3000);
+  $server->listen({port => 3000});
+
+Create a new listen socket. Note that TLS support depends on L<IO::Socket::SSL>
+(2.009+).
+
+These options are currently available:
+
+=over 2
+
+=item address
+
+  address => '127.0.0.1'
+
+Local address to listen on, defaults to C<0.0.0.0>.
+
+=item backlog
+
+  backlog => 128
+
+Maximum backlog size, defaults to C<SOMAXCONN>.
+
+=item fd
+
+  fd => 3
+
+File descriptor with an already prepared listen socket.
+
+=item path
+
+  path => '/tmp/myapp.sock'
+
+Path for UNIX domain socket to listen on.
+
+=item port
+
+  port => 80
+
+Port to listen on, defaults to a random port.
+
+=item reuse
+
+  reuse => 1
+
+Allow multiple servers to use the same port with the C<SO_REUSEPORT> socket
+option.
+
+=item single_accept
+
+  single_accept => 1
+
+Only accept one connection at a time.
+
+=item tls
+
+  tls => 1
+
+Enable TLS.
+
+=item tls_ca
+
+  tls_ca => '/etc/tls/ca.crt'
+
+Path to TLS certificate authority file.
+
+=item tls_cert
+
+  tls_cert => '/etc/tls/server.crt'
+  tls_cert => {'mojolicious.org' => '/etc/tls/mojo.crt'}
+
+Path to the TLS cert file, defaults to a built-in test certificate.
+
+=item tls_ciphers
+
+  tls_ciphers => 'AES128-GCM-SHA256:RC4:HIGH:!MD5:!aNULL:!EDH'
+
+TLS cipher specification string. For more information about the format see
+L<https://www.openssl.org/docs/manmaster/apps/ciphers.html#CIPHER-STRINGS>.
+
+=item tls_key
+
+  tls_key => '/etc/tls/server.key'
+  tls_key => {'mojolicious.org' => '/etc/tls/mojo.key'}
+
+Path to the TLS key file, defaults to a built-in test key.
+
+=item tls_protocols
+
+  tls_protocols => ['foo', 'bar']
+
+ALPN protocols to negotiate.
+
+=item tls_verify
+
+  tls_verify => 0x00
+
+TLS verification mode.
+
+=item tls_version
+
+  tls_version => 'TLSv1_2'
+
+TLS protocol version.
+
+=back
+
+=head2 port
+
+  my $port = $server->port;
+
+Get port this server is listening on.
+
+=head2 start
+
+  $server->start;
+
+Start or resume accepting connections.
+
+=head2 stop
+
+  $server->stop;
+
+Stop accepting connections.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/IOLoop/Stream.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/IOLoop/Stream.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/IOLoop/Stream.pm	(revision 32205)
@@ -0,0 +1,317 @@
+package Mojo::IOLoop::Stream;
+use Mojo::Base 'Mojo::EventEmitter';
+
+use Errno qw(EAGAIN ECONNRESET EINTR EWOULDBLOCK);
+use Mojo::IOLoop;
+use Mojo::Util;
+use Scalar::Util 'weaken';
+
+has reactor => sub { Mojo::IOLoop->singleton->reactor };
+
+sub DESTROY { Mojo::Util::_global_destruction() or shift->close }
+
+sub close {
+  my $self = shift;
+  return unless my $reactor = $self->reactor;
+  return unless my $handle  = delete $self->timeout(0)->{handle};
+  $reactor->remove($handle);
+  $self->emit('close');
+}
+
+sub close_gracefully { $_[0]->is_writing ? $_[0]{graceful}++ : $_[0]->close }
+
+sub handle { shift->{handle} }
+
+sub is_readable {
+  my $self = shift;
+  $self->_again;
+  return $self->{handle} && Mojo::Util::_readable(0, fileno $self->{handle});
+}
+
+sub is_writing {
+  my $self = shift;
+  return undef unless $self->{handle};
+  return !!length($self->{buffer}) || $self->has_subscribers('drain');
+}
+
+sub new { shift->SUPER::new(handle => shift, buffer => '', timeout => 15) }
+
+sub start {
+  my $self = shift;
+
+  # Resume
+  my $reactor = $self->reactor;
+  return $reactor->watch($self->{handle}, 1, $self->is_writing)
+    if delete $self->{paused};
+
+  weaken $self;
+  my $cb = sub { pop() ? $self->_write : $self->_read };
+  $reactor->io($self->timeout($self->{timeout})->{handle} => $cb);
+}
+
+sub steal_handle {
+  my $self = shift;
+  $self->reactor->remove($self->{handle});
+  return delete $self->{handle};
+}
+
+sub stop {
+  my $self = shift;
+  $self->reactor->watch($self->{handle}, 0, $self->is_writing)
+    unless $self->{paused}++;
+}
+
+sub timeout {
+  my $self = shift;
+
+  return $self->{timeout} unless @_;
+
+  my $reactor = $self->reactor;
+  $reactor->remove(delete $self->{timer}) if $self->{timer};
+  return $self unless my $timeout = $self->{timeout} = shift;
+  weaken $self;
+  $self->{timer}
+    = $reactor->timer($timeout => sub { $self->emit('timeout')->close });
+
+  return $self;
+}
+
+sub write {
+  my ($self, $chunk, $cb) = @_;
+
+  # IO::Socket::SSL will corrupt data with the wrong internal representation
+  utf8::downgrade $chunk;
+  $self->{buffer} .= $chunk;
+  if ($cb) { $self->once(drain => $cb) }
+  elsif (!length $self->{buffer}) { return $self }
+  $self->reactor->watch($self->{handle}, !$self->{paused}, 1)
+    if $self->{handle};
+
+  return $self;
+}
+
+sub _again { $_[0]->reactor->again($_[0]{timer}) if $_[0]{timer} }
+
+sub _read {
+  my $self = shift;
+
+  my $read = $self->{handle}->sysread(my $buffer, 131072, 0);
+  return $read == 0 ? $self->close : $self->emit(read => $buffer)->_again
+    if defined $read;
+
+  # Retry
+  return if $! == EAGAIN || $! == EINTR || $! == EWOULDBLOCK;
+
+  # Closed (maybe real error)
+  $! == ECONNRESET ? $self->close : $self->emit(error => $!)->close;
+}
+
+sub _write {
+  my $self = shift;
+
+  # Handle errors only when reading (to avoid timing problems)
+  my $handle = $self->{handle};
+  if (length $self->{buffer}) {
+    return unless defined(my $written = $handle->syswrite($self->{buffer}));
+    $self->emit(write => substr($self->{buffer}, 0, $written, ''))->_again;
+  }
+
+  $self->emit('drain') unless length $self->{buffer};
+  return if $self->is_writing;
+  return $self->close if $self->{graceful};
+  $self->reactor->watch($handle, !$self->{paused}, 0) if $self->{handle};
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::IOLoop::Stream - Non-blocking I/O stream
+
+=head1 SYNOPSIS
+
+  use Mojo::IOLoop::Stream;
+
+  # Create stream
+  my $stream = Mojo::IOLoop::Stream->new($handle);
+  $stream->on(read => sub {
+    my ($stream, $bytes) = @_;
+    ...
+  });
+  $stream->on(close => sub {
+    my $stream = shift;
+    ...
+  });
+  $stream->on(error => sub {
+    my ($stream, $err) = @_;
+    ...
+  });
+
+  # Start and stop watching for new data
+  $stream->start;
+  $stream->stop;
+
+  # Start reactor if necessary
+  $stream->reactor->start unless $stream->reactor->is_running;
+
+=head1 DESCRIPTION
+
+L<Mojo::IOLoop::Stream> is a container for I/O streams used by L<Mojo::IOLoop>.
+
+=head1 EVENTS
+
+L<Mojo::IOLoop::Stream> inherits all events from L<Mojo::EventEmitter> and can
+emit the following new ones.
+
+=head2 close
+
+  $stream->on(close => sub {
+    my $stream = shift;
+    ...
+  });
+
+Emitted if the stream gets closed.
+
+=head2 drain
+
+  $stream->on(drain => sub {
+    my $stream = shift;
+    ...
+  });
+
+Emitted once all data has been written.
+
+=head2 error
+
+  $stream->on(error => sub {
+    my ($stream, $err) = @_;
+    ...
+  });
+
+Emitted if an error occurs on the stream, fatal if unhandled.
+
+=head2 read
+
+  $stream->on(read => sub {
+    my ($stream, $bytes) = @_;
+    ...
+  });
+
+Emitted if new data arrives on the stream.
+
+=head2 timeout
+
+  $stream->on(timeout => sub {
+    my $stream = shift;
+    ...
+  });
+
+Emitted if the stream has been inactive for too long and will get closed
+automatically.
+
+=head2 write
+
+  $stream->on(write => sub {
+    my ($stream, $bytes) = @_;
+    ...
+  });
+
+Emitted if new data has been written to the stream.
+
+=head1 ATTRIBUTES
+
+L<Mojo::IOLoop::Stream> implements the following attributes.
+
+=head2 reactor
+
+  my $reactor = $stream->reactor;
+  $stream     = $stream->reactor(Mojo::Reactor::Poll->new);
+
+Low-level event reactor, defaults to the C<reactor> attribute value of the
+global L<Mojo::IOLoop> singleton.
+
+=head1 METHODS
+
+L<Mojo::IOLoop::Stream> inherits all methods from L<Mojo::EventEmitter> and
+implements the following new ones.
+
+=head2 close
+
+  $stream->close;
+
+Close stream immediately.
+
+=head2 close_gracefully
+
+  $stream->close_gracefully;
+
+Close stream gracefully.
+
+=head2 handle
+
+  my $handle = $stream->handle;
+
+Get handle for stream, usually an L<IO::Socket::IP> or L<IO::Socket::SSL>
+object.
+
+=head2 is_readable
+
+  my $bool = $stream->is_readable;
+
+Quick non-blocking check if stream is readable, useful for identifying tainted
+sockets.
+
+=head2 is_writing
+
+  my $bool = $stream->is_writing;
+
+Check if stream is writing.
+
+=head2 new
+
+  my $stream = Mojo::IOLoop::Stream->new($handle);
+
+Construct a new L<Mojo::IOLoop::Stream> object.
+
+=head2 start
+
+  $stream->start;
+
+Start or resume watching for new data on the stream.
+
+=head2 steal_handle
+
+  my $handle = $stream->steal_handle;
+
+Steal L</"handle"> and prevent it from getting closed automatically.
+
+=head2 stop
+
+  $stream->stop;
+
+Stop watching for new data on the stream.
+
+=head2 timeout
+
+  my $timeout = $stream->timeout;
+  $stream     = $stream->timeout(45);
+
+Maximum amount of time in seconds stream can be inactive before getting closed
+automatically, defaults to C<15>. Setting the value to C<0> will allow this
+stream to be inactive indefinitely.
+
+=head2 write
+
+  $stream = $stream->write($bytes);
+  $stream = $stream->write($bytes => sub {...});
+
+Write data to stream, the optional drain callback will be executed once all data
+has been written.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/IOLoop/Stream/HTTPClient.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/IOLoop/Stream/HTTPClient.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/IOLoop/Stream/HTTPClient.pm	(revision 32205)
@@ -0,0 +1,205 @@
+package Mojo::IOLoop::Stream::HTTPClient;
+use Mojo::Base 'Mojo::IOLoop::Stream';
+
+use Mojo::Transaction::WebSocket;
+use Mojo::Util 'term_escape';
+use Mojo::WebSocket 'challenge';
+use Scalar::Util 'weaken';
+
+use constant DEBUG => $ENV{MOJO_CLIENT_DEBUG} || 0;
+
+has request_timeout => sub { $ENV{MOJO_REQUEST_TIMEOUT} // 0 };
+
+sub new {
+  my $self = shift->SUPER::new(@_);
+  $self->on(read => sub { shift->_read_content(shift) });
+  $self->on(close => sub { $_[0]->{closing}++ || $_[0]->_finish(1) });
+  return $self;
+}
+
+sub process {
+  my ($self, $tx) = @_;
+
+  $self->{tx} = $tx;
+  my $handle = $self->handle;
+  unless ($handle->isa('IO::Socket::UNIX')) {
+    $tx->local_address($handle->sockhost)->local_port($handle->sockport);
+    $tx->remote_address($handle->peerhost)->remote_port($handle->peerport);
+  }
+
+  weaken $self;
+  $tx->on(resume => sub { $self->_write_content });
+  if (my $timeout = $self->request_timeout) {
+    $self->{req_timeout} = $self->reactor->timer(
+      $timeout => sub { $self->_error('Request timeout') });
+  }
+  $self->_write_content;
+}
+
+sub _error {
+  my ($self, $err) = @_;
+  $self->{tx}->res->error({message => $err}) if $self->{tx};
+  $self->_finish(1);
+}
+
+sub _finish {
+  my ($self, $close) = @_;
+
+  # Remove request timeout and finish transaction
+  $self->reactor->remove($self->{req_timeout}) if $self->{req_timeout};
+  return ++$self->{closing} && $self->close unless my $tx = delete $self->{tx};
+
+  # Premature connection close
+  my $res = $tx->res->finish;
+  if ($close && !$res->code && !$res->error) {
+    $res->error({message => 'Premature connection close'});
+  }
+
+  # Upgrade connection to WebSocket
+  if (my $ws = $self->_upgrade($tx)) {
+    $self->emit(upgrade => $ws);
+    return $ws->client_read($ws->handshake->res->content->leftovers);
+  }
+
+  ++$self->{closing} && $self->close_gracefully
+    if $tx->error || !$tx->keep_alive;
+  $res->error({message => $res->message, code => $res->code}) if $res->is_error;
+  $tx->closed;
+}
+
+sub _read_content {
+  my ($self, $chunk) = @_;
+
+  # Corrupted connection
+  return $self->close unless my $tx = $self->{tx};
+
+  warn term_escape "-- Client <<< Server (@{[_url($tx)]})\n$chunk\n" if DEBUG;
+  $tx->client_read($chunk);
+  $self->_finish if $tx->is_finished;
+}
+
+sub _upgrade {
+  my ($self, $tx) = @_;
+  my $code = $tx->res->code // 0;
+  return undef unless $tx->req->is_handshake && $code == 101;
+  my $ws = Mojo::Transaction::WebSocket->new(handshake => $tx, masked => 1);
+  return challenge($ws) ? $ws->established(1) : undef;
+}
+
+sub _url { shift->req->url->to_abs }
+
+sub _write_content {
+  my $self = shift;
+
+  # Protect from resume event recursion
+  return if !(my $tx = $self->{tx}) || $self->{cont_writing};
+  local $self->{cont_writing} = 1;
+  my $chunk = $tx->client_write;
+  warn term_escape "-- Client >>> Server (@{[_url($tx)]})\n$chunk\n" if DEBUG;
+  return unless length $chunk;
+  $self->write($chunk => sub { $_[0]->_write_content });
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::IOLoop::Stream::HTTPClient - Non-blocking I/O HTTP client stream
+
+=head1 SYNOPSIS
+
+  use Mojo::IOLoop::Client;
+  use Mojo::IOLoop::Stream::HTTPClient;
+  use Mojo::Transaction::HTTP;
+  
+  # Create transaction
+  my $tx = Mojo::Transaction::HTTP->new;
+  $tx->req->method('GET')
+  $tx->url->parse('https://mojolicious.org');
+  $tx->on(
+    finish => sub {
+      my $tx = shift;
+      say $tx->res->code;
+    }
+  );
+  
+  # Create socket connection
+  my $client = Mojo::IOLoop::Client->new;
+  $client->on(
+    connect => sub {
+      my $stream = Mojo::IOLoop::Stream::HTTPClient->new(pop);
+      $stream->start;
+      $stream->process($tx);
+    }
+  );
+  $client->connect(address => 'mojolicious.org', port => 80);
+
+  # Start reactor if necessary
+  $stream->reactor->start unless $stream->reactor->is_running;
+
+=head1 DESCRIPTION
+
+L<Mojo::IOLoop::Stream::HTTPClient> is a container for I/O streams used by
+L<Mojo::IOLoop> to support the HTTP protocol client-side.
+
+=head1 EVENTS
+
+L<Mojo::IOLoop::Stream::HTTPClient> inherits all events from
+L<Mojo::IOLoop::Stream> and can emit the following new ones.
+
+=head2 upgrade
+
+  $stream->on(upgrade => sub {
+    my ($stream, $ws) = @_;
+    ...
+  });
+
+Emitted when the connection should be upgraded to the WebSocket protocol.
+
+=head1 ATTRIBUTES
+
+L<Mojo::IOLoop::Stream::HTTPClient> inherits all attributes from
+L<Mojo::IOLoop::Stream> and implements the following ones.
+
+=head2 request_timeout
+
+  my $timeout = $stream->request_timeout;
+  $stream     = $stream->request_timeout(5);
+
+Maximum amount of time in seconds sending the request and receiving a whole
+response may take before getting canceled, defaults to the value of the
+C<MOJO_REQUEST_TIMEOUT> environment variable or C<0>. Setting the value to C<0>
+will allow to wait indefinitely.
+
+=head1 METHODS
+
+L<Mojo::IOLoop::Stream::HTTPClient> inherits all methods from
+L<Mojo::IOLoop::Stream> and implements the following new ones.
+
+=head2 new
+
+  my $stream = Mojo::IOLoop::Stream::HTTPClient->new($handle);
+
+Construct a new L<Mojo::IOLoop::Stream::HTTPClient> object.
+
+=head2 process
+
+  $stream->process(Mojo::Transaction::HTTP->new);
+
+Process a L<Mojo::Transaction::HTTP> object with the current connection.
+
+=head1 DEBUGGING
+
+You can set the C<MOJO_CLIENT_DEBUG> environment variable to get some advanced
+diagnostics information printed to C<STDERR>.
+
+  MOJO_CLIENT_DEBUG=1
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicious.org>.
+
+=cut
+
Index: main/trunk/greenstone2/perllib/cpan/Mojo/IOLoop/Stream/HTTPServer.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/IOLoop/Stream/HTTPServer.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/IOLoop/Stream/HTTPServer.pm	(revision 32205)
@@ -0,0 +1,244 @@
+package Mojo::IOLoop::Stream::HTTPServer;
+use Mojo::Base 'Mojo::IOLoop::Stream';
+
+use Mojo::Server;
+use Mojo::Transaction::WebSocket;
+use Mojo::Util 'term_escape';
+use Mojo::WebSocket 'server_handshake';
+use Scalar::Util 'weaken';
+
+use constant DEBUG => $ENV{MOJO_SERVER_DEBUG} || 0;
+
+has app => sub { Mojo::Server->new->build_app('Mojo::HelloWorld') };
+has max_requests => 100;
+
+sub new {
+  my $self = shift->SUPER::new(@_);
+  $self->on(read  => sub { shift->_read_content(shift) });
+  $self->on(close => sub { shift->_close });
+  return $self;
+}
+
+sub _build_tx {
+  my $self = shift;
+
+  my $tx = $self->app->build_tx;
+  $tx->res->headers->server('Mojolicious (Perl)');
+  my $handle = $self->handle;
+  unless ($handle->isa('IO::Socket::UNIX')) {
+    $tx->local_address($handle->sockhost)->local_port($handle->sockport);
+    $tx->remote_address($handle->peerhost)->remote_port($handle->peerport);
+  }
+  $tx->req->url->base->scheme('https') if $handle->isa('IO::Socket::SSL');
+
+  weaken $self;
+  $tx->on(
+    request => sub {
+      my $tx = shift;
+
+      # WebSocket
+      my $req = $tx->req;
+      if ($req->is_handshake) {
+        my $ws = $self->{next}
+          = Mojo::Transaction::WebSocket->new(handshake => $tx);
+        $self->emit(request => server_handshake $ws);
+      }
+
+      # HTTP
+      else { $self->emit(request => $tx) }
+
+      # Last keep-alive request or corrupted connection
+      $tx->res->headers->connection('close')
+        if ($self->{keep_alive} || 1) >= $self->max_requests || $req->error;
+
+      $tx->on(resume => sub { $self->_write_content });
+      $self->_write_content;
+    }
+  );
+
+  $self->emit(start => $tx);
+
+  # Kept alive if we have more than one request on the connection
+  return ++$self->{keep_alive} > 1 ? $tx->kept_alive(1) : $tx;
+}
+
+sub _close { delete($_[0]->{tx})->closed if $_[0]->{tx} }
+
+sub _finish {
+  my $self = shift;
+
+  # Always remove connection for WebSockets
+  return unless my $tx = $self->{tx};
+
+  # Finish transaction
+  delete($self->{tx})->closed;
+
+  # Upgrade connection to WebSocket
+  if (my $ws = delete $self->{next}) {
+
+    # Successful upgrade
+    if ($ws->handshake->res->code == 101) {
+      $self->emit(upgrade => $ws->established(1));
+    }
+
+    # Failed upgrade
+    else { $ws->closed }
+  }
+
+  # Close connection if necessary
+  return $self->close_gracefully if $tx->error || !$tx->keep_alive;
+
+  # Build new transaction for leftovers
+  return unless length(my $leftovers = $tx->req->content->leftovers);
+  $self->{tx} = $tx = $self->_build_tx;
+  $tx->server_read($leftovers);
+}
+
+sub _read_content {
+  my ($self, $chunk) = @_;
+  my $tx = $self->{tx} ||= $self->_build_tx;
+  warn term_escape "-- Server <<< Client (@{[_url($tx)]})\n$chunk\n" if DEBUG;
+  $tx->server_read($chunk);
+}
+
+sub _url { shift->req->url->to_abs }
+
+sub _write_content {
+  my $self = shift;
+
+  # Protect from resume event recursion
+  return if !(my $tx = $self->{tx}) || $self->{cont_writing};
+  local $self->{cont_writing} = 1;
+  my $chunk = $tx->server_write;
+  warn term_escape "-- Server >>> Client (@{[_url($tx)]})\n$chunk\n" if DEBUG;
+  my $next
+    = $tx->is_finished ? '_finish' : length $chunk ? '_write_content' : undef;
+  return $self->write($chunk) unless $next;
+  $self->write($chunk => sub { shift->$next() });
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::IOLoop::Stream::HTTPServer - Non-blocking I/O HTTP server stream
+
+=head1 SYNOPSIS
+
+  use Mojo::IOLoop::Server;
+  use Mojo::IOLoop::Stream::HTTPServer;
+  
+  # Create listen socket
+  my $server = Mojo::IOLoop::Server->new;
+  $server->on(
+    accept => sub {
+      my $stream = Mojo::IOLoop::Stream::HTTPServer->new(pop);
+  
+      $stream->on(
+        request => sub {
+          my ($stream, $tx) = @_;
+          $tx->res->code(200);
+          $tx->res->headers->content_type('text/plain');
+          $tx->res->body('Hello World!');
+          $tx->resume;
+        }
+      );
+      $stream->start;
+    }
+  );
+  $server->listen(port => 3000);
+  
+  # Start reactor if necessary
+  $stream->reactor->start unless $stream->reactor->is_running;
+
+=head1 DESCRIPTION
+
+L<Mojo::IOLoop::Stream::HTTPServer> is a container for I/O streams used by
+L<Mojo::IOLoop> to support the HTTP protocol server-side.
+
+=head1 EVENTS
+
+L<Mojo::IOLoop::Stream::HTTPServer> inherits all events from
+L<Mojo::IOLoop::Stream> and can emit the following new ones.
+
+=head2 request
+
+  $stream->on(request => sub {
+    my ($sream, $tx) = @_;
+    ...
+  });
+
+Emitted when a request is ready and needs to be handled.
+
+  $stream->on(request => sub {
+    my ($stream, $tx) = @_;
+    $tx->res->code(200);
+    $tx->res->headers->content_type('text/plain');
+    $tx->res->body('Hello World!');
+    $tx->resume;
+  });
+
+=head2 start
+
+  $stream->on(start => sub {
+    my ($stream, $tx) = @_;
+    ...
+  });
+
+Emitted whenever a transaction for a new request is about to start.
+
+=head2 upgrade
+
+  $stream->on(upgrade => sub {
+    my ($stream, $ws) = @_;
+    ...
+  });
+
+Emitted when the connection should be upgraded to the WebSocket protocol.
+
+=head1 ATTRIBUTES
+
+L<Mojo::IOLoop::Stream::HTTPServer> inherits all attributes from
+L<Mojo::IOLoop::Stream> and implements the following ones.
+
+=head2 app
+
+  my $app = $stream->app;
+  $stream = $stream->app(Mojolicious->new);
+
+Application responsible for building transactions, defaults to a
+L<Mojo::HelloWorld> object.
+
+=head2 max_requests
+
+  my $max = $stream->max_requests;
+  $stream = $stream->max_requests(250);
+
+Maximum number of keep-alive requests per connection, defaults to C<100>.
+
+=head1 METHODS
+
+L<Mojo::IOLoop::Stream::HTTPServer> inherits all methods from
+L<Mojo::IOLoop::Stream> and implements the following new ones.
+
+=head2 new
+
+  my $stream = Mojo::IOLoop::Stream::HTTPServer->new($handle);
+
+Construct a new L<Mojo::IOLoop::Stream::HTTPServer> object.
+
+=head1 DEBUGGING
+
+You can set the C<MOJO_SERVER_DEBUG> environment variable to get some advanced
+diagnostics information printed to C<STDERR>.
+
+  MOJO_SERVER_DEBUG=1
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicious.org>.
+
+=cut
+
Index: main/trunk/greenstone2/perllib/cpan/Mojo/IOLoop/Stream/WebSocketClient.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/IOLoop/Stream/WebSocketClient.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/IOLoop/Stream/WebSocketClient.pm	(revision 32205)
@@ -0,0 +1,79 @@
+package Mojo::IOLoop::Stream::WebSocketClient;
+use Mojo::Base 'Mojo::IOLoop::Stream::HTTPClient';
+
+use Scalar::Util 'weaken';
+
+sub process {
+  my ($self, $tx) = @_;
+  $self->{tx} = $tx;
+  weaken $self;
+  $tx->on(resume => sub { $self->_write_content });
+  $self->_write_content;
+}
+
+sub _finish {
+  my $self = shift;
+  return ++$self->{closing} && $self->close unless $self->{tx};
+  delete($self->{tx})->closed;
+  ++$self->{closing} && $self->close_gracefully;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::IOLoop::Stream::WebSocketClient - Non-blocking I/O WebSocket client stream
+
+=head1 SYNOPSIS
+
+  use Mojo::IOLoop::Stream::WebSocketClient;
+  use Mojo::Transaction::WebSocket;
+  
+  # Create transaction
+  my $ws = Mojo::Transaction::WebSocket->new;
+  $ws->on(message => sub {
+    my ($ws, $msg) = @_;
+    say "Message: $msg";
+  });
+  
+  # Create stream and process transaction with it
+  my $stream = Mojo::IOLoop::Stream::WebSocketClient->new($handle);
+  $stream->process($ws);
+
+  # Start reactor if necessary
+  $stream->reactor->start unless $stream->reactor->is_running;
+
+=head1 DESCRIPTION
+
+L<Mojo::IOLoop::Stream::WebSocketClient> is a container for I/O streams used by
+L<Mojo::IOLoop> to support the WebSocket protocol client-side.
+
+=head1 EVENTS
+
+L<Mojo::IOLoop::Stream::WebSocketClient> inherits all events from
+L<Mojo::IOLoop::Stream::HTTPClient>.
+
+=head1 ATTRIBUTES
+
+L<Mojo::IOLoop::Stream::WebSocketClient> inherits all attributes from
+L<Mojo::IOLoop::Stream::HTTPClient>.
+
+=head1 METHODS
+
+L<Mojo::IOLoop::Stream::WebSocketClient> inherits all methods from
+L<Mojo::IOLoop::Stream::HTTPClient> and implements the following new ones.
+
+=head2 process
+
+  $stream->process(Mojo::Transaction::WebSocket->new);
+
+Process a L<Mojo::Transaction::WebSocket> object.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicious.org>.
+
+=cut
+
Index: main/trunk/greenstone2/perllib/cpan/Mojo/IOLoop/Stream/WebSocketServer.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/IOLoop/Stream/WebSocketServer.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/IOLoop/Stream/WebSocketServer.pm	(revision 32205)
@@ -0,0 +1,76 @@
+package Mojo::IOLoop::Stream::WebSocketServer;
+use Mojo::Base 'Mojo::IOLoop::Stream::HTTPServer';
+
+use Scalar::Util 'weaken';
+
+sub process {
+  my ($self, $tx) = @_;
+  $self->{tx} = $tx;
+  weaken $self;
+  $tx->on(resume => sub { $self->_write_content });
+  $self->_write_content;
+}
+
+sub _close { delete($_[0]->{tx})->closed if $_[0]->{tx} }
+
+sub _finish { shift->close_gracefully }
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::IOLoop::Stream::WebSocketServer - Non-blocking I/O WebSocket server stream
+
+=head1 SYNOPSIS
+
+  use Mojo::IOLoop::Stream::WebSocketServer;
+  use Mojo::Transaction::WebSocket;
+  
+  # Create transaction
+  my $ws = Mojo::Transaction::WebSocket->new;
+  $ws->on(message => sub {
+    my ($ws, $msg) = @_;
+    say "Message: $msg";
+  });
+
+  # Create stream and process transaction with it
+  my $stream = Mojo::IOLoop::Stream::WebSocketServer->new($handle);
+  $stream->process($ws);
+
+  # Start reactor if necessary
+  $stream->reactor->start unless $stream->reactor->is_running;
+
+=head1 DESCRIPTION
+
+L<Mojo::IOLoop::Stream::WebSocketServer> is a container for I/O streams used by
+L<Mojo::IOLoop> to support the WebSocket protocol server-side.
+
+=head1 EVENTS
+
+L<Mojo::IOLoop::Stream::WEBSocketServer> inherits all events from
+L<Mojo::IOLoop::Stream::HTTPServer>.
+
+=head1 ATTRIBUTES
+
+L<Mojo::IOLoop::Stream::WebSocketServer> inherits all attributes from
+L<Mojo::IOLoop::Stream::HTTPServer>.
+
+=head1 METHODS
+
+L<Mojo::IOLoop::Stream::WebSocketServer> inherits all methods from
+L<Mojo::IOLoop::Stream::HTTPServer> and implements the following new ones.
+
+=head2 process
+
+  $stream->process(Mojo::Transaction::WebSocket->new);
+
+Process a L<Mojo::Transaction::WebSocket> object.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicious.org>.
+
+=cut
+
Index: main/trunk/greenstone2/perllib/cpan/Mojo/IOLoop/Subprocess.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/IOLoop/Subprocess.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/IOLoop/Subprocess.pm	(revision 32205)
@@ -0,0 +1,150 @@
+package Mojo::IOLoop::Subprocess;
+use Mojo::Base -base;
+
+use Carp 'croak';
+use Config;
+use Mojo::IOLoop;
+use Mojo::IOLoop::Stream;
+use POSIX ();
+use Storable;
+
+has deserialize => sub { \&Storable::thaw };
+has ioloop      => sub { Mojo::IOLoop->singleton };
+has serialize   => sub { \&Storable::freeze };
+
+sub pid { shift->{pid} }
+
+sub run {
+  my ($self, $child, $parent) = @_;
+
+  # No fork emulation support
+  croak 'Subprocesses do not support fork emulation' if $Config{d_pseudofork};
+
+  # Pipe for subprocess communication
+  pipe(my $reader, my $writer) or croak "Can't create pipe: $!";
+  $writer->autoflush(1);
+
+  # Child
+  croak "Can't fork: $!" unless defined(my $pid = $self->{pid} = fork);
+  unless ($pid) {
+    $self->ioloop->reset;
+    my $results = eval { [$self->$child] } || [];
+    print $writer $self->serialize->([$@, @$results]);
+    POSIX::_exit(0);
+  }
+
+  # Parent
+  my $me     = $$;
+  my $stream = Mojo::IOLoop::Stream->new($reader)->timeout(0);
+  $self->ioloop->stream($stream);
+  my $buffer = '';
+  $stream->on(read => sub { $buffer .= pop });
+  $stream->on(
+    close => sub {
+      return unless $$ == $me;
+      waitpid $pid, 0;
+      my $results = eval { $self->deserialize->($buffer) } || [];
+      $self->$parent(shift(@$results) // $@, @$results);
+    }
+  );
+  return $self;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::IOLoop::Subprocess - Subprocesses
+
+=head1 SYNOPSIS
+
+  use Mojo::IOLoop::Subprocess;
+
+  # Operation that would block the event loop for 5 seconds
+  my $subprocess = Mojo::IOLoop::Subprocess->new;
+  $subprocess->run(
+    sub {
+      my $subprocess = shift;
+      sleep 5;
+      return 'â¥', 'Mojolicious';
+    },
+    sub {
+      my ($subprocess, $err, @results) = @_;
+      say "Subprocess error: $err" and return if $err;
+      say "I $results[0] $results[1]!";
+    }
+  );
+
+  # Start event loop if necessary
+  $subprocess->ioloop->start unless $subprocess->ioloop->is_running;
+
+=head1 DESCRIPTION
+
+L<Mojo::IOLoop::Subprocess> allows L<Mojo::IOLoop> to perform computationally
+expensive operations in subprocesses, without blocking the event loop.
+
+=head1 ATTRIBUTES
+
+L<Mojo::IOLoop::Subprocess> implements the following attributes.
+
+=head2 deserialize
+
+  my $cb      = $subprocess->deserialize;
+  $subprocess = $subprocess->deserialize(sub {...});
+
+A callback used to deserialize subprocess return values, defaults to using
+L<Storable>.
+
+  $subprocess->deserialize(sub {
+    my $bytes = shift;
+    return [];
+  });
+
+=head2 ioloop
+
+  my $loop    = $subprocess->ioloop;
+  $subprocess = $subprocess->ioloop(Mojo::IOLoop->new);
+
+Event loop object to control, defaults to the global L<Mojo::IOLoop> singleton.
+
+=head2 serialize
+
+  my $cb      = $subprocess->serialize;
+  $subprocess = $subprocess->serialize(sub {...});
+
+A callback used to serialize subprocess return values, defaults to using
+L<Storable>.
+
+  $subprocess->serialize(sub {
+    my $array = shift;
+    return '';
+  });
+
+=head1 METHODS
+
+L<Mojo::IOLoop::Subprocess> inherits all methods from L<Mojo::Base> and
+implements the following new ones.
+
+=head2 pid
+
+  my $pid = $subprocess->pid;
+
+Process id of the spawned subprocess if available.
+
+=head2 run
+
+  $subprocess = $subprocess->run(sub {...}, sub {...});
+
+Execute the first callback in a child process and wait for it to return one or
+more values, without blocking L</"ioloop"> in the parent process. Then execute
+the second callback in the parent process with the results. The return values of
+the first callback and exceptions thrown by it, will be serialized with
+L<Storable>, so they can be shared between processes.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/IOLoop/TLS.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/IOLoop/TLS.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/IOLoop/TLS.pm	(revision 32205)
@@ -0,0 +1,243 @@
+package Mojo::IOLoop::TLS;
+use Mojo::Base 'Mojo::EventEmitter';
+
+use Mojo::File 'path';
+use Mojo::IOLoop;
+use Scalar::Util 'weaken';
+
+# TLS support requires IO::Socket::SSL
+use constant TLS => $ENV{MOJO_NO_TLS}
+  ? 0
+  : eval { require IO::Socket::SSL; IO::Socket::SSL->VERSION('2.009'); 1 };
+use constant READ  => TLS ? IO::Socket::SSL::SSL_WANT_READ()  : 0;
+use constant WRITE => TLS ? IO::Socket::SSL::SSL_WANT_WRITE() : 0;
+
+has reactor => sub { Mojo::IOLoop->singleton->reactor };
+
+# To regenerate the certificate run this command (18.04.2012)
+# openssl req -new -x509 -keyout server.key -out server.crt -nodes -days 7300
+my $CERT = path(__FILE__)->sibling('resources', 'server.crt')->to_string;
+my $KEY  = path(__FILE__)->sibling('resources', 'server.key')->to_string;
+
+sub DESTROY { shift->_cleanup }
+
+sub can_tls {TLS}
+
+sub negotiate {
+  my ($self, $args) = (shift, ref $_[0] ? $_[0] : {@_});
+
+  return $self->emit(error => 'IO::Socket::SSL 2.009+ required for TLS support')
+    unless TLS;
+
+  my $handle = $self->{handle};
+  return $self->emit(error => $IO::Socket::SSL::SSL_ERROR)
+    unless IO::Socket::SSL->start_SSL($handle, %{$self->_expand($args)});
+  $self->reactor->io($handle
+      = $handle => sub { $self->_tls($handle, $args->{server}) });
+}
+
+sub new { shift->SUPER::new(handle => shift) }
+
+sub _cleanup {
+  my $self = shift;
+  return unless my $reactor = $self->reactor;
+  $reactor->remove($self->{handle}) if $self->{handle};
+  return $self;
+}
+
+sub _expand {
+  my ($self, $args) = @_;
+
+  weaken $self;
+  my $tls = {
+    SSL_error_trap     => sub { $self->_cleanup->emit(error => $_[1]) },
+    SSL_startHandshake => 0
+  };
+  $tls->{SSL_alpn_protocols} = $args->{tls_protocols} if $args->{tls_protocols};
+  $tls->{SSL_ca_file} = $args->{tls_ca}
+    if $args->{tls_ca} && -T $args->{tls_ca};
+  $tls->{SSL_cert_file}   = $args->{tls_cert}    if $args->{tls_cert};
+  $tls->{SSL_cipher_list} = $args->{tls_ciphers} if $args->{tls_ciphers};
+  $tls->{SSL_key_file}    = $args->{tls_key}     if $args->{tls_key};
+  $tls->{SSL_server}      = $args->{server}      if $args->{server};
+  $tls->{SSL_verify_mode} = $args->{tls_verify}  if defined $args->{tls_verify};
+  $tls->{SSL_version}     = $args->{tls_version} if $args->{tls_version};
+
+  if ($args->{server}) {
+    $tls->{SSL_cert_file} ||= $CERT;
+    $tls->{SSL_key_file}  ||= $KEY;
+  }
+  else {
+    $tls->{SSL_hostname}
+      = IO::Socket::SSL->can_client_sni ? $args->{address} : '';
+    $tls->{SSL_verifycn_name} = $args->{address};
+  }
+
+  return $tls;
+}
+
+sub _tls {
+  my ($self, $handle, $server) = @_;
+
+  return $self->_cleanup->emit(upgrade => delete $self->{handle})
+    if $server ? $handle->accept_SSL : $handle->connect_SSL;
+
+  # Switch between reading and writing
+  my $err = $IO::Socket::SSL::SSL_ERROR;
+  if    ($err == READ)  { $self->reactor->watch($handle, 1, 0) }
+  elsif ($err == WRITE) { $self->reactor->watch($handle, 1, 1) }
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::IOLoop::TLS - Non-blocking TLS handshake
+
+=head1 SYNOPSIS
+
+  use Mojo::IOLoop::TLS;
+
+  # Negotiate TLS
+  my $tls = Mojo::IOLoop::TLS->new($old_handle);
+  $tls->on(upgrade => sub {
+    my ($tls, $new_handle) = @_;
+    ...
+  });
+  $tls->on(error => sub {
+    my ($tls, $err) = @_;
+    ...
+  });
+  $tls->negotiate(server => 1, tls_version => 'TLSv1_2');
+
+  # Start reactor if necessary
+  $tls->reactor->start unless $tls->reactor->is_running;
+
+=head1 DESCRIPTION
+
+L<Mojo::IOLoop::TLS> negotiates TLS for L<Mojo::IOLoop>.
+
+=head1 EVENTS
+
+L<Mojo::IOLoop::TLS> inherits all events from L<Mojo::EventEmitter> and can
+emit the following new ones.
+
+=head2 upgrade
+
+  $tls->on(upgrade => sub {
+    my ($tls, $handle) = @_;
+    ...
+  });
+
+Emitted once TLS has been negotiated.
+
+=head2 error
+
+  $tls->on(error => sub {
+    my ($tls, $err) = @_;
+    ...
+  });
+
+Emitted if an error occurs during negotiation, fatal if unhandled.
+
+=head1 ATTRIBUTES
+
+L<Mojo::IOLoop::TLS> implements the following attributes.
+
+=head2 reactor
+
+  my $reactor = $tls->reactor;
+  $tls        = $tls->reactor(Mojo::Reactor::Poll->new);
+
+Low-level event reactor, defaults to the C<reactor> attribute value of the
+global L<Mojo::IOLoop> singleton.
+
+=head1 METHODS
+
+L<Mojo::IOLoop::TLS> inherits all methods from L<Mojo::EventEmitter> and
+implements the following new ones.
+
+=head2 can_tls
+
+  my $bool = Mojo::IOLoop::TLS->can_tls;
+
+True if L<IO::Socket::SSL> 2.009+ is installed and TLS support enabled.
+
+=head2 negotiate
+
+  $tls->negotiate(server => 1, tls_version => 'TLSv1_2');
+  $tls->negotiate({server => 1, tls_version => 'TLSv1_2'});
+
+Negotiate TLS.
+
+These options are currently available:
+
+=over 2
+
+=item server
+
+  server => 1
+
+Negotiate TLS from the server-side, defaults to the client-side.
+
+=item tls_ca
+
+  tls_ca => '/etc/tls/ca.crt'
+
+Path to TLS certificate authority file.
+
+=item tls_cert
+
+  tls_cert => '/etc/tls/server.crt'
+  tls_cert => {'mojolicious.org' => '/etc/tls/mojo.crt'}
+
+Path to the TLS cert file, defaults to a built-in test certificate on the
+server-side.
+
+=item tls_ciphers
+
+  tls_ciphers => 'AES128-GCM-SHA256:RC4:HIGH:!MD5:!aNULL:!EDH'
+
+TLS cipher specification string. For more information about the format see
+L<https://www.openssl.org/docs/manmaster/apps/ciphers.html#CIPHER-STRINGS>.
+
+=item tls_key
+
+  tls_key => '/etc/tls/server.key'
+  tls_key => {'mojolicious.org' => '/etc/tls/mojo.key'}
+
+Path to the TLS key file, defaults to a built-in test key on the server-side.
+
+=item tls_protocols
+
+  tls_protocols => ['foo', 'bar']
+
+ALPN protocols to negotiate.
+
+=item tls_verify
+
+  tls_verify => 0x00
+
+TLS verification mode.
+
+=item tls_version
+
+  tls_version => 'TLSv1_2'
+
+TLS protocol version.
+
+=back
+
+=head2 new
+
+  my $tls = Mojo::IOLoop::TLS->new($handle);
+
+Construct a new L<Mojo::IOLoop::Stream> object.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/IOLoop/resources/server.crt
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/IOLoop/resources/server.crt	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/IOLoop/resources/server.crt	(revision 32205)
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDaTCCAtKgAwIBAgIJAI+AzotR68CTMA0GCSqGSIb3DQEBBQUAMIGAMQswCQYD
+VQQGEwJERTEWMBQGA1UECBMNTmllZGVyc2FjaHNlbjESMBAGA1UEBxMJSGFtYmVy
+Z2VuMRQwEgYDVQQKEwtNb2pvbGljaW91czESMBAGA1UEAxMJbG9jYWxob3N0MRsw
+GQYJKoZIhvcNAQkBFgxzcmlAY3Bhbi5vcmcwHhcNMTIwNDE4MTczOTU5WhcNMzIw
+NDEzMTczOTU5WjCBgDELMAkGA1UEBhMCREUxFjAUBgNVBAgTDU5pZWRlcnNhY2hz
+ZW4xEjAQBgNVBAcTCUhhbWJlcmdlbjEUMBIGA1UEChMLTW9qb2xpY2lvdXMxEjAQ
+BgNVBAMTCWxvY2FsaG9zdDEbMBkGCSqGSIb3DQEJARYMc3JpQGNwYW4ub3JnMIGf
+MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCohcU0qG+hHn6JK8XdygAJo7EuRqG2
+5GSHaRRMyYgd89tEluInMH86tVcktJ1s/0VVvr5anAp8L7Pgu01Wr13OfgIzBxCz
+51ZIFxq4DtimBftXs9Z9M0sui2NuIPDrMEjkYUhUsxMEZcDSp2KJjDosZjSYUiiF
+G2ACvVGXSrS16QIDAQABo4HoMIHlMB0GA1UdDgQWBBSrZ+hIlPTgV7xx2O9wzdIO
+/d4osDCBtQYDVR0jBIGtMIGqgBSrZ+hIlPTgV7xx2O9wzdIO/d4osKGBhqSBgzCB
+gDELMAkGA1UEBhMCREUxFjAUBgNVBAgTDU5pZWRlcnNhY2hzZW4xEjAQBgNVBAcT
+CUhhbWJlcmdlbjEUMBIGA1UEChMLTW9qb2xpY2lvdXMxEjAQBgNVBAMTCWxvY2Fs
+aG9zdDEbMBkGCSqGSIb3DQEJARYMc3JpQGNwYW4ub3JnggkAj4DOi1HrwJMwDAYD
+VR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQAq6MXA7ZeO7B7vAcWxQKeLPKSy
+Jzkb1bC/agaISDbOwuZ1AoQSj6OQHKhNIdY5v/oLQJ0B8wB0dIigqn1WVacDtPgu
+PKSrxpqieDCh2bJ7+dyQIzQHgtZqPHi5k1PyNNXQxC94kPWdFp6PpF0M/y97aCxC
+ZQjKgDfncFWY3FHqUw==
+-----END CERTIFICATE-----
Index: main/trunk/greenstone2/perllib/cpan/Mojo/IOLoop/resources/server.key
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/IOLoop/resources/server.key	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/IOLoop/resources/server.key	(revision 32205)
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQCohcU0qG+hHn6JK8XdygAJo7EuRqG25GSHaRRMyYgd89tEluIn
+MH86tVcktJ1s/0VVvr5anAp8L7Pgu01Wr13OfgIzBxCz51ZIFxq4DtimBftXs9Z9
+M0sui2NuIPDrMEjkYUhUsxMEZcDSp2KJjDosZjSYUiiFG2ACvVGXSrS16QIDAQAB
+AoGALSdqp6lZ/7nD/c0Uv1CYofySROv3+KFJrl6hadG1/xCP99jVz9pWvMxKBTO/
+2qyrT0ZEitK0nIHLmLOXDVr/rxzbxP/kHmkOLKj45jW31BSap89tUpFjFQXFfjwT
+YnOgOB4+eqQuGwigCqabcQPtFC4fU7Qzk7pdz/kO4FjR0GECQQDdXthCKgS7E5Zy
+qqzjepxYvKgkWPD3G9H6I8LOtiVBdcehflF8Y61OGsEST3pbOhrijhY281VnD1AG
+pNL1rOhDAkEAwuKKTN+2GF3m1mPtGW9jpkP8gU2zcO945U0jxpn2srjQ9oIoB45Y
+gqtE6yybRY4BBd+hMdgeH5dXSwsZW+FMYwJASrFy5LhKylisndoq5cJ8OJDHZyQ/
+ghF4Ax/H3nmlDnZQOpRlqEP1uPHcDXKVxWxQn/rzUe0+9rw681Lv/4ctAwJAfyLO
+2muvHaJUr1QtH0S9m4AKwEfyYiC3m8+BIVTbzagoGki62IMSVtxob4uAGBYVsME9
+JYk5zZ4rgndRKdGGxQJBAIpbdLBKArvnpbYIqNJGG83mUZ/VZaQl0G+S3zGkgre9
+KjIuz10nNMNAKmGRrTbClLtvAQ9MVa3Xjnp+XmxPFho=
+-----END RSA PRIVATE KEY-----
Index: main/trunk/greenstone2/perllib/cpan/Mojo/JSON.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/JSON.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/JSON.pm	(revision 32205)
@@ -0,0 +1,383 @@
+package Mojo::JSON;
+use Mojo::Base -strict;
+
+use Carp 'croak';
+use Exporter 'import';
+use JSON::PP ();
+use Mojo::Util;
+use Scalar::Util 'blessed';
+
+our @EXPORT_OK = qw(decode_json encode_json false from_json j to_json true);
+
+# Escaped special character map (with u2028 and u2029)
+my %ESCAPE = (
+  '"'     => '"',
+  '\\'    => '\\',
+  '/'     => '/',
+  'b'     => "\x08",
+  'f'     => "\x0c",
+  'n'     => "\x0a",
+  'r'     => "\x0d",
+  't'     => "\x09",
+  'u2028' => "\x{2028}",
+  'u2029' => "\x{2029}"
+);
+my %REVERSE = map { $ESCAPE{$_} => "\\$_" } keys %ESCAPE;
+for (0x00 .. 0x1f) { $REVERSE{pack 'C', $_} //= sprintf '\u%.4X', $_ }
+
+sub decode_json {
+  my $err = _decode(\my $value, shift);
+  return defined $err ? croak $err : $value;
+}
+
+sub encode_json { Mojo::Util::encode 'UTF-8', _encode_value(shift) }
+
+sub false () {JSON::PP::false}
+
+sub from_json {
+  my $err = _decode(\my $value, shift, 1);
+  return defined $err ? croak $err : $value;
+}
+
+sub j {
+  return encode_json($_[0]) if ref $_[0] eq 'ARRAY' || ref $_[0] eq 'HASH';
+  return eval { decode_json($_[0]) };
+}
+
+sub to_json { _encode_value(shift) }
+
+sub true () {JSON::PP::true}
+
+sub _decode {
+  my $valueref = shift;
+
+  eval {
+
+    # Missing input
+    die "Missing or empty input\n" unless length(local $_ = shift);
+
+    # UTF-8
+    $_ = Mojo::Util::decode 'UTF-8', $_ unless shift;
+    die "Input is not UTF-8 encoded\n" unless defined;
+
+    # Value
+    $$valueref = _decode_value();
+
+    # Leftover data
+    /\G[\x20\x09\x0a\x0d]*\z/gc or _throw('Unexpected data');
+  } ? return undef : chomp $@;
+
+  return $@;
+}
+
+sub _decode_array {
+  my @array;
+  until (m/\G[\x20\x09\x0a\x0d]*\]/gc) {
+
+    # Value
+    push @array, _decode_value();
+
+    # Separator
+    redo if /\G[\x20\x09\x0a\x0d]*,/gc;
+
+    # End
+    last if /\G[\x20\x09\x0a\x0d]*\]/gc;
+
+    # Invalid character
+    _throw('Expected comma or right square bracket while parsing array');
+  }
+
+  return \@array;
+}
+
+sub _decode_object {
+  my %hash;
+  until (m/\G[\x20\x09\x0a\x0d]*\}/gc) {
+
+    # Quote
+    /\G[\x20\x09\x0a\x0d]*"/gc
+      or _throw('Expected string while parsing object');
+
+    # Key
+    my $key = _decode_string();
+
+    # Colon
+    /\G[\x20\x09\x0a\x0d]*:/gc or _throw('Expected colon while parsing object');
+
+    # Value
+    $hash{$key} = _decode_value();
+
+    # Separator
+    redo if /\G[\x20\x09\x0a\x0d]*,/gc;
+
+    # End
+    last if /\G[\x20\x09\x0a\x0d]*\}/gc;
+
+    # Invalid character
+    _throw('Expected comma or right curly bracket while parsing object');
+  }
+
+  return \%hash;
+}
+
+sub _decode_string {
+  my $pos = pos;
+
+  # Extract string with escaped characters
+  m!\G((?:(?:[^\x00-\x1f\\"]|\\(?:["\\/bfnrt]|u[0-9a-fA-F]{4})){0,32766})*)!gc;
+  my $str = $1;
+
+  # Invalid character
+  unless (m/\G"/gc) {
+    _throw('Unexpected character or invalid escape while parsing string')
+      if /\G[\x00-\x1f\\]/;
+    _throw('Unterminated string');
+  }
+
+  # Unescape popular characters
+  if (index($str, '\\u') < 0) {
+    $str =~ s!\\(["\\/bfnrt])!$ESCAPE{$1}!gs;
+    return $str;
+  }
+
+  # Unescape everything else
+  my $buffer = '';
+  while ($str =~ /\G([^\\]*)\\(?:([^u])|u(.{4}))/gc) {
+    $buffer .= $1;
+
+    # Popular character
+    if ($2) { $buffer .= $ESCAPE{$2} }
+
+    # Escaped
+    else {
+      my $ord = hex $3;
+
+      # Surrogate pair
+      if (($ord & 0xf800) == 0xd800) {
+
+        # High surrogate
+        ($ord & 0xfc00) == 0xd800
+          or pos = $pos + pos($str), _throw('Missing high-surrogate');
+
+        # Low surrogate
+        $str =~ /\G\\u([Dd][C-Fc-f]..)/gc
+          or pos = $pos + pos($str), _throw('Missing low-surrogate');
+
+        $ord = 0x10000 + ($ord - 0xd800) * 0x400 + (hex($1) - 0xdc00);
+      }
+
+      # Character
+      $buffer .= pack 'U', $ord;
+    }
+  }
+
+  # The rest
+  return $buffer . substr $str, pos($str), length($str);
+}
+
+sub _decode_value {
+
+  # Leading whitespace
+  /\G[\x20\x09\x0a\x0d]*/gc;
+
+  # String
+  return _decode_string() if /\G"/gc;
+
+  # Object
+  return _decode_object() if /\G\{/gc;
+
+  # Array
+  return _decode_array() if /\G\[/gc;
+
+  # Number
+  return 0 + $1
+    if /\G([-]?(?:0|[1-9][0-9]*)(?:\.[0-9]*)?(?:[eE][+-]?[0-9]+)?)/gc;
+
+  # True
+  return true() if /\Gtrue/gc;
+
+  # False
+  return false() if /\Gfalse/gc;
+
+  # Null
+  return undef if /\Gnull/gc;
+
+  # Invalid character
+  _throw('Expected string, array, object, number, boolean or null');
+}
+
+sub _encode_array {
+  '[' . join(',', map { _encode_value($_) } @{$_[0]}) . ']';
+}
+
+sub _encode_object {
+  my $object = shift;
+  my @pairs = map { _encode_string($_) . ':' . _encode_value($object->{$_}) }
+    sort keys %$object;
+  return '{' . join(',', @pairs) . '}';
+}
+
+sub _encode_string {
+  my $str = shift;
+  $str =~ s!([\x00-\x1f\x{2028}\x{2029}\\"/])!$REVERSE{$1}!gs;
+  return "\"$str\"";
+}
+
+sub _encode_value {
+  my $value = shift;
+
+  # Reference
+  if (my $ref = ref $value) {
+
+    # Object
+    return _encode_object($value) if $ref eq 'HASH';
+
+    # Array
+    return _encode_array($value) if $ref eq 'ARRAY';
+
+    # True or false
+    return $$value ? 'true' : 'false' if $ref eq 'SCALAR';
+    return $value  ? 'true' : 'false' if $ref eq 'JSON::PP::Boolean';
+
+    # Everything else
+    return _encode_string($value)
+      unless blessed $value && (my $sub = $value->can('TO_JSON'));
+    return _encode_value($value->$sub);
+  }
+
+  # Null
+  return 'null' unless defined $value;
+
+  # Number
+  no warnings 'numeric';
+  return $value
+    if !utf8::is_utf8($value)
+    && length((my $dummy = '') & $value)
+    && 0 + $value eq $value
+    && $value * 0 == 0;
+
+  # String
+  return _encode_string($value);
+}
+
+sub _throw {
+
+  # Leading whitespace
+  /\G[\x20\x09\x0a\x0d]*/gc;
+
+  # Context
+  my $context = 'Malformed JSON: ' . shift;
+  if (m/\G\z/gc) { $context .= ' before end of data' }
+  else {
+    my @lines = split "\n", substr($_, 0, pos);
+    $context .= ' at line ' . @lines . ', offset ' . length(pop @lines || '');
+  }
+
+  die "$context\n";
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::JSON - Minimalistic JSON
+
+=head1 SYNOPSIS
+
+  use Mojo::JSON qw(decode_json encode_json);
+
+  my $bytes = encode_json {foo => [1, 2], bar => 'hello!', baz => \1};
+  my $hash  = decode_json $bytes;
+
+=head1 DESCRIPTION
+
+L<Mojo::JSON> is a minimalistic and possibly the fastest pure-Perl
+implementation of L<RFC 8259|http://tools.ietf.org/html/rfc8259>.
+
+It supports normal Perl data types like scalar, array reference, hash reference
+and will try to call the C<TO_JSON> method on blessed references, or stringify
+them if it doesn't exist. Differentiating between strings and numbers in Perl
+is hard, depending on how it has been used, a scalar can be both at the same
+time. The string value has a higher precedence unless both representations are
+equivalent.
+
+  [1, -2, 3]     -> [1, -2, 3]
+  {"foo": "bar"} -> {foo => 'bar'}
+
+Literal names will be translated to and from L<Mojo::JSON> constants or a
+similar native Perl value.
+
+  true  -> Mojo::JSON->true
+  false -> Mojo::JSON->false
+  null  -> undef
+
+In addition scalar references will be used to generate booleans, based on if
+their values are true or false.
+
+  \1 -> true
+  \0 -> false
+
+The two Unicode whitespace characters C<u2028> and C<u2029> will always be
+escaped to make JSONP easier, and the character C</> to prevent XSS attacks.
+
+  "\x{2028}\x{2029}</script>" -> "\u2028\u2029<\/script>"
+
+=head1 FUNCTIONS
+
+L<Mojo::JSON> implements the following functions, which can be imported
+individually.
+
+=head2 decode_json
+
+  my $value = decode_json $bytes;
+
+Decode JSON to Perl value and die if decoding fails.
+
+=head2 encode_json
+
+  my $bytes = encode_json {i => 'â¥ mojolicious'};
+
+Encode Perl value to JSON.
+
+=head2 false
+
+  my $false = false;
+
+False value, used because Perl has no native equivalent.
+
+=head2 from_json
+
+  my $value = from_json $chars;
+
+Decode JSON text that is not C<UTF-8> encoded to Perl value and die if decoding
+fails.
+
+=head2 j
+
+  my $bytes = j [1, 2, 3];
+  my $bytes = j {i => 'â¥ mojolicious'};
+  my $value = j $bytes;
+
+Encode Perl data structure (which may only be an array reference or hash
+reference) or decode JSON, an C<undef> return value indicates a bare C<null> or
+that decoding failed.
+
+=head2 to_json
+
+  my $chars = to_json {i => 'â¥ mojolicious'};
+
+Encode Perl value to JSON text without C<UTF-8> encoding it.
+
+=head2 true
+
+  my $true = true;
+
+True value, used because Perl has no native equivalent.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/JSON/Pointer.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/JSON/Pointer.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/JSON/Pointer.pm	(revision 32205)
@@ -0,0 +1,122 @@
+package Mojo::JSON::Pointer;
+use Mojo::Base -base;
+
+has 'data';
+
+sub contains { shift->_pointer(1, @_) }
+sub get      { shift->_pointer(0, @_) }
+
+sub new { @_ > 1 ? shift->SUPER::new(data => shift) : shift->SUPER::new }
+
+sub _pointer {
+  my ($self, $contains, $pointer) = @_;
+
+  my $data = $self->data;
+  return $contains ? 1 : $data unless $pointer =~ s!^/!!;
+  for my $p (length $pointer ? (split '/', $pointer, -1) : ($pointer)) {
+    $p =~ s!~1!/!g;
+    $p =~ s/~0/~/g;
+
+    # Hash
+    if (ref $data eq 'HASH' && exists $data->{$p}) { $data = $data->{$p} }
+
+    # Array
+    elsif (ref $data eq 'ARRAY' && $p =~ /^\d+$/ && @$data > $p) {
+      $data = $data->[$p];
+    }
+
+    # Nothing
+    else { return undef }
+  }
+
+  return $contains ? 1 : $data;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::JSON::Pointer - JSON Pointers
+
+=head1 SYNOPSIS
+
+  use Mojo::JSON::Pointer;
+
+  my $pointer = Mojo::JSON::Pointer->new({foo => [23, 'bar']});
+  say $pointer->get('/foo/1');
+  say 'Contains "/foo".' if $pointer->contains('/foo');
+
+=head1 DESCRIPTION
+
+L<Mojo::JSON::Pointer> is an implementation of
+L<RFC 6901|http://tools.ietf.org/html/rfc6901>.
+
+=head1 ATTRIBUTES
+
+L<Mojo::JSON::Pointer> implements the following attributes.
+
+=head2 data
+
+  my $data = $pointer->data;
+  $pointer = $pointer->data({foo => 'bar'});
+
+Data structure to be processed.
+
+=head1 METHODS
+
+L<Mojo::JSON::Pointer> inherits all methods from L<Mojo::Base> and implements
+the following new ones.
+
+=head2 contains
+
+  my $bool = $pointer->contains('/foo/1');
+
+Check if L</"data"> contains a value that can be identified with the given JSON
+Pointer.
+
+  # True
+  Mojo::JSON::Pointer->new('just a string')->contains('');
+  Mojo::JSON::Pointer->new({'â¥' => 'mojolicious'})->contains('/â¥');
+  Mojo::JSON::Pointer->new({foo => 'bar', baz => [4, 5]})->contains('/foo');
+  Mojo::JSON::Pointer->new({foo => 'bar', baz => [4, 5]})->contains('/baz/1');
+
+  # False
+  Mojo::JSON::Pointer->new({'â¥' => 'mojolicious'})->contains('/â');
+  Mojo::JSON::Pointer->new({foo => 'bar', baz => [4, 5]})->contains('/bar');
+  Mojo::JSON::Pointer->new({foo => 'bar', baz => [4, 5]})->contains('/baz/9');
+
+=head2 get
+
+  my $value = $pointer->get('/foo/bar');
+
+Extract value from L</"data"> identified by the given JSON Pointer.
+
+  # "just a string"
+  Mojo::JSON::Pointer->new('just a string')->get('');
+
+  # "mojolicious"
+  Mojo::JSON::Pointer->new({'â¥' => 'mojolicious'})->get('/â¥');
+
+  # "bar"
+  Mojo::JSON::Pointer->new({foo => 'bar', baz => [4, 5, 6]})->get('/foo');
+
+  # "4"
+  Mojo::JSON::Pointer->new({foo => 'bar', baz => [4, 5, 6]})->get('/baz/0');
+
+  # "6"
+  Mojo::JSON::Pointer->new({foo => 'bar', baz => [4, 5, 6]})->get('/baz/2');
+
+=head2 new
+
+  my $pointer = Mojo::JSON::Pointer->new;
+  my $pointer = Mojo::JSON::Pointer->new({foo => 'bar'});
+
+Build new L<Mojo::JSON::Pointer> object.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/Loader.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/Loader.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/Loader.pm	(revision 32205)
@@ -0,0 +1,185 @@
+package Mojo::Loader;
+use Mojo::Base -strict;
+
+use Exporter 'import';
+use Mojo::Exception;
+use Mojo::File 'path';
+use Mojo::Util qw(b64_decode class_to_path);
+
+our @EXPORT_OK
+  = qw(data_section file_is_binary find_modules find_packages load_class);
+
+my (%BIN, %CACHE);
+
+sub data_section { $_[0] ? $_[1] ? _all($_[0])->{$_[1]} : _all($_[0]) : undef }
+
+sub file_is_binary { keys %{_all($_[0])} ? !!$BIN{$_[0]}{$_[1]} : undef }
+
+sub find_modules {
+  my $ns = shift;
+
+  my %modules;
+  for my $directory (@INC) {
+    next unless -d (my $path = path($directory, split(/::|'/, $ns)));
+    $modules{"${ns}::$_"}++
+      for $path->list->grep(qr/\.pm$/)->map('basename', '.pm')->each;
+  }
+
+  return sort keys %modules;
+}
+
+sub find_packages {
+  my $ns = shift;
+  no strict 'refs';
+  return sort map { /^(.+)::$/ ? "${ns}::$1" : () } keys %{"${ns}::"};
+}
+
+sub load_class {
+  my $class = shift;
+
+  # Invalid class name
+  return 1 if ($class || '') !~ /^\w(?:[\w:']*\w)?$/;
+
+  # Load if not already loaded
+  return undef if $class->can('new') || eval "require $class; 1";
+
+  # Does not exist
+  return 1 if $@ =~ /^Can't locate \Q@{[class_to_path $class]}\E in \@INC/;
+
+  # Real error
+  return Mojo::Exception->new($@)->inspect;
+}
+
+sub _all {
+  my $class = shift;
+
+  return $CACHE{$class} if $CACHE{$class};
+  local $.;
+  my $handle = do { no strict 'refs'; \*{"${class}::DATA"} };
+  return {} unless fileno $handle;
+  seek $handle, 0, 0;
+  my $data = join '', <$handle>;
+
+  # Ignore everything before __DATA__ (some versions seek to start of file)
+  $data =~ s/^.*\n__DATA__\r?\n/\n/s;
+
+  # Ignore everything after __END__
+  $data =~ s/\n__END__\r?\n.*$/\n/s;
+
+  # Split files
+  (undef, my @files) = split /^@@\s*(.+?)\s*\r?\n/m, $data;
+
+  # Find data
+  my $all = $CACHE{$class} = {};
+  while (@files) {
+    my ($name, $data) = splice @files, 0, 2;
+    $all->{$name} = $name =~ s/\s*\(\s*base64\s*\)$//
+      && ++$BIN{$class}{$name} ? b64_decode $data : $data;
+  }
+
+  return $all;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::Loader - Load all kinds of things
+
+=head1 SYNOPSIS
+
+  use Mojo::Loader qw(data_section find_modules load_class);
+
+  # Find modules in a namespace
+  for my $module (find_modules 'Some::Namespace') {
+
+    # Load them safely
+    my $e = load_class $module;
+    warn qq{Loading "$module" failed: $e} and next if ref $e;
+
+    # And extract files from the DATA section
+    say data_section($module, 'some_file.txt');
+  }
+
+=head1 DESCRIPTION
+
+L<Mojo::Loader> is a class loader and plugin framework. Aside from finding
+modules and loading classes, it allows multiple files to be stored in the
+C<DATA> section of a class, which can then be accessed individually.
+
+  package Foo;
+
+  1;
+  __DATA__
+
+  @@ test.txt
+  This is the first file.
+
+  @@ test2.html (base64)
+  VGhpcyBpcyB0aGUgc2Vjb25kIGZpbGUu
+
+  @@ test
+  This is the
+  third file.
+
+Each file has a header starting with C<@@>, followed by the file name and
+optional instructions for decoding its content. Currently only the Base64
+encoding is supported, which can be quite convenient for the storage of binary
+data.
+
+=head1 FUNCTIONS
+
+L<Mojo::Loader> implements the following functions, which can be imported
+individually.
+
+=head2 data_section
+
+  my $all   = data_section 'Foo::Bar';
+  my $index = data_section 'Foo::Bar', 'index.html';
+
+Extract embedded file from the C<DATA> section of a class, all files will be
+cached once they have been accessed for the first time.
+
+  # List embedded files
+  say for keys %{data_section 'Foo::Bar'};
+
+=head2 file_is_binary
+
+  my $bool = file_is_binary 'Foo::Bar', 'test.png';
+
+Check if embedded file from the C<DATA> section of a class was Base64 encoded.
+
+=head2 find_packages
+
+  my @pkgs = find_packages 'MyApp::Namespace';
+
+Search for packages in a namespace non-recursively.
+
+=head2 find_modules
+
+  my @modules = find_modules 'MyApp::Namespace';
+
+Search for modules in a namespace non-recursively.
+
+=head2 load_class
+
+  my $e = load_class 'Foo::Bar';
+
+Load a class and catch exceptions, returns a false value if loading was
+successful, a true value if the class was not found, or a L<Mojo::Exception>
+object if loading failed. Note that classes are checked for a C<new> method to
+see if they are already loaded, so trying to load the same class multiple times
+may yield different results.
+
+  # Handle exceptions
+  if (my $e = load_class 'Foo::Bar') {
+    die ref $e ? "Exception: $e" : 'Not found!';
+  }
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/Log.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/Log.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/Log.pm	(revision 32205)
@@ -0,0 +1,262 @@
+package Mojo::Log;
+use Mojo::Base 'Mojo::EventEmitter';
+
+use Carp 'croak';
+use Fcntl ':flock';
+use Mojo::File;
+use Mojo::Util 'encode';
+
+has format => sub { shift->short ? \&_short : \&_default };
+has handle => sub {
+
+  # STDERR
+  return \*STDERR unless my $path = shift->path;
+
+  # File
+  return Mojo::File->new($path)->open('>>');
+};
+has history          => sub { [] };
+has level            => 'debug';
+has max_history_size => 10;
+has 'path';
+has short => sub { $ENV{MOJO_LOG_SHORT} };
+
+# Supported log levels
+my %LEVEL = (debug => 1, info => 2, warn => 3, error => 4, fatal => 5);
+
+# Systemd magic numbers
+my %MAGIC = (debug => 7, info => 6, warn => 4, error => 3, fatal => 2);
+
+sub append {
+  my ($self, $msg) = @_;
+
+  return unless my $handle = $self->handle;
+  flock $handle, LOCK_EX;
+  $handle->print(encode('UTF-8', $msg)) or croak "Can't write to log: $!";
+  flock $handle, LOCK_UN;
+}
+
+sub debug { shift->_log(debug => @_) }
+sub error { shift->_log(error => @_) }
+sub fatal { shift->_log(fatal => @_) }
+sub info  { shift->_log(info  => @_) }
+
+sub is_level { $LEVEL{pop()} >= $LEVEL{$ENV{MOJO_LOG_LEVEL} || shift->level} }
+
+sub new {
+  my $self = shift->SUPER::new(@_);
+  $self->on(message => \&_message);
+  return $self;
+}
+
+sub warn { shift->_log(warn => @_) }
+
+sub _default {
+  '[' . localtime(shift) . '] [' . shift() . '] ' . join "\n", @_, '';
+}
+
+sub _log { shift->emit('message', shift, @_) }
+
+sub _message {
+  my ($self, $level) = (shift, shift);
+
+  return unless $self->is_level($level);
+
+  my $max     = $self->max_history_size;
+  my $history = $self->history;
+  push @$history, my $msg = [time, $level, @_];
+  shift @$history while @$history > $max;
+
+  $self->append($self->format->(@$msg));
+}
+
+sub _short {
+  my ($time, $level) = (shift, shift);
+  my ($magic, $short) = ("<$MAGIC{$level}>", substr($level, 0, 1));
+  return "${magic}[$short] " . join("\n$magic", @_) . "\n";
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::Log - Simple logger
+
+=head1 SYNOPSIS
+
+  use Mojo::Log;
+
+  # Log to STDERR
+  my $log = Mojo::Log->new;
+
+  # Customize log file location and minimum log level
+  my $log = Mojo::Log->new(path => '/var/log/mojo.log', level => 'warn');
+
+  # Log messages
+  $log->debug('Not sure what is happening here');
+  $log->info('FYI: it happened again');
+  $log->warn('This might be a problem');
+  $log->error('Garden variety error');
+  $log->fatal('Boom');
+
+=head1 DESCRIPTION
+
+L<Mojo::Log> is a simple logger for L<Mojo> projects.
+
+=head1 EVENTS
+
+L<Mojo::Log> inherits all events from L<Mojo::EventEmitter> and can emit the
+following new ones.
+
+=head2 message
+
+  $log->on(message => sub {
+    my ($log, $level, @lines) = @_;
+    ...
+  });
+
+Emitted when a new message gets logged.
+
+  $log->on(message => sub {
+    my ($log, $level, @lines) = @_;
+    say "$level: ", @lines;
+  });
+
+=head1 ATTRIBUTES
+
+L<Mojo::Log> implements the following attributes.
+
+=head2 format
+
+  my $cb = $log->format;
+  $log   = $log->format(sub {...});
+
+A callback for formatting log messages.
+
+  $log->format(sub {
+    my ($time, $level, @lines) = @_;
+    return "[Thu May 15 17:47:04 2014] [info] I â¥ Mojolicious\n";
+  });
+
+=head2 handle
+
+  my $handle = $log->handle;
+  $log       = $log->handle(IO::Handle->new);
+
+Log filehandle used by default L</"message"> event, defaults to opening
+L</"path"> or C<STDERR>.
+
+=head2 history
+
+  my $history = $log->history;
+  $log        = $log->history([[time, 'debug', 'That went wrong']]);
+
+The last few logged messages.
+
+=head2 level
+
+  my $level = $log->level;
+  $log      = $log->level('debug');
+
+Active log level, defaults to C<debug>. Available log levels are C<debug>,
+C<info>, C<warn>, C<error> and C<fatal>, in that order. Note that the
+C<MOJO_LOG_LEVEL> environment variable can override this value.
+
+=head2 max_history_size
+
+  my $size = $log->max_history_size;
+  $log     = $log->max_history_size(5);
+
+Maximum number of logged messages to store in L</"history">, defaults to C<10>.
+
+=head2 path
+
+  my $path = $log->path
+  $log     = $log->path('/var/log/mojo.log');
+
+Log file path used by L</"handle">.
+
+=head2 short
+
+  my $bool = $log->short;
+  $log     = $log->short($bool);
+
+Generate short log messages without a timestamp, suitable for systemd, defaults
+to the value of the C<MOJO_LOG_SHORT> environment variables.
+
+=head1 METHODS
+
+L<Mojo::Log> inherits all methods from L<Mojo::EventEmitter> and implements the
+following new ones.
+
+=head2 append
+
+  $log->append("[Thu May 15 17:47:04 2014] [info] I â¥ Mojolicious\n");
+
+Append message to L</"handle">.
+
+=head2 debug
+
+  $log = $log->debug('You screwed up, but that is ok');
+  $log = $log->debug('All', 'cool');
+
+Emit L</"message"> event and log C<debug> message.
+
+=head2 error
+
+  $log = $log->error('You really screwed up this time');
+  $log = $log->error('Wow', 'seriously');
+
+Emit L</"message"> event and log C<error> message.
+
+=head2 fatal
+
+  $log = $log->fatal('Its over...');
+  $log = $log->fatal('Bye', 'bye');
+
+Emit L</"message"> event and log C<fatal> message.
+
+=head2 info
+
+  $log = $log->info('You are bad, but you prolly know already');
+  $log = $log->info('Ok', 'then');
+
+Emit L</"message"> event and log C<info> message.
+
+=head2 is_level
+
+  my $bool = $log->is_level('debug');
+
+Check active log L</"level">.
+
+  # True
+  $log->level('debug')->is_level('debug');
+  $log->level('debug')->is_level('info');
+
+  # False
+  $log->level('info')->is_level('debug');
+  $log->level('fatal')->is_level('warn');
+
+=head2 new
+
+  my $log = Mojo::Log->new;
+  my $log = Mojo::Log->new(level => 'warn');
+  my $log = Mojo::Log->new({level => 'warn'});
+
+Construct a new L<Mojo::Log> object and subscribe to L</"message"> event with
+default logger.
+
+=head2 warn
+
+  $log = $log->warn('Dont do that Dave...');
+  $log = $log->warn('No', 'really');
+
+Emit L</"message"> event and log C<warn> message.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/Message.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/Message.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/Message.pm	(revision 32205)
@@ -0,0 +1,664 @@
+package Mojo::Message;
+use Mojo::Base 'Mojo::EventEmitter';
+
+use Carp 'croak';
+use Mojo::Asset::Memory;
+use Mojo::Content::Single;
+use Mojo::DOM;
+use Mojo::JSON 'j';
+use Mojo::JSON::Pointer;
+use Mojo::Parameters;
+use Mojo::Upload;
+use Mojo::Util 'decode';
+
+has content          => sub { Mojo::Content::Single->new };
+has default_charset  => 'UTF-8';
+has max_line_size    => sub { $ENV{MOJO_MAX_LINE_SIZE} || 8192 };
+has max_message_size => sub { $ENV{MOJO_MAX_MESSAGE_SIZE} // 16777216 };
+has version          => '1.1';
+
+sub body {
+  my $self = shift;
+
+  # Get
+  my $content = $self->content;
+  return $content->is_multipart ? '' : $content->asset->slurp unless @_;
+
+  # Set (multipart content needs to be downgraded)
+  $content = $self->content(Mojo::Content::Single->new)->content
+    if $content->is_multipart;
+  $content->asset(Mojo::Asset::Memory->new->add_chunk(@_));
+
+  return $self;
+}
+
+sub body_params {
+  my $self = shift;
+
+  return $self->{body_params} if $self->{body_params};
+  my $params = $self->{body_params} = Mojo::Parameters->new;
+  $params->charset($self->content->charset || $self->default_charset);
+
+  # "application/x-www-form-urlencoded"
+  my $type = $self->headers->content_type // '';
+  if ($type =~ m!application/x-www-form-urlencoded!i) {
+    $params->parse($self->content->asset->slurp);
+  }
+
+  # "multipart/form-data"
+  elsif ($type =~ m!multipart/form-data!i) {
+    $params->append(@$_[0, 1]) for @{$self->_parse_formdata};
+  }
+
+  return $params;
+}
+
+sub body_size { shift->content->body_size }
+
+sub build_body       { shift->_build('get_body_chunk') }
+sub build_headers    { shift->_build('get_header_chunk') }
+sub build_start_line { shift->_build('get_start_line_chunk') }
+
+sub cookie { shift->_cache('cookies', 0, @_) }
+
+sub cookies { croak 'Method "cookies" not implemented by subclass' }
+
+sub dom {
+  my $self = shift;
+  return undef if $self->content->is_multipart;
+  my $dom = $self->{dom} ||= Mojo::DOM->new($self->text);
+  return @_ ? $dom->find(@_) : $dom;
+}
+
+sub error {
+  my $self = shift;
+  return $self->{error} unless @_;
+  $self->{error} = shift;
+  return $self->finish;
+}
+
+sub every_cookie { shift->_cache('cookies', 1, @_) }
+sub every_upload { shift->_cache('uploads', 1, @_) }
+
+sub extract_start_line {
+  croak 'Method "extract_start_line" not implemented by subclass';
+}
+
+sub finish {
+  my $self = shift;
+  $self->{state} = 'finished';
+  return $self->{finished}++ ? $self : $self->emit('finish');
+}
+
+sub fix_headers {
+  my $self = shift;
+  return $self if $self->{fix}++;
+
+  # Content-Length or Connection (unless chunked transfer encoding is used)
+  my $content = $self->content;
+  my $headers = $content->headers;
+  if ($content->is_multipart) { $headers->remove('Content-Length') }
+  elsif ($content->is_chunked || $headers->content_length) { return $self }
+  if   ($content->is_dynamic) { $headers->connection('close') }
+  else                        { $headers->content_length($self->body_size) }
+
+  return $self;
+}
+
+sub get_body_chunk {
+  my ($self, $offset) = @_;
+
+  $self->emit('progress', 'body', $offset);
+  my $chunk = $self->content->get_body_chunk($offset);
+  return $chunk if !defined $chunk || length $chunk;
+  $self->finish;
+
+  return $chunk;
+}
+
+sub get_header_chunk {
+  my ($self, $offset) = @_;
+  $self->emit('progress', 'headers', $offset);
+  return $self->fix_headers->content->get_header_chunk($offset);
+}
+
+sub get_start_line_chunk {
+  croak 'Method "get_start_line_chunk" not implemented by subclass';
+}
+
+sub header_size { shift->fix_headers->content->header_size }
+
+sub headers { shift->content->headers }
+
+sub is_finished { (shift->{state} // '') eq 'finished' }
+
+sub is_limit_exceeded { !!shift->{limit} }
+
+sub json {
+  my ($self, $pointer) = @_;
+  return undef if $self->content->is_multipart;
+  my $data = $self->{json} //= j($self->body);
+  return $pointer ? Mojo::JSON::Pointer->new($data)->get($pointer) : $data;
+}
+
+sub parse {
+  my ($self, $chunk) = @_;
+
+  return $self if $self->{error};
+  $self->{raw_size} += length $chunk;
+  $self->{buffer} .= $chunk;
+
+  # Start-line
+  unless ($self->{state}) {
+
+    # Check start-line size
+    my $len = index $self->{buffer}, "\x0a";
+    $len = length $self->{buffer} if $len < 0;
+    return $self->_limit('Maximum start-line size exceeded')
+      if $len > $self->max_line_size;
+
+    $self->{state} = 'content' if $self->extract_start_line(\$self->{buffer});
+  }
+
+  # Content
+  my $state = $self->{state} // '';
+  $self->content($self->content->parse(delete $self->{buffer}))
+    if $state eq 'content' || $state eq 'finished';
+
+  # Check message size
+  my $max = $self->max_message_size;
+  return $self->_limit('Maximum message size exceeded')
+    if $max && $max < $self->{raw_size};
+
+  # Check header size
+  return $self->_limit('Maximum header size exceeded')
+    if $self->headers->is_limit_exceeded;
+
+  # Check buffer size
+  return $self->_limit('Maximum buffer size exceeded')
+    if $self->content->is_limit_exceeded;
+
+  return $self->emit('progress')->content->is_finished ? $self->finish : $self;
+}
+
+sub start_line_size {
+  croak 'Method "start_line_size" not implemented by subclass';
+}
+
+sub text {
+  my $self    = shift;
+  my $body    = $self->body;
+  my $charset = $self->content->charset || $self->default_charset;
+  return $charset ? decode($charset, $body) // $body : $body;
+}
+
+sub to_string {
+  my $self = shift;
+  return $self->build_start_line . $self->build_headers . $self->build_body;
+}
+
+sub upload { shift->_cache('uploads', 0, @_) }
+
+sub uploads {
+  my $self = shift;
+
+  my @uploads;
+  for my $data (@{$self->_parse_formdata(1)}) {
+    my $upload = Mojo::Upload->new(
+      name     => $data->[0],
+      filename => $data->[2],
+      asset    => $data->[1]->asset,
+      headers  => $data->[1]->headers
+    );
+    push @uploads, $upload;
+  }
+
+  return \@uploads;
+}
+
+sub _build {
+  my ($self, $method) = @_;
+
+  my ($buffer, $offset) = ('', 0);
+  while (1) {
+
+    # No chunk yet, try again
+    next unless defined(my $chunk = $self->$method($offset));
+
+    # End of part
+    last unless my $len = length $chunk;
+
+    $offset += $len;
+    $buffer .= $chunk;
+  }
+
+  return $buffer;
+}
+
+sub _cache {
+  my ($self, $method, $all, $name) = @_;
+
+  # Cache objects by name
+  unless ($self->{$method}) {
+    $self->{$method} = {};
+    push @{$self->{$method}{$_->name}}, $_ for @{$self->$method};
+  }
+
+  my $objects = $self->{$method}{$name} || [];
+  return $all ? $objects : $objects->[-1];
+}
+
+sub _limit { ++$_[0]{limit} and return $_[0]->error({message => $_[1]}) }
+
+sub _parse_formdata {
+  my ($self, $upload) = @_;
+
+  my @formdata;
+  my $content = $self->content;
+  return \@formdata unless $content->is_multipart;
+  my $charset = $content->charset || $self->default_charset;
+
+  # Check all parts recursively
+  my @parts = ($content);
+  while (my $part = shift @parts) {
+
+    if ($part->is_multipart) {
+      unshift @parts, @{$part->parts};
+      next;
+    }
+
+    next unless my $disposition = $part->headers->content_disposition;
+    my ($filename) = $disposition =~ /[; ]filename="((?:\\"|[^"])*)"/;
+    next if $upload && !defined $filename || !$upload && defined $filename;
+    my ($name) = $disposition =~ /[; ]name="((?:\\"|[^;"])*)"/;
+    $part = $part->asset->slurp unless $upload;
+
+    if ($charset) {
+      $name     = decode($charset, $name) // $name         if $name;
+      $filename = decode($charset, $filename) // $filename if $filename;
+      $part = decode($charset, $part) // $part unless $upload;
+    }
+
+    push @formdata, [$name, $part, $filename];
+  }
+
+  return \@formdata;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::Message - HTTP message base class
+
+=head1 SYNOPSIS
+
+  package Mojo::Message::MyMessage;
+  use Mojo::Base 'Mojo::Message';
+
+  sub cookies              {...}
+  sub extract_start_line   {...}
+  sub get_start_line_chunk {...}
+  sub start_line_size      {...}
+
+=head1 DESCRIPTION
+
+L<Mojo::Message> is an abstract base class for HTTP message containers, based on
+L<RFC 7230|http://tools.ietf.org/html/rfc7230>,
+L<RFC 7231|http://tools.ietf.org/html/rfc7231> and
+L<RFC 2388|http://tools.ietf.org/html/rfc2388>, like L<Mojo::Message::Request>
+and L<Mojo::Message::Response>.
+
+=head1 EVENTS
+
+L<Mojo::Message> inherits all events from L<Mojo::EventEmitter> and can emit
+the following new ones.
+
+=head2 finish
+
+  $msg->on(finish => sub {
+    my $msg = shift;
+    ...
+  });
+
+Emitted after message building or parsing is finished.
+
+  my $before = time;
+  $msg->on(finish => sub {
+    my $msg = shift;
+    $msg->headers->header('X-Parser-Time' => time - $before);
+  });
+
+=head2 progress
+
+  $msg->on(progress => sub {
+    my $msg = shift;
+    ...
+  });
+
+Emitted when message building or parsing makes progress.
+
+  # Building
+  $msg->on(progress => sub {
+    my ($msg, $state, $offset) = @_;
+    say qq{Building "$state" at offset $offset};
+  });
+
+  # Parsing
+  $msg->on(progress => sub {
+    my $msg = shift;
+    return unless my $len = $msg->headers->content_length;
+    my $size = $msg->content->progress;
+    say 'Progress: ', $size == $len ? 100 : int($size / ($len / 100)), '%';
+  });
+
+=head1 ATTRIBUTES
+
+L<Mojo::Message> implements the following attributes.
+
+=head2 content
+
+  my $msg = $msg->content;
+  $msg    = $msg->content(Mojo::Content::Single->new);
+
+Message content, defaults to a L<Mojo::Content::Single> object.
+
+=head2 default_charset
+
+  my $charset = $msg->default_charset;
+  $msg        = $msg->default_charset('UTF-8');
+
+Default charset used by L</"text"> and to extract data from
+C<application/x-www-form-urlencoded> or C<multipart/form-data> message body,
+defaults to C<UTF-8>.
+
+=head2 max_line_size
+
+  my $size = $msg->max_line_size;
+  $msg     = $msg->max_line_size(1024);
+
+Maximum start-line size in bytes, defaults to the value of the
+C<MOJO_MAX_LINE_SIZE> environment variable or C<8192> (8KiB).
+
+=head2 max_message_size
+
+  my $size = $msg->max_message_size;
+  $msg     = $msg->max_message_size(1024);
+
+Maximum message size in bytes, defaults to the value of the
+C<MOJO_MAX_MESSAGE_SIZE> environment variable or C<16777216> (16MiB). Setting
+the value to C<0> will allow messages of indefinite size.
+
+=head2 version
+
+  my $version = $msg->version;
+  $msg        = $msg->version('1.1');
+
+HTTP version of message, defaults to C<1.1>.
+
+=head1 METHODS
+
+L<Mojo::Message> inherits all methods from L<Mojo::EventEmitter> and implements
+the following new ones.
+
+=head2 body
+
+  my $bytes = $msg->body;
+  $msg      = $msg->body('Hello!');
+
+Slurp or replace L</"content">.
+
+=head2 body_params
+
+  my $params = $msg->body_params;
+
+C<POST> parameters extracted from C<application/x-www-form-urlencoded> or
+C<multipart/form-data> message body, usually a L<Mojo::Parameters> object. Note
+that this method caches all data, so it should not be called before the entire
+message body has been received. Parts of the message body need to be loaded
+into memory to parse C<POST> parameters, so you have to make sure it is not
+excessively large. There's a 16MiB limit for requests and a 2GiB limit for
+responses by default.
+
+  # Get POST parameter names and values
+  my $hash = $msg->body_params->to_hash;
+
+=head2 body_size
+
+  my $size = $msg->body_size;
+
+Content size in bytes.
+
+=head2 build_body
+
+  my $bytes = $msg->build_body;
+
+Render whole body with L</"get_body_chunk">.
+
+=head2 build_headers
+
+  my $bytes = $msg->build_headers;
+
+Render all headers with L</"get_header_chunk">.
+
+=head2 build_start_line
+
+  my $bytes = $msg->build_start_line;
+
+Render start-line with L</"get_start_line_chunk">.
+
+=head2 cookie
+
+  my $cookie = $msg->cookie('foo');
+
+Access message cookies, usually L<Mojo::Cookie::Request> or
+L<Mojo::Cookie::Response> objects. If there are multiple cookies sharing the
+same name, and you want to access more than just the last one, you can use
+L</"every_cookie">. Note that this method caches all data, so it should not be
+called before all headers have been received.
+
+  # Get cookie value
+  say $msg->cookie('foo')->value;
+
+=head2 cookies
+
+  my $cookies = $msg->cookies;
+
+Access message cookies. Meant to be overloaded in a subclass.
+
+=head2 dom
+
+  my $dom        = $msg->dom;
+  my $collection = $msg->dom('a[href]');
+
+Retrieve message body from L</"text"> and turn it into a L<Mojo::DOM> object,
+an optional selector can be used to call the method L<Mojo::DOM/"find"> on it
+right away, which then returns a L<Mojo::Collection> object. Note that this
+method caches all data, so it should not be called before the entire message
+body has been received. The whole message body needs to be loaded into memory
+to parse it, so you have to make sure it is not excessively large. There's a
+16MiB limit for requests and a 2GiB limit for responses by default.
+
+  # Perform "find" right away
+  say $msg->dom('h1, h2, h3')->map('text')->join("\n");
+
+  # Use everything else Mojo::DOM has to offer
+  say $msg->dom->at('title')->text;
+  say $msg->dom->at('body')->children->map('tag')->uniq->join("\n");
+
+=head2 error
+
+  my $err = $msg->error;
+  $msg    = $msg->error({message => 'Parser error'});
+
+Get or set message error, an C<undef> return value indicates that there is no
+error.
+
+  # Connection or parser error
+  $msg->error({message => 'Connection refused'});
+
+  # 4xx/5xx response
+  $msg->error({message => 'Internal Server Error', code => 500});
+
+=head2 every_cookie
+
+  my $cookies = $msg->every_cookie('foo');
+
+Similar to L</"cookie">, but returns all message cookies sharing the same name
+as an array reference.
+
+  # Get first cookie value
+  say $msg->every_cookie('foo')->[0]->value;
+
+=head2 every_upload
+
+  my $uploads = $msg->every_upload('foo');
+
+Similar to L</"upload">, but returns all file uploads sharing the same name as
+an array reference.
+
+  # Get content of first uploaded file
+  say $msg->every_upload('foo')->[0]->asset->slurp;
+
+=head2 extract_start_line
+
+  my $bool = $msg->extract_start_line(\$str);
+
+Extract start-line from string. Meant to be overloaded in a subclass.
+
+=head2 finish
+
+  $msg = $msg->finish;
+
+Finish message parser/generator.
+
+=head2 fix_headers
+
+  $msg = $msg->fix_headers;
+
+Make sure message has all required headers.
+
+=head2 get_body_chunk
+
+  my $bytes = $msg->get_body_chunk($offset);
+
+Get a chunk of body data starting from a specific position. Note that it might
+not be possible to get the same chunk twice if content was generated
+dynamically.
+
+=head2 get_header_chunk
+
+  my $bytes = $msg->get_header_chunk($offset);
+
+Get a chunk of header data, starting from a specific position. Note that this
+method finalizes the message.
+
+=head2 get_start_line_chunk
+
+  my $bytes = $msg->get_start_line_chunk($offset);
+
+Get a chunk of start-line data starting from a specific position. Meant to be
+overloaded in a subclass.
+
+=head2 header_size
+
+  my $size = $msg->header_size;
+
+Size of headers in bytes. Note that this method finalizes the message.
+
+=head2 headers
+
+  my $headers = $msg->headers;
+
+Message headers, usually a L<Mojo::Headers> object.
+
+  # Longer version
+  my $headers = $msg->content->headers;
+
+=head2 is_finished
+
+  my $bool = $msg->is_finished;
+
+Check if message parser/generator is finished.
+
+=head2 is_limit_exceeded
+
+  my $bool = $msg->is_limit_exceeded;
+
+Check if message has exceeded L</"max_line_size">, L</"max_message_size">,
+L<Mojo::Content/"max_buffer_size"> or L<Mojo::Headers/"max_line_size">.
+
+=head2 json
+
+  my $value = $msg->json;
+  my $value = $msg->json('/foo/bar');
+
+Decode JSON message body directly using L<Mojo::JSON> if possible, an C<undef>
+return value indicates a bare C<null> or that decoding failed. An optional JSON
+Pointer can be used to extract a specific value with L<Mojo::JSON::Pointer>.
+Note that this method caches all data, so it should not be called before the
+entire message body has been received. The whole message body needs to be
+loaded into memory to parse it, so you have to make sure it is not excessively
+large. There's a 16MiB limit for requests and a 2GiB limit for responses by
+default.
+
+  # Extract JSON values
+  say $msg->json->{foo}{bar}[23];
+  say $msg->json('/foo/bar/23');
+
+=head2 parse
+
+  $msg = $msg->parse('HTTP/1.1 200 OK...');
+
+Parse message chunk.
+
+=head2 start_line_size
+
+  my $size = $msg->start_line_size;
+
+Size of the start-line in bytes. Meant to be overloaded in a subclass.
+
+=head2 text
+
+  my $str = $msg->text;
+
+Retrieve L</"body"> and try to decode it with L<Mojo::Content/"charset"> or
+L</"default_charset">.
+
+=head2 to_string
+
+  my $str = $msg->to_string;
+
+Render whole message. Note that this method finalizes the message, and that it
+might not be possible to render the same message twice if content was generated
+dynamically.
+
+=head2 upload
+
+  my $upload = $msg->upload('foo');
+
+Access C<multipart/form-data> file uploads, usually L<Mojo::Upload> objects. If
+there are multiple uploads sharing the same name, and you want to access more
+than just the last one, you can use L</"every_upload">. Note that this method
+caches all data, so it should not be called before the entire message body has
+been received.
+
+  # Get content of uploaded file
+  say $msg->upload('foo')->asset->slurp;
+
+=head2 uploads
+
+  my $uploads = $msg->uploads;
+
+All C<multipart/form-data> file uploads, usually L<Mojo::Upload> objects.
+
+  # Names of all uploads
+  say $_->name for @{$msg->uploads};
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/Message/Request.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/Message/Request.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/Message/Request.pm	(revision 32205)
@@ -0,0 +1,473 @@
+package Mojo::Message::Request;
+use Mojo::Base 'Mojo::Message';
+
+use Mojo::Cookie::Request;
+use Mojo::Util qw(b64_encode b64_decode sha1_sum);
+use Mojo::URL;
+
+my ($SEED, $COUNTER) = (rand, int rand 0xffffff);
+
+has env => sub { {} };
+has method => 'GET';
+has [qw(proxy reverse_proxy)];
+has request_id => sub {
+  substr sha1_sum($SEED . $$ . ($COUNTER = ($COUNTER + 1) % 0xffffff)), 0, 8;
+};
+has url => sub { Mojo::URL->new };
+has via_proxy => 1;
+
+sub clone {
+  my $self = shift;
+
+  # Dynamic requests cannot be cloned
+  return undef unless my $content = $self->content->clone;
+  my $clone = $self->new(
+    content => $content,
+    method  => $self->method,
+    url     => $self->url->clone,
+    version => $self->version
+  );
+  $clone->{proxy} = $self->{proxy}->clone if $self->{proxy};
+
+  return $clone;
+}
+
+sub cookies {
+  my $self = shift;
+
+  # Parse cookies
+  my $headers = $self->headers;
+  return [map { @{Mojo::Cookie::Request->parse($_)} } $headers->cookie]
+    unless @_;
+
+  # Add cookies
+  my @cookies = map { ref $_ eq 'HASH' ? Mojo::Cookie::Request->new($_) : $_ }
+    $headers->cookie || (), @_;
+  $headers->cookie(join '; ', @cookies);
+
+  return $self;
+}
+
+sub every_param { shift->params->every_param(@_) }
+
+sub extract_start_line {
+  my ($self, $bufref) = @_;
+
+  # Ignore any leading empty lines
+  return undef unless $$bufref =~ s/^\s*(.*?)\x0d?\x0a//;
+
+  # We have a (hopefully) full request-line
+  return !$self->error({message => 'Bad request start-line'})
+    unless $1 =~ /^(\S+)\s+(\S+)\s+HTTP\/(\d\.\d)$/;
+  my $url    = $self->method($1)->version($3)->url;
+  my $target = $2;
+  return !!$url->host_port($target) if $1 eq 'CONNECT';
+  return !!$url->parse($target)->fragment(undef) if $target =~ /^[^:\/?#]+:/;
+  return !!$url->path_query($target);
+}
+
+sub fix_headers {
+  my $self = shift;
+  $self->{fix} ? return $self : $self->SUPER::fix_headers(@_);
+
+  # Host
+  my $url     = $self->url;
+  my $headers = $self->headers;
+  $headers->host($url->host_port) unless $headers->host;
+
+  # Basic authentication
+  if ((my $info = $url->userinfo) && !$headers->authorization) {
+    $headers->authorization('Basic ' . b64_encode($info, ''));
+  }
+
+  # Basic proxy authentication
+  return $self unless (my $proxy = $self->proxy) && $self->via_proxy;
+  return $self unless my $info = $proxy->userinfo;
+  $headers->proxy_authorization('Basic ' . b64_encode($info, ''))
+    unless $headers->proxy_authorization;
+  return $self;
+}
+
+sub get_start_line_chunk {
+  my ($self, $offset) = @_;
+  $self->_start_line->emit(progress => 'start_line', $offset);
+  return substr $self->{start_buffer}, $offset, 131072;
+}
+
+sub is_handshake { lc($_[0]->headers->upgrade // '') eq 'websocket' }
+
+sub is_secure {
+  my $url = shift->url;
+  return ($url->protocol || $url->base->protocol) eq 'https';
+}
+
+sub is_xhr {
+  (shift->headers->header('X-Requested-With') // '') =~ /XMLHttpRequest/i;
+}
+
+sub param { shift->params->param(@_) }
+
+sub params {
+  my $self = shift;
+  return $self->{params}
+    ||= $self->body_params->clone->append($self->query_params);
+}
+
+sub parse {
+  my $self = shift;
+  my ($env, $chunk) = ref $_[0] ? (shift, '') : (undef, shift);
+
+  # Parse CGI environment
+  $self->env($env)->_parse_env($env) if $env;
+
+  # Parse normal message
+  if (($self->{state} // '') ne 'cgi') { $self->SUPER::parse($chunk) }
+
+  # Parse CGI content
+  else { $self->content($self->content->parse_body($chunk))->SUPER::parse('') }
+
+  # Check if we can fix things that require all headers
+  return $self unless $self->is_finished;
+
+  # Base URL
+  my $base = $self->url->base;
+  $base->scheme('http') unless $base->scheme;
+  my $headers = $self->headers;
+  if (!$base->host && (my $host = $headers->host)) { $base->host_port($host) }
+
+  # Basic authentication
+  if (my $basic = _basic($headers->authorization)) { $base->userinfo($basic) }
+
+  # Basic proxy authentication
+  my $basic = _basic($headers->proxy_authorization);
+  $self->proxy(Mojo::URL->new->userinfo($basic)) if $basic;
+
+  # "X-Forwarded-Proto"
+  $base->scheme('https')
+    if $self->reverse_proxy
+    && ($headers->header('X-Forwarded-Proto') // '') eq 'https';
+
+  return $self;
+}
+
+sub query_params { shift->url->query }
+
+sub start_line_size { length shift->_start_line->{start_buffer} }
+
+sub _basic { $_[0] && $_[0] =~ /Basic (.+)$/ ? b64_decode $1 : undef }
+
+sub _parse_env {
+  my ($self, $env) = @_;
+
+  # Bypass normal message parser
+  $self->{state} = 'cgi';
+
+  # Extract headers
+  my $headers = $self->headers;
+  my $url     = $self->url;
+  my $base    = $url->base;
+  for my $name (keys %$env) {
+    my $value = $env->{$name};
+    next unless $name =~ s/^HTTP_//i;
+    $name =~ y/_/-/;
+    $headers->header($name => $value);
+
+    # Host/Port
+    $value =~ s/:(\d+)$// ? $base->host($value)->port($1) : $base->host($value)
+      if $name eq 'HOST';
+  }
+
+  # Content-Type is a special case on some servers
+  $headers->content_type($env->{CONTENT_TYPE}) if $env->{CONTENT_TYPE};
+
+  # Content-Length is a special case on some servers
+  $headers->content_length($env->{CONTENT_LENGTH}) if $env->{CONTENT_LENGTH};
+
+  # Query
+  $url->query->parse($env->{QUERY_STRING}) if $env->{QUERY_STRING};
+
+  # Method
+  $self->method($env->{REQUEST_METHOD}) if $env->{REQUEST_METHOD};
+
+  # Scheme/Version
+  $base->scheme($1) and $self->version($2)
+    if ($env->{SERVER_PROTOCOL} // '') =~ m!^([^/]+)/([^/]+)$!;
+
+  # HTTPS
+  $base->scheme('https') if uc($env->{HTTPS} // '') eq 'ON';
+
+  # Path
+  my $path = $url->path->parse($env->{PATH_INFO} ? $env->{PATH_INFO} : '');
+
+  # Base path
+  if (my $value = $env->{SCRIPT_NAME}) {
+
+    # Make sure there is a trailing slash (important for merging)
+    $base->path->parse($value =~ m!/$! ? $value : "$value/");
+
+    # Remove SCRIPT_NAME prefix if necessary
+    my $buffer = $path->to_string;
+    $value =~ s!^/|/$!!g;
+    $buffer =~ s!^/?\Q$value\E/?!!;
+    $buffer =~ s!^/!!;
+    $path->parse($buffer);
+  }
+}
+
+sub _start_line {
+  my $self = shift;
+
+  return $self if defined $self->{start_buffer};
+
+  # Path
+  my $url  = $self->url;
+  my $path = $url->path_query;
+  $path = "/$path" unless $path =~ m!^/!;
+
+  # CONNECT
+  my $method = uc $self->method;
+  if ($method eq 'CONNECT') {
+    my $port = $url->port // ($url->protocol eq 'https' ? '443' : '80');
+    $path = $url->ihost . ":$port";
+  }
+
+  # Proxy
+  elsif ($self->proxy && $self->via_proxy && $url->protocol ne 'https') {
+    $path = $url->clone->userinfo(undef) unless $self->is_handshake;
+  }
+
+  $self->{start_buffer} = "$method $path HTTP/@{[$self->version]}\x0d\x0a";
+
+  return $self;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::Message::Request - HTTP request
+
+=head1 SYNOPSIS
+
+  use Mojo::Message::Request;
+
+  # Parse
+  my $req = Mojo::Message::Request->new;
+  $req->parse("GET /foo HTTP/1.0\x0d\x0a");
+  $req->parse("Content-Length: 12\x0d\x0a");
+  $req->parse("Content-Type: text/plain\x0d\x0a\x0d\x0a");
+  $req->parse('Hello World!');
+  say $req->method;
+  say $req->headers->content_type;
+  say $req->body;
+
+  # Build
+  my $req = Mojo::Message::Request->new;
+  $req->url->parse('http://127.0.0.1/foo/bar');
+  $req->method('GET');
+  say $req->to_string;
+
+=head1 DESCRIPTION
+
+L<Mojo::Message::Request> is a container for HTTP requests, based on
+L<RFC 7230|http://tools.ietf.org/html/rfc7230>,
+L<RFC 7231|http://tools.ietf.org/html/rfc7231>,
+L<RFC 7235|http://tools.ietf.org/html/rfc7235> and
+L<RFC 2817|http://tools.ietf.org/html/rfc2817>.
+
+=head1 EVENTS
+
+L<Mojo::Message::Request> inherits all events from L<Mojo::Message>.
+
+=head1 ATTRIBUTES
+
+L<Mojo::Message::Request> inherits all attributes from L<Mojo::Message> and
+implements the following new ones.
+
+=head2 env
+
+  my $env = $req->env;
+  $req    = $req->env({PATH_INFO => '/'});
+
+Direct access to the C<CGI> or C<PSGI> environment hash if available.
+
+  # Check CGI version
+  my $version = $req->env->{GATEWAY_INTERFACE};
+
+  # Check PSGI version
+  my $version = $req->env->{'psgi.version'};
+
+=head2 method
+
+  my $method = $req->method;
+  $req       = $req->method('POST');
+
+HTTP request method, defaults to C<GET>.
+
+=head2 proxy
+
+  my $url = $req->proxy;
+  $req    = $req->proxy(Mojo::URL->new('http://127.0.0.1:3000'));
+
+Proxy URL for request.
+
+=head2 reverse_proxy
+
+  my $bool = $req->reverse_proxy;
+  $req     = $req->reverse_proxy($bool);
+
+Request has been performed through a reverse proxy.
+
+=head2 request_id
+
+  my $id = $req->request_id;
+  $req   = $req->request_id('aee7d5d8');
+
+Request ID, defaults to a reasonably unique value.
+
+=head2 url
+
+  my $url = $req->url;
+  $req    = $req->url(Mojo::URL->new);
+
+HTTP request URL, defaults to a L<Mojo::URL> object.
+
+  # Get request information
+  my $info = $req->url->to_abs->userinfo;
+  my $host = $req->url->to_abs->host;
+  my $path = $req->url->to_abs->path;
+
+=head2 via_proxy
+
+  my $bool = $req->via_proxy;
+  $req     = $req->via_proxy($bool);
+
+Request can be performed through a proxy server.
+
+=head1 METHODS
+
+L<Mojo::Message::Request> inherits all methods from L<Mojo::Message> and
+implements the following new ones.
+
+=head2 clone
+
+  my $clone = $req->clone;
+
+Return a new L<Mojo::Message::Request> object cloned from this request if
+possible, otherwise return C<undef>.
+
+=head2 cookies
+
+  my $cookies = $req->cookies;
+  $req        = $req->cookies(Mojo::Cookie::Request->new);
+  $req        = $req->cookies({name => 'foo', value => 'bar'});
+
+Access request cookies, usually L<Mojo::Cookie::Request> objects.
+
+  # Names of all cookies
+  say $_->name for @{$req->cookies};
+
+=head2 every_param
+
+  my $values = $req->every_param('foo');
+
+Similar to L</"param">, but returns all values sharing the same name as an
+array reference.
+
+  # Get first value
+  say $req->every_param('foo')->[0];
+
+=head2 extract_start_line
+
+  my $bool = $req->extract_start_line(\$str);
+
+Extract request-line from string.
+
+=head2 fix_headers
+
+  $req = $req->fix_headers;
+
+Make sure request has all required headers.
+
+=head2 get_start_line_chunk
+
+  my $bytes = $req->get_start_line_chunk($offset);
+
+Get a chunk of request-line data starting from a specific position. Note that
+this method finalizes the request.
+
+=head2 is_handshake
+
+  my $bool = $req->is_handshake;
+
+Check C<Upgrade> header for C<websocket> value.
+
+=head2 is_secure
+
+  my $bool = $req->is_secure;
+
+Check if connection is secure.
+
+=head2 is_xhr
+
+  my $bool = $req->is_xhr;
+
+Check C<X-Requested-With> header for C<XMLHttpRequest> value.
+
+=head2 param
+
+  my $value = $req->param('foo');
+
+Access C<GET> and C<POST> parameters extracted from the query string and
+C<application/x-www-form-urlencoded> or C<multipart/form-data> message body. If
+there are multiple values sharing the same name, and you want to access more
+than just the last one, you can use L</"every_param">. Note that this method
+caches all data, so it should not be called before the entire request body has
+been received. Parts of the request body need to be loaded into memory to parse
+C<POST> parameters, so you have to make sure it is not excessively large.
+There's a 16MiB limit for requests by default.
+
+=head2 params
+
+  my $params = $req->params;
+
+All C<GET> and C<POST> parameters extracted from the query string and
+C<application/x-www-form-urlencoded> or C<multipart/form-data> message body,
+usually a L<Mojo::Parameters> object. Note that this method caches all data, so
+it should not be called before the entire request body has been received. Parts
+of the request body need to be loaded into memory to parse C<POST> parameters,
+so you have to make sure it is not excessively large. There's a 16MiB limit for
+requests by default.
+
+  # Get parameter names and values
+  my $hash = $req->params->to_hash;
+
+=head2 parse
+
+  $req = $req->parse('GET /foo/bar HTTP/1.1');
+  $req = $req->parse({PATH_INFO => '/'});
+
+Parse HTTP request chunks or environment hash.
+
+=head2 query_params
+
+  my $params = $req->query_params;
+
+All C<GET> parameters, usually a L<Mojo::Parameters> object.
+
+  # Turn GET parameters to hash and extract value
+  say $req->query_params->to_hash->{foo};
+
+=head2 start_line_size
+
+  my $size = $req->start_line_size;
+
+Size of the request-line in bytes. Note that this method finalizes the request.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/Message/Response.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/Message/Response.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/Message/Response.pm	(revision 32205)
@@ -0,0 +1,324 @@
+package Mojo::Message::Response;
+use Mojo::Base 'Mojo::Message';
+
+use Mojo::Cookie::Response;
+use Mojo::Date;
+
+has [qw(code message)];
+has max_message_size => sub { $ENV{MOJO_MAX_MESSAGE_SIZE} // 2147483648 };
+
+# Umarked codes are from RFC 7231
+my %MESSAGES = (
+  100 => 'Continue',
+  101 => 'Switching Protocols',
+  102 => 'Processing',                         # RFC 2518 (WebDAV)
+  103 => 'Early Hints',                        # RFC 8297
+  200 => 'OK',
+  201 => 'Created',
+  202 => 'Accepted',
+  203 => 'Non-Authoritative Information',
+  204 => 'No Content',
+  205 => 'Reset Content',
+  206 => 'Partial Content',
+  207 => 'Multi-Status',                       # RFC 2518 (WebDAV)
+  208 => 'Already Reported',                   # RFC 5842
+  226 => 'IM Used',                            # RFC 3229
+  300 => 'Multiple Choices',
+  301 => 'Moved Permanently',
+  302 => 'Found',
+  303 => 'See Other',
+  304 => 'Not Modified',
+  305 => 'Use Proxy',
+  307 => 'Temporary Redirect',
+  308 => 'Permanent Redirect',                 # RFC 7538
+  400 => 'Bad Request',
+  401 => 'Unauthorized',
+  402 => 'Payment Required',
+  403 => 'Forbidden',
+  404 => 'Not Found',
+  405 => 'Method Not Allowed',
+  406 => 'Not Acceptable',
+  407 => 'Proxy Authentication Required',
+  408 => 'Request Timeout',
+  409 => 'Conflict',
+  410 => 'Gone',
+  411 => 'Length Required',
+  412 => 'Precondition Failed',
+  413 => 'Request Entity Too Large',
+  414 => 'Request-URI Too Long',
+  415 => 'Unsupported Media Type',
+  416 => 'Request Range Not Satisfiable',
+  417 => 'Expectation Failed',
+  418 => "I'm a teapot",                       # RFC 2324 :)
+  421 => 'Misdirected Request',                # RFC 7540
+  422 => 'Unprocessable Entity',               # RFC 2518 (WebDAV)
+  423 => 'Locked',                             # RFC 2518 (WebDAV)
+  424 => 'Failed Dependency',                  # RFC 2518 (WebDAV)
+  425 => 'Unordered Colection',                # RFC 3648 (WebDAV)
+  426 => 'Upgrade Required',                   # RFC 2817
+  428 => 'Precondition Required',              # RFC 6585
+  429 => 'Too Many Requests',                  # RFC 6585
+  431 => 'Request Header Fields Too Large',    # RFC 6585
+  451 => 'Unavailable For Legal Reasons',      # RFC 7725
+  500 => 'Internal Server Error',
+  501 => 'Not Implemented',
+  502 => 'Bad Gateway',
+  503 => 'Service Unavailable',
+  504 => 'Gateway Timeout',
+  505 => 'HTTP Version Not Supported',
+  506 => 'Variant Also Negotiates',            # RFC 2295
+  507 => 'Insufficient Storage',               # RFC 2518 (WebDAV)
+  508 => 'Loop Detected',                      # RFC 5842
+  509 => 'Bandwidth Limit Exceeded',           # Unofficial
+  510 => 'Not Extended',                       # RFC 2774
+  511 => 'Network Authentication Required'     # RFC 6585
+);
+
+sub cookies {
+  my $self = shift;
+
+  # Parse cookies
+  my $headers = $self->headers;
+  return [@{Mojo::Cookie::Response->parse($headers->set_cookie)}] unless @_;
+
+  # Add cookies
+  $headers->add('Set-Cookie' => "$_")
+    for map { ref $_ eq 'HASH' ? Mojo::Cookie::Response->new($_) : $_ } @_;
+
+  return $self;
+}
+
+sub default_message { $MESSAGES{$_[1] || $_[0]->code // 404} || '' }
+
+sub extract_start_line {
+  my ($self, $bufref) = @_;
+
+  # We have a full response line
+  return undef unless $$bufref =~ s/^(.*?)\x0d?\x0a//;
+  return !$self->error({message => 'Bad response start-line'})
+    unless $1 =~ m!^\s*HTTP/(\d\.\d)\s+(\d\d\d)\s*(.+)?$!;
+
+  my $content = $self->content;
+  $content->skip_body(1) if $self->code($2)->is_empty;
+  defined $content->$_ or $content->$_(1) for qw(auto_decompress auto_relax);
+  $content->expect_close(1) if $1 eq '1.0';
+  return !!$self->version($1)->message($3);
+}
+
+sub fix_headers {
+  my $self = shift;
+  $self->{fix} ? return $self : $self->SUPER::fix_headers(@_);
+
+  # Date
+  my $headers = $self->headers;
+  $headers->date(Mojo::Date->new->to_string) unless $headers->date;
+
+  # RFC 7230 3.3.2
+  $headers->remove('Content-Length') if $self->is_empty;
+
+  return $self;
+}
+
+sub get_start_line_chunk {
+  my ($self, $offset) = @_;
+  $self->_start_line->emit(progress => 'start_line', $offset);
+  return substr $self->{start_buffer}, $offset, 131072;
+}
+
+sub is_client_error { shift->_status_class(400) }
+
+sub is_empty {
+  my $self = shift;
+  return undef unless my $code = $self->code;
+  return $self->is_info || $code == 204 || $code == 304;
+}
+
+sub is_error { shift->_status_class(400, 500) }
+sub is_info { shift->_status_class(100) }
+sub is_redirect     { shift->_status_class(300) }
+sub is_server_error { shift->_status_class(500) }
+
+sub is_success { shift->_status_class(200) }
+
+sub start_line_size { length shift->_start_line->{start_buffer} }
+
+sub _start_line {
+  my $self = shift;
+
+  return $self if defined $self->{start_buffer};
+  my $code = $self->code    || 404;
+  my $msg  = $self->message || $self->default_message;
+  $self->{start_buffer} = "HTTP/@{[$self->version]} $code $msg\x0d\x0a";
+
+  return $self;
+}
+
+sub _status_class {
+  my ($self, @classes) = @_;
+  return undef unless my $code = $self->code;
+  return !!grep { $code >= $_ && $code < ($_ + 100) } @classes;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::Message::Response - HTTP response
+
+=head1 SYNOPSIS
+
+  use Mojo::Message::Response;
+
+  # Parse
+  my $res = Mojo::Message::Response->new;
+  $res->parse("HTTP/1.0 200 OK\x0d\x0a");
+  $res->parse("Content-Length: 12\x0d\x0a");
+  $res->parse("Content-Type: text/plain\x0d\x0a\x0d\x0a");
+  $res->parse('Hello World!');
+  say $res->code;
+  say $res->headers->content_type;
+  say $res->body;
+
+  # Build
+  my $res = Mojo::Message::Response->new;
+  $res->code(200);
+  $res->headers->content_type('text/plain');
+  $res->body('Hello World!');
+  say $res->to_string;
+
+=head1 DESCRIPTION
+
+L<Mojo::Message::Response> is a container for HTTP responses, based on
+L<RFC 7230|http://tools.ietf.org/html/rfc7230> and
+L<RFC 7231|http://tools.ietf.org/html/rfc7231>.
+
+=head1 EVENTS
+
+L<Mojo::Message::Response> inherits all events from L<Mojo::Message>.
+
+=head1 ATTRIBUTES
+
+L<Mojo::Message::Response> inherits all attributes from L<Mojo::Message> and
+implements the following new ones.
+
+=head2 code
+
+  my $code = $res->code;
+  $res     = $res->code(200);
+
+HTTP response status code.
+
+=head2 max_message_size
+
+  my $size = $res->max_message_size;
+  $res     = $res->max_message_size(1024);
+
+Maximum message size in bytes, defaults to the value of the
+C<MOJO_MAX_MESSAGE_SIZE> environment variable or C<2147483648> (2GiB). Setting
+the value to C<0> will allow messages of indefinite size.
+
+=head2 message
+
+  my $msg = $res->message;
+  $res    = $res->message('OK');
+
+HTTP response status message.
+
+=head1 METHODS
+
+L<Mojo::Message::Response> inherits all methods from L<Mojo::Message> and
+implements the following new ones.
+
+=head2 cookies
+
+  my $cookies = $res->cookies;
+  $res        = $res->cookies(Mojo::Cookie::Response->new);
+  $res        = $res->cookies({name => 'foo', value => 'bar'});
+
+Access response cookies, usually L<Mojo::Cookie::Response> objects.
+
+  # Names of all cookies
+  say $_->name for @{$res->cookies};
+
+=head2 default_message
+
+  my $msg = $res->default_message;
+  my $msg = $res->default_message(418);
+
+Generate default response message for status code, defaults to using
+L</"code">.
+
+=head2 extract_start_line
+
+  my $bool = $res->extract_start_line(\$str);
+
+Extract status-line from string.
+
+=head2 fix_headers
+
+  $res = $res->fix_headers;
+
+Make sure response has all required headers.
+
+=head2 get_start_line_chunk
+
+  my $bytes = $res->get_start_line_chunk($offset);
+
+Get a chunk of status-line data starting from a specific position. Note that
+this method finalizes the response.
+
+=head2 is_client_error
+
+  my $bool = $res->is_client_error;
+
+Check if this response has a C<4xx> status L</"code">.
+
+=head2 is_empty
+
+  my $bool = $res->is_empty;
+
+Check if this response has a C<1xx>, C<204> or C<304> status L</"code">.
+
+=head2 is_error
+
+  my $bool = $res->is_error;
+
+Check if this response has a C<4xx> or C<5xx> status L</"code">.
+
+=head2 is_info
+
+  my $bool = $res->is_info;
+
+Check if this response has a C<1xx> status L</"code">.
+
+=head2 is_redirect
+
+  my $bool = $res->is_redirect;
+
+Check if this response has a C<3xx> status L</"code">.
+
+=head2 is_server_error
+
+  my $bool = $res->is_server_error;
+
+Check if this response has a C<5xx> status L</"code">.
+
+=head2 is_success
+
+  my $bool = $res->is_success;
+
+Check if this response has a C<2xx> status L</"code">.
+
+=head2 start_line_size
+
+  my $size = $req->start_line_size;
+
+Size of the status-line in bytes. Note that this method finalizes the response.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/Parameters.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/Parameters.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/Parameters.pm	(revision 32205)
@@ -0,0 +1,384 @@
+package Mojo::Parameters;
+use Mojo::Base -base;
+use overload
+  '@{}'    => sub { shift->pairs },
+  bool     => sub {1},
+  '""'     => sub { shift->to_string },
+  fallback => 1;
+
+use Mojo::Util qw(decode encode url_escape url_unescape);
+
+has charset => 'UTF-8';
+
+sub append {
+  my $self = shift;
+
+  my $old = $self->pairs;
+  my @new = @_ == 1 ? @{shift->pairs} : @_;
+  while (my ($name, $value) = splice @new, 0, 2) {
+
+    # Multiple values
+    if (ref $value eq 'ARRAY') { push @$old, $name => $_ // '' for @$value }
+
+    # Single value
+    elsif (defined $value) { push @$old, $name => $value }
+  }
+
+  return $self;
+}
+
+sub clone {
+  my $self = shift;
+
+  my $clone = $self->new;
+  if   (exists $self->{charset}) { $clone->{charset} = $self->{charset} }
+  if   (defined $self->{string}) { $clone->{string}  = $self->{string} }
+  else                           { $clone->{pairs}   = [@{$self->pairs}] }
+
+  return $clone;
+}
+
+sub every_param {
+  my ($self, $name) = @_;
+
+  my @values;
+  my $pairs = $self->pairs;
+  for (my $i = 0; $i < @$pairs; $i += 2) {
+    push @values, $pairs->[$i + 1] if $pairs->[$i] eq $name;
+  }
+
+  return \@values;
+}
+
+sub merge {
+  my $self = shift;
+
+  my @pairs = @_ == 1 ? @{shift->pairs} : @_;
+  while (my ($name, $value) = splice @pairs, 0, 2) {
+    defined $value ? $self->param($name => $value) : $self->remove($name);
+  }
+
+  return $self;
+}
+
+sub names { [sort keys %{shift->to_hash}] }
+
+sub new { @_ > 1 ? shift->SUPER::new->parse(@_) : shift->SUPER::new }
+
+sub pairs {
+  my $self = shift;
+
+  # Replace parameters
+  if (@_) {
+    $self->{pairs} = shift;
+    delete $self->{string};
+    return $self;
+  }
+
+  # Parse string
+  if (defined(my $str = delete $self->{string})) {
+    my $pairs = $self->{pairs} = [];
+    return $pairs unless length $str;
+
+    my $charset = $self->charset;
+    for my $pair (split '&', $str) {
+      next unless $pair =~ /^([^=]+)(?:=(.*))?$/;
+      my ($name, $value) = ($1, $2 // '');
+
+      # Replace "+" with whitespace, unescape and decode
+      s/\+/ /g for $name, $value;
+      $name  = url_unescape $name;
+      $name  = decode($charset, $name) // $name if $charset;
+      $value = url_unescape $value;
+      $value = decode($charset, $value) // $value if $charset;
+
+      push @$pairs, $name, $value;
+    }
+  }
+
+  return $self->{pairs} ||= [];
+}
+
+sub param {
+  my ($self, $name) = (shift, shift);
+  return $self->every_param($name)->[-1] unless @_;
+  $self->remove($name);
+  return $self->append($name => ref $_[0] eq 'ARRAY' ? $_[0] : [@_]);
+}
+
+sub parse {
+  my $self = shift;
+
+  # Pairs
+  return $self->append(@_) if @_ > 1;
+
+  # String
+  $self->{string} = shift;
+  return $self;
+}
+
+sub remove {
+  my ($self, $name) = @_;
+  my $pairs = $self->pairs;
+  my $i     = 0;
+  $pairs->[$i] eq $name ? splice @$pairs, $i, 2 : ($i += 2) while $i < @$pairs;
+  return $self;
+}
+
+sub to_hash {
+  my $self = shift;
+
+  my %hash;
+  my $pairs = $self->pairs;
+  for (my $i = 0; $i < @$pairs; $i += 2) {
+    my ($name, $value) = @{$pairs}[$i, $i + 1];
+
+    # Array
+    if (exists $hash{$name}) {
+      $hash{$name} = [$hash{$name}] if ref $hash{$name} ne 'ARRAY';
+      push @{$hash{$name}}, $value;
+    }
+
+    # String
+    else { $hash{$name} = $value }
+  }
+
+  return \%hash;
+}
+
+sub to_string {
+  my $self = shift;
+
+  # String (RFC 3986)
+  my $charset = $self->charset;
+  if (defined(my $str = $self->{string})) {
+    $str = encode $charset, $str if $charset;
+    return url_escape $str, '^A-Za-z0-9\-._~%!$&\'()*+,;=:@/?';
+  }
+
+  # Build pairs (HTML Living Standard)
+  my $pairs = $self->pairs;
+  return '' unless @$pairs;
+  my @pairs;
+  for (my $i = 0; $i < @$pairs; $i += 2) {
+    my ($name, $value) = @{$pairs}[$i, $i + 1];
+
+    # Escape and replace whitespace with "+"
+    $name  = encode $charset,   $name if $charset;
+    $name  = url_escape $name,  '^*\-.0-9A-Z_a-z';
+    $value = encode $charset,   $value if $charset;
+    $value = url_escape $value, '^*\-.0-9A-Z_a-z';
+    s/\%20/\+/g for $name, $value;
+
+    push @pairs, "$name=$value";
+  }
+
+  return join '&', @pairs;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::Parameters - Parameters
+
+=head1 SYNOPSIS
+
+  use Mojo::Parameters;
+
+  # Parse
+  my $params = Mojo::Parameters->new('foo=bar&baz=23');
+  say $params->param('baz');
+
+  # Build
+  my $params = Mojo::Parameters->new(foo => 'bar', baz => 23);
+  push @$params, i => 'â¥ mojolicious';
+  say "$params";
+
+=head1 DESCRIPTION
+
+L<Mojo::Parameters> is a container for form parameters used by L<Mojo::URL>,
+based on L<RFC 3986|http://tools.ietf.org/html/rfc3986> and the
+L<HTML Living Standard|https://html.spec.whatwg.org>.
+
+=head1 ATTRIBUTES
+
+L<Mojo::Parameters> implements the following attributes.
+
+=head2 charset
+
+  my $charset = $params->charset;
+  $params     = $params->charset('UTF-8');
+
+Charset used for encoding and decoding parameters, defaults to C<UTF-8>.
+
+  # Disable encoding and decoding
+  $params->charset(undef);
+
+=head1 METHODS
+
+L<Mojo::Parameters> inherits all methods from L<Mojo::Base> and implements the
+following new ones.
+
+=head2 append
+
+  $params = $params->append(foo => 'ba&r');
+  $params = $params->append(foo => ['ba&r', 'baz']);
+  $params = $params->append(foo => ['bar', 'baz'], bar => 23);
+  $params = $params->append(Mojo::Parameters->new);
+
+Append parameters. Note that this method will normalize the parameters.
+
+  # "foo=bar&foo=baz"
+  Mojo::Parameters->new('foo=bar')->append(Mojo::Parameters->new('foo=baz'));
+
+  # "foo=bar&foo=baz"
+  Mojo::Parameters->new('foo=bar')->append(foo => 'baz');
+
+  # "foo=bar&foo=baz&foo=yada"
+  Mojo::Parameters->new('foo=bar')->append(foo => ['baz', 'yada']);
+
+  # "foo=bar&foo=baz&foo=yada&bar=23"
+  Mojo::Parameters->new('foo=bar')->append(foo => ['baz', 'yada'], bar => 23);
+
+=head2 clone
+
+  my $params2 = $params->clone;
+
+Return a new L<Mojo::Parameters> object cloned from these parameters.
+
+=head2 every_param
+
+  my $values = $params->every_param('foo');
+
+Similar to L</"param">, but returns all values sharing the same name as an
+array reference. Note that this method will normalize the parameters.
+
+  # Get first value
+  say $params->every_param('foo')->[0];
+
+=head2 merge
+
+  $params = $params->merge(foo => 'ba&r');
+  $params = $params->merge(foo => ['ba&r', 'baz']);
+  $params = $params->merge(foo => ['bar', 'baz'], bar => 23);
+  $params = $params->merge(Mojo::Parameters->new);
+
+Merge parameters. Note that this method will normalize the parameters.
+
+  # "foo=baz"
+  Mojo::Parameters->new('foo=bar')->merge(Mojo::Parameters->new('foo=baz'));
+
+  # "yada=yada&foo=baz"
+  Mojo::Parameters->new('foo=bar&yada=yada')->merge(foo => 'baz');
+
+  # "yada=yada"
+  Mojo::Parameters->new('foo=bar&yada=yada')->merge(foo => undef);
+
+=head2 names
+
+  my $names = $params->names;
+
+Return an array reference with all parameter names.
+
+  # Names of all parameters
+  say for @{$params->names};
+
+=head2 new
+
+  my $params = Mojo::Parameters->new;
+  my $params = Mojo::Parameters->new('foo=b%3Bar&baz=23');
+  my $params = Mojo::Parameters->new(foo => 'b&ar');
+  my $params = Mojo::Parameters->new(foo => ['ba&r', 'baz']);
+  my $params = Mojo::Parameters->new(foo => ['bar', 'baz'], bar => 23);
+
+Construct a new L<Mojo::Parameters> object and L</"parse"> parameters if
+necessary.
+
+=head2 pairs
+
+  my $array = $params->pairs;
+  $params   = $params->pairs([foo => 'b&ar', baz => 23]);
+
+Parsed parameter pairs. Note that this method will normalize the parameters.
+
+  # Remove all parameters
+  $params->pairs([]);
+
+=head2 param
+
+  my $value = $params->param('foo');
+  $params   = $params->param(foo => 'ba&r');
+  $params   = $params->param(foo => qw(ba&r baz));
+  $params   = $params->param(foo => ['ba;r', 'baz']);
+
+Access parameter values. If there are multiple values sharing the same name,
+and you want to access more than just the last one, you can use
+L</"every_param">. Note that this method will normalize the parameters.
+
+=head2 parse
+
+  $params = $params->parse('foo=b%3Bar&baz=23');
+
+Parse parameters.
+
+=head2 remove
+
+  $params = $params->remove('foo');
+
+Remove parameters. Note that this method will normalize the parameters.
+
+  # "bar=yada"
+  Mojo::Parameters->new('foo=bar&foo=baz&bar=yada')->remove('foo');
+
+=head2 to_hash
+
+  my $hash = $params->to_hash;
+
+Turn parameters into a hash reference. Note that this method will normalize the
+parameters.
+
+  # "baz"
+  Mojo::Parameters->new('foo=bar&foo=baz')->to_hash->{foo}[1];
+
+=head2 to_string
+
+  my $str = $params->to_string;
+
+Turn parameters into a string.
+
+  # "foo=bar&baz=23"
+  Mojo::Parameters->new->pairs([foo => 'bar', baz => 23])->to_string;
+
+=head1 OPERATORS
+
+L<Mojo::Parameters> overloads the following operators.
+
+=head2 array
+
+  my @pairs = @$params;
+
+Alias for L</"pairs">. Note that this will normalize the parameters.
+
+  say $params->[0];
+  say for @$params;
+
+=head2 bool
+
+  my $bool = !!$params;
+
+Always true.
+
+=head2 stringify
+
+  my $str = "$params";
+
+Alias for L</"to_string">.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/Path.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/Path.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/Path.pm	(revision 32205)
@@ -0,0 +1,352 @@
+package Mojo::Path;
+use Mojo::Base -base;
+use overload
+  '@{}'    => sub { shift->parts },
+  bool     => sub {1},
+  '""'     => sub { shift->to_string },
+  fallback => 1;
+
+use Mojo::Util qw(decode encode url_escape url_unescape);
+
+has charset => 'UTF-8';
+
+sub canonicalize {
+  my $self = shift;
+
+  my $parts = $self->parts;
+  for (my $i = 0; $i <= $#$parts;) {
+    if (!length $parts->[$i] || $parts->[$i] eq '.' || $parts->[$i] eq '...') {
+      splice @$parts, $i, 1;
+    }
+    elsif ($i < 1 || $parts->[$i] ne '..' || $parts->[$i - 1] eq '..') { $i++ }
+    else { splice @$parts, --$i, 2 }
+  }
+
+  return @$parts ? $self : $self->trailing_slash(undef);
+}
+
+sub clone {
+  my $self = shift;
+
+  my $clone = $self->new;
+  if (exists $self->{charset}) { $clone->{charset} = $self->{charset} }
+  if (my $parts = $self->{parts}) {
+    $clone->{$_} = $self->{$_} for qw(leading_slash trailing_slash);
+    $clone->{parts} = [@$parts];
+  }
+  else { $clone->{path} = $self->{path} }
+
+  return $clone;
+}
+
+sub contains { $_[1] eq '/' || $_[0]->to_route =~ m!^\Q$_[1]\E(?:/|$)! }
+
+sub leading_slash { shift->_parse(leading_slash => @_) }
+
+sub merge {
+  my ($self, $path) = @_;
+
+  # Replace
+  return $self->parse($path) if $path =~ m!^/!;
+
+  # Merge
+  pop @{$self->parts} unless $self->trailing_slash;
+  $path = $self->new($path);
+  push @{$self->parts}, @{$path->parts};
+  return $self->trailing_slash($path->trailing_slash);
+}
+
+sub new { @_ > 1 ? shift->SUPER::new->parse(@_) : shift->SUPER::new }
+
+sub parse {
+  my $self = shift;
+  $self->{path} = shift;
+  delete @$self{qw(leading_slash parts trailing_slash)};
+  return $self;
+}
+
+sub parts { shift->_parse(parts => @_) }
+
+sub to_abs_string {
+  my $path = shift->to_string;
+  return $path =~ m!^/! ? $path : "/$path";
+}
+
+sub to_dir {
+  my $clone = shift->clone;
+  pop @{$clone->parts} unless $clone->trailing_slash;
+  return $clone->trailing_slash(!!@{$clone->parts});
+}
+
+sub to_route {
+  my $clone = shift->clone;
+  return '/' . join '/', @{$clone->parts}, $clone->trailing_slash ? '' : ();
+}
+
+sub to_string {
+  my $self = shift;
+
+  # Path
+  my $charset = $self->charset;
+  if (defined(my $path = $self->{path})) {
+    $path = encode $charset, $path if $charset;
+    return url_escape $path, '^A-Za-z0-9\-._~!$&\'()*+,;=%:@/';
+  }
+
+  # Build path
+  my @parts = @{$self->parts};
+  @parts = map { encode $charset, $_ } @parts if $charset;
+  my $path = join '/',
+    map { url_escape $_, '^A-Za-z0-9\-._~!$&\'()*+,;=:@' } @parts;
+  $path = "/$path" if $self->leading_slash;
+  $path = "$path/" if $self->trailing_slash;
+  return $path;
+}
+
+sub trailing_slash { shift->_parse(trailing_slash => @_) }
+
+sub _parse {
+  my ($self, $name) = (shift, shift);
+
+  unless ($self->{parts}) {
+    my $path    = url_unescape delete($self->{path}) // '';
+    my $charset = $self->charset;
+    $path = decode($charset, $path) // $path if $charset;
+    $self->{leading_slash}  = $path =~ s!^/!!;
+    $self->{trailing_slash} = $path =~ s!/$!!;
+    $self->{parts}          = [split '/', $path, -1];
+  }
+
+  return $self->{$name} unless @_;
+  $self->{$name} = shift;
+  return $self;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::Path - Path
+
+=head1 SYNOPSIS
+
+  use Mojo::Path;
+
+  # Parse
+  my $path = Mojo::Path->new('/foo%2Fbar%3B/baz.html');
+  say $path->[0];
+
+  # Build
+  my $path = Mojo::Path->new('/i/â¥');
+  push @$path, 'mojolicious';
+  say "$path";
+
+=head1 DESCRIPTION
+
+L<Mojo::Path> is a container for paths used by L<Mojo::URL>, based on
+L<RFC 3986|http://tools.ietf.org/html/rfc3986>.
+
+=head1 ATTRIBUTES
+
+L<Mojo::Path> implements the following attributes.
+
+=head2 charset
+
+  my $charset = $path->charset;
+  $path       = $path->charset('UTF-8');
+
+Charset used for encoding and decoding, defaults to C<UTF-8>.
+
+  # Disable encoding and decoding
+  $path->charset(undef);
+
+=head1 METHODS
+
+L<Mojo::Path> inherits all methods from L<Mojo::Base> and implements the
+following new ones.
+
+=head2 canonicalize
+
+  $path = $path->canonicalize;
+
+Canonicalize path by resolving C<.> and C<..>, in addition C<...> will be
+treated as C<.> to protect from path traversal attacks.
+
+  # "/foo/baz"
+  Mojo::Path->new('/foo/./bar/../baz')->canonicalize;
+
+  # "/../baz"
+  Mojo::Path->new('/foo/../bar/../../baz')->canonicalize;
+
+  # "/foo/bar"
+  Mojo::Path->new('/foo/.../bar')->canonicalize;
+
+=head2 clone
+
+  my $clone = $path->clone;
+
+Return a new L<Mojo::Path> object cloned from this path.
+
+=head2 contains
+
+  my $bool = $path->contains('/i/â¥/mojolicious');
+
+Check if path contains given prefix.
+
+  # True
+  Mojo::Path->new('/foo/bar')->contains('/');
+  Mojo::Path->new('/foo/bar')->contains('/foo');
+  Mojo::Path->new('/foo/bar')->contains('/foo/bar');
+
+  # False
+  Mojo::Path->new('/foo/bar')->contains('/f');
+  Mojo::Path->new('/foo/bar')->contains('/bar');
+  Mojo::Path->new('/foo/bar')->contains('/whatever');
+
+=head2 leading_slash
+
+  my $bool = $path->leading_slash;
+  $path    = $path->leading_slash($bool);
+
+Path has a leading slash. Note that this method will normalize the path and
+that C<%2F> will be treated as C</> for security reasons.
+
+  # "/foo/bar"
+  Mojo::Path->new('foo/bar')->leading_slash(1);
+
+  # "foo/bar"
+  Mojo::Path->new('/foo/bar')->leading_slash(0);
+
+=head2 merge
+
+  $path = $path->merge('/foo/bar');
+  $path = $path->merge('foo/bar');
+  $path = $path->merge(Mojo::Path->new);
+
+Merge paths. Note that this method will normalize both paths if necessary and
+that C<%2F> will be treated as C</> for security reasons.
+
+  # "/baz/yada"
+  Mojo::Path->new('/foo/bar')->merge('/baz/yada');
+
+  # "/foo/baz/yada"
+  Mojo::Path->new('/foo/bar')->merge('baz/yada');
+
+  # "/foo/bar/baz/yada"
+  Mojo::Path->new('/foo/bar/')->merge('baz/yada');
+
+=head2 new
+
+  my $path = Mojo::Path->new;
+  my $path = Mojo::Path->new('/foo%2Fbar%3B/baz.html');
+
+Construct a new L<Mojo::Path> object and L</"parse"> path if necessary.
+
+=head2 parse
+
+  $path = $path->parse('/foo%2Fbar%3B/baz.html');
+
+Parse path.
+
+=head2 to_abs_string
+
+  my $str = $path->to_abs_string;
+
+Turn path into an absolute string.
+
+  # "/i/%E2%99%A5/mojolicious"
+  Mojo::Path->new('/i/%E2%99%A5/mojolicious')->to_abs_string;
+  Mojo::Path->new('i/%E2%99%A5/mojolicious')->to_abs_string;
+
+=head2 parts
+
+  my $parts = $path->parts;
+  $path     = $path->parts([qw(foo bar baz)]);
+
+The path parts. Note that this method will normalize the path and that C<%2F>
+will be treated as C</> for security reasons.
+
+  # Part with slash
+  push @{$path->parts}, 'foo/bar';
+
+=head2 to_dir
+
+  my $dir = $route->to_dir;
+
+Clone path and remove everything after the right-most slash.
+
+  # "/i/%E2%99%A5/"
+  Mojo::Path->new('/i/%E2%99%A5/mojolicious')->to_dir->to_abs_string;
+
+  # "i/%E2%99%A5/"
+  Mojo::Path->new('i/%E2%99%A5/mojolicious')->to_dir->to_abs_string;
+
+=head2 to_route
+
+  my $route = $path->to_route;
+
+Turn path into a route.
+
+  # "/i/â¥/mojolicious"
+  Mojo::Path->new('/i/%E2%99%A5/mojolicious')->to_route;
+  Mojo::Path->new('i/%E2%99%A5/mojolicious')->to_route;
+
+=head2 to_string
+
+  my $str = $path->to_string;
+
+Turn path into a string.
+
+  # "/i/%E2%99%A5/mojolicious"
+  Mojo::Path->new('/i/%E2%99%A5/mojolicious')->to_string;
+
+  # "i/%E2%99%A5/mojolicious"
+  Mojo::Path->new('i/%E2%99%A5/mojolicious')->to_string;
+
+=head2 trailing_slash
+
+  my $bool = $path->trailing_slash;
+  $path    = $path->trailing_slash($bool);
+
+Path has a trailing slash. Note that this method will normalize the path and
+that C<%2F> will be treated as C</> for security reasons.
+
+  # "/foo/bar/"
+  Mojo::Path->new('/foo/bar')->trailing_slash(1);
+
+  # "/foo/bar"
+  Mojo::Path->new('/foo/bar/')->trailing_slash(0);
+
+=head1 OPERATORS
+
+L<Mojo::Path> overloads the following operators.
+
+=head2 array
+
+  my @parts = @$path;
+
+Alias for L</"parts">. Note that this will normalize the path and that C<%2F>
+will be treated as C</> for security reasons.
+
+  say $path->[0];
+  say for @$path;
+
+=head2 bool
+
+  my $bool = !!$path;
+
+Always true.
+
+=head2 stringify
+
+  my $str = "$path";
+
+Alias for L</"to_string">.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/Promise.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/Promise.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/Promise.pm	(revision 32205)
@@ -0,0 +1,354 @@
+package Mojo::Promise;
+use Mojo::Base -base;
+
+use Mojo::IOLoop;
+use Scalar::Util qw(blessed weaken);
+
+has ioloop => sub { Mojo::IOLoop->singleton };
+
+sub all {
+  my ($class, @promises) = @_;
+
+  my $all       = $class->new;
+  my $results   = [];
+  my $remaining = scalar @promises;
+  for my $i (0 .. $#promises) {
+    $promises[$i]->then(
+      sub {
+        $results->[$i] = [@_];
+        $all->resolve(@$results) if --$remaining <= 0;
+      },
+      sub { $all->reject(@_) }
+    );
+  }
+
+  return @promises ? $all : $all->resolve;
+}
+
+sub catch { shift->then(undef, shift) }
+
+sub finally {
+  my ($self, $finally) = @_;
+
+  my $new = $self->_clone;
+  push @{$self->{resolve}}, sub { _finally($new, $finally, 'resolve', @_) };
+  push @{$self->{reject}},  sub { _finally($new, $finally, 'reject',  @_) };
+
+  $self->_defer if $self->{result};
+
+  return $new;
+}
+
+sub race {
+  my ($class, @promises) = @_;
+  my $new = $class->new;
+  $_->then(sub { $new->resolve(@_) }, sub { $new->reject(@_) }) for @promises;
+  return $new;
+}
+
+sub reject  { shift->_settle('reject',  @_) }
+sub resolve { shift->_settle('resolve', @_) }
+
+sub then {
+  my ($self, $resolve, $reject) = @_;
+
+  my $new = $self->_clone;
+  push @{$self->{resolve}}, sub { _then($new, $resolve, 'resolve', @_) };
+  push @{$self->{reject}},  sub { _then($new, $reject,  'reject',  @_) };
+
+  $self->_defer if $self->{result};
+
+  return $new;
+}
+
+sub wait {
+  my $self = shift;
+  return if (my $loop = $self->ioloop)->is_running;
+  $self->finally(sub { $loop->stop });
+  $loop->start;
+}
+
+sub _clone {
+  my $self  = shift;
+  my $clone = $self->new;
+  weaken $clone->ioloop($self->ioloop)->{ioloop};
+  return $clone;
+}
+
+sub _defer {
+  my $self = shift;
+
+  return unless my $result = $self->{result};
+  my $cbs = $self->{status} eq 'resolve' ? $self->{resolve} : $self->{reject};
+  @{$self}{qw(resolve reject)} = ([], []);
+
+  $self->ioloop->next_tick(sub { $_->(@$result) for @$cbs });
+}
+
+sub _finally {
+  my ($new, $finally, $method, @result) = @_;
+  my ($res) = eval { $finally->(@result) };
+  return $new->$method(@result)
+    unless $res && blessed $res && $res->can('then');
+  $res->then(sub { $new->$method(@result) }, sub { $new->$method(@result) });
+}
+
+sub _settle {
+  my ($self, $status) = (shift, shift);
+
+  $_[0]->then(sub { $self->resolve(@_); () }, sub { $self->reject(@_); () })
+    and return $self
+    if blessed $_[0] && $_[0]->can('then');
+
+  return $self if $self->{result};
+
+  @{$self}{qw(result status)} = ([@_], $status);
+  $self->_defer;
+  return $self;
+}
+
+sub _then {
+  my ($new, $cb, $method, @result) = @_;
+
+  return $new->$method(@result) unless defined $cb;
+
+  my @res;
+  return $new->reject($@) unless eval { @res = $cb->(@result); 1 };
+  return $new->resolve(@res);
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::Promise - Promises/A+
+
+=head1 SYNOPSIS
+
+  use Mojo::Promise;
+  use Mojo::UserAgent;
+
+  # Wrap continuation-passing style APIs with promises
+  my $ua = Mojo::UserAgent->new;
+  sub get {
+    my $promise = Mojo::Promise->new;
+    $ua->get(@_ => sub {
+      my ($ua, $tx) = @_;
+      my $err = $tx->error;
+      $promise->resolve($tx) if !$err || $err->{code};
+      $promise->reject($err->{message});
+    });
+    return $promise;
+  }
+
+  # Perform non-blocking operations sequentially
+  get('https://mojolicious.org')->then(sub {
+    my $mojo = shift;
+    say $mojo->res->code;
+    return get('https://metacpan.org');
+  })->then(sub {
+    my $cpan = shift;
+    say $cpan->res->code;
+  })->catch(sub {
+    my $err = shift;
+    warn "Something went wrong: $err";
+  })->wait;
+
+  # Synchronize non-blocking operations (all)
+  my $mojo = get('https://mojolicious.org');
+  my $cpan = get('https://metacpan.org');
+  Mojo::Promise->all($mojo, $cpan)->then(sub {
+    my ($mojo, $cpan) = @_;
+    say $mojo->[0]->res->code;
+    say $cpan->[0]->res->code;
+  })->catch(sub {
+    my $err = shift;
+    warn "Something went wrong: $err";
+  })->wait;
+
+  # Synchronize non-blocking operations (race)
+  my $mojo = get('https://mojolicious.org');
+  my $cpan = get('https://metacpan.org');
+  Mojo::Promise->race($mojo, $cpan)->then(sub {
+    my $tx = shift;
+    say $tx->req->url, ' won!';
+  })->catch(sub {
+    my $err = shift;
+    warn "Something went wrong: $err";
+  })->wait;
+
+=head1 DESCRIPTION
+
+L<Mojo::Promise> is a Perl-ish implementation of
+L<Promises/A+|https://promisesaplus.com>.
+
+=head1 STATES
+
+A promise is an object representing the eventual completion or failure of a
+non-blocking operation. It allows non-blocking functions to return values, like
+blocking functions. But instead of immediately returning the final value, the
+non-blocking function returns a promise to supply the value at some point in the
+future.
+
+A promise can be in one of three states:
+
+=over 2
+
+=item pending
+
+Initial state, neither fulfilled nor rejected.
+
+=item fulfilled
+
+Meaning that the operation completed successfully.
+
+=item rejected
+
+Meaning that the operation failed.
+
+=back
+
+A pending promise can either be fulfilled with a value or rejected with a
+reason. When either happens, the associated handlers queued up by a promise's
+L</"then"> method are called.
+
+=head1 ATTRIBUTES
+
+L<Mojo::Promise> implements the following attributes.
+
+=head2 ioloop
+
+  my $loop = $promise->ioloop;
+  $promise = $promise->ioloop(Mojo::IOLoop->new);
+
+Event loop object to control, defaults to the global L<Mojo::IOLoop> singleton.
+
+=head1 METHODS
+
+L<Mojo::Promise> inherits all methods from L<Mojo::Base> and implements
+the following new ones.
+
+=head2 all
+
+  my $new = Mojo::Promise->all(@promises);
+
+Returns a new L<Mojo::Promise> object that either fulfills when all of the
+passed L<Mojo::Promise> objects have fulfilled or rejects as soon as one of them
+rejects. If the returned promise fulfills, it is fulfilled with the values from
+the fulfilled promises in the same order as the passed promises. This method can
+be useful for aggregating results of multiple promises.
+
+=head2 catch
+
+  my $new = $promise->catch(sub {...});
+
+Appends a rejection handler callback to the promise, and returns a new
+L<Mojo::Promise> object resolving to the return value of the callback if it is
+called, or to its original fulfillment value if the promise is instead
+fulfilled.
+
+  # Longer version
+  my $new = $promise->then(undef, sub {...});
+
+  # Pass along the rejection reason
+  $promise->catch(sub {
+    my @reason = @_;
+    warn "Something went wrong: $reason[0]";
+    return @reason;
+  });
+
+  # Change the rejection reason
+  $promise->catch(sub {
+    my @reason = @_;
+    return "This is bad: $reason[0]";
+  });
+
+=head2 finally
+
+  my $new = $promise->finally(sub {...});
+
+Appends a fulfillment and rejection handler to the promise, and returns a new
+L<Mojo::Promise> object resolving to the original fulfillment value or rejection
+reason.
+
+  # Do something on fulfillment and rejection
+  $promise->finally(sub {
+    my @value_or_reason = @_;
+    say "We are done!";
+  });
+
+=head2 race
+
+  my $new = Mojo::Promise->race(@promises);
+
+Returns a new L<Mojo::Promise> object that fulfills or rejects as soon as one of
+the passed L<Mojo::Promise> objects fulfills or rejects, with the value or
+reason from that promise.
+
+=head2 reject
+
+  $promise = $promise->reject(@reason);
+
+Reject the promise with one or more rejection reasons.
+
+  # Generate rejected promise
+  my $promise = Mojo::Promise->new->reject('Something went wrong: Oops');
+
+=head2 resolve
+
+  $promise = $promise->resolve(@value);
+
+Resolve the promise with one or more fulfillment values.
+
+  # Generate fulfilled promise
+  my $promise = Mojo::Promise->new->resolve('The result is: 24');
+
+=head2 then
+
+  my $new = $promise->then(sub {...});
+  my $new = $promise->then(sub {...}, sub {...});
+  my $new = $promise->then(undef, sub {...});
+
+Appends fulfillment and rejection handlers to the promise, and returns a new
+L<Mojo::Promise> object resolving to the return value of the called handler.
+
+  # Pass along the fulfillment value or rejection reason
+  $promise->then(
+    sub {
+      my @value = @_;
+      say "The result is $value[0]";
+      return @value;
+    },
+    sub {
+      my @reason = @_;
+      warn "Something went wrong: $reason[0]";
+      return @reason;
+    }
+  );
+
+  # Change the fulfillment value or rejection reason
+  $promise->then(
+    sub {
+      my @value = @_;
+      return "This is good: $value[0]";
+    },
+    sub {
+      my @reason = @_;
+      return "This is bad: $reason[0]";
+    }
+  );
+
+=head2 wait
+
+  $promise->wait;
+
+Start L</"ioloop"> and stop it again once the promise has been fulfilled or
+rejected, does nothing when L</"ioloop"> is already running.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/Reactor.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/Reactor.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/Reactor.pm	(revision 32205)
@@ -0,0 +1,211 @@
+package Mojo::Reactor;
+use Mojo::Base 'Mojo::EventEmitter';
+
+use Carp 'croak';
+use Config;
+use Mojo::Loader 'load_class';
+
+my %DETECTED;
+
+sub again { croak 'Method "again" not implemented by subclass' }
+
+sub detect {
+  my $default = 'Mojo::Reactor::' . ($Config{d_pseudofork} ? 'Poll' : 'EV');
+  my $try = $ENV{MOJO_REACTOR} || $default;
+  return $DETECTED{$try} ||= load_class($try) ? 'Mojo::Reactor::Poll' : $try;
+}
+
+sub io         { croak 'Method "io" not implemented by subclass' }
+sub is_running { croak 'Method "is_running" not implemented by subclass' }
+sub next_tick  { croak 'Method "next_tick" not implemented by subclass' }
+sub one_tick   { croak 'Method "one_tick" not implemented by subclass' }
+sub recurring  { croak 'Method "recurring" not implemented by subclass' }
+sub remove     { croak 'Method "remove" not implemented by subclass' }
+sub reset      { croak 'Method "reset" not implemented by subclass' }
+sub start      { croak 'Method "start" not implemented by subclass' }
+sub stop       { croak 'Method "stop" not implemented by subclass' }
+sub timer      { croak 'Method "timer" not implemented by subclass' }
+sub watch      { croak 'Method "watch" not implemented by subclass' }
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::Reactor - Low-level event reactor base class
+
+=head1 SYNOPSIS
+
+  package Mojo::Reactor::MyEventLoop;
+  use Mojo::Base 'Mojo::Reactor';
+
+  sub again      {...}
+  sub io         {...}
+  sub is_running {...}
+  sub next_tick  {...}
+  sub one_tick   {...}
+  sub recurring  {...}
+  sub remove     {...}
+  sub reset      {...}
+  sub start      {...}
+  sub stop       {...}
+  sub timer      {...}
+  sub watch      {...}
+
+=head1 DESCRIPTION
+
+L<Mojo::Reactor> is an abstract base class for low-level event reactors, like
+L<Mojo::Reactor::EV> and L<Mojo::Reactor::Poll>.
+
+=head1 EVENTS
+
+L<Mojo::Reactor> inherits all events from L<Mojo::EventEmitter> and can emit
+the following new ones.
+
+=head2 error
+
+  $reactor->on(error => sub {
+    my ($reactor, $err) = @_;
+    ...
+  });
+
+Emitted for exceptions caught in callbacks, fatal if unhandled. Note that if
+this event is unhandled or fails it might kill your program, so you need to be
+careful.
+
+  $reactor->on(error => sub {
+    my ($reactor, $err) = @_;
+    say "Something very bad happened: $err";
+  });
+
+=head1 METHODS
+
+L<Mojo::Reactor> inherits all methods from L<Mojo::EventEmitter> and implements
+the following new ones.
+
+=head2 again
+
+  $reactor->again($id);
+
+Restart timer. Meant to be overloaded in a subclass. Note that this method
+requires an active timer.
+
+=head2 detect
+
+  my $class = Mojo::Reactor->detect;
+
+Detect and load the best reactor implementation available, will try the value
+of the C<MOJO_REACTOR> environment variable, L<Mojo::Reactor::EV> or
+L<Mojo::Reactor::Poll>.
+
+  # Instantiate best reactor implementation available
+  my $reactor = Mojo::Reactor->detect->new;
+
+=head2 io
+
+  $reactor = $reactor->io($handle => sub {...});
+
+Watch handle for I/O events, invoking the callback whenever handle becomes
+readable or writable. Meant to be overloaded in a subclass.
+
+  # Callback will be executed twice if handle becomes readable and writable
+  $reactor->io($handle => sub {
+    my ($reactor, $writable) = @_;
+    say $writable ? 'Handle is writable' : 'Handle is readable';
+  });
+
+=head2 is_running
+
+  my $bool = $reactor->is_running;
+
+Check if reactor is running. Meant to be overloaded in a subclass.
+
+=head2 next_tick
+
+  my $undef = $reactor->next_tick(sub {...});
+
+Execute callback as soon as possible, but not before returning or other
+callbacks that have been registered with this method, always returns C<undef>.
+Meant to be overloaded in a subclass.
+
+=head2 one_tick
+
+  $reactor->one_tick;
+
+Run reactor until an event occurs. Note that this method can recurse back into
+the reactor, so you need to be careful. Meant to be overloaded in a subclass.
+
+  # Don't block longer than 0.5 seconds
+  my $id = $reactor->timer(0.5 => sub {});
+  $reactor->one_tick;
+  $reactor->remove($id);
+
+=head2 recurring
+
+  my $id = $reactor->recurring(0.25 => sub {...});
+
+Create a new recurring timer, invoking the callback repeatedly after a given
+amount of time in seconds. Meant to be overloaded in a subclass.
+
+=head2 remove
+
+  my $bool = $reactor->remove($handle);
+  my $bool = $reactor->remove($id);
+
+Remove handle or timer. Meant to be overloaded in a subclass.
+
+=head2 reset
+
+  $reactor->reset;
+
+Remove all handles and timers. Meant to be overloaded in a subclass.
+
+=head2 start
+
+  $reactor->start;
+
+Start watching for I/O and timer events, this will block until L</"stop"> is
+called. Note that some reactors stop automatically if there are no events being
+watched anymore. Meant to be overloaded in a subclass.
+
+  # Start reactor only if it is not running already
+  $reactor->start unless $reactor->is_running;
+
+=head2 stop
+
+  $reactor->stop;
+
+Stop watching for I/O and timer events. Meant to be overloaded in a subclass.
+
+=head2 timer
+
+  my $id = $reactor->timer(0.5 => sub {...});
+
+Create a new timer, invoking the callback after a given amount of time in
+seconds. Meant to be overloaded in a subclass.
+
+=head2 watch
+
+  $reactor = $reactor->watch($handle, $readable, $writable);
+
+Change I/O events to watch handle for with true and false values. Meant to be
+overloaded in a subclass. Note that this method requires an active I/O watcher.
+
+  # Watch only for readable events
+  $reactor->watch($handle, 1, 0);
+
+  # Watch only for writable events
+  $reactor->watch($handle, 0, 1);
+
+  # Watch for readable and writable events
+  $reactor->watch($handle, 1, 1);
+
+  # Pause watching for events
+  $reactor->watch($handle, 0, 0);
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/Reactor/EV.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/Reactor/EV.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/Reactor/EV.pm	(revision 32205)
@@ -0,0 +1,207 @@
+package Mojo::Reactor::EV;
+use Mojo::Base 'Mojo::Reactor::Poll';
+
+use Carp 'croak';
+use EV 4.0;
+
+my $EV;
+
+sub DESTROY { undef $EV }
+
+sub again {
+  croak 'Timer not active' unless my $timer = shift->{timers}{shift()};
+  $timer->{watcher}->again;
+}
+
+sub is_running { !!EV::depth }
+
+# We have to fall back to Mojo::Reactor::Poll, since EV is unique
+sub new { $EV++ ? Mojo::Reactor::Poll->new : shift->SUPER::new }
+
+sub one_tick { EV::run(EV::RUN_ONCE) }
+
+sub recurring { shift->_timer(1, @_) }
+
+sub start {EV::run}
+
+sub stop { EV::break(EV::BREAK_ALL) }
+
+sub timer { shift->_timer(0, @_) }
+
+sub watch {
+  my ($self, $handle, $read, $write) = @_;
+
+  my $fd = fileno $handle;
+  croak 'I/O watcher not active' unless my $io = $self->{io}{$fd};
+
+  my $mode = 0;
+  $mode |= EV::READ  if $read;
+  $mode |= EV::WRITE if $write;
+
+  if ($mode == 0) { delete $io->{watcher} }
+  elsif (my $w = $io->{watcher}) { $w->events($mode) }
+  else {
+    my $cb = sub {
+      my ($w, $revents) = @_;
+      $self->_try('I/O watcher', $self->{io}{$fd}{cb}, 0)
+        if EV::READ & $revents;
+      $self->_try('I/O watcher', $self->{io}{$fd}{cb}, 1)
+        if EV::WRITE & $revents && $self->{io}{$fd};
+    };
+    $io->{watcher} = EV::io($fd, $mode, $cb);
+  }
+
+  return $self;
+}
+
+sub _timer {
+  my ($self, $recurring, $after, $cb) = @_;
+  $after ||= 0.0001 if $recurring;
+
+  my $id      = $self->_id;
+  my $wrapper = sub {
+    delete $self->{timers}{$id} unless $recurring;
+    $self->_try('Timer', $cb);
+  };
+  EV::now_update() if $after > 0;
+  $self->{timers}{$id}{watcher} = EV::timer($after, $after, $wrapper);
+
+  return $id;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::Reactor::EV - Low-level event reactor with libev support
+
+=head1 SYNOPSIS
+
+  use Mojo::Reactor::EV;
+
+  # Watch if handle becomes readable or writable
+  my $reactor = Mojo::Reactor::EV->new;
+  $reactor->io($first => sub {
+    my ($reactor, $writable) = @_;
+    say $writable ? 'First handle is writable' : 'First handle is readable';
+  });
+
+  # Change to watching only if handle becomes writable
+  $reactor->watch($first, 0, 1);
+
+  # Turn file descriptor into handle and watch if it becomes readable
+  my $second = IO::Handle->new_from_fd($fd, 'r');
+  $reactor->io($second => sub {
+    my ($reactor, $writable) = @_;
+    say $writable ? 'Second handle is writable' : 'Second handle is readable';
+  })->watch($second, 1, 0);
+
+  # Add a timer
+  $reactor->timer(15 => sub {
+    my $reactor = shift;
+    $reactor->remove($first);
+    $reactor->remove($second);
+    say 'Timeout!';
+  });
+
+  # Start reactor if necessary
+  $reactor->start unless $reactor->is_running;
+
+=head1 DESCRIPTION
+
+L<Mojo::Reactor::EV> is a low-level event reactor based on L<EV> (4.0+).
+
+=head1 EVENTS
+
+L<Mojo::Reactor::EV> inherits all events from L<Mojo::Reactor::Poll>.
+
+=head1 METHODS
+
+L<Mojo::Reactor::EV> inherits all methods from L<Mojo::Reactor::Poll> and
+implements the following new ones.
+
+=head2 again
+
+  $reactor->again($id);
+
+Restart timer. Note that this method requires an active timer.
+
+=head2 is_running
+
+  my $bool = $reactor->is_running;
+
+Check if reactor is running.
+
+=head2 new
+
+  my $reactor = Mojo::Reactor::EV->new;
+
+Construct a new L<Mojo::Reactor::EV> object.
+
+=head2 one_tick
+
+  $reactor->one_tick;
+
+Run reactor until an event occurs or no events are being watched anymore.
+
+  # Don't block longer than 0.5 seconds
+  my $id = $reactor->timer(0.5 => sub {});
+  $reactor->one_tick;
+  $reactor->remove($id);
+
+=head2 recurring
+
+  my $id = $reactor->recurring(0.25 => sub {...});
+
+Create a new recurring timer, invoking the callback repeatedly after a given
+amount of time in seconds.
+
+=head2 start
+
+  $reactor->start;
+
+Start watching for I/O and timer events, this will block until L</"stop"> is
+called or no events are being watched anymore.
+
+  # Start reactor only if it is not running already
+  $reactor->start unless $reactor->is_running;
+
+=head2 stop
+
+  $reactor->stop;
+
+Stop watching for I/O and timer events.
+
+=head2 timer
+
+  my $id = $reactor->timer(0.5 => sub {...});
+
+Create a new timer, invoking the callback after a given amount of time in
+seconds.
+
+=head2 watch
+
+  $reactor = $reactor->watch($handle, $readable, $writable);
+
+Change I/O events to watch handle for with true and false values. Note that
+this method requires an active I/O watcher.
+
+  # Watch only for readable events
+  $reactor->watch($handle, 1, 0);
+
+  # Watch only for writable events
+  $reactor->watch($handle, 0, 1);
+
+  # Watch for readable and writable events
+  $reactor->watch($handle, 1, 1);
+
+  # Pause watching for events
+  $reactor->watch($handle, 0, 0);
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/Reactor/Poll.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/Reactor/Poll.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/Reactor/Poll.pm	(revision 32205)
@@ -0,0 +1,308 @@
+package Mojo::Reactor::Poll;
+use Mojo::Base 'Mojo::Reactor';
+
+use Carp 'croak';
+use IO::Poll qw(POLLERR POLLHUP POLLIN POLLNVAL POLLOUT POLLPRI);
+use List::Util 'min';
+use Mojo::Util qw(md5_sum steady_time);
+use Time::HiRes 'usleep';
+
+sub again {
+  croak 'Timer not active' unless my $timer = shift->{timers}{shift()};
+  $timer->{time} = steady_time + $timer->{after};
+}
+
+sub io {
+  my ($self, $handle, $cb) = @_;
+  $self->{io}{fileno($handle) // croak 'Handle is closed'} = {cb => $cb};
+  return $self->watch($handle, 1, 1);
+}
+
+sub is_running { !!shift->{running} }
+
+sub next_tick {
+  my ($self, $cb) = @_;
+  push @{$self->{next_tick}}, $cb;
+  $self->{next_timer} //= $self->timer(0 => \&_next);
+  return undef;
+}
+
+sub one_tick {
+  my $self = shift;
+
+  # Just one tick
+  local $self->{running} = 1 unless $self->{running};
+
+  # Wait for one event
+  my $i;
+  until ($i || !$self->{running}) {
+
+    # Stop automatically if there is nothing to watch
+    return $self->stop unless keys %{$self->{timers}} || keys %{$self->{io}};
+
+    # Calculate ideal timeout based on timers and round up to next millisecond
+    my $min = min map { $_->{time} } values %{$self->{timers}};
+    my $timeout = defined $min ? $min - steady_time : 0.5;
+    $timeout = $timeout <= 0 ? 0 : int($timeout * 1000) + 1;
+
+    # I/O
+    if (keys %{$self->{io}}) {
+      my @poll = map { $_ => $self->{io}{$_}{mode} } keys %{$self->{io}};
+
+      # This may break in the future, but is worth it for performance
+      if (IO::Poll::_poll($timeout, @poll) > 0) {
+        while (my ($fd, $mode) = splice @poll, 0, 2) {
+
+          if ($mode & (POLLIN | POLLPRI | POLLNVAL | POLLHUP | POLLERR)) {
+            next unless my $io = $self->{io}{$fd};
+            ++$i and $self->_try('I/O watcher', $io->{cb}, 0);
+          }
+          next unless $mode & POLLOUT && (my $io = $self->{io}{$fd});
+          ++$i and $self->_try('I/O watcher', $io->{cb}, 1);
+        }
+      }
+    }
+
+    # Wait for timeout if poll can't be used
+    elsif ($timeout) { usleep($timeout * 1000) }
+
+    # Timers (time should not change in between timers)
+    my $now = steady_time;
+    for my $id (keys %{$self->{timers}}) {
+      next unless my $t = $self->{timers}{$id};
+      next unless $t->{time} <= $now;
+
+      # Recurring timer
+      if (exists $t->{recurring}) { $t->{time} = $now + $t->{recurring} }
+
+      # Normal timer
+      else { $self->remove($id) }
+
+      ++$i and $self->_try('Timer', $t->{cb}) if $t->{cb};
+    }
+  }
+}
+
+sub recurring { shift->_timer(1, @_) }
+
+sub remove {
+  my ($self, $remove) = @_;
+  return !!delete $self->{timers}{$remove} unless ref $remove;
+  return !!delete $self->{io}{fileno($remove) // croak 'Handle is closed'};
+}
+
+sub reset { delete @{shift()}{qw(events io next_tick next_timer timers)} }
+
+sub start {
+  my $self = shift;
+  local $self->{running} = ($self->{running} || 0) + 1;
+  $self->one_tick while $self->{running};
+}
+
+sub stop { delete shift->{running} }
+
+sub timer { shift->_timer(0, @_) }
+
+sub watch {
+  my ($self, $handle, $read, $write) = @_;
+
+  croak 'I/O watcher not active' unless my $io = $self->{io}{fileno $handle};
+  $io->{mode} = 0;
+  $io->{mode} |= POLLIN | POLLPRI if $read;
+  $io->{mode} |= POLLOUT if $write;
+
+  return $self;
+}
+
+sub _id {
+  my $self = shift;
+  my $id;
+  do { $id = md5_sum 't' . steady_time . rand } while $self->{timers}{$id};
+  return $id;
+}
+
+sub _next {
+  my $self = shift;
+  delete $self->{next_timer};
+  while (my $cb = shift @{$self->{next_tick}}) { $self->$cb }
+}
+
+sub _timer {
+  my ($self, $recurring, $after, $cb) = @_;
+
+  my $id    = $self->_id;
+  my $timer = $self->{timers}{$id}
+    = {cb => $cb, after => $after, time => steady_time + $after};
+  $timer->{recurring} = $after if $recurring;
+
+  return $id;
+}
+
+sub _try {
+  my ($self, $what, $cb) = (shift, shift, shift);
+  eval { $self->$cb(@_); 1 } or $self->emit(error => "$what failed: $@");
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::Reactor::Poll - Low-level event reactor with poll support
+
+=head1 SYNOPSIS
+
+  use Mojo::Reactor::Poll;
+
+  # Watch if handle becomes readable or writable
+  my $reactor = Mojo::Reactor::Poll->new;
+  $reactor->io($first => sub {
+    my ($reactor, $writable) = @_;
+    say $writable ? 'First handle is writable' : 'First handle is readable';
+  });
+
+  # Change to watching only if handle becomes writable
+  $reactor->watch($first, 0, 1);
+
+  # Turn file descriptor into handle and watch if it becomes readable
+  my $second = IO::Handle->new_from_fd($fd, 'r');
+  $reactor->io($second => sub {
+    my ($reactor, $writable) = @_;
+    say $writable ? 'Second handle is writable' : 'Second handle is readable';
+  })->watch($second, 1, 0);
+
+  # Add a timer
+  $reactor->timer(15 => sub {
+    my $reactor = shift;
+    $reactor->remove($first);
+    $reactor->remove($second);
+    say 'Timeout!';
+  });
+
+  # Start reactor if necessary
+  $reactor->start unless $reactor->is_running;
+
+=head1 DESCRIPTION
+
+L<Mojo::Reactor::Poll> is a low-level event reactor based on L<IO::Poll>.
+
+=head1 EVENTS
+
+L<Mojo::Reactor::Poll> inherits all events from L<Mojo::Reactor>.
+
+=head1 METHODS
+
+L<Mojo::Reactor::Poll> inherits all methods from L<Mojo::Reactor> and
+implements the following new ones.
+
+=head2 again
+
+  $reactor->again($id);
+
+Restart timer. Note that this method requires an active timer.
+
+=head2 io
+
+  $reactor = $reactor->io($handle => sub {...});
+
+Watch handle for I/O events, invoking the callback whenever handle becomes
+readable or writable.
+
+  # Callback will be executed twice if handle becomes readable and writable
+  $reactor->io($handle => sub {
+    my ($reactor, $writable) = @_;
+    say $writable ? 'Handle is writable' : 'Handle is readable';
+  });
+
+=head2 is_running
+
+  my $bool = $reactor->is_running;
+
+Check if reactor is running.
+
+=head2 next_tick
+
+  my $undef = $reactor->next_tick(sub {...});
+
+Execute callback as soon as possible, but not before returning or other
+callbacks that have been registered with this method, always returns C<undef>.
+
+=head2 one_tick
+
+  $reactor->one_tick;
+
+Run reactor until an event occurs or no events are being watched anymore.
+
+  # Don't block longer than 0.5 seconds
+  my $id = $reactor->timer(0.5 => sub {});
+  $reactor->one_tick;
+  $reactor->remove($id);
+
+=head2 recurring
+
+  my $id = $reactor->recurring(0.25 => sub {...});
+
+Create a new recurring timer, invoking the callback repeatedly after a given
+amount of time in seconds.
+
+=head2 remove
+
+  my $bool = $reactor->remove($handle);
+  my $bool = $reactor->remove($id);
+
+Remove handle or timer.
+
+=head2 reset
+
+  $reactor->reset;
+
+Remove all handles and timers.
+
+=head2 start
+
+  $reactor->start;
+
+Start watching for I/O and timer events, this will block until L</"stop"> is
+called or no events are being watched anymore.
+
+  # Start reactor only if it is not running already
+  $reactor->start unless $reactor->is_running;
+
+=head2 stop
+
+  $reactor->stop;
+
+Stop watching for I/O and timer events.
+
+=head2 timer
+
+  my $id = $reactor->timer(0.5 => sub {...});
+
+Create a new timer, invoking the callback after a given amount of time in
+seconds.
+
+=head2 watch
+
+  $reactor = $reactor->watch($handle, $readable, $writable);
+
+Change I/O events to watch handle for with true and false values. Note that
+this method requires an active I/O watcher.
+
+  # Watch only for readable events
+  $reactor->watch($handle, 1, 0);
+
+  # Watch only for writable events
+  $reactor->watch($handle, 0, 1);
+
+  # Watch for readable and writable events
+  $reactor->watch($handle, 1, 1);
+
+  # Pause watching for events
+  $reactor->watch($handle, 0, 0);
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/Server.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/Server.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/Server.pm	(revision 32205)
@@ -0,0 +1,197 @@
+package Mojo::Server;
+use Mojo::Base 'Mojo::EventEmitter';
+
+use Carp 'croak';
+use Mojo::File 'path';
+use Mojo::Loader 'load_class';
+use Mojo::Util 'md5_sum';
+use POSIX ();
+use Scalar::Util 'blessed';
+
+has app           => sub { shift->build_app('Mojo::HelloWorld') };
+has reverse_proxy => sub { $ENV{MOJO_REVERSE_PROXY} };
+
+sub build_app {
+  my ($self, $app) = (shift, shift);
+  local $ENV{MOJO_EXE};
+  return $self->app($app->new(@_))->app unless my $e = load_class $app;
+  die ref $e ? $e : qq{Can't find application class "$app" in \@INC. (@INC)\n};
+}
+
+sub build_tx {
+  my $self = shift;
+  my $tx   = $self->app->build_tx;
+  $tx->req->reverse_proxy(1) if $self->reverse_proxy;
+  return $tx;
+}
+
+sub daemonize {
+
+  # Fork and kill parent
+  die "Can't fork: $!" unless defined(my $pid = fork);
+  exit 0 if $pid;
+  POSIX::setsid or die "Can't start a new session: $!";
+
+  # Close filehandles
+  open STDIN,  '</dev/null';
+  open STDOUT, '>/dev/null';
+  open STDERR, '>&STDOUT';
+}
+
+sub load_app {
+  my ($self, $path) = @_;
+
+  # Clean environment (reset FindBin defensively)
+  {
+    local $0 = $path = path($path)->to_abs->to_string;
+    require FindBin;
+    FindBin->again;
+    local $ENV{MOJO_APP_LOADER} = 1;
+    local $ENV{MOJO_EXE};
+
+    # Try to load application from script into sandbox
+    delete $INC{$path};
+    my $app = eval
+      "package Mojo::Server::Sandbox::@{[md5_sum $path]}; require \$path";
+    die qq{Can't load application from file "$path": $@} if $@;
+    die qq{File "$path" did not return an application object.\n}
+      unless blessed $app && $app->can('handler');
+    $self->app($app);
+  };
+  FindBin->again;
+
+  return $self->app;
+}
+
+sub new {
+  my $self = shift->SUPER::new(@_);
+  $self->on(request => sub { shift->app->handler(shift) });
+  return $self;
+}
+
+sub run { croak 'Method "run" not implemented by subclass' }
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::Server - HTTP/WebSocket server base class
+
+=head1 SYNOPSIS
+
+  package Mojo::Server::MyServer;
+  use Mojo::Base 'Mojo::Server';
+
+  sub run {
+    my $self = shift;
+
+    # Get a transaction
+    my $tx = $self->build_tx;
+
+    # Emit "request" event
+    $self->emit(request => $tx);
+  }
+
+=head1 DESCRIPTION
+
+L<Mojo::Server> is an abstract base class for HTTP/WebSocket servers and server
+interfaces, like L<Mojo::Server::CGI>, L<Mojo::Server::Daemon>,
+L<Mojo::Server::Hypnotoad>, L<Mojo::Server::Morbo>, L<Mojo::Server::Prefork>
+and L<Mojo::Server::PSGI>.
+
+=head1 EVENTS
+
+L<Mojo::Server> inherits all events from L<Mojo::EventEmitter> and can emit the
+following new ones.
+
+=head2 request
+
+  $server->on(request => sub {
+    my ($server, $tx) = @_;
+    ...
+  });
+
+Emitted when a request is ready and needs to be handled.
+
+  $server->on(request => sub {
+    my ($server, $tx) = @_;
+    $tx->res->code(200);
+    $tx->res->headers->content_type('text/plain');
+    $tx->res->body('Hello World!');
+    $tx->resume;
+  });
+
+=head1 ATTRIBUTES
+
+L<Mojo::Server> implements the following attributes.
+
+=head2 app
+
+  my $app = $server->app;
+  $server = $server->app(MojoSubclass->new);
+
+Application this server handles, defaults to a L<Mojo::HelloWorld> object.
+
+=head2 reverse_proxy
+
+  my $bool = $server->reverse_proxy;
+  $server  = $server->reverse_proxy($bool);
+
+This server operates behind a reverse proxy, defaults to the value of the
+C<MOJO_REVERSE_PROXY> environment variable.
+
+=head1 METHODS
+
+L<Mojo::Server> inherits all methods from L<Mojo::EventEmitter> and implements
+the following new ones.
+
+=head2 build_app
+
+  my $app = $server->build_app('MyApp');
+  my $app = $server->build_app('MyApp', log => Mojo::Log->new);
+  my $app = $server->build_app('MyApp', {log => Mojo::Log->new});
+
+Build application from class and assign it to L</"app">.
+
+=head2 build_tx
+
+  my $tx = $server->build_tx;
+
+Let application build a transaction.
+
+=head2 daemonize
+
+  $server->daemonize;
+
+Daemonize server process.
+
+=head2 load_app
+
+  my $app = $server->load_app('/home/sri/myapp.pl');
+
+Load application from script and assign it to L</"app">.
+
+  say Mojo::Server->new->load_app('./myapp.pl')->home;
+
+=head2 new
+
+  my $server = Mojo::Server->new;
+  my $server = Mojo::Server->new(reverse_proxy => 1);
+  my $server = Mojo::Server->new({reverse_proxy => 1});
+
+Construct a new L<Mojo::Server> object and subscribe to L</"request"> event
+with default request handling.
+
+=head2 run
+
+  $server->run;
+
+Run server. Meant to be overloaded in a subclass.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/Server/CGI.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/Server/CGI.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/Server/CGI.pm	(revision 32205)
@@ -0,0 +1,135 @@
+package Mojo::Server::CGI;
+use Mojo::Base 'Mojo::Server';
+
+has 'nph';
+
+sub run {
+  my $self = shift;
+
+  my $tx  = $self->build_tx;
+  my $req = $tx->req->parse(\%ENV);
+  $tx->local_port($ENV{SERVER_PORT})->remote_address($ENV{REMOTE_ADDR});
+
+  # Request body (may block if we try to read too much)
+  binmode STDIN;
+  my $len = $req->headers->content_length;
+  until ($req->is_finished) {
+    my $chunk = ($len && $len < 131072) ? $len : 131072;
+    last unless my $read = STDIN->read(my $buffer, $chunk, 0);
+    $req->parse($buffer);
+    last if ($len -= $read) <= 0;
+  }
+
+  $self->emit(request => $tx);
+
+  # Response start-line
+  STDOUT->autoflush(1);
+  binmode STDOUT;
+  my $res = $tx->res->fix_headers;
+  return undef if $self->nph && !_write($res, 'get_start_line_chunk');
+
+  # Response headers
+  my $code = $res->code    || 404;
+  my $msg  = $res->message || $res->default_message;
+  $res->headers->status("$code $msg") unless $self->nph;
+  return undef unless _write($res, 'get_header_chunk');
+
+  # Response body
+  return undef unless $tx->is_empty || _write($res, 'get_body_chunk');
+
+  # Finish transaction
+  $tx->closed;
+
+  return $res->code;
+}
+
+sub _write {
+  my ($res, $method) = @_;
+
+  my $offset = 0;
+  while (1) {
+
+    # No chunk yet, try again
+    sleep 1 and next unless defined(my $chunk = $res->$method($offset));
+
+    # End of part
+    last unless my $len = length $chunk;
+
+    # Make sure we can still write
+    $offset += $len;
+    return undef unless STDOUT->opened;
+    print STDOUT $chunk;
+  }
+
+  return 1;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::Server::CGI - CGI server
+
+=head1 SYNOPSIS
+
+  use Mojo::Server::CGI;
+
+  my $cgi = Mojo::Server::CGI->new;
+  $cgi->unsubscribe('request')->on(request => sub {
+    my ($cgi, $tx) = @_;
+
+    # Request
+    my $method = $tx->req->method;
+    my $path   = $tx->req->url->path;
+
+    # Response
+    $tx->res->code(200);
+    $tx->res->headers->content_type('text/plain');
+    $tx->res->body("$method request for $path!");
+
+    # Resume transaction
+    $tx->resume;
+  });
+  $cgi->run;
+
+=head1 DESCRIPTION
+
+L<Mojo::Server::CGI> is a simple and portable implementation of
+L<RFC 3875|http://tools.ietf.org/html/rfc3875>.
+
+See L<Mojolicious::Guides::Cookbook/"DEPLOYMENT"> for more.
+
+=head1 EVENTS
+
+L<Mojo::Server::CGI> inherits all events from L<Mojo::Server>.
+
+=head1 ATTRIBUTES
+
+L<Mojo::Server::CGI> inherits all attributes from L<Mojo::Server> and
+implements the following new ones.
+
+=head2 nph
+
+  my $bool = $cgi->nph;
+  $cgi     = $cgi->nph($bool);
+
+Activate non-parsed header mode.
+
+=head1 METHODS
+
+L<Mojo::Server::CGI> inherits all methods from L<Mojo::Server> and implements
+the following new ones.
+
+=head2 run
+
+  my $status = $cgi->run;
+
+Run CGI.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/Server/Daemon.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/Server/Daemon.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/Server/Daemon.pm	(revision 32205)
@@ -0,0 +1,461 @@
+package Mojo::Server::Daemon;
+use Mojo::Base 'Mojo::Server';
+
+use Carp 'croak';
+use Mojo::IOLoop;
+use Mojo::IOLoop::Stream::HTTPServer;
+use Mojo::IOLoop::Stream::WebSocketServer;
+use Mojo::URL;
+use Scalar::Util 'weaken';
+
+use constant DEBUG => $ENV{MOJO_SERVER_DEBUG} || 0;
+
+has acceptors => sub { [] };
+has [qw(backlog max_clients silent)];
+has inactivity_timeout => sub { $ENV{MOJO_INACTIVITY_TIMEOUT} // 15 };
+has ioloop             => sub { Mojo::IOLoop->singleton };
+has listen => sub { [split ',', $ENV{MOJO_LISTEN} || 'http://*:3000'] };
+has max_requests => 100;
+
+sub DESTROY {
+  return if Mojo::Util::_global_destruction();
+  my $self = shift;
+  my $loop = $self->ioloop;
+  $loop->remove($_) for keys %{$self->{connections} || {}}, @{$self->acceptors};
+}
+
+sub close_connections {
+  my $self = shift;
+  my $loop = $self->ioloop;
+  $_->max_requests(1)
+    for map { $loop->stream($_) || () } keys %{$self->{connections} || {}};
+}
+
+sub ports { [map { $_[0]->ioloop->acceptor($_)->port } @{$_[0]->acceptors}] }
+
+sub run {
+  my $self = shift;
+
+  # Make sure the event loop can be stopped in regular intervals
+  my $loop = $self->ioloop;
+  my $int = $loop->recurring(1 => sub { });
+  local $SIG{INT} = local $SIG{TERM} = sub { $loop->stop };
+  $self->start->ioloop->start;
+  $loop->remove($int);
+}
+
+sub start {
+  my $self = shift;
+
+  my $loop = $self->ioloop;
+  if (my $max = $self->max_clients) { $loop->max_connections($max) }
+
+  # Resume accepting connections
+  if (my $servers = $self->{servers}) {
+    push @{$self->acceptors}, $loop->acceptor(delete $servers->{$_})
+      for keys %$servers;
+  }
+
+  # Start listening
+  elsif (!@{$self->acceptors}) {
+    $self->app->server($self);
+    $self->_listen($_) for @{$self->listen};
+  }
+
+  return $self;
+}
+
+sub stop {
+  my $self = shift;
+
+  # Suspend accepting connections but keep listen sockets open
+  my $loop = $self->ioloop;
+  while (my $id = shift @{$self->acceptors}) {
+    my $server = $self->{servers}{$id} = $loop->acceptor($id);
+    $loop->remove($id);
+    $server->stop;
+  }
+
+  return $self;
+}
+
+sub _debug { $_[0]->app->log->debug($_[2]) if $_[0]{connections}{$_[1]}{tx} }
+
+sub _listen {
+  my ($self, $listen) = @_;
+
+  my $url   = Mojo::URL->new($listen);
+  my $proto = $url->protocol;
+  croak qq{Invalid listen location "$listen"}
+    unless $proto eq 'http' || $proto eq 'https' || $proto eq 'http+unix';
+
+  my $query   = $url->query;
+  my $options = {
+    backlog      => $self->backlog,
+    stream_class => 'Mojo::IOLoop::Stream::HTTPServer'
+  };
+  $options->{$_} = $query->param($_) for qw(fd single_accept reuse);
+  if ($proto eq 'http+unix') { $options->{path} = $url->host }
+  else {
+    if ((my $host = $url->host) ne '*') { $options->{address} = $host }
+    if (my $port = $url->port) { $options->{port} = $port }
+  }
+  $options->{"tls_$_"} = $query->param($_) for qw(ca ciphers version);
+  /^(.*)_(cert|key)$/ and $options->{"tls_$2"}{$1} = $query->param($_)
+    for @{$query->names};
+  if (my $cert = $query->param('cert')) { $options->{'tls_cert'}{''} = $cert }
+  if (my $key  = $query->param('key'))  { $options->{'tls_key'}{''}  = $key }
+  my $verify = $query->param('verify');
+  $options->{tls_verify} = hex $verify if defined $verify;
+  $options->{tls} = $proto eq 'https';
+
+  weaken $self;
+  push @{$self->acceptors}, $self->ioloop->server(
+    $options => sub {
+      my ($loop, $stream, $id) = @_;
+
+      my $c = $self->{connections}{$id} = {};
+      warn "-- Accept $id (@{[$stream->handle->peerhost]})\n" if DEBUG;
+      $stream->timeout($self->inactivity_timeout);
+      $stream->max_requests($self->max_requests);
+      weaken $stream->app($self)->{app};
+
+      $stream->on(close => sub { $self && $self->_remove($id) });
+      $stream->on(error =>
+          sub { $self && $self->app->log->error(pop) && $self->_remove($id) });
+      $stream->on(request => sub { $self->_request($id, pop) });
+      $stream->on(start => sub { $c->{tx} = pop->connection($id) });
+      $stream->on(timeout => sub { $self->_debug($id, 'Inactivity timeout') });
+      $stream->on(upgrade => sub { $self->_upgrade($id, pop) });
+    }
+  );
+
+  return if $self->silent;
+  $self->app->log->info(qq{Listening at "$url"});
+  $query->pairs([]);
+  $url->host('127.0.0.1') if $url->host eq '*';
+  say 'Server available at ', $options->{path} // $url;
+}
+
+sub _remove {
+  my ($self, $id) = @_;
+  $self->ioloop->remove($id);
+  delete $self->{connections}{$id};
+}
+
+sub _request {
+  my ($self, $id, $tx) = @_;
+  if (my $error = $tx->error) { $self->_debug($id, $error->{message}) }
+
+  weaken $self;
+  $tx->on(finish => sub { delete $self->{connections}{$id}{tx} });
+  $self->emit(request => $tx);
+}
+
+sub _upgrade {
+  my ($self, $id, $ws) = @_;
+
+  my $loop    = $self->ioloop;
+  my $timeout = $loop->stream($id)->timeout;
+  my $stream  = $loop->transition($id, 'Mojo::IOLoop::Stream::WebSocketServer');
+  $stream->timeout($timeout);
+
+  weaken $self;
+  $stream->on(timeout => sub { $self->_debug($id, 'Inactivity timeout') });
+  $stream->on(close => sub { $self && $self->_remove($id) });
+  $stream->on(
+    error => sub { $self && $self->app->log->error(pop) && $self->_remove($id) }
+  );
+
+  $self->{connections}{$id} = {tx => $ws};
+  $stream->process($ws);
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::Server::Daemon - Non-blocking I/O HTTP and WebSocket server
+
+=head1 SYNOPSIS
+
+  use Mojo::Server::Daemon;
+
+  my $daemon = Mojo::Server::Daemon->new(listen => ['http://*:8080']);
+  $daemon->unsubscribe('request')->on(request => sub {
+    my ($daemon, $tx) = @_;
+
+    # Request
+    my $method = $tx->req->method;
+    my $path   = $tx->req->url->path;
+
+    # Response
+    $tx->res->code(200);
+    $tx->res->headers->content_type('text/plain');
+    $tx->res->body("$method request for $path!");
+
+    # Resume transaction
+    $tx->resume;
+  });
+  $daemon->run;
+
+=head1 DESCRIPTION
+
+L<Mojo::Server::Daemon> is a full featured, highly portable non-blocking I/O
+HTTP and WebSocket server, with IPv6, TLS, SNI, Comet (long polling), keep-alive
+and multiple event loop support.
+
+For better scalability (epoll, kqueue) and to provide non-blocking name
+resolution, SOCKS5 as well as TLS support, the optional modules L<EV> (4.0+),
+L<Net::DNS::Native> (0.15+), L<IO::Socket::Socks> (0.64+) and
+L<IO::Socket::SSL> (2.009+) will be used automatically if possible. Individual
+features can also be disabled with the C<MOJO_NO_NNR>, C<MOJO_NO_SOCKS> and
+C<MOJO_NO_TLS> environment variables.
+
+See L<Mojolicious::Guides::Cookbook/"DEPLOYMENT"> for more.
+
+=head1 SIGNALS
+
+The L<Mojo::Server::Daemon> process can be controlled at runtime with the
+following signals.
+
+=head2 INT, TERM
+
+Shut down server immediately.
+
+=head1 EVENTS
+
+L<Mojo::Server::Daemon> inherits all events from L<Mojo::Server>.
+
+=head1 ATTRIBUTES
+
+L<Mojo::Server::Daemon> inherits all attributes from L<Mojo::Server> and
+implements the following new ones.
+
+=head2 acceptors
+
+  my $acceptors = $daemon->acceptors;
+  $daemon       = $daemon->acceptors(['6be0c140ef00a389c5d039536b56d139']);
+
+Active acceptor ids.
+
+  # Check port
+  mu $port = $daemon->ioloop->acceptor($daemon->acceptors->[0])->port;
+
+=head2 backlog
+
+  my $backlog = $daemon->backlog;
+  $daemon     = $daemon->backlog(128);
+
+Listen backlog size, defaults to C<SOMAXCONN>.
+
+=head2 inactivity_timeout
+
+  my $timeout = $daemon->inactivity_timeout;
+  $daemon     = $daemon->inactivity_timeout(5);
+
+Maximum amount of time in seconds a connection can be inactive before getting
+closed, defaults to the value of the C<MOJO_INACTIVITY_TIMEOUT> environment
+variable or C<15>. Setting the value to C<0> will allow connections to be
+inactive indefinitely.
+
+=head2 ioloop
+
+  my $loop = $daemon->ioloop;
+  $daemon  = $daemon->ioloop(Mojo::IOLoop->new);
+
+Event loop object to use for I/O operations, defaults to the global
+L<Mojo::IOLoop> singleton.
+
+=head2 listen
+
+  my $listen = $daemon->listen;
+  $daemon    = $daemon->listen(['https://127.0.0.1:8080']);
+
+Array reference with one or more locations to listen on, defaults to the value
+of the C<MOJO_LISTEN> environment variable or C<http://*:3000> (shortcut for
+C<http://0.0.0.0:3000>).
+
+  # Listen on all IPv4 interfaces
+  $daemon->listen(['http://*:3000']);
+
+  # Listen on all IPv4 and IPv6 interfaces
+  $daemon->listen(['http://[::]:3000']);
+
+  # Listen on IPv6 interface
+  $daemon->listen(['http://[::1]:4000']);
+
+  # Listen on IPv4 and IPv6 interfaces
+  $daemon->listen(['http://127.0.0.1:3000', 'http://[::1]:3000']);
+
+  # Listen on UNIX domain socket "/tmp/myapp.sock" (percent encoded slash)
+  $daemon->listen(['http+unix://%2Ftmp%2Fmyapp.sock']);
+
+  # File descriptor, as used by systemd
+  $daemon->listen(['http://127.0.0.1?fd=3']);
+
+  # Allow multiple servers to use the same port (SO_REUSEPORT)
+  $daemon->listen(['http://*:8080?reuse=1']);
+
+  # Listen on two ports with HTTP and HTTPS at the same time
+  $daemon->listen(['http://*:3000', 'https://*:4000']);
+
+  # Use a custom certificate and key
+  $daemon->listen(['https://*:3000?cert=/x/server.crt&key=/y/server.key']);
+
+  # Domain specific certificates and keys (SNI)
+  $daemon->listen(
+    ['https://*:3000?example.com_cert=/x/my.crt&example.com_key=/y/my.key']);
+
+  # Or even a custom certificate authority
+  $daemon->listen(
+    ['https://*:3000?cert=/x/server.crt&key=/y/server.key&ca=/z/ca.crt']);
+
+These parameters are currently available:
+
+=over 2
+
+=item ca
+
+  ca=/etc/tls/ca.crt
+
+Path to TLS certificate authority file used to verify the peer certificate.
+
+=item cert
+
+  cert=/etc/tls/server.crt
+  mojolicious.org_cert=/etc/tls/mojo.crt
+
+Path to the TLS cert file, defaults to a built-in test certificate.
+
+=item ciphers
+
+  ciphers=AES128-GCM-SHA256:RC4:HIGH:!MD5:!aNULL:!EDH
+
+TLS cipher specification string. For more information about the format see
+L<https://www.openssl.org/docs/manmaster/apps/ciphers.html#CIPHER-STRINGS>.
+
+=item fd
+
+  fd=3
+
+File descriptor with an already prepared listen socket.
+
+=item key
+
+  key=/etc/tls/server.key
+  mojolicious.org_key=/etc/tls/mojo.key
+
+Path to the TLS key file, defaults to a built-in test key.
+
+=item reuse
+
+  reuse=1
+
+Allow multiple servers to use the same port with the C<SO_REUSEPORT> socket
+option.
+
+=item single_accept
+
+  single_accept=1
+
+Only accept one connection at a time.
+
+=item verify
+
+  verify=0x00
+
+TLS verification mode.
+
+=item version
+
+  version=TLSv1_2
+
+TLS protocol version.
+
+=back
+
+=head2 max_clients
+
+  my $max = $daemon->max_clients;
+  $daemon = $daemon->max_clients(100);
+
+Maximum number of accepted connections this server is allowed to handle
+concurrently, before stopping to accept new incoming connections, passed along
+to L<Mojo::IOLoop/"max_connections">.
+
+=head2 max_requests
+
+  my $max = $daemon->max_requests;
+  $daemon = $daemon->max_requests(250);
+
+Maximum number of keep-alive requests per connection, defaults to C<100>.
+
+=head2 silent
+
+  my $bool = $daemon->silent;
+  $daemon  = $daemon->silent($bool);
+
+Disable console messages.
+
+=head1 METHODS
+
+L<Mojo::Server::Daemon> inherits all methods from L<Mojo::Server> and
+implements the following new ones.
+
+=head2 close_connections
+
+  $daemon->close_connections;
+
+Stop accepting new requests and close all connections after finising those
+currently being processed.
+
+=head2 ports
+
+  my $ports = $daemon->ports;
+
+Get all ports this server is currently listening on.
+
+  # All ports
+  say for @{$daemon->ports};
+
+=head2 run
+
+  $daemon->run;
+
+Run server and wait for L</"SIGNALS">.
+
+=head2 start
+
+  $daemon = $daemon->start;
+
+Start or resume accepting connections through L</"ioloop">.
+
+  # Listen on random port
+  my $port = $daemon->listen(['http://127.0.0.1'])->start->ports->[0];
+
+  # Run multiple web servers concurrently
+  my $daemon1 = Mojo::Server::Daemon->new(listen => ['http://*:3000'])->start;
+  my $daemon2 = Mojo::Server::Daemon->new(listen => ['http://*:4000'])->start;
+  Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
+
+=head2 stop
+
+  $daemon = $daemon->stop;
+
+Stop accepting connections through L</"ioloop">.
+
+=head1 DEBUGGING
+
+You can set the C<MOJO_SERVER_DEBUG> environment variable to get some advanced
+diagnostics information printed to C<STDERR>.
+
+  MOJO_SERVER_DEBUG=1
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/Server/Hypnotoad.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/Server/Hypnotoad.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/Server/Hypnotoad.pm	(revision 32205)
@@ -0,0 +1,409 @@
+package Mojo::Server::Hypnotoad;
+use Mojo::Base -base;
+
+# "Bender: I was God once.
+#  God: Yes, I saw. You were doing well, until everyone died."
+use Config;
+use Mojo::File 'path';
+use Mojo::Server::Prefork;
+use Mojo::Util 'steady_time';
+use Scalar::Util 'weaken';
+
+has prefork => sub { Mojo::Server::Prefork->new(listen => ['http://*:8080']) };
+has upgrade_timeout => 180;
+
+sub configure {
+  my ($self, $name) = @_;
+
+  # Hypnotoad settings
+  my $prefork = $self->prefork;
+  my $c = $prefork->app->config($name) || {};
+  $self->upgrade_timeout($c->{upgrade_timeout}) if $c->{upgrade_timeout};
+
+  # Pre-fork settings
+  $prefork->reverse_proxy($c->{proxy})   if defined $c->{proxy};
+  $prefork->max_clients($c->{clients})   if $c->{clients};
+  $prefork->max_requests($c->{requests}) if $c->{requests};
+  defined $c->{$_} and $prefork->$_($c->{$_})
+    for qw(accepts backlog graceful_timeout heartbeat_interval),
+    qw(heartbeat_timeout inactivity_timeout listen pid_file spare workers);
+}
+
+sub run {
+  my ($self, $app) = @_;
+
+  # No fork emulation support
+  _exit('Hypnotoad does not support fork emulation.') if $Config{d_pseudofork};
+
+  # Remember executable and application for later
+  $ENV{HYPNOTOAD_EXE} ||= $0;
+  $0 = $ENV{HYPNOTOAD_APP} ||= path($app)->to_abs->to_string;
+
+  # This is a production server
+  $ENV{MOJO_MODE} ||= 'production';
+
+  # Clean start (to make sure everything works)
+  die "Can't exec: $!"
+    if !$ENV{HYPNOTOAD_REV}++ && !exec $^X, $ENV{HYPNOTOAD_EXE};
+
+  # Preload application and configure server
+  my $prefork = $self->prefork->cleanup(0);
+  $prefork->load_app($app)->config->{hypnotoad}{pid_file}
+    //= path($ENV{HYPNOTOAD_APP})->sibling('hypnotoad.pid')->to_string;
+  $self->configure('hypnotoad');
+  weaken $self;
+  $prefork->on(wait   => sub { $self->_manage });
+  $prefork->on(reap   => sub { $self->_cleanup(pop) });
+  $prefork->on(finish => sub { $self->_finish });
+
+  # Testing
+  _exit('Everything looks good!') if $ENV{HYPNOTOAD_TEST};
+
+  # Stop running server
+  $self->_stop if $ENV{HYPNOTOAD_STOP};
+
+  # Initiate hot deployment
+  $self->_hot_deploy unless $ENV{HYPNOTOAD_PID};
+
+  # Daemonize as early as possible (but not for restarts)
+  local $SIG{USR2} = sub { $self->{upgrade} ||= steady_time };
+  $prefork->start;
+  $prefork->daemonize if !$ENV{HYPNOTOAD_FOREGROUND} && $ENV{HYPNOTOAD_REV} < 3;
+
+  # Start accepting connections
+  $prefork->cleanup(1)->run;
+}
+
+sub _cleanup {
+  my ($self, $pid) = @_;
+
+  # Clean up failed upgrade
+  return unless ($self->{new} || '') eq $pid;
+  $self->prefork->app->log->error('Zero downtime software upgrade failed');
+  delete @$self{qw(new upgrade)};
+}
+
+sub _exit { say shift and exit 0 }
+
+sub _finish {
+  my $self = shift;
+
+  $self->{finish} = 1;
+  return unless my $new = $self->{new};
+
+  my $prefork = $self->prefork->cleanup(0);
+  unlink $prefork->pid_file;
+  $prefork->ensure_pid_file($new);
+}
+
+sub _hot_deploy {
+
+  # Make sure server is running
+  return unless my $pid = shift->prefork->check_pid;
+
+  # Start hot deployment
+  kill 'USR2', $pid;
+  _exit("Starting hot deployment for Hypnotoad server $pid.");
+}
+
+sub _manage {
+  my $self = shift;
+
+  # Upgraded (wait for all workers to send a heartbeat)
+  my $prefork = $self->prefork;
+  my $log     = $prefork->app->log;
+  if ($ENV{HYPNOTOAD_PID} && $ENV{HYPNOTOAD_PID} ne $$) {
+    return unless $prefork->healthy == $prefork->workers;
+    $log->info("Upgrade successful, stopping $ENV{HYPNOTOAD_PID}");
+    kill 'QUIT', $ENV{HYPNOTOAD_PID};
+  }
+  $ENV{HYPNOTOAD_PID} = $$ unless ($ENV{HYPNOTOAD_PID} // '') eq $$;
+
+  # Upgrade
+  if ($self->{upgrade} && !$self->{finished}) {
+
+    # Fresh start
+    my $ut = $self->upgrade_timeout;
+    unless ($self->{new}) {
+      $log->info("Starting zero downtime software upgrade ($ut seconds)");
+      die "Can't fork: $!" unless defined(my $pid = $self->{new} = fork);
+      exec $^X, $ENV{HYPNOTOAD_EXE} or die "Can't exec: $!" unless $pid;
+    }
+
+    # Timeout
+    kill 'KILL', $self->{new} if $self->{upgrade} + $ut <= steady_time;
+  }
+}
+
+sub _stop {
+  _exit('Hypnotoad server not running.')
+    unless my $pid = shift->prefork->check_pid;
+  kill 'QUIT', $pid;
+  _exit("Stopping Hypnotoad server $pid gracefully.");
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::Server::Hypnotoad - A production web serv...ALL GLORY TO THE HYPNOTOAD!
+
+=head1 SYNOPSIS
+
+  use Mojo::Server::Hypnotoad;
+
+  my $hypnotoad = Mojo::Server::Hypnotoad->new;
+  $hypnotoad->run('/home/sri/myapp.pl');
+
+=head1 DESCRIPTION
+
+L<Mojo::Server::Hypnotoad> is a full featured, UNIX optimized, pre-forking
+non-blocking I/O HTTP and WebSocket server, built around the very well tested
+and reliable L<Mojo::Server::Prefork>, with IPv6, TLS, SNI, UNIX domain socket,
+Comet (long polling), keep-alive, multiple event loop and hot deployment support
+that just works. Note that the server uses signals for process management, so
+you should avoid modifying signal handlers in your applications.
+
+To start applications with it you can use the L<hypnotoad> script, which
+listens on port C<8080>, automatically daemonizes the server process and
+defaults to C<production> mode for L<Mojolicious> and L<Mojolicious::Lite>
+applications.
+
+  $ hypnotoad ./myapp.pl
+
+You can run the same command again for automatic hot deployment.
+
+  $ hypnotoad ./myapp.pl
+  Starting hot deployment for Hypnotoad server 31841.
+
+This second invocation will load the application again, detect the process id
+file with it, and send a L</"USR2"> signal to the already running server.
+
+For better scalability (epoll, kqueue) and to provide non-blocking name
+resolution, SOCKS5 as well as TLS support, the optional modules L<EV> (4.0+),
+L<Net::DNS::Native> (0.15+), L<IO::Socket::Socks> (0.64+) and
+L<IO::Socket::SSL> (2.009+) will be used automatically if possible. Individual
+features can also be disabled with the C<MOJO_NO_NNR>, C<MOJO_NO_SOCKS> and
+C<MOJO_NO_TLS> environment variables.
+
+See L<Mojolicious::Guides::Cookbook/"DEPLOYMENT"> for more.
+
+=head1 MANAGER SIGNALS
+
+The L<Mojo::Server::Hypnotoad> manager process can be controlled at runtime
+with the following signals.
+
+=head2 INT, TERM
+
+Shut down server immediately.
+
+=head2 QUIT
+
+Shut down server gracefully.
+
+=head2 TTIN
+
+Increase worker pool by one.
+
+=head2 TTOU
+
+Decrease worker pool by one.
+
+=head2 USR2
+
+Attempt zero downtime software upgrade (hot deployment) without losing any
+incoming connections.
+
+  Manager (old)
+  |- Worker [1]
+  |- Worker [2]
+  |- Worker [3]
+  |- Worker [4]
+  +- Manager (new)
+     |- Worker [1]
+     |- Worker [2]
+     |- Worker [3]
+     +- Worker [4]
+
+The new manager will automatically send a L</"QUIT"> signal to the old manager
+and take over serving requests after starting up successfully.
+
+=head1 WORKER SIGNALS
+
+L<Mojo::Server::Hypnotoad> worker processes can be controlled at runtime with
+the following signals.
+
+=head2 QUIT
+
+Stop worker gracefully.
+
+=head1 SETTINGS
+
+L<Mojo::Server::Hypnotoad> can be configured with the following settings, see
+L<Mojolicious::Guides::Cookbook/"Hypnotoad"> for examples.
+
+=head2 accepts
+
+  accepts => 100
+
+Maximum number of connections a worker is allowed to accept, before stopping
+gracefully and then getting replaced with a newly started worker, defaults to
+the value of L<Mojo::Server::Prefork/"accepts">. Setting the value to C<0> will
+allow workers to accept new connections indefinitely. Note that up to half of
+this value can be subtracted randomly to improve load balancing, and to make
+sure that not all workers restart at the same time.
+
+=head2 backlog
+
+  backlog => 128
+
+Listen backlog size, defaults to the value of
+L<Mojo::Server::Daemon/"backlog">.
+
+=head2 clients
+
+  clients => 100
+
+Maximum number of accepted connections each worker process is allowed to handle
+concurrently, before stopping to accept new incoming connections, defaults to
+the value of L<Mojo::IOLoop/"max_connections">. Note that high concurrency works
+best with applications that perform mostly non-blocking operations, to optimize
+for blocking operations you can decrease this value and increase L</"workers">
+instead for better performance.
+
+=head2 graceful_timeout
+
+  graceful_timeout => 15
+
+Maximum amount of time in seconds stopping a worker gracefully may take before
+being forced, defaults to the value of
+L<Mojo::Server::Prefork/"graceful_timeout">. Note that this value should usually
+be a little larger than the maximum amount of time you expect any one request to
+take.
+
+=head2 heartbeat_interval
+
+  heartbeat_interval => 3
+
+Heartbeat interval in seconds, defaults to the value of
+L<Mojo::Server::Prefork/"heartbeat_interval">.
+
+=head2 heartbeat_timeout
+
+  heartbeat_timeout => 2
+
+Maximum amount of time in seconds before a worker without a heartbeat will be
+stopped gracefully, defaults to the value of
+L<Mojo::Server::Prefork/"heartbeat_timeout">. Note that this value should
+usually be a little larger than the maximum amount of time you expect any one
+operation to block the event loop.
+
+=head2 inactivity_timeout
+
+  inactivity_timeout => 10
+
+Maximum amount of time in seconds a connection can be inactive before getting
+closed, defaults to the value of L<Mojo::Server::Daemon/"inactivity_timeout">.
+Setting the value to C<0> will allow connections to be inactive indefinitely.
+
+=head2 listen
+
+  listen => ['http://*:80']
+
+Array reference with one or more locations to listen on, defaults to
+C<http://*:8080>. See also L<Mojo::Server::Daemon/"listen"> for more examples.
+
+=head2 pid_file
+
+  pid_file => '/var/run/hypnotoad.pid'
+
+Full path to process id file, defaults to C<hypnotoad.pid> in the same
+directory as the application. Note that this value can only be changed after
+the server has been stopped.
+
+=head2 proxy
+
+  proxy => 1
+
+Activate reverse proxy support, which allows for the C<X-Forwarded-For> and
+C<X-Forwarded-Proto> headers to be picked up automatically, defaults to the
+value of L<Mojo::Server/"reverse_proxy">.
+
+=head2 requests
+
+  requests => 50
+
+Number of keep-alive requests per connection, defaults to the value of
+L<Mojo::Server::Daemon/"max_requests">.
+
+=head2 spare
+
+  spare => 4
+
+Temporarily spawn up to this number of additional workers if there is a need,
+defaults to the value of L<Mojo::Server::Prefork/"spare">. This allows for new
+workers to be started while old ones are still shutting down gracefully,
+drastically reducing the performance cost of worker restarts.
+
+=head2 upgrade_timeout
+
+  upgrade_timeout => 45
+
+Maximum amount of time in seconds a zero downtime software upgrade may take
+before getting canceled, defaults to C<180>.
+
+=head2 workers
+
+  workers => 10
+
+Number of worker processes, defaults to the value of
+L<Mojo::Server::Prefork/"workers">. A good rule of thumb is two worker
+processes per CPU core for applications that perform mostly non-blocking
+operations, blocking operations often require more and benefit from decreasing
+concurrency with L</"clients"> (often as low as C<1>). Note that during zero
+downtime software upgrades there will be twice as many workers active for a
+short amount of time.
+
+=head1 ATTRIBUTES
+
+L<Mojo::Server::Hypnotoad> implements the following attributes.
+
+=head2 prefork
+
+  my $prefork = $hypnotoad->prefork;
+  $hypnotoad  = $hypnotoad->prefork(Mojo::Server::Prefork->new);
+
+L<Mojo::Server::Prefork> object this server manages.
+
+=head2 upgrade_timeout
+
+  my $timeout = $hypnotoad->upgrade_timeout;
+  $hypnotoad  = $hypnotoad->upgrade_timeout(15);
+
+Maximum amount of time in seconds a zero downtime software upgrade may take
+before getting canceled, defaults to C<180>.
+
+=head1 METHODS
+
+L<Mojo::Server::Hypnotoad> inherits all methods from L<Mojo::Base> and
+implements the following new ones.
+
+=head2 configure
+
+  $hypnotoad->configure('hypnotoad');
+
+Configure server from application settings.
+
+=head2 run
+
+  $hypnotoad->run('script/my_app');
+
+Run server for application and wait for L</"MANAGER SIGNALS">.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/Server/Morbo.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/Server/Morbo.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/Server/Morbo.pm	(revision 32205)
@@ -0,0 +1,153 @@
+package Mojo::Server::Morbo;
+use Mojo::Base -base;
+
+# "Linda: With Haley's Comet out of ice, Earth is experiencing the devastating
+#         effects of sudden, intense global warming.
+#  Morbo: Morbo is pleased but sticky."
+use Mojo::Loader 'load_class';
+use Mojo::Server::Daemon;
+use POSIX 'WNOHANG';
+
+has backend => sub {
+  my $backend = $ENV{MOJO_MORBO_BACKEND} || 'Poll';
+  $backend = "Mojo::Server::Morbo::Backend::$backend";
+  return $backend->new unless my $e = load_class $backend;
+  die $e if ref $e;
+  die qq{Can't find Morbo backend class "$backend" in \@INC. (@INC)\n};
+};
+has daemon => sub { Mojo::Server::Daemon->new };
+
+sub run {
+  my ($self, $app) = @_;
+
+  # Clean manager environment
+  local $SIG{INT} = local $SIG{TERM} = sub {
+    $self->{finished} = 1;
+    kill 'TERM', $self->{worker} if $self->{worker};
+  };
+  unshift @{$self->backend->watch}, $0 = $app;
+  $self->{modified} = 1;
+
+  # Prepare and cache listen sockets for smooth restarting
+  $self->daemon->start->stop;
+
+  $self->_manage until $self->{finished} && !$self->{worker};
+  exit 0;
+}
+
+sub _manage {
+  my $self = shift;
+
+  if (my @files = @{$self->backend->modified_files}) {
+    say @files == 1
+      ? qq{File "@{[$files[0]]}" changed, restarting.}
+      : qq{@{[scalar @files]} files changed, restarting.}
+      if $ENV{MORBO_VERBOSE};
+    kill 'TERM', $self->{worker} if $self->{worker};
+    $self->{modified} = 1;
+  }
+
+  if (my $pid = $self->{worker}) {
+    delete $self->{worker} if waitpid($pid, WNOHANG) == $pid;
+  }
+
+  $self->_spawn if !$self->{worker} && delete $self->{modified};
+}
+
+sub _spawn {
+  my $self = shift;
+
+  # Manager
+  my $manager = $$;
+  die "Can't fork: $!" unless defined(my $pid = $self->{worker} = fork);
+  return if $pid;
+
+  # Worker
+  my $daemon = $self->daemon;
+  $daemon->load_app($self->backend->watch->[0]);
+  $daemon->ioloop->recurring(1 => sub { shift->stop unless kill 0, $manager });
+  $daemon->run;
+  exit 0;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::Server::Morbo - Tonight at 11...DOOOOOOOOOOOOOOOM!
+
+=head1 SYNOPSIS
+
+  use Mojo::Server::Morbo;
+
+  my $morbo = Mojo::Server::Morbo->new;
+  $morbo->run('/home/sri/myapp.pl');
+
+=head1 DESCRIPTION
+
+L<Mojo::Server::Morbo> is a full featured, self-restart capable non-blocking
+I/O HTTP and WebSocket server, built around the very well tested and reliable
+L<Mojo::Server::Daemon>, with IPv6, TLS, SNI, UNIX domain socket, Comet (long
+polling), keep-alive and multiple event loop support. Note that the server uses
+signals for process management, so you should avoid modifying signal handlers in
+your applications.
+
+To start applications with it you can use the L<morbo> script.
+
+  $ morbo ./myapp.pl
+  Server available at http://127.0.0.1:3000
+
+For better scalability (epoll, kqueue) and to provide non-blocking name
+resolution, SOCKS5 as well as TLS support, the optional modules L<EV> (4.0+),
+L<Net::DNS::Native> (0.15+), L<IO::Socket::Socks> (0.64+) and
+L<IO::Socket::SSL> (2.009+) will be used automatically if possible. Individual
+features can also be disabled with the C<MOJO_NO_NNR>, C<MOJO_NO_SOCKS> and
+C<MOJO_NO_TLS> environment variables.
+
+See L<Mojolicious::Guides::Cookbook/"DEPLOYMENT"> for more.
+
+=head1 SIGNALS
+
+The L<Mojo::Server::Morbo> process can be controlled at runtime with the
+following signals.
+
+=head2 INT, TERM
+
+Shut down server immediately.
+
+=head1 ATTRIBUTES
+
+L<Mojo::Server::Morbo> implements the following attributes.
+
+=head2 backend
+
+  my $backend = $morbo->backend;
+  $morbo      = $morbo->backend(Mojo::Server::Morbo::Backend::Poll->new);
+
+Backend, usually a L<Mojo::Server::Morbo::Backend::Poll> object.
+
+=head2 daemon
+
+  my $daemon = $morbo->daemon;
+  $morbo     = $morbo->daemon(Mojo::Server::Daemon->new);
+
+L<Mojo::Server::Daemon> object this server manages.
+
+=head1 METHODS
+
+L<Mojo::Server::Morbo> inherits all methods from L<Mojo::Base> and implements
+the following new ones.
+
+=head2 run
+
+  $morbo->run('script/my_app');
+
+Run server for application and wait for L</"SIGNALS">.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/Server/Morbo/Backend.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/Server/Morbo/Backend.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/Server/Morbo/Backend.pm	(revision 32205)
@@ -0,0 +1,75 @@
+package Mojo::Server::Morbo::Backend;
+use Mojo::Base -base;
+
+use Carp 'croak';
+
+has watch => sub { [qw(lib templates)] };
+has watch_timeout => sub { $ENV{MOJO_MORBO_TIMEOUT} || 1 };
+
+sub modified_files {
+  croak 'Method "modified_files" not implemented by subclass';
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::Server::Morbo::Backend - Morbo backend base class
+
+=head1 SYNOPSIS
+
+  package Mojo::Server::Morbo::Backend::Inotify:
+  use Mojo::Base 'Mojo::Server::Morbo::Backend';
+
+  sub modified_files {...}
+
+=head1 DESCRIPTION
+
+L<Mojo::Server::Morbo::Backend> is an abstract base class for Morbo backends,
+like L<Mojo::Server::Morbo::Backend::Poll>.
+
+=head1 ATTRIBUTES
+
+L<Mojo::Server::Morbo::Backend> implements the following attributes.
+
+=head2 watch
+
+  my $watch = $backend->watch;
+  $backend  = $backend->watch(['/home/sri/my_app']);
+
+Files and directories to watch for changes, defaults to the application script
+as well as the C<lib> and C<templates> directories in the current working
+directory.
+
+=head2 watch_timeout
+
+  my $timeout = $backend->watch_timeout;
+  $backend    = $backend->watch_timeout(10);
+
+Maximum amount of time in seconds a backend may block when waiting for files to
+change, defaults to the value of the C<MOJO_MORBO_TIMEOUT> environment variable
+or C<1>.
+
+=head1 METHODS
+
+L<Mojo::Server::Morbo::Backend> inherits all methods from L<Mojo::Base> and
+implements the following new ones.
+
+=head2 modified_files
+
+  my $files = $backend->modified_files;
+
+Check if files from L</"watch"> have been modified since the last check and
+return an array reference with the results. Meant to be overloaded in a
+subclass.
+
+  # All files that have been modified
+  say for @{$backend->modified_files};
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/Server/Morbo/Backend/Poll.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/Server/Morbo/Backend/Poll.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/Server/Morbo/Backend/Poll.pm	(revision 32205)
@@ -0,0 +1,72 @@
+package Mojo::Server::Morbo::Backend::Poll;
+use Mojo::Base 'Mojo::Server::Morbo::Backend';
+
+use Mojo::File 'path';
+
+sub modified_files {
+  my $self = shift;
+
+  my $cache = $self->{cache} ||= {};
+  my @files;
+  for my $file (map { -f $_ && -r _ ? $_ : _list($_) } @{$self->watch}) {
+    my ($size, $mtime) = (stat $file)[7, 9];
+    next unless defined $size and defined $mtime;
+    my $stats = $cache->{$file} ||= [$^T, $size];
+    next if $mtime <= $stats->[0] && $size == $stats->[1];
+    @$stats = ($mtime, $size);
+    push @files, $file;
+  }
+  sleep $self->watch_timeout unless @files;
+
+  return \@files;
+}
+
+sub _list { path(shift)->list_tree->map('to_string')->each }
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::Server::Morbo::Backend::Poll - Morbo default backend
+
+=head1 SYNOPSIS
+
+  use Mojo::Server::Morbo::Backend::Poll;
+
+  my $backend = Mojo::Server::Morbo::Backend::Poll->new;
+  if (my $files = $backend->modified_files) {
+    ...
+  }
+
+=head1 DESCRIPTION
+
+L<Mojo::Server::Morbo::Backend:Poll> is the default backend for
+L<Mojo::Server::Morbo>.
+
+=head1 ATTRIBUTES
+
+L<Mojo::Server::Morbo::Backend::Poll> inherits all attributes from
+L<Mojo::Server::Morbo::Backend>.
+
+=head1 METHODS
+
+L<Mojo::Server::Morbo::Backend::Poll> inherits all methods from
+L<Mojo::Server::Morbo::Backend> and implements the following new ones.
+
+=head2 modified_files
+
+  my $files = $backend->modified_files;
+
+Check file size and mtime to determine which files have changed, this is not
+particularly efficient, but very portable.
+
+  # All files that have been modified
+  say for @{$backend->modified_files};
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/Server/PSGI.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/Server/PSGI.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/Server/PSGI.pm	(revision 32205)
@@ -0,0 +1,130 @@
+package Mojo::Server::PSGI;
+use Mojo::Base 'Mojo::Server';
+
+sub run {
+  my ($self, $env) = @_;
+
+  my $tx  = $self->build_tx;
+  my $req = $tx->req->parse($env);
+  $tx->local_port($env->{SERVER_PORT})->remote_address($env->{REMOTE_ADDR});
+
+  # Request body (may block if we try to read too much)
+  my $len = $env->{CONTENT_LENGTH};
+  until ($req->is_finished) {
+    my $chunk = ($len && $len < 131072) ? $len : 131072;
+    last unless my $read = $env->{'psgi.input'}->read(my $buffer, $chunk, 0);
+    $req->parse($buffer);
+    last if ($len -= $read) <= 0;
+  }
+
+  $self->emit(request => $tx);
+
+  # Response headers
+  my $res  = $tx->res->fix_headers;
+  my $hash = $res->headers->to_hash(1);
+  my @headers;
+  for my $name (keys %$hash) { push @headers, $name, $_ for @{$hash->{$name}} }
+
+  # PSGI response
+  my $io = Mojo::Server::PSGI::_IO->new(tx => $tx, empty => $tx->is_empty);
+  return [$res->code // 404, \@headers, $io];
+}
+
+sub to_psgi_app {
+  my $self = shift;
+
+  # Preload application and wrap it
+  $self->app->server($self);
+  return sub { $self->run(@_) }
+}
+
+package Mojo::Server::PSGI::_IO;
+use Mojo::Base -base;
+
+# Finish transaction
+sub close { shift->{tx}->closed }
+
+sub getline {
+  my $self = shift;
+
+  # Empty
+  return undef if $self->{empty};
+
+  # No content yet, try again later
+  my $chunk = $self->{tx}->res->get_body_chunk($self->{offset} //= 0);
+  return '' unless defined $chunk;
+
+  # End of content
+  return undef unless length $chunk;
+
+  $self->{offset} += length $chunk;
+  return $chunk;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::Server::PSGI - PSGI server
+
+=head1 SYNOPSIS
+
+  use Mojo::Server::PSGI;
+
+  my $psgi = Mojo::Server::PSGI->new;
+  $psgi->unsubscribe('request')->on(request => sub {
+    my ($psgi, $tx) = @_;
+
+    # Request
+    my $method = $tx->req->method;
+    my $path   = $tx->req->url->path;
+
+    # Response
+    $tx->res->code(200);
+    $tx->res->headers->content_type('text/plain');
+    $tx->res->body("$method request for $path!");
+
+    # Resume transaction
+    $tx->resume;
+  });
+  my $app = $psgi->to_psgi_app;
+
+=head1 DESCRIPTION
+
+L<Mojo::Server::PSGI> allows L<Mojolicious> applications to run on all L<PSGI>
+compatible servers.
+
+See L<Mojolicious::Guides::Cookbook/"DEPLOYMENT"> for more.
+
+=head1 EVENTS
+
+L<Mojo::Server::PSGI> inherits all events from L<Mojo::Server>.
+
+=head1 ATTRIBUTES
+
+L<Mojo::Server::PSGI> inherits all attributes from L<Mojo::Server>.
+
+=head1 METHODS
+
+L<Mojo::Server::PSGI> inherits all methods from L<Mojo::Server> and implements
+the following new ones.
+
+=head2 run
+
+  my $res = $psgi->run($env);
+
+Run L<PSGI>.
+
+=head2 to_psgi_app
+
+  my $app = $psgi->to_psgi_app;
+
+Turn L<Mojolicious> application into L<PSGI> application.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/Server/Prefork.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/Server/Prefork.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/Server/Prefork.pm	(revision 32205)
@@ -0,0 +1,470 @@
+package Mojo::Server::Prefork;
+use Mojo::Base 'Mojo::Server::Daemon';
+
+use Config;
+use File::Spec::Functions 'tmpdir';
+use Mojo::File 'path';
+use Mojo::Util 'steady_time';
+use POSIX 'WNOHANG';
+use Scalar::Util 'weaken';
+
+has accepts            => 10000;
+has cleanup            => 1;
+has graceful_timeout   => 120;
+has heartbeat_timeout  => 30;
+has heartbeat_interval => 5;
+has pid_file           => sub { path(tmpdir, 'prefork.pid')->to_string };
+has spare              => 2;
+has workers            => 4;
+
+sub DESTROY { unlink $_[0]->pid_file if $_[0]->cleanup }
+
+sub check_pid {
+  my $file = shift->pid_file;
+  return undef unless open my $handle, '<', $file;
+  my $pid = <$handle>;
+  chomp $pid;
+
+  # Running
+  return $pid if $pid && kill 0, $pid;
+
+  # Not running
+  unlink $file;
+  return undef;
+}
+
+sub ensure_pid_file {
+  my ($self, $pid) = @_;
+
+  # Check if PID file already exists
+  return if -e (my $file = $self->pid_file);
+
+  # Create PID file
+  $self->app->log->error(qq{Can't create process id file "$file": $!})
+    and die qq{Can't create process id file "$file": $!}
+    unless open my $handle, '>', $file;
+  $self->app->log->info(qq{Creating process id file "$file"});
+  chmod 0644, $handle;
+  print $handle "$pid\n";
+}
+
+sub healthy {
+  scalar grep { $_->{healthy} } values %{shift->{pool}};
+}
+
+sub run {
+  my $self = shift;
+
+  # No fork emulation support
+  say 'Pre-forking does not support fork emulation.' and exit 0
+    if $Config{d_pseudofork};
+
+  # Pipe for worker communication
+  pipe($self->{reader}, $self->{writer}) or die "Can't create pipe: $!";
+
+  # Clean manager environment
+  local $SIG{CHLD} = sub {
+    while ((my $pid = waitpid -1, WNOHANG) > 0) {
+      $self->emit(reap => $pid)->_stopped($pid);
+    }
+  };
+  local $SIG{INT} = local $SIG{TERM} = sub { $self->_term };
+  local $SIG{QUIT} = sub { $self->_term(1) };
+  local $SIG{TTIN} = sub { $self->workers($self->workers + 1) };
+  local $SIG{TTOU} = sub {
+    $self->workers > 0 ? $self->workers($self->workers - 1) : return;
+    for my $w (values %{$self->{pool}}) {
+      ($w->{graceful} = steady_time) and last unless $w->{graceful};
+    }
+  };
+
+  # Preload application before starting workers
+  $self->start->app->log->info("Manager $$ started");
+  $self->ioloop->max_accepts($self->accepts);
+  $self->{running} = 1;
+  $self->_manage while $self->{running};
+  $self->app->log->info("Manager $$ stopped");
+}
+
+sub _heartbeat { shift->{writer}->syswrite("$$:$_[0]\n") or exit 0 }
+
+sub _manage {
+  my $self = shift;
+
+  # Spawn more workers if necessary and check PID file
+  if (!$self->{finished}) {
+    my $graceful = grep { $_->{graceful} } values %{$self->{pool}};
+    my $spare = $self->spare;
+    $spare = $graceful ? $graceful > $spare ? $spare : $graceful : 0;
+    my $need = ($self->workers - keys %{$self->{pool}}) + $spare;
+    $self->_spawn while $need-- > 0;
+    $self->ensure_pid_file($$);
+  }
+
+  # Shutdown
+  elsif (!keys %{$self->{pool}}) { return delete $self->{running} }
+
+  # Wait for heartbeats
+  $self->_wait;
+
+  my $interval = $self->heartbeat_interval;
+  my $ht       = $self->heartbeat_timeout;
+  my $gt       = $self->graceful_timeout;
+  my $log      = $self->app->log;
+  my $time     = steady_time;
+
+  for my $pid (keys %{$self->{pool}}) {
+    next unless my $w = $self->{pool}{$pid};
+
+    # No heartbeat (graceful stop)
+    $log->error("Worker $pid has no heartbeat ($ht seconds), restarting")
+      and $w->{graceful} = $time
+      if !$w->{graceful} && ($w->{time} + $interval + $ht <= $time);
+
+    # Graceful stop with timeout
+    my $graceful = $w->{graceful} ||= $self->{graceful} ? $time : undef;
+    $log->info("Stopping worker $pid gracefully ($gt seconds)")
+      and (kill 'QUIT', $pid or $self->_stopped($pid))
+      if $graceful && !$w->{quit}++;
+    $w->{force} = 1 if $graceful && $graceful + $gt <= $time;
+
+    # Normal stop
+    $log->warn("Stopping worker $pid immediately")
+      and (kill 'KILL', $pid or $self->_stopped($pid))
+      if $w->{force} || ($self->{finished} && !$graceful);
+  }
+}
+
+sub _spawn {
+  my $self = shift;
+
+  # Manager
+  die "Can't fork: $!" unless defined(my $pid = fork);
+  return $self->emit(spawn => $pid)->{pool}{$pid} = {time => steady_time}
+    if $pid;
+
+  # Heartbeat messages
+  my $loop     = $self->cleanup(0)->ioloop;
+  my $finished = 0;
+  $loop->on(finish => sub { $finished = 1 });
+  weaken $self;
+  my $cb = sub { $self->_heartbeat($finished) };
+  $loop->next_tick($cb);
+  $loop->recurring($self->heartbeat_interval => $cb);
+
+  # Clean worker environment
+  $SIG{$_} = 'DEFAULT' for qw(CHLD INT TERM TTIN TTOU);
+  $SIG{QUIT} = sub { $loop->stop_gracefully };
+  $loop->on(finish => sub { $self->max_requests(1)->close_connections });
+  delete $self->{reader};
+  srand;
+
+  $self->app->log->info("Worker $$ started");
+  $loop->start;
+  exit 0;
+}
+
+sub _stopped {
+  my ($self, $pid) = @_;
+
+  return unless my $w = delete $self->{pool}{$pid};
+
+  my $log = $self->app->log;
+  $log->info("Worker $pid stopped");
+  $log->error("Worker $pid stopped too early, shutting down") and $self->_term
+    unless $w->{healthy};
+}
+
+sub _term {
+  my ($self, $graceful) = @_;
+  @{$self->emit(finish => $graceful)}{qw(finished graceful)} = (1, $graceful);
+}
+
+sub _wait {
+  my $self = shift;
+
+  # Poll for heartbeats
+  my $reader = $self->emit('wait')->{reader};
+  return unless Mojo::Util::_readable(1000, fileno($reader));
+  return unless $reader->sysread(my $chunk, 4194304);
+
+  # Update heartbeats (and stop gracefully if necessary)
+  my $time = steady_time;
+  while ($chunk =~ /(\d+):(\d)\n/g) {
+    next unless my $w = $self->{pool}{$1};
+    @$w{qw(healthy time)} = (1, $time) and $self->emit(heartbeat => $1);
+    $w->{graceful} ||= $time if $2;
+  }
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::Server::Prefork - Pre-forking non-blocking I/O HTTP and WebSocket server
+
+=head1 SYNOPSIS
+
+  use Mojo::Server::Prefork;
+
+  my $prefork = Mojo::Server::Prefork->new(listen => ['http://*:8080']);
+  $prefork->unsubscribe('request')->on(request => sub {
+    my ($prefork, $tx) = @_;
+
+    # Request
+    my $method = $tx->req->method;
+    my $path   = $tx->req->url->path;
+
+    # Response
+    $tx->res->code(200);
+    $tx->res->headers->content_type('text/plain');
+    $tx->res->body("$method request for $path!");
+
+    # Resume transaction
+    $tx->resume;
+  });
+  $prefork->run;
+
+=head1 DESCRIPTION
+
+L<Mojo::Server::Prefork> is a full featured, UNIX optimized, pre-forking
+non-blocking I/O HTTP and WebSocket server, built around the very well tested
+and reliable L<Mojo::Server::Daemon>, with IPv6, TLS, SNI, UNIX domain socket,
+Comet (long polling), keep-alive and multiple event loop support. Note that the
+server uses signals for process management, so you should avoid modifying signal
+handlers in your applications.
+
+For better scalability (epoll, kqueue) and to provide non-blocking name
+resolution, SOCKS5 as well as TLS support, the optional modules L<EV> (4.0+),
+L<Net::DNS::Native> (0.15+), L<IO::Socket::Socks> (0.64+) and
+L<IO::Socket::SSL> (1.84+) will be used automatically if possible. Individual
+features can also be disabled with the C<MOJO_NO_NNR>, C<MOJO_NO_SOCKS> and
+C<MOJO_NO_TLS> environment variables.
+
+See L<Mojolicious::Guides::Cookbook/"DEPLOYMENT"> for more.
+
+=head1 MANAGER SIGNALS
+
+The L<Mojo::Server::Prefork> manager process can be controlled at runtime with
+the following signals.
+
+=head2 INT, TERM
+
+Shut down server immediately.
+
+=head2 QUIT
+
+Shut down server gracefully.
+
+=head2 TTIN
+
+Increase worker pool by one.
+
+=head2 TTOU
+
+Decrease worker pool by one.
+
+=head1 WORKER SIGNALS
+
+L<Mojo::Server::Prefork> worker processes can be controlled at runtime with the
+following signals.
+
+=head2 QUIT
+
+Stop worker gracefully.
+
+=head1 EVENTS
+
+L<Mojo::Server::Prefork> inherits all events from L<Mojo::Server::Daemon> and
+can emit the following new ones.
+
+=head2 finish
+
+  $prefork->on(finish => sub {
+    my ($prefork, $graceful) = @_;
+    ...
+  });
+
+Emitted when the server shuts down.
+
+  $prefork->on(finish => sub {
+    my ($prefork, $graceful) = @_;
+    say $graceful ? 'Graceful server shutdown' : 'Server shutdown';
+  });
+
+=head2 heartbeat
+
+  $prefork->on(heartbeat => sub {
+    my ($prefork, $pid) = @_;
+    ...
+  });
+
+Emitted when a heartbeat message has been received from a worker.
+
+  $prefork->on(heartbeat => sub {
+    my ($prefork, $pid) = @_;
+    say "Worker $pid has a heartbeat";
+  });
+
+=head2 reap
+
+  $prefork->on(reap => sub {
+    my ($prefork, $pid) = @_;
+    ...
+  });
+
+Emitted when a child process exited.
+
+  $prefork->on(reap => sub {
+    my ($prefork, $pid) = @_;
+    say "Worker $pid stopped";
+  });
+
+=head2 spawn
+
+  $prefork->on(spawn => sub {
+    my ($prefork, $pid) = @_;
+    ...
+  });
+
+Emitted when a worker process is spawned.
+
+  $prefork->on(spawn => sub {
+    my ($prefork, $pid) = @_;
+    say "Worker $pid started";
+  });
+
+=head2 wait
+
+  $prefork->on(wait => sub {
+    my $prefork = shift;
+    ...
+  });
+
+Emitted when the manager starts waiting for new heartbeat messages.
+
+  $prefork->on(wait => sub {
+    my $prefork = shift;
+    my $workers = $prefork->workers;
+    say "Waiting for heartbeat messages from $workers workers";
+  });
+
+=head1 ATTRIBUTES
+
+L<Mojo::Server::Prefork> inherits all attributes from L<Mojo::Server::Daemon>
+and implements the following new ones.
+
+=head2 accepts
+
+  my $accepts = $prefork->accepts;
+  $prefork    = $prefork->accepts(100);
+
+Maximum number of connections a worker is allowed to accept, before stopping
+gracefully and then getting replaced with a newly started worker, passed along
+to L<Mojo::IOLoop/"max_accepts">, defaults to C<10000>. Setting the value to
+C<0> will allow workers to accept new connections indefinitely. Note that up to
+half of this value can be subtracted randomly to improve load balancing, and to
+make sure that not all workers restart at the same time.
+
+=head2 cleanup
+
+  my $bool = $prefork->cleanup;
+  $prefork = $prefork->cleanup($bool);
+
+Delete L</"pid_file"> automatically once it is not needed anymore, defaults to
+a true value.
+
+=head2 graceful_timeout
+
+  my $timeout = $prefork->graceful_timeout;
+  $prefork    = $prefork->graceful_timeout(15);
+
+Maximum amount of time in seconds stopping a worker gracefully may take before
+being forced, defaults to C<120>. Note that this value should usually be a
+little larger than the maximum amount of time you expect any one request to
+take.
+
+=head2 heartbeat_interval
+
+  my $interval = $prefork->heartbeat_interval;
+  $prefork     = $prefork->heartbeat_interval(3);
+
+Heartbeat interval in seconds, defaults to C<5>.
+
+=head2 heartbeat_timeout
+
+  my $timeout = $prefork->heartbeat_timeout;
+  $prefork    = $prefork->heartbeat_timeout(2);
+
+Maximum amount of time in seconds before a worker without a heartbeat will be
+stopped gracefully, defaults to C<30>. Note that this value should usually be a
+little larger than the maximum amount of time you expect any one operation to
+block the event loop.
+
+=head2 pid_file
+
+  my $file = $prefork->pid_file;
+  $prefork = $prefork->pid_file('/tmp/prefork.pid');
+
+Full path of process id file, defaults to C<prefork.pid> in a temporary
+directory.
+
+=head2 spare
+
+  my $spare = $prefork->spare;
+  $prefork  = $prefork->spare(4);
+
+Temporarily spawn up to this number of additional workers if there is a need,
+defaults to C<2>. This allows for new workers to be started while old ones are
+still shutting down gracefully, drastically reducing the performance cost of
+worker restarts.
+
+=head2 workers
+
+  my $workers = $prefork->workers;
+  $prefork    = $prefork->workers(10);
+
+Number of worker processes, defaults to C<4>. A good rule of thumb is two
+worker processes per CPU core for applications that perform mostly non-blocking
+operations, blocking operations often require more and benefit from decreasing
+concurrency with L<Mojo::Server::Daemon/"clients"> (often as low as C<1>).
+
+=head1 METHODS
+
+L<Mojo::Server::Prefork> inherits all methods from L<Mojo::Server::Daemon> and
+implements the following new ones.
+
+=head2 check_pid
+
+  my $pid = $prefork->check_pid;
+
+Get process id for running server from L</"pid_file"> or delete it if server is
+not running.
+
+  say 'Server is not running' unless $prefork->check_pid;
+
+=head2 ensure_pid_file
+
+  $prefork->ensure_pid_file($pid);
+
+Ensure L</"pid_file"> exists.
+
+=head2 healthy
+
+  my $healthy = $prefork->healthy;
+
+Number of currently active worker processes with a heartbeat.
+
+=head2 run
+
+  $prefork->run;
+
+Run server and wait for L</"MANAGER SIGNALS">.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/Template.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/Template.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/Template.pm	(revision 32205)
@@ -0,0 +1,690 @@
+package Mojo::Template;
+use Mojo::Base -base;
+
+use Carp 'croak';
+use Mojo::ByteStream;
+use Mojo::Exception;
+use Mojo::File 'path';
+use Mojo::Util qw(decode encode monkey_patch);
+
+use constant DEBUG => $ENV{MOJO_TEMPLATE_DEBUG} || 0;
+
+has [qw(append code prepend unparsed)] => '';
+has [qw(auto_escape compiled vars)];
+has capture_end   => 'end';
+has capture_start => 'begin';
+has comment_mark  => '#';
+has encoding      => 'UTF-8';
+has escape        => sub { \&Mojo::Util::xml_escape };
+has [qw(escape_mark expression_mark trim_mark)] => '=';
+has [qw(line_start replace_mark)] => '%';
+has name      => 'template';
+has namespace => 'Mojo::Template::SandBox';
+has tag_start => '<%';
+has tag_end   => '%>';
+has tree      => sub { [] };
+
+sub parse {
+  my ($self, $template) = @_;
+
+  # Clean start
+  $self->unparsed($template)->tree(\my @tree)->compiled(undef);
+
+  my $tag     = $self->tag_start;
+  my $replace = $self->replace_mark;
+  my $expr    = $self->expression_mark;
+  my $escp    = $self->escape_mark;
+  my $cpen    = $self->capture_end;
+  my $cmnt    = $self->comment_mark;
+  my $cpst    = $self->capture_start;
+  my $trim    = $self->trim_mark;
+  my $end     = $self->tag_end;
+  my $start   = $self->line_start;
+
+  my $line_re
+    = qr/^(\s*)\Q$start\E(?:(\Q$replace\E)|(\Q$cmnt\E)|(\Q$expr\E))?(.*)$/;
+  my $token_re = qr/
+    (
+      \Q$tag\E(?:\Q$replace\E|\Q$cmnt\E)                   # Replace
+    |
+      \Q$tag$expr\E(?:\Q$escp\E)?(?:\s*\Q$cpen\E(?!\w))?   # Expression
+    |
+      \Q$tag\E(?:\s*\Q$cpen\E(?!\w))?                      # Code
+    |
+      (?:(?<!\w)\Q$cpst\E\s*)?(?:\Q$trim\E)?\Q$end\E       # End
+    )
+  /x;
+  my $cpen_re = qr/^\Q$tag\E(?:\Q$expr\E)?(?:\Q$escp\E)?\s*\Q$cpen\E(.*)$/;
+  my $end_re  = qr/^(?:(\Q$cpst\E)\s*)?(\Q$trim\E)?\Q$end\E$/;
+
+  # Split lines
+  my $op = 'text';
+  my ($trimming, $capture);
+  for my $line (split "\n", $template) {
+
+    # Turn Perl line into mixed line
+    if ($op eq 'text' && $line =~ $line_re) {
+
+      # Escaped start
+      if ($2) { $line = "$1$start$5" }
+
+      # Comment
+      elsif ($3) { $line = "$tag$3 $trim$end" }
+
+      # Expression or code
+      else { $line = $4 ? "$1$tag$4$5 $end" : "$tag$5 $trim$end" }
+    }
+
+    # Escaped line ending
+    $line .= "\n" if $line !~ s/\\\\$/\\\n/ && $line !~ s/\\$//;
+
+    # Mixed line
+    for my $token (split $token_re, $line) {
+
+      # Capture end
+      ($token, $capture) = ("$tag$1", 1) if $token =~ $cpen_re;
+
+      # End
+      if ($op ne 'text' && $token =~ $end_re) {
+
+        # Capture start
+        splice @tree, -1, 0, ['cpst'] if $1;
+
+        # Trim left side
+        _trim(\@tree) if ($trimming = $2) && @tree > 1;
+
+        # Hint at end
+        push @tree, [$op = 'text', ''];
+      }
+
+      # Code
+      elsif ($token eq $tag) { $op = 'code' }
+
+      # Expression
+      elsif ($token eq "$tag$expr") { $op = 'expr' }
+
+      # Expression that needs to be escaped
+      elsif ($token eq "$tag$expr$escp") { $op = 'escp' }
+
+      # Comment
+      elsif ($token eq "$tag$cmnt") { $op = 'cmnt' }
+
+      # Text (comments are just ignored)
+      elsif ($op ne 'cmnt') {
+
+        # Replace
+        $token = $tag if $token eq "$tag$replace";
+
+        # Trim right side (convert whitespace to line noise)
+        if ($trimming && $token =~ s/^(\s+)//) {
+          push @tree, ['code', $1];
+          $trimming = 0;
+        }
+
+        # Token (with optional capture end)
+        push @tree, $capture ? ['cpen'] : (), [$op, $token];
+        $capture = 0;
+      }
+    }
+
+    # Optimize successive text lines separated by a newline
+    push @tree, ['line'] and next
+      if $tree[-4] && $tree[-4][0] ne 'line'
+      || (!$tree[-3] || $tree[-3][0] ne 'text' || $tree[-3][1] !~ /\n$/)
+      || ($tree[-2][0] ne 'line' || $tree[-1][0] ne 'text');
+    $tree[-3][1] .= pop(@tree)->[1];
+  }
+
+  return $self;
+}
+
+sub process {
+  my $self = shift;
+
+  # Use a local stack trace for compile exceptions
+  my $compiled = $self->compiled;
+  unless ($compiled) {
+    my $code = $self->_compile->code;
+    monkey_patch $self->namespace, '_escape', $self->escape;
+    return Mojo::Exception->new($@)->inspect($self->unparsed, $code)
+      ->trace->verbose(1)
+      unless $compiled = eval $self->_wrap($code, @_);
+    $self->compiled($compiled);
+  }
+
+  # Use a real stack trace for normal exceptions
+  local $SIG{__DIE__} = sub {
+    CORE::die $_[0] if ref $_[0];
+    CORE::die Mojo::Exception->new(shift)
+      ->trace->inspect($self->unparsed, $self->code)->verbose(1);
+  };
+
+  my $output;
+  return eval { $output = $compiled->(@_); 1 } ? $output : $@;
+}
+
+sub render { shift->parse(shift)->process(@_) }
+
+sub render_file {
+  my ($self, $path) = (shift, shift);
+
+  $self->name($path) unless defined $self->{name};
+  my $template = path($path)->slurp;
+  my $encoding = $self->encoding;
+  croak qq{Template "$path" has invalid encoding}
+    if $encoding && !defined($template = decode $encoding, $template);
+
+  return $self->render($template, @_);
+}
+
+sub _compile {
+  my $self = shift;
+
+  my $tree   = $self->tree;
+  my $escape = $self->auto_escape;
+
+  my @blocks = ('');
+  my ($i, $capture, $multi);
+  while (++$i <= @$tree && (my $next = $tree->[$i])) {
+    my ($op, $value) = @{$tree->[$i - 1]};
+    push @blocks, '' and next if $op eq 'line';
+    my $newline = chomp($value //= '');
+
+    # Text (quote and fix line ending)
+    if ($op eq 'text') {
+      $value = join "\n", map { quotemeta $_ } split("\n", $value, -1);
+      $value .= '\n' if $newline;
+      $blocks[-1] .= "\$_O .= \"" . $value . "\";" if length $value;
+    }
+
+    # Code or multi-line expression
+    elsif ($op eq 'code' || $multi) { $blocks[-1] .= $value }
+
+    # Capture end
+    elsif ($op eq 'cpen') {
+      $blocks[-1] .= 'return Mojo::ByteStream->new($_O) }';
+
+      # No following code
+      $blocks[-1] .= ';' if ($next->[1] // '') =~ /^\s*$/;
+    }
+
+    # Expression
+    if ($op eq 'expr' || $op eq 'escp') {
+
+      # Escaped
+      if (!$multi && ($op eq 'escp' && !$escape || $op eq 'expr' && $escape)) {
+        $blocks[-1] .= "\$_O .= _escape scalar + $value";
+      }
+
+      # Raw
+      elsif (!$multi) { $blocks[-1] .= "\$_O .= scalar + $value" }
+
+      # Multi-line
+      $multi = !$next || $next->[0] ne 'text';
+
+      # Append semicolon
+      $blocks[-1] .= ';' unless $multi || $capture;
+    }
+
+    # Capture start
+    if ($op eq 'cpst') { $capture = 1 }
+    elsif ($capture) {
+      $blocks[-1] .= "sub { my \$_O = ''; ";
+      $capture = 0;
+    }
+  }
+
+  return $self->code(join "\n", @blocks)->tree([]);
+}
+
+sub _line {
+  my $name = shift->name;
+  $name =~ y/"//d;
+  return qq{#line @{[shift]} "$name"};
+}
+
+sub _trim {
+  my $tree = shift;
+
+  # Skip captures
+  my $i = $tree->[-2][0] eq 'cpst' || $tree->[-2][0] eq 'cpen' ? -3 : -2;
+
+  # Only trim text
+  return unless $tree->[$i][0] eq 'text';
+
+  # Convert whitespace text to line noise
+  splice @$tree, $i, 0, ['code', $1] if $tree->[$i][1] =~ s/(\s+)$//;
+}
+
+sub _wrap {
+  my ($self, $body, $vars) = @_;
+
+  # Variables
+  my $args = '';
+  if ($self->vars && (my @vars = grep {/^\w+$/} keys %$vars)) {
+    $args = 'my (' . join(',', map {"\$$_"} @vars) . ')';
+    $args .= '= @{shift()}{qw(' . join(' ', @vars) . ')};';
+  }
+
+  # Wrap lines
+  my $num = () = $body =~ /\n/g;
+  my $code = $self->_line(1) . "\npackage @{[$self->namespace]};";
+  $code .= "use Mojo::Base -strict; no warnings 'ambiguous';";
+  $code .= "sub { my \$_O = ''; @{[$self->prepend]};{ $args { $body\n";
+  $code .= $self->_line($num + 1) . "\n;}@{[$self->append]}; } \$_O };";
+
+  warn "-- Code for @{[$self->name]}\n@{[encode 'UTF-8', $code]}\n\n" if DEBUG;
+  return $code;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::Template - Perl-ish templates
+
+=head1 SYNOPSIS
+
+  use Mojo::Template;
+
+  # Use Perl modules
+  my $mt = Mojo::Template->new;
+  say $mt->render(<<'EOF');
+  % use Time::Piece;
+  <div>
+    % my $now = localtime;
+    Time: <%= $now->hms %>
+  </div>
+  EOF
+
+  # Render with arguments
+  say $mt->render(<<'EOF', [1 .. 13], 'Hello World!');
+  % my ($numbers, $title) = @_;
+  <div>
+    <h1><%= $title %></h1>
+    % for my $i (@$numbers) {
+      Test <%= $i %>
+    % }
+  </div>
+  EOF
+
+  # Render with named variables
+  say $mt->vars(1)->render(<<'EOF', {title => 'Hello World!'});
+  <div>
+    <h1><%= $title %></h1>
+    %= 5 + 5
+  </div>
+  EOF
+
+=head1 DESCRIPTION
+
+L<Mojo::Template> is a minimalistic, fast, and very Perl-ish template engine,
+designed specifically for all those small tasks that come up during big
+projects. Like preprocessing a configuration file, generating text from heredocs
+and stuff like that.
+
+See L<Mojolicious::Guides::Rendering> for information on how to generate
+content with the L<Mojolicious> renderer.
+
+=head1 SYNTAX
+
+For all templates L<strict>, L<warnings>, L<utf8> and Perl 5.10
+L<features|feature> are automatically enabled.
+
+  <% Perl code %>
+  <%= Perl expression, replaced with result %>
+  <%== Perl expression, replaced with XML escaped result %>
+  <%# Comment, useful for debugging %>
+  <%% Replaced with "<%", useful for generating templates %>
+  % Perl code line, treated as "<% line =%>" (explained later)
+  %= Perl expression line, treated as "<%= line %>"
+  %== Perl expression line, treated as "<%== line %>"
+  %# Comment line, useful for debugging
+  %% Replaced with "%", useful for generating templates
+
+Escaping behavior can be reversed with the L</"auto_escape"> attribute, this is
+the default in L<Mojolicious> C<.ep> templates, for example.
+
+  <%= Perl expression, replaced with XML escaped result %>
+  <%== Perl expression, replaced with result %>
+
+L<Mojo::ByteStream> objects are always excluded from automatic escaping.
+
+  % use Mojo::ByteStream 'b';
+  <%= b('<div>excluded!</div>') %>
+
+Whitespace characters around tags can be trimmed by adding an additional equal
+sign to the end of a tag.
+
+  <% for (1 .. 3) { %>
+    <%= 'Trim all whitespace characters around this expression' =%>
+  <% } %>
+
+Newline characters can be escaped with a backslash.
+
+  This is <%= 1 + 1 %> a\
+  single line
+
+And a backslash in front of a newline character can be escaped with another
+backslash.
+
+  This will <%= 1 + 1 %> result\\
+  in multiple\\
+  lines
+
+A newline character gets appended automatically to every template, unless the
+last character is a backslash. And empty lines at the end of a template are
+ignored.
+
+  There is <%= 1 + 1 %> no newline at the end here\
+
+You can capture whole template blocks for reuse later with the C<begin> and
+C<end> keywords. Just be aware that both keywords are part of the surrounding
+tag and not actual Perl code, so there can only be whitespace after C<begin>
+and before C<end>.
+
+  <% my $block = begin %>
+    <% my $name = shift; =%>
+    Hello <%= $name %>.
+  <% end %>
+  <%= $block->('Baerbel') %>
+  <%= $block->('Wolfgang') %>
+
+Perl lines can also be indented freely.
+
+  % my $block = begin
+    % my $name = shift;
+    Hello <%= $name %>.
+  % end
+  %= $block->('Baerbel')
+  %= $block->('Wolfgang')
+
+L<Mojo::Template> templates get compiled to a Perl subroutine, that means you
+can access arguments simply via C<@_>.
+
+  % my ($foo, $bar) = @_;
+  % my $x = shift;
+  test 123 <%= $foo %>
+
+The compilation of templates to Perl code can make debugging a bit tricky, but
+L<Mojo::Template> will return L<Mojo::Exception> objects that stringify to
+error messages with context.
+
+  Bareword "xx" not allowed while "strict subs" in use at template line 4.
+  2: </head>
+  3: <body>
+  4: % my $i = 2; xx
+  5: %= $i * 2
+  6: </body>
+
+=head1 ATTRIBUTES
+
+L<Mojo::Template> implements the following attributes.
+
+=head2 auto_escape
+
+  my $bool = $mt->auto_escape;
+  $mt      = $mt->auto_escape($bool);
+
+Activate automatic escaping.
+
+  # "&lt;html&gt;"
+  Mojo::Template->new(auto_escape => 1)->render("<%= '<html>' %>");
+
+=head2 append
+
+  my $code = $mt->append;
+  $mt      = $mt->append('warn "Processed template"');
+
+Append Perl code to compiled template. Note that this code should not contain
+newline characters, or line numbers in error messages might end up being wrong.
+
+=head2 capture_end
+
+  my $end = $mt->capture_end;
+  $mt     = $mt->capture_end('end');
+
+Keyword indicating the end of a capture block, defaults to C<end>.
+
+  <% my $block = begin %>
+    Some data!
+  <% end %>
+
+=head2 capture_start
+
+  my $start = $mt->capture_start;
+  $mt       = $mt->capture_start('begin');
+
+Keyword indicating the start of a capture block, defaults to C<begin>.
+
+  <% my $block = begin %>
+    Some data!
+  <% end %>
+
+=head2 code
+
+  my $code = $mt->code;
+  $mt      = $mt->code($code);
+
+Perl code for template if available.
+
+=head2 comment_mark
+
+  my $mark = $mt->comment_mark;
+  $mt      = $mt->comment_mark('#');
+
+Character indicating the start of a comment, defaults to C<#>.
+
+  <%# This is a comment %>
+
+=head2 compiled
+
+  my $compiled = $mt->compiled;
+  $mt          = $mt->compiled($compiled);
+
+Compiled template code if available.
+
+=head2 encoding
+
+  my $encoding = $mt->encoding;
+  $mt          = $mt->encoding('UTF-8');
+
+Encoding used for template files, defaults to C<UTF-8>.
+
+=head2 escape
+
+  my $cb = $mt->escape;
+  $mt    = $mt->escape(sub {...});
+
+A callback used to escape the results of escaped expressions, defaults to
+L<Mojo::Util/"xml_escape">.
+
+  $mt->escape(sub {
+    my $str = shift;
+    return reverse $str;
+  });
+
+=head2 escape_mark
+
+  my $mark = $mt->escape_mark;
+  $mt      = $mt->escape_mark('=');
+
+Character indicating the start of an escaped expression, defaults to C<=>.
+
+  <%== $foo %>
+
+=head2 expression_mark
+
+  my $mark = $mt->expression_mark;
+  $mt      = $mt->expression_mark('=');
+
+Character indicating the start of an expression, defaults to C<=>.
+
+  <%= $foo %>
+
+=head2 line_start
+
+  my $start = $mt->line_start;
+  $mt       = $mt->line_start('%');
+
+Character indicating the start of a code line, defaults to C<%>.
+
+  % $foo = 23;
+
+=head2 name
+
+  my $name = $mt->name;
+  $mt      = $mt->name('foo.mt');
+
+Name of template currently being processed, defaults to C<template>. Note that
+this value should not contain quotes or newline characters, or error messages
+might end up being wrong.
+
+=head2 namespace
+
+  my $namespace = $mt->namespace;
+  $mt           = $mt->namespace('main');
+
+Namespace used to compile templates, defaults to C<Mojo::Template::SandBox>.
+Note that namespaces should only be shared very carefully between templates,
+since functions and global variables will not be cleared automatically.
+
+=head2 prepend
+
+  my $code = $mt->prepend;
+  $mt      = $mt->prepend('my $self = shift;');
+
+Prepend Perl code to compiled template. Note that this code should not contain
+newline characters, or line numbers in error messages might end up being wrong.
+
+=head2 replace_mark
+
+  my $mark = $mt->replace_mark;
+  $mt      = $mt->replace_mark('%');
+
+Character used for escaping the start of a tag or line, defaults to C<%>.
+
+  <%% my $foo = 23; %>
+
+=head2 tag_start
+
+  my $start = $mt->tag_start;
+  $mt       = $mt->tag_start('<%');
+
+Characters indicating the start of a tag, defaults to C<E<lt>%>.
+
+  <% $foo = 23; %>
+
+=head2 tag_end
+
+  my $end = $mt->tag_end;
+  $mt     = $mt->tag_end('%>');
+
+Characters indicating the end of a tag, defaults to C<%E<gt>>.
+
+  <%= $foo %>
+
+=head2 tree
+
+  my $tree = $mt->tree;
+  $mt      = $mt->tree([['text', 'foo'], ['line']]);
+
+Template in parsed form if available. Note that this structure should only be
+used very carefully since it is very dynamic.
+
+=head2 trim_mark
+
+  my $mark = $mt->trim_mark;
+  $mt      = $mt->trim_mark('-');
+
+Character activating automatic whitespace trimming, defaults to C<=>.
+
+  <%= $foo =%>
+
+=head2 unparsed
+
+  my $unparsed = $mt->unparsed;
+  $mt          = $mt->unparsed('<%= 1 + 1 %>');
+
+Raw unparsed template if available.
+
+=head2 vars
+
+  my $bool = $mt->vars;
+  $mt      = $mt->vars($bool);
+
+Instead of a list of values, use a hash reference with named variables to pass
+data to templates.
+
+  # "works!"
+  Mojo::Template->new(vars => 1)->render('<%= $test %>!', {test => 'works'});
+
+=head1 METHODS
+
+L<Mojo::Template> inherits all methods from L<Mojo::Base> and implements the
+following new ones.
+
+=head2 parse
+
+  $mt = $mt->parse('<%= 1 + 1 %>');
+
+Parse template into L</"tree">.
+
+=head2 process
+
+  my $output = $mt->process;
+  my $output = $mt->process(@args);
+  my $output = $mt->process({foo => 'bar'});
+
+Process previously parsed template and return the result, or a
+L<Mojo::Exception> object if rendering failed.
+
+  # Parse and process
+  say Mojo::Template->new->parse('Hello <%= $_[0] %>')->process('Bender');
+
+  # Reuse template (for much better performance)
+  my $mt = Mojo::Template->new;
+  say $mt->render('Hello <%= $_[0] %>!', 'Bender');
+  say $mt->process('Fry');
+  say $mt->process('Leela');
+
+=head2 render
+
+  my $output = $mt->render('<%= 1 + 1 %>');
+  my $output = $mt->render('<%= shift() + shift() %>', @args);
+  my $output = $mt->render('<%= $foo %>', {foo => 'bar'});
+
+Render template and return the result, or a L<Mojo::Exception> object if
+rendering failed.
+
+  # Longer version
+  my $output = $mt->parse('<%= 1 + 1 %>')->process;
+
+  # Render with arguments
+  say Mojo::Template->new->render('<%= $_[0] %>', 'bar');
+
+  # Render with named variables
+  say Mojo::Template->new(vars => 1)->render('<%= $foo %>', {foo => 'bar'});
+
+=head2 render_file
+
+  my $output = $mt->render_file('/tmp/foo.mt');
+  my $output = $mt->render_file('/tmp/foo.mt', @args);
+  my $output = $mt->render_file('/tmp/bar.mt', {foo => 'bar'});
+
+Same as L</"render">, but renders a template file.
+
+=head1 DEBUGGING
+
+You can set the C<MOJO_TEMPLATE_DEBUG> environment variable to get some
+advanced diagnostics information printed to C<STDERR>.
+
+  MOJO_TEMPLATE_DEBUG=1
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/Transaction.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/Transaction.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/Transaction.pm	(revision 32205)
@@ -0,0 +1,306 @@
+package Mojo::Transaction;
+use Mojo::Base 'Mojo::EventEmitter';
+
+use Carp 'croak';
+use Mojo::Message::Request;
+use Mojo::Message::Response;
+
+has [
+  qw(kept_alive local_address local_port original_remote_address remote_port)];
+has req => sub { Mojo::Message::Request->new };
+has res => sub { Mojo::Message::Response->new };
+
+sub client_read  { croak 'Method "client_read" not implemented by subclass' }
+sub client_write { croak 'Method "client_write" not implemented by subclass' }
+
+sub closed { shift->completed->emit('finish') }
+
+sub completed { ++$_[0]{completed} and return $_[0] }
+
+sub connection {
+  my $self = shift;
+  return $self->emit(connection => $self->{connection} = shift) if @_;
+  return $self->{connection};
+}
+
+sub error { $_[0]->req->error || $_[0]->res->error }
+
+sub is_finished { !!shift->{completed} }
+
+sub is_websocket {undef}
+
+sub remote_address {
+  my $self = shift;
+
+  return $self->original_remote_address(@_) if @_;
+  return $self->original_remote_address unless $self->req->reverse_proxy;
+
+  # Reverse proxy
+  return ($self->req->headers->header('X-Forwarded-For') // '') =~ /([^,\s]+)$/
+    ? $1
+    : $self->original_remote_address;
+}
+
+sub result {
+  my $self = shift;
+  my $err  = $self->error;
+  return !$err || $err->{code} ? $self->res : croak $err->{message};
+}
+
+sub server_read  { croak 'Method "server_read" not implemented by subclass' }
+sub server_write { croak 'Method "server_write" not implemented by subclass' }
+
+sub success { $_[0]->error ? undef : $_[0]->res }
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::Transaction - Transaction base class
+
+=head1 SYNOPSIS
+
+  package Mojo::Transaction::MyTransaction;
+  use Mojo::Base 'Mojo::Transaction';
+
+  sub client_read  {...}
+  sub client_write {...}
+  sub server_read  {...}
+  sub server_write {...}
+
+=head1 DESCRIPTION
+
+L<Mojo::Transaction> is an abstract base class for transactions, like
+L<Mojo::Transaction::HTTP> and L<Mojo::Transaction::WebSocket>.
+
+=head1 EVENTS
+
+L<Mojo::Transaction> inherits all events from L<Mojo::EventEmitter> and can
+emit the following new ones.
+
+=head2 connection
+
+  $tx->on(connection => sub {
+    my ($tx, $connection) = @_;
+    ...
+  });
+
+Emitted when a connection has been assigned to transaction.
+
+=head2 finish
+
+  $tx->on(finish => sub {
+    my $tx = shift;
+    ...
+  });
+
+Emitted when transaction is finished.
+
+=head1 ATTRIBUTES
+
+L<Mojo::Transaction> implements the following attributes.
+
+=head2 kept_alive
+
+  my $bool = $tx->kept_alive;
+  $tx      = $tx->kept_alive($bool);
+
+Connection has been kept alive.
+
+=head2 local_address
+
+  my $address = $tx->local_address;
+  $tx         = $tx->local_address('127.0.0.1');
+
+Local interface address.
+
+=head2 local_port
+
+  my $port = $tx->local_port;
+  $tx      = $tx->local_port(8080);
+
+Local interface port.
+
+=head2 original_remote_address
+
+  my $address = $tx->original_remote_address;
+  $tx         = $tx->original_remote_address('127.0.0.1');
+
+Remote interface address.
+
+=head2 remote_port
+
+  my $port = $tx->remote_port;
+  $tx      = $tx->remote_port(8081);
+
+Remote interface port.
+
+=head2 req
+
+  my $req = $tx->req;
+  $tx     = $tx->req(Mojo::Message::Request->new);
+
+HTTP request, defaults to a L<Mojo::Message::Request> object.
+
+  # Access request information
+  my $method = $tx->req->method;
+  my $url    = $tx->req->url->to_abs;
+  my $info   = $tx->req->url->to_abs->userinfo;
+  my $host   = $tx->req->url->to_abs->host;
+  my $agent  = $tx->req->headers->user_agent;
+  my $custom = $tx->req->headers->header('Custom-Header');
+  my $bytes  = $tx->req->body;
+  my $str    = $tx->req->text;
+  my $hash   = $tx->req->params->to_hash;
+  my $all    = $tx->req->uploads;
+  my $value  = $tx->req->json;
+  my $foo    = $tx->req->json('/23/foo');
+  my $dom    = $tx->req->dom;
+  my $bar    = $tx->req->dom('div.bar')->first->text;
+
+=head2 res
+
+  my $res = $tx->res;
+  $tx     = $tx->res(Mojo::Message::Response->new);
+
+HTTP response, defaults to a L<Mojo::Message::Response> object.
+
+  # Access response information
+  my $code    = $tx->res->code;
+  my $message = $tx->res->message;
+  my $server  = $tx->res->headers->server;
+  my $custom  = $tx->res->headers->header('Custom-Header');
+  my $bytes   = $tx->res->body;
+  my $str     = $tx->res->text;
+  my $value   = $tx->res->json;
+  my $foo     = $tx->res->json('/23/foo');
+  my $dom     = $tx->res->dom;
+  my $bar     = $tx->res->dom('div.bar')->first->text;
+
+=head1 METHODS
+
+L<Mojo::Transaction> inherits all methods from L<Mojo::EventEmitter> and
+implements the following new ones.
+
+=head2 client_read
+
+  $tx->client_read($bytes);
+
+Read data client-side, used to implement user agents such as L<Mojo::UserAgent>.
+Meant to be overloaded in a subclass.
+
+=head2 client_write
+
+  my $bytes = $tx->client_write;
+
+Write data client-side, used to implement user agents such as
+L<Mojo::UserAgent>. Meant to be overloaded in a subclass.
+
+=head2 closed
+
+  $tx = $tx->closed;
+
+Same as L</"completed">, but also indicates that all transaction data has been
+sent.
+
+=head2 completed
+
+  $tx = $tx->completed;
+
+Low-level method to finalize transaction.
+
+=head2 connection
+
+  my $id = $tx->connection;
+  $tx    = $tx->connection($id);
+
+Connection identifier.
+
+=head2 error
+
+  my $err = $tx->error;
+
+Get request or response error and return C<undef> if there is no error,
+commonly used together with L</"success">.
+
+  # Longer version
+  my $err = $tx->req->error || $tx->res->error;
+
+  # Check for 4xx/5xx response and connection errors
+  if (my $err = $tx->error) {
+    die "$err->{code} response: $err->{message}" if $err->{code};
+    die "Connection error: $err->{message}";
+  }
+
+=head2 is_finished
+
+  my $bool = $tx->is_finished;
+
+Check if transaction is finished.
+
+=head2 is_websocket
+
+  my $bool = $tx->is_websocket;
+
+False, this is not a L<Mojo::Transaction::WebSocket> object.
+
+=head2 remote_address
+
+  my $address = $tx->remote_address;
+  $tx         = $tx->remote_address('127.0.0.1');
+
+Same as L</"original_remote_address"> or the last value of the
+C<X-Forwarded-For> header if L</"req"> has been performed through a reverse
+proxy.
+
+=head2 result
+
+  my $res = $tx->result;
+
+Returns the L<Mojo::Message::Response> object from L</"res"> or dies if a
+connection error has occurred.
+
+  # Fine grained response handling (dies on connection errors)
+  my $res = $tx->result;
+  if    ($res->is_success)  { say $res->body }
+  elsif ($res->is_error)    { say $res->message }
+  elsif ($res->code == 301) { say $res->headers->location }
+  else                      { say 'Whatever...' }
+
+=head2 server_read
+
+  $tx->server_read($bytes);
+
+Read data server-side, used to implement web servers such as
+L<Mojo::Server::Daemon>. Meant to be overloaded in a subclass.
+
+=head2 server_write
+
+  my $bytes = $tx->server_write;
+
+Write data server-side, used to implement web servers such as
+L<Mojo::Server::Daemon>. Meant to be overloaded in a subclass.
+
+=head2 success
+
+  my $res = $tx->success;
+
+Returns the L<Mojo::Message::Response> object from L</"res"> if transaction was
+successful or C<undef> otherwise. Connection and parser errors have only a
+message in L</"error">, C<400> and C<500> responses also a code.
+
+  # Manual exception handling
+  if (my $res = $tx->success) { say $res->body }
+  else {
+    my $err = $tx->error;
+    die "$err->{code} response: $err->{message}" if $err->{code};
+    die "Connection error: $err->{message}";
+  }
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/Transaction/HTTP.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/Transaction/HTTP.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/Transaction/HTTP.pm	(revision 32205)
@@ -0,0 +1,309 @@
+package Mojo::Transaction::HTTP;
+use Mojo::Base 'Mojo::Transaction';
+
+has 'previous';
+
+sub client_read {
+  my ($self, $chunk) = @_;
+
+  # Skip body for HEAD request
+  my $res = $self->res;
+  $res->content->skip_body(1) if uc $self->req->method eq 'HEAD';
+  return unless $res->parse($chunk)->is_finished;
+
+  # Unexpected 1xx response
+  return $self->completed if !$res->is_info || $res->headers->upgrade;
+  $self->res($res->new)->emit(unexpected => $res);
+  return unless length(my $leftovers = $res->content->leftovers);
+  $self->client_read($leftovers);
+}
+
+sub client_write { shift->_write(0) }
+
+sub is_empty { !!(uc $_[0]->req->method eq 'HEAD' || $_[0]->res->is_empty) }
+
+sub keep_alive {
+  my $self = shift;
+
+  # Close
+  my $req      = $self->req;
+  my $res      = $self->res;
+  my $req_conn = lc($req->headers->connection // '');
+  my $res_conn = lc($res->headers->connection // '');
+  return undef if $req_conn eq 'close' || $res_conn eq 'close';
+
+  # Keep-alive is optional for 1.0
+  return $res_conn eq 'keep-alive' if $res->version eq '1.0';
+  return $req_conn eq 'keep-alive' if $req->version eq '1.0';
+
+  # Keep-alive is the default for 1.1
+  return 1;
+}
+
+sub redirects {
+  my $previous = shift;
+  my @redirects;
+  unshift @redirects, $previous while $previous = $previous->previous;
+  return \@redirects;
+}
+
+sub resume { ++$_[0]{writing} and return $_[0]->emit('resume') }
+
+sub server_read {
+  my ($self, $chunk) = @_;
+
+  # Parse request
+  my $req = $self->req;
+  $req->parse($chunk) unless $req->error;
+
+  # Generate response
+  $self->emit('request') if $req->is_finished && !$self->{handled}++;
+}
+
+sub server_write { shift->_write(1) }
+
+sub _body {
+  my ($self, $msg, $finish) = @_;
+
+  # Prepare body chunk
+  my $buffer = $msg->get_body_chunk($self->{offset});
+  my $written = defined $buffer ? length $buffer : 0;
+  $self->{write} = $msg->content->is_dynamic ? 1 : ($self->{write} - $written);
+  $self->{offset} += $written;
+
+  # Delayed
+  $self->{writing} = 0 unless defined $buffer;
+
+  # Finished
+  $finish ? $self->completed : ($self->{writing} = 0)
+    if $self->{write} <= 0 || defined $buffer && !length $buffer;
+
+  return $buffer // '';
+}
+
+sub _headers {
+  my ($self, $msg, $head) = @_;
+
+  # Prepare header chunk
+  my $buffer = $msg->get_header_chunk($self->{offset});
+  my $written = defined $buffer ? length $buffer : 0;
+  $self->{write} -= $written;
+  $self->{offset} += $written;
+
+  # Switch to body
+  if ($self->{write} <= 0) {
+    @$self{qw(http_state offset)} = ('body', 0);
+
+    # Response without body
+    if ($head && $self->is_empty) { $self->completed->{http_state} = 'empty' }
+
+    # Body
+    else { $self->{write} = $msg->content->is_dynamic ? 1 : $msg->body_size }
+  }
+
+  return $buffer;
+}
+
+sub _start_line {
+  my ($self, $msg) = @_;
+
+  # Prepare start-line chunk
+  my $buffer = $msg->get_start_line_chunk($self->{offset});
+  my $written = defined $buffer ? length $buffer : 0;
+  $self->{write} -= $written;
+  $self->{offset} += $written;
+
+  # Switch to headers
+  @$self{qw(http_state write offset)} = ('headers', $msg->header_size, 0)
+    if $self->{write} <= 0;
+
+  return $buffer;
+}
+
+sub _write {
+  my ($self, $server) = @_;
+
+  # Client starts writing right away
+  return '' unless $server ? $self->{writing} : ($self->{writing} //= 1);
+
+  # Nothing written yet
+  $self->{$_} ||= 0 for qw(offset write);
+  my $msg = $server ? $self->res : $self->req;
+  @$self{qw(http_state write)} = ('start_line', $msg->start_line_size)
+    unless $self->{http_state};
+
+  # Start-line
+  my $chunk = '';
+  $chunk .= $self->_start_line($msg) if $self->{http_state} eq 'start_line';
+
+  # Headers
+  $chunk .= $self->_headers($msg, $server) if $self->{http_state} eq 'headers';
+
+  # Body
+  $chunk .= $self->_body($msg, $server) if $self->{http_state} eq 'body';
+
+  return $chunk;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::Transaction::HTTP - HTTP transaction
+
+=head1 SYNOPSIS
+
+  use Mojo::Transaction::HTTP;
+
+  # Client
+  my $tx = Mojo::Transaction::HTTP->new;
+  $tx->req->method('GET');
+  $tx->req->url->parse('http://example.com');
+  $tx->req->headers->accept('application/json');
+  say $tx->res->code;
+  say $tx->res->headers->content_type;
+  say $tx->res->body;
+  say $tx->remote_address;
+
+  # Server
+  my $tx = Mojo::Transaction::HTTP->new;
+  say $tx->req->method;
+  say $tx->req->url->to_abs;
+  say $tx->req->headers->accept;
+  say $tx->remote_address;
+  $tx->res->code(200);
+  $tx->res->headers->content_type('text/plain');
+  $tx->res->body('Hello World!');
+
+=head1 DESCRIPTION
+
+L<Mojo::Transaction::HTTP> is a container for HTTP transactions, based on
+L<RFC 7230|http://tools.ietf.org/html/rfc7230> and
+L<RFC 7231|http://tools.ietf.org/html/rfc7231>.
+
+=head1 EVENTS
+
+L<Mojo::Transaction::HTTP> inherits all events from L<Mojo::Transaction> and
+can emit the following new ones.
+
+=head2 request
+
+  $tx->on(request => sub {
+    my $tx = shift;
+    ...
+  });
+
+Emitted when a request is ready and needs to be handled.
+
+  $tx->on(request => sub {
+    my $tx = shift;
+    $tx->res->headers->header('X-Bender' => 'Bite my shiny metal ass!');
+  });
+
+=head2 resume
+
+  $tx->on(resume => sub {
+    my $tx = shift;
+    ...
+  });
+
+Emitted when transaction is resumed.
+
+=head2 unexpected
+
+  $tx->on(unexpected => sub {
+    my ($tx, $res) = @_;
+    ...
+  });
+
+Emitted for unexpected C<1xx> responses that will be ignored.
+
+  $tx->on(unexpected => sub {
+    my $tx = shift;
+    $tx->res->on(finish => sub { say 'Follow-up response is finished.' });
+  });
+
+=head1 ATTRIBUTES
+
+L<Mojo::Transaction::HTTP> inherits all attributes from L<Mojo::Transaction>
+and implements the following new ones.
+
+=head2 previous
+
+  my $previous = $tx->previous;
+  $tx          = $tx->previous(Mojo::Transaction::HTTP->new);
+
+Previous transaction that triggered this follow-up transaction, usually a
+L<Mojo::Transaction::HTTP> object.
+
+  # Paths of previous requests
+  say $tx->previous->previous->req->url->path;
+  say $tx->previous->req->url->path;
+
+=head1 METHODS
+
+L<Mojo::Transaction::HTTP> inherits all methods from L<Mojo::Transaction> and
+implements the following new ones.
+
+=head2 client_read
+
+  $tx->client_read($bytes);
+
+Read data client-side, used to implement user agents such as L<Mojo::UserAgent>.
+
+=head2 client_write
+
+  my $bytes = $tx->client_write;
+
+Write data client-side, used to implement user agents such as
+L<Mojo::UserAgent>.
+
+=head2 is_empty
+
+  my $bool = $tx->is_empty;
+
+Check transaction for C<HEAD> request and C<1xx>, C<204> or C<304> response.
+
+=head2 keep_alive
+
+  my $bool = $tx->keep_alive;
+
+Check if connection can be kept alive.
+
+=head2 redirects
+
+  my $redirects = $tx->redirects;
+
+Return an array reference with all previous transactions that preceded this
+follow-up transaction.
+
+  # Paths of all previous requests
+  say $_->req->url->path for @{$tx->redirects};
+
+=head2 resume
+
+  $tx = $tx->resume;
+
+Resume transaction.
+
+=head2 server_read
+
+  $tx->server_read($bytes);
+
+Read data server-side, used to implement web servers such as
+L<Mojo::Server::Daemon>.
+
+=head2 server_write
+
+  my $bytes = $tx->server_write;
+
+Write data server-side, used to implement web servers such as
+L<Mojo::Server::Daemon>.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/Transaction/WebSocket.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/Transaction/WebSocket.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/Transaction/WebSocket.pm	(revision 32205)
@@ -0,0 +1,520 @@
+package Mojo::Transaction::WebSocket;
+use Mojo::Base 'Mojo::Transaction';
+
+use Compress::Raw::Zlib 'Z_SYNC_FLUSH';
+use List::Util 'first';
+use Mojo::JSON qw(encode_json j);
+use Mojo::Util qw(decode encode trim);
+use Mojo::WebSocket
+  qw(WS_BINARY WS_CLOSE WS_CONTINUATION WS_PING WS_PONG WS_TEXT);
+
+has [qw(compressed established handshake masked)];
+has max_websocket_size => sub { $ENV{MOJO_MAX_WEBSOCKET_SIZE} || 262144 };
+
+sub build_message {
+  my ($self, $frame) = @_;
+
+  # Text
+  $frame = {text => encode('UTF-8', $frame)} if ref $frame ne 'HASH';
+
+  # JSON
+  $frame->{text} = encode_json($frame->{json}) if exists $frame->{json};
+
+  # Raw text or binary
+  if (exists $frame->{text}) { $frame = [1, 0, 0, 0, WS_TEXT, $frame->{text}] }
+  else { $frame = [1, 0, 0, 0, WS_BINARY, $frame->{binary}] }
+
+  # "permessage-deflate" extension
+  return $frame unless $self->compressed;
+  my $deflate = $self->{deflate} ||= Compress::Raw::Zlib::Deflate->new(
+    AppendOutput => 1,
+    MemLevel     => 8,
+    WindowBits   => -15
+  );
+  $deflate->deflate($frame->[5], my $out);
+  $deflate->flush($out, Z_SYNC_FLUSH);
+  @$frame[1, 5] = (1, substr($out, 0, length($out) - 4));
+
+  return $frame;
+}
+
+sub client_read  { shift->server_read(@_) }
+sub client_write { shift->server_write(@_) }
+
+sub closed {
+  my $self = shift->completed;
+  return $self->emit(finish => $self->{close} ? (@{$self->{close}}) : 1006);
+}
+
+sub connection { shift->handshake->connection }
+
+sub finish {
+  my $self = shift;
+
+  my $close = $self->{close} = [@_];
+  my $payload = $close->[0] ? pack('n', $close->[0]) : '';
+  $payload .= encode 'UTF-8', $close->[1] if defined $close->[1];
+  $close->[0] //= 1005;
+  $self->send([1, 0, 0, 0, WS_CLOSE, $payload])->{closing} = 1;
+
+  return $self;
+}
+
+sub is_websocket {1}
+
+sub kept_alive    { shift->handshake->kept_alive }
+sub local_address { shift->handshake->local_address }
+sub local_port    { shift->handshake->local_port }
+
+sub parse_message {
+  my ($self, $frame) = @_;
+
+  $self->emit(frame => $frame);
+
+  # Ping/Pong
+  my $op = $frame->[4];
+  return $self->send([1, 0, 0, 0, WS_PONG, $frame->[5]]) if $op == WS_PING;
+  return if $op == WS_PONG;
+
+  # Close
+  if ($op == WS_CLOSE) {
+    return $self->finish unless length $frame->[5] >= 2;
+    return $self->finish(unpack('n', substr($frame->[5], 0, 2, '')),
+      decode('UTF-8', $frame->[5]));
+  }
+
+  # Append chunk and check message size
+  $self->{op} = $op unless exists $self->{op};
+  $self->{message} .= $frame->[5];
+  my $max = $self->max_websocket_size;
+  return $self->finish(1009) if length $self->{message} > $max;
+
+  # No FIN bit (Continuation)
+  return unless $frame->[0];
+
+  # "permessage-deflate" extension (handshake and RSV1)
+  my $msg = delete $self->{message};
+  if ($self->compressed && $frame->[1]) {
+    my $inflate = $self->{inflate} ||= Compress::Raw::Zlib::Inflate->new(
+      Bufsize     => $max,
+      LimitOutput => 1,
+      WindowBits  => -15
+    );
+    $inflate->inflate(($msg .= "\x00\x00\xff\xff"), my $out);
+    return $self->finish(1009) if length $msg;
+    $msg = $out;
+  }
+
+  $self->emit(json => j($msg)) if $self->has_subscribers('json');
+  $op = delete $self->{op};
+  $self->emit($op == WS_TEXT ? 'text' : 'binary' => $msg);
+  $self->emit(message => $op == WS_TEXT ? decode 'UTF-8', $msg : $msg)
+    if $self->has_subscribers('message');
+}
+
+sub protocol { shift->res->headers->sec_websocket_protocol }
+
+sub remote_address { shift->handshake->remote_address }
+sub remote_port    { shift->handshake->remote_port }
+sub req            { shift->handshake->req }
+sub res            { shift->handshake->res }
+
+sub resume { $_[0]->handshake->resume and return $_[0] }
+
+sub send {
+  my ($self, $msg, $cb) = @_;
+  $self->once(drain => $cb) if $cb;
+  $msg = $self->build_message($msg) unless ref $msg eq 'ARRAY';
+  $self->{write} .= Mojo::WebSocket::build_frame($self->masked, @$msg);
+  return $self->emit('resume');
+}
+
+sub server_read {
+  my ($self, $chunk) = @_;
+
+  $self->{read} .= $chunk;
+  my $max = $self->max_websocket_size;
+  while (my $frame = Mojo::WebSocket::parse_frame(\$self->{read}, $max)) {
+    $self->finish(1009) and last unless ref $frame;
+    $self->parse_message($frame);
+  }
+
+  $self->emit('resume');
+}
+
+sub server_write {
+  my $self = shift;
+  $self->emit('drain') unless length($self->{write} //= '');
+  $self->completed if !length $self->{write} && $self->{closing};
+  return delete $self->{write};
+}
+
+sub with_compression {
+  my $self = shift;
+
+  # "permessage-deflate" extension
+  $self->compressed(1)
+    and $self->res->headers->sec_websocket_extensions('permessage-deflate')
+    if ($self->req->headers->sec_websocket_extensions // '')
+    =~ /permessage-deflate/;
+}
+
+sub with_protocols {
+  my $self = shift;
+
+  my %protos = map { trim($_) => 1 } split ',',
+    $self->req->headers->sec_websocket_protocol // '';
+  return undef unless defined(my $proto = first { $protos{$_} } @_);
+
+  $self->res->headers->sec_websocket_protocol($proto);
+  return $proto;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::Transaction::WebSocket - WebSocket transaction
+
+=head1 SYNOPSIS
+
+  use Mojo::Transaction::WebSocket;
+
+  # Send and receive WebSocket messages
+  my $ws = Mojo::Transaction::WebSocket->new;
+  $ws->send('Hello World!');
+  $ws->on(message => sub {
+    my ($ws, $msg) = @_;
+    say "Message: $msg";
+  });
+  $ws->on(finish => sub {
+    my ($ws, $code, $reason) = @_;
+    say "WebSocket closed with status $code.";
+  });
+
+=head1 DESCRIPTION
+
+L<Mojo::Transaction::WebSocket> is a container for WebSocket transactions, based
+on L<RFC 6455|http://tools.ietf.org/html/rfc6455> and
+L<RFC 7692|http://tools.ietf.org/html/rfc7692>.
+
+=head1 EVENTS
+
+L<Mojo::Transaction::WebSocket> inherits all events from L<Mojo::Transaction>
+and can emit the following new ones.
+
+=head2 binary
+
+  $ws->on(binary => sub {
+    my ($ws, $bytes) = @_;
+    ...
+  });
+
+Emitted when a complete WebSocket binary message has been received.
+
+  $ws->on(binary => sub {
+    my ($ws, $bytes) = @_;
+    say "Binary: $bytes";
+  });
+
+=head2 drain
+
+  $ws->on(drain => sub {
+    my $ws = shift;
+    ...
+  });
+
+Emitted once all data has been sent.
+
+  $ws->on(drain => sub {
+    my $ws = shift;
+    $ws->send(time);
+  });
+
+=head2 finish
+
+  $ws->on(finish => sub {
+    my ($ws, $code, $reason) = @_;
+    ...
+  });
+
+Emitted when the WebSocket connection has been closed.
+
+=head2 frame
+
+  $ws->on(frame => sub {
+    my ($ws, $frame) = @_;
+    ...
+  });
+
+Emitted when a WebSocket frame has been received.
+
+  $ws->on(frame => sub {
+    my ($ws, $frame) = @_;
+    say "FIN: $frame->[0]";
+    say "RSV1: $frame->[1]";
+    say "RSV2: $frame->[2]";
+    say "RSV3: $frame->[3]";
+    say "Opcode: $frame->[4]";
+    say "Payload: $frame->[5]";
+  });
+
+=head2 json
+
+  $ws->on(json => sub {
+    my ($ws, $json) = @_;
+    ...
+  });
+
+Emitted when a complete WebSocket message has been received, all text and
+binary messages will be automatically JSON decoded. Note that this event only
+gets emitted when it has at least one subscriber.
+
+  $ws->on(json => sub {
+    my ($ws, $hash) = @_;
+    say "Message: $hash->{msg}";
+  });
+
+=head2 message
+
+  $ws->on(message => sub {
+    my ($ws, $msg) = @_;
+    ...
+  });
+
+Emitted when a complete WebSocket message has been received, text messages will
+be automatically decoded. Note that this event only gets emitted when it has at
+least one subscriber.
+
+  $ws->on(message => sub {
+    my ($ws, $msg) = @_;
+    say "Message: $msg";
+  });
+
+=head2 resume
+
+  $tx->on(resume => sub {
+    my $tx = shift;
+    ...
+  });
+
+Emitted when transaction is resumed.
+
+=head2 text
+
+  $ws->on(text => sub {
+    my ($ws, $bytes) = @_;
+    ...
+  });
+
+Emitted when a complete WebSocket text message has been received.
+
+  $ws->on(text => sub {
+    my ($ws, $bytes) = @_;
+    say "Text: $bytes";
+  });
+
+=head1 ATTRIBUTES
+
+L<Mojo::Transaction::WebSocket> inherits all attributes from
+L<Mojo::Transaction> and implements the following new ones.
+
+=head2 compressed
+
+  my $bool = $ws->compressed;
+  $ws      = $ws->compressed($bool);
+
+Compress messages with C<permessage-deflate> extension.
+
+=head2 established
+
+  my $bool = $ws->established;
+  $ws      = $ws->established($bool);
+
+WebSocket connection established.
+
+=head2 handshake
+
+  my $handshake = $ws->handshake;
+  $ws           = $ws->handshake(Mojo::Transaction::HTTP->new);
+
+The original handshake transaction, usually a L<Mojo::Transaction::HTTP> object.
+
+=head2 masked
+
+  my $bool = $ws->masked;
+  $ws      = $ws->masked($bool);
+
+Mask outgoing frames with XOR cipher and a random 32-bit key.
+
+=head2 max_websocket_size
+
+  my $size = $ws->max_websocket_size;
+  $ws      = $ws->max_websocket_size(1024);
+
+Maximum WebSocket message size in bytes, defaults to the value of the
+C<MOJO_MAX_WEBSOCKET_SIZE> environment variable or C<262144> (256KiB).
+
+=head1 METHODS
+
+L<Mojo::Transaction::WebSocket> inherits all methods from L<Mojo::Transaction>
+and implements the following new ones.
+
+=head2 build_message
+
+  my $frame = $ws->build_message({binary => $bytes});
+  my $frame = $ws->build_message({text   => $bytes});
+  my $frame = $ws->build_message({json   => {test => [1, 2, 3]}});
+  my $frame = $ws->build_message($chars);
+
+Build WebSocket message.
+
+=head2 client_read
+
+  $ws->client_read($data);
+
+Read data client-side, used to implement user agents such as L<Mojo::UserAgent>.
+
+=head2 client_write
+
+  my $bytes = $ws->client_write;
+
+Write data client-side, used to implement user agents such as
+L<Mojo::UserAgent>.
+
+=head2 closed
+
+  $tx = $tx->closed;
+
+Same as L<Mojo::Transaction/"completed">, but also indicates that all
+transaction data has been sent.
+
+=head2 connection
+
+  my $id = $ws->connection;
+
+Connection identifier.
+
+=head2 finish
+
+  $ws = $ws->finish;
+  $ws = $ws->finish(1000);
+  $ws = $ws->finish(1003 => 'Cannot accept data!');
+
+Close WebSocket connection gracefully.
+
+=head2 is_websocket
+
+  my $bool = $ws->is_websocket;
+
+True, this is a L<Mojo::Transaction::WebSocket> object.
+
+=head2 kept_alive
+
+  my $bool = $ws->kept_alive;
+
+Connection has been kept alive.
+
+=head2 local_address
+
+  my $address = $ws->local_address;
+
+Local interface address.
+
+=head2 local_port
+
+  my $port = $ws->local_port;
+
+Local interface port.
+
+=head2 parse_message
+
+  $ws->parse_message([$fin, $rsv1, $rsv2, $rsv3, $op, $payload]);
+
+Parse WebSocket message.
+
+=head2 protocol
+
+  my $proto = $ws->protocol;
+
+Return negotiated subprotocol or C<undef>.
+
+=head2 remote_address
+
+  my $address = $ws->remote_address;
+
+Remote interface address.
+
+=head2 remote_port
+
+  my $port = $ws->remote_port;
+
+Remote interface port.
+
+=head2 req
+
+  my $req = $ws->req;
+
+Handshake request, usually a L<Mojo::Message::Request> object.
+
+=head2 res
+
+  my $res = $ws->res;
+
+Handshake response, usually a L<Mojo::Message::Response> object.
+
+=head2 resume
+
+  $ws = $ws->resume;
+
+Resume L</"handshake"> transaction.
+
+=head2 send
+
+  $ws = $ws->send({binary => $bytes});
+  $ws = $ws->send({text   => $bytes});
+  $ws = $ws->send({json   => {test => [1, 2, 3]}});
+  $ws = $ws->send([$fin, $rsv1, $rsv2, $rsv3, $op, $payload]);
+  $ws = $ws->send($chars);
+  $ws = $ws->send($chars => sub {...});
+
+Send message or frame non-blocking via WebSocket, the optional drain callback
+will be executed once all data has been written.
+
+  # Send "Ping" frame
+  use Mojo::WebSocket 'WS_PING';
+  $ws->send([1, 0, 0, 0, WS_PING, 'Hello World!']);
+
+=head2 server_read
+
+  $ws->server_read($data);
+
+Read data server-side, used to implement web servers such as
+L<Mojo::Server::Daemon>.
+
+=head2 server_write
+
+  my $bytes = $ws->server_write;
+
+Write data server-side, used to implement web servers such as
+L<Mojo::Server::Daemon>.
+
+=head2 with_compression
+
+  $ws->with_compression;
+
+Negotiate C<permessage-deflate> extension for this WebSocket connection.
+
+=head2 with_protocols
+
+  my $proto = $ws->with_protocols('v2.proto', 'v1.proto');
+
+Negotiate subprotocol for this WebSocket connection.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/URL.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/URL.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/URL.pm	(revision 32205)
@@ -0,0 +1,533 @@
+package Mojo::URL;
+use Mojo::Base -base;
+use overload bool => sub {1}, '""' => sub { shift->to_string }, fallback => 1;
+
+use Mojo::Parameters;
+use Mojo::Path;
+use Mojo::Util
+  qw(decode encode punycode_decode punycode_encode url_escape url_unescape);
+
+has base => sub { Mojo::URL->new };
+has [qw(fragment host port scheme userinfo)];
+
+sub clone {
+  my $self  = shift;
+  my $clone = $self->new;
+  @$clone{keys %$self} = values %$self;
+  $clone->{$_} && ($clone->{$_} = $clone->{$_}->clone) for qw(base path query);
+  return $clone;
+}
+
+sub host_port {
+  my ($self, $host_port) = @_;
+
+  if (defined $host_port) {
+    $self->port($1) if $host_port =~ s/:(\d+)$//;
+    my $host = url_unescape $host_port;
+    return $host =~ /[^\x00-\x7f]/ ? $self->ihost($host) : $self->host($host);
+  }
+
+  return undef unless defined(my $host = $self->ihost);
+  return $host unless defined(my $port = $self->port);
+  return "$host:$port";
+}
+
+sub ihost {
+  my $self = shift;
+
+  # Decode
+  return $self->host(join '.',
+    map { /^xn--(.+)$/ ? punycode_decode $1 : $_ } split(/\./, shift, -1))
+    if @_;
+
+  # Check if host needs to be encoded
+  return undef unless defined(my $host = $self->host);
+  return $host unless $host =~ /[^\x00-\x7f]/;
+
+  # Encode
+  return join '.',
+    map { /[^\x00-\x7f]/ ? ('xn--' . punycode_encode $_) : $_ }
+    split(/\./, $host, -1);
+}
+
+sub is_abs { !!shift->scheme }
+
+sub new { @_ > 1 ? shift->SUPER::new->parse(@_) : shift->SUPER::new }
+
+sub parse {
+  my ($self, $url) = @_;
+
+  # Official regex from RFC 3986
+  $url =~ m!^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?!;
+  $self->scheme($2)                         if defined $2;
+  $self->path($5)                           if defined $5;
+  $self->query($7)                          if defined $7;
+  $self->fragment(_decode(url_unescape $9)) if defined $9;
+  if (defined(my $auth = $4)) {
+    $self->userinfo(_decode(url_unescape $1)) if $auth =~ s/^([^\@]+)\@//;
+    $self->host_port($auth);
+  }
+
+  return $self;
+}
+
+sub password { (shift->userinfo // '') =~ /:(.*)$/ ? $1 : undef }
+
+sub path {
+  my $self = shift;
+
+  # Old path
+  $self->{path} ||= Mojo::Path->new;
+  return $self->{path} unless @_;
+
+  # New path
+  $self->{path} = ref $_[0] ? $_[0] : $self->{path}->merge($_[0]);
+
+  return $self;
+}
+
+sub path_query {
+  my ($self, $pq) = @_;
+
+  if (defined $pq) {
+    return $self unless $pq =~ /^([^?#]*)(?:\?([^#]*))?/;
+    return defined $2 ? $self->path($1)->query($2) : $self->path($1);
+  }
+
+  my $query = $self->query->to_string;
+  return $self->path->to_string . (length $query ? "?$query" : '');
+}
+
+sub protocol { lc(shift->scheme // '') }
+
+sub query {
+  my $self = shift;
+
+  # Old parameters
+  my $q = $self->{query} ||= Mojo::Parameters->new;
+  return $q unless @_;
+
+  # Replace with list
+  if (@_ > 1) { $q->pairs([])->parse(@_) }
+
+  # Merge with array
+  elsif (ref $_[0] eq 'ARRAY') { $q->merge(@{$_[0]}) }
+
+  # Append hash
+  elsif (ref $_[0] eq 'HASH') { $q->append(%{$_[0]}) }
+
+  # New parameters
+  else { $self->{query} = ref $_[0] ? $_[0] : $q->parse($_[0]) }
+
+  return $self;
+}
+
+sub to_abs {
+  my $self = shift;
+
+  my $abs = $self->clone;
+  return $abs if $abs->is_abs;
+
+  # Scheme
+  my $base = shift || $abs->base;
+  $abs->base($base)->scheme($base->scheme);
+
+  # Authority
+  return $abs if $abs->host;
+  $abs->userinfo($base->userinfo)->host($base->host)->port($base->port);
+
+  # Absolute path
+  my $path = $abs->path;
+  return $abs if $path->leading_slash;
+
+  # Inherit path
+  if (!@{$path->parts}) {
+    $abs->path($base->path->clone->canonicalize);
+
+    # Query
+    $abs->query($base->query->clone) unless length $abs->query->to_string;
+  }
+
+  # Merge paths
+  else { $abs->path($base->path->clone->merge($path)->canonicalize) }
+
+  return $abs;
+}
+
+sub to_string        { shift->_string(0) }
+sub to_unsafe_string { shift->_string(1) }
+
+sub username { (shift->userinfo // '') =~ /^([^:]+)/ ? $1 : undef }
+
+sub _decode { decode('UTF-8', $_[0]) // $_[0] }
+
+sub _encode { url_escape encode('UTF-8', $_[0]), $_[1] }
+
+sub _string {
+  my ($self, $unsafe) = @_;
+
+  # Scheme
+  my $url = '';
+  if (my $proto = $self->protocol) { $url .= "$proto:" }
+
+  # Authority
+  my $auth = $self->host_port;
+  $auth = _encode($auth, '^A-Za-z0-9\-._~!$&\'()*+,;=:\[\]') if defined $auth;
+  if ($unsafe && defined(my $info = $self->userinfo)) {
+    $auth = _encode($info, '^A-Za-z0-9\-._~!$&\'()*+,;=:') . '@' . $auth;
+  }
+  $url .= "//$auth" if defined $auth;
+
+  # Path and query
+  my $path = $self->path_query;
+  $url .= !$auth || !length $path || $path =~ m!^[/?]! ? $path : "/$path";
+
+  # Fragment
+  return $url unless defined(my $fragment = $self->fragment);
+  return $url . '#' . _encode($fragment, '^A-Za-z0-9\-._~!$&\'()*+,;=:@/?');
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::URL - Uniform Resource Locator
+
+=head1 SYNOPSIS
+
+  use Mojo::URL;
+
+  # Parse
+  my $url = Mojo::URL->new('http://sri:foo@example.com:3000/foo?foo=bar#23');
+  say $url->scheme;
+  say $url->userinfo;
+  say $url->host;
+  say $url->port;
+  say $url->path;
+  say $url->query;
+  say $url->fragment;
+
+  # Build
+  my $url = Mojo::URL->new;
+  $url->scheme('http');
+  $url->host('example.com');
+  $url->port(3000);
+  $url->path('/foo/bar');
+  $url->query(foo => 'bar');
+  $url->fragment(23);
+  say "$url";
+
+=head1 DESCRIPTION
+
+L<Mojo::URL> implements a subset of
+L<RFC 3986|http://tools.ietf.org/html/rfc3986>,
+L<RFC 3987|http://tools.ietf.org/html/rfc3987> and the
+L<URL Living Standard|https://url.spec.whatwg.org> for Uniform Resource
+Locators with support for IDNA and IRIs.
+
+=head1 ATTRIBUTES
+
+L<Mojo::URL> implements the following attributes.
+
+=head2 base
+
+  my $base = $url->base;
+  $url     = $url->base(Mojo::URL->new);
+
+Base of this URL, defaults to a L<Mojo::URL> object.
+
+  "http://example.com/a/b?c"
+  Mojo::URL->new("/a/b?c")->base(Mojo::URL->new("http://example.com"))->to_abs;
+
+=head2 fragment
+
+  my $fragment = $url->fragment;
+  $url         = $url->fragment('â¥mojoliciousâ¥');
+
+Fragment part of this URL.
+
+  # "yada"
+  Mojo::URL->new('http://example.com/foo?bar=baz#yada')->fragment;
+
+=head2 host
+
+  my $host = $url->host;
+  $url     = $url->host('127.0.0.1');
+
+Host part of this URL.
+
+  # "example.com"
+  Mojo::URL->new('http://sri:t3st@example.com:8080/foo')->host;
+
+=head2 port
+
+  my $port = $url->port;
+  $url     = $url->port(8080);
+
+Port part of this URL.
+
+  # "8080"
+  Mojo::URL->new('http://sri:t3st@example.com:8080/foo')->port;
+
+=head2 scheme
+
+  my $scheme = $url->scheme;
+  $url       = $url->scheme('http');
+
+Scheme part of this URL.
+
+  # "http"
+  Mojo::URL->new('http://example.com/foo')->scheme;
+
+=head2 userinfo
+
+  my $info = $url->userinfo;
+  $url     = $url->userinfo('root:â¥');
+
+Userinfo part of this URL.
+
+  # "sri:t3st"
+  Mojo::URL->new('https://sri:t3st@example.com/foo')->userinfo;
+
+=head1 METHODS
+
+L<Mojo::URL> inherits all methods from L<Mojo::Base> and implements the
+following new ones.
+
+=head2 clone
+
+  my $url2 = $url->clone;
+
+Return a new L<Mojo::URL> object cloned from this URL.
+
+=head2 host_port
+
+  my $host_port = $url->host_port;
+  $url          = $url->host_port('example.com:8080');
+
+Normalized version of L</"host"> and L</"port">.
+
+  # "xn--n3h.net:8080"
+  Mojo::URL->new('http://â.net:8080/test')->host_port;
+
+  # "example.com"
+  Mojo::URL->new('http://example.com/test')->host_port;
+
+=head2 ihost
+
+  my $ihost = $url->ihost;
+  $url      = $url->ihost('xn--bcher-kva.ch');
+
+Host part of this URL in punycode format.
+
+  # "xn--n3h.net"
+  Mojo::URL->new('http://â.net')->ihost;
+
+  # "example.com"
+  Mojo::URL->new('http://example.com')->ihost;
+
+=head2 is_abs
+
+  my $bool = $url->is_abs;
+
+Check if URL is absolute.
+
+  # True
+  Mojo::URL->new('http://example.com')->is_abs;
+  Mojo::URL->new('http://example.com/test/index.html')->is_abs;
+
+  # False
+  Mojo::URL->new('test/index.html')->is_abs;
+  Mojo::URL->new('/test/index.html')->is_abs;
+  Mojo::URL->new('//example.com/test/index.html')->is_abs;
+
+=head2 new
+
+  my $url = Mojo::URL->new;
+  my $url = Mojo::URL->new('http://127.0.0.1:3000/foo?f=b&baz=2#foo');
+
+Construct a new L<Mojo::URL> object and L</"parse"> URL if necessary.
+
+=head2 parse
+
+  $url = $url->parse('http://127.0.0.1:3000/foo/bar?fo=o&baz=23#foo');
+
+Parse relative or absolute URL.
+
+  # "/test/123"
+  $url->parse('/test/123?foo=bar')->path;
+
+  # "example.com"
+  $url->parse('http://example.com/test/123?foo=bar')->host;
+
+  # "sri@example.com"
+  $url->parse('mailto:sri@example.com')->path;
+
+=head2 password
+
+  my $password = $url->password;
+
+Password part of L</"userinfo">.
+
+  # "s3cret"
+  Mojo::URL->new('http://isabel:s3cret@mojolicious.org')->password;
+
+  # "s:3:c:r:e:t"
+  Mojo::URL->new('http://isabel:s:3:c:r:e:t@mojolicious.org')->password;
+
+=head2 path
+
+  my $path = $url->path;
+  $url     = $url->path('foo/bar');
+  $url     = $url->path('/foo/bar');
+  $url     = $url->path(Mojo::Path->new);
+
+Path part of this URL, relative paths will be merged with
+L<Mojo::Path/"merge">, defaults to a L<Mojo::Path> object.
+
+  # "perldoc"
+  Mojo::URL->new('http://example.com/perldoc/Mojo')->path->parts->[0];
+
+  # "/perldoc/DOM/HTML"
+  Mojo::URL->new('http://example.com/perldoc/Mojo')->path->merge('DOM/HTML');
+
+  # "http://example.com/DOM/HTML"
+  Mojo::URL->new('http://example.com/perldoc/Mojo')->path('/DOM/HTML');
+
+  # "http://example.com/perldoc/DOM/HTML"
+  Mojo::URL->new('http://example.com/perldoc/Mojo')->path('DOM/HTML');
+
+  # "http://example.com/perldoc/Mojo/DOM/HTML"
+  Mojo::URL->new('http://example.com/perldoc/Mojo/')->path('DOM/HTML');
+
+=head2 path_query
+
+  my $path_query = $url->path_query;
+  $url           = $url->path_query('/foo/bar?a=1&b=2');
+
+Normalized version of L</"path"> and L</"query">.
+
+  # "/test?a=1&b=2"
+  Mojo::URL->new('http://example.com/test?a=1&b=2')->path_query;
+
+  # "/"
+  Mojo::URL->new('http://example.com/')->path_query;
+
+=head2 protocol
+
+  my $proto = $url->protocol;
+
+Normalized version of L</"scheme">.
+
+  # "http"
+  Mojo::URL->new('HtTp://example.com')->protocol;
+
+=head2 query
+
+  my $query = $url->query;
+  $url      = $url->query([merge => 'with']);
+  $url      = $url->query({append => 'to'});
+  $url      = $url->query(replace => 'with');
+  $url      = $url->query('a=1&b=2');
+  $url      = $url->query(Mojo::Parameters->new);
+
+Query part of this URL, key/value pairs in an array reference will be merged
+with L<Mojo::Parameters/"merge">, and key/value pairs in a hash reference
+appended with L<Mojo::Parameters/"append">, defaults to a L<Mojo::Parameters>
+object.
+
+  # "2"
+  Mojo::URL->new('http://example.com?a=1&b=2')->query->param('b');
+
+  # "a=2&b=2&c=3"
+  Mojo::URL->new('http://example.com?a=1&b=2')->query->merge(a => 2, c => 3);
+
+  # "http://example.com?a=2&c=3"
+  Mojo::URL->new('http://example.com?a=1&b=2')->query(a => 2, c => 3);
+
+  # "http://example.com?a=2&a=3"
+  Mojo::URL->new('http://example.com?a=1&b=2')->query(a => [2, 3]);
+
+  # "http://example.com?a=2&b=2&c=3"
+  Mojo::URL->new('http://example.com?a=1&b=2')->query([a => 2, c => 3]);
+
+  # "http://example.com?b=2"
+  Mojo::URL->new('http://example.com?a=1&b=2')->query([a => undef]);
+
+  # "http://example.com?a=1&b=2&a=2&c=3"
+  Mojo::URL->new('http://example.com?a=1&b=2')->query({a => 2, c => 3});
+
+=head2 to_abs
+
+  my $abs = $url->to_abs;
+  my $abs = $url->to_abs(Mojo::URL->new('http://example.com/foo'));
+
+Return a new L<Mojo::URL> object cloned from this relative URL and turn it into
+an absolute one using L</"base"> or provided base URL.
+
+  # "http://example.com/foo/baz.xml?test=123"
+  Mojo::URL->new('baz.xml?test=123')
+    ->to_abs(Mojo::URL->new('http://example.com/foo/bar.html'));
+
+  # "http://example.com/baz.xml?test=123"
+  Mojo::URL->new('/baz.xml?test=123')
+    ->to_abs(Mojo::URL->new('http://example.com/foo/bar.html'));
+
+  # "http://example.com/foo/baz.xml?test=123"
+  Mojo::URL->new('//example.com/foo/baz.xml?test=123')
+    ->to_abs(Mojo::URL->new('http://example.com/foo/bar.html'));
+
+=head2 to_string
+
+  my $str = $url->to_string;
+
+Turn URL into a string. Note that L</"userinfo"> will not be included for
+security reasons.
+
+  # "http://mojolicious.org"
+  Mojo::URL->new->scheme('http')->host('mojolicious.org')->to_string;
+
+  # "http://mojolicious.org"
+  Mojo::URL->new('http://daniel:s3cret@mojolicious.org')->to_string;
+
+=head2 to_unsafe_string
+
+  my $str = $url->to_unsafe_string;
+
+Same as L</"to_string">, but includes L</"userinfo">.
+
+  # "http://daniel:s3cret@mojolicious.org"
+  Mojo::URL->new('http://daniel:s3cret@mojolicious.org')->to_unsafe_string;
+
+=head2 username
+
+  my $username = $url->username;
+
+Username part of L</"userinfo">.
+
+  # "isabel"
+  Mojo::URL->new('http://isabel:s3cret@mojolicious.org')->username;
+
+=head1 OPERATORS
+
+L<Mojo::URL> overloads the following operators.
+
+=head2 bool
+
+  my $bool = !!$url;
+
+Always true.
+
+=head2 stringify
+
+  my $str = "$url";
+
+Alias for L</"to_string">.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/Upload.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/Upload.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/Upload.pm	(revision 32205)
@@ -0,0 +1,91 @@
+package Mojo::Upload;
+use Mojo::Base -base;
+
+has [qw(asset filename headers name)];
+
+sub move_to { $_[0]->asset->move_to($_[1]) and return $_[0] }
+
+sub size  { shift->asset->size }
+sub slurp { shift->asset->slurp }
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::Upload - Upload
+
+=head1 SYNOPSIS
+
+  use Mojo::Upload;
+
+  my $upload = Mojo::Upload->new;
+  say $upload->filename;
+  $upload->move_to('/home/sri/foo.txt');
+
+=head1 DESCRIPTION
+
+L<Mojo::Upload> is a container for uploaded files.
+
+=head1 ATTRIBUTES
+
+L<Mojo::Upload> implements the following attributes.
+
+=head2 asset
+
+  my $asset = $upload->asset;
+  $upload   = $upload->asset(Mojo::Asset::File->new);
+
+Asset containing the uploaded data, usually a L<Mojo::Asset::File> or
+L<Mojo::Asset::Memory> object.
+
+=head2 filename
+
+  my $filename = $upload->filename;
+  $upload      = $upload->filename('foo.txt');
+
+Name of the uploaded file.
+
+=head2 headers
+
+  my $headers = $upload->headers;
+  $upload     = $upload->headers(Mojo::Headers->new);
+
+Headers for upload, usually a L<Mojo::Headers> object.
+
+=head2 name
+
+  my $name = $upload->name;
+  $upload  = $upload->name('foo');
+
+Name of the upload.
+
+=head1 METHODS
+
+L<Mojo::Upload> inherits all methods from L<Mojo::Base> and implements the
+following new ones.
+
+=head2 move_to
+
+  $upload = $upload->move_to('/home/sri/foo.txt');
+
+Move uploaded data into a specific file.
+
+=head2 size
+
+  my $size = $upload->size;
+
+Size of uploaded data in bytes.
+
+=head2 slurp
+
+  my $bytes = $upload->slurp;
+
+Read all uploaded data at once.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/UserAgent.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/UserAgent.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/UserAgent.pm	(revision 32205)
@@ -0,0 +1,1079 @@
+package Mojo::UserAgent;
+use Mojo::Base 'Mojo::EventEmitter';
+
+# "Fry: Since when is the Internet about robbing people of their privacy?
+#  Bender: August 6, 1991."
+use Mojo::IOLoop;
+use Mojo::IOLoop::Stream::HTTPClient;
+use Mojo::IOLoop::Stream::WebSocketClient;
+use Mojo::Promise;
+use Mojo::Util 'monkey_patch';
+use Mojo::UserAgent::CookieJar;
+use Mojo::UserAgent::Proxy;
+use Mojo::UserAgent::Server;
+use Mojo::UserAgent::Transactor;
+use Scalar::Util 'weaken';
+
+use constant DEBUG => $ENV{MOJO_CLIENT_DEBUG} || 0;
+
+has ca                 => sub { $ENV{MOJO_CA_FILE} };
+has cert               => sub { $ENV{MOJO_CERT_FILE} };
+has connect_timeout    => sub { $ENV{MOJO_CONNECT_TIMEOUT} || 10 };
+has cookie_jar         => sub { Mojo::UserAgent::CookieJar->new };
+has inactivity_timeout => sub { $ENV{MOJO_INACTIVITY_TIMEOUT} // 20 };
+has insecure           => sub { $ENV{MOJO_INSECURE} };
+has [qw(local_address max_response_size)];
+has ioloop => sub { Mojo::IOLoop->new };
+has key    => sub { $ENV{MOJO_KEY_FILE} };
+has max_connections => 5;
+has max_redirects   => sub { $ENV{MOJO_MAX_REDIRECTS} || 0 };
+has proxy           => sub { Mojo::UserAgent::Proxy->new };
+has request_timeout => sub { $ENV{MOJO_REQUEST_TIMEOUT} // 0 };
+has server => sub { Mojo::UserAgent::Server->new(ioloop => shift->ioloop) };
+has transactor => sub { Mojo::UserAgent::Transactor->new };
+
+# Common HTTP methods
+for my $name (qw(DELETE GET HEAD OPTIONS PATCH POST PUT)) {
+  monkey_patch __PACKAGE__, lc $name, sub {
+    my ($self, $cb) = (shift, ref $_[-1] eq 'CODE' ? pop : undef);
+    return $self->start($self->build_tx($name, @_), $cb);
+  };
+  monkey_patch __PACKAGE__, lc($name) . '_p', sub {
+    my $self = shift;
+    return $self->start_p($self->build_tx($name, @_));
+  };
+}
+
+sub DESTROY { Mojo::Util::_global_destruction() or shift->_cleanup }
+
+sub build_tx           { shift->transactor->tx(@_) }
+sub build_websocket_tx { shift->transactor->websocket(@_) }
+
+sub start {
+  my ($self, $tx, $cb) = @_;
+
+  # Fork-safety
+  $self->_cleanup->server->restart unless ($self->{pid} //= $$) eq $$;
+
+  # Non-blocking
+  if ($cb) {
+    warn "-- Non-blocking request (@{[_url($tx)]})\n" if DEBUG;
+    return $self->_start(Mojo::IOLoop->singleton, $tx, $cb);
+  }
+
+  # Blocking
+  warn "-- Blocking request (@{[_url($tx)]})\n" if DEBUG;
+  $self->_start($self->ioloop, $tx => sub { shift->ioloop->stop; $tx = shift });
+  $self->ioloop->start;
+
+  return $tx;
+}
+
+sub start_p {
+  my ($self, $tx) = @_;
+
+  my $promise = Mojo::Promise->new;
+  $self->start(
+    $tx => sub {
+      my ($self, $tx) = @_;
+      my $err = $tx->error;
+      return $promise->reject($err->{message}) if $err && !$err->{code};
+      return $promise->reject('WebSocket handshake failed')
+        if $tx->req->is_handshake && !$tx->is_websocket;
+      $promise->resolve($tx);
+    }
+  );
+
+  return $promise;
+}
+
+sub websocket {
+  my ($self, $cb) = (shift, pop);
+  $self->start($self->build_websocket_tx(@_), $cb);
+}
+
+sub websocket_p {
+  my $self = shift;
+  return $self->start_p($self->build_websocket_tx(@_));
+}
+
+sub _cleanup {
+  my $self = shift;
+  delete $self->{pid};
+  $self->_error($_, 'Premature connection close')
+    for keys %{$self->{connections} || {}};
+  return $self;
+}
+
+sub _connect {
+  my ($self, $loop, $peer, $tx, $handle, $cb) = @_;
+
+  my $t = $self->transactor;
+  my ($proto, $host, $port) = $peer ? $t->peer($tx) : $t->endpoint($tx);
+
+  my %options = (
+    timeout      => $self->connect_timeout,
+    stream_class => 'Mojo::IOLoop::Stream::HTTPClient'
+  );
+  if ($proto eq 'http+unix') { $options{path} = $host }
+  else                       { @options{qw(address port)} = ($host, $port) }
+  if (my $local = $self->local_address) { $options{local_address} = $local }
+  $options{handle} = $handle if $handle;
+
+  # SOCKS
+  if ($proto eq 'socks') {
+    @options{qw(socks_address socks_port)} = @options{qw(address port)};
+    ($proto, @options{qw(address port)}) = $t->endpoint($tx);
+    my $userinfo = $tx->req->via_proxy(0)->proxy->userinfo;
+    @options{qw(socks_user socks_pass)} = split ':', $userinfo if $userinfo;
+  }
+
+  # TLS
+  if ($options{tls} = $proto eq 'https') {
+    map { $options{"tls_$_"} = $self->$_ } qw(ca cert key);
+    $options{tls_verify} = 0x00 if $self->insecure;
+  }
+
+  weaken $self;
+  my $id;
+  return $id = $loop->client(
+    %options => sub {
+      my ($loop, $err, $stream) = @_;
+
+      # Connection error
+      return unless $self;
+      return $self->_error($id, $err) if $err;
+
+      # Connection established
+      $stream->on(timeout => sub { $self->_error($id, 'Inactivity timeout') });
+      $stream->on(close => sub { $self && $self->_finish($id, 1) });
+      $stream->on(error => sub { $self && $self->_error($id, pop) });
+      $stream->on(upgrade => sub { $self->_upgrade($id, pop) });
+      $self->$cb($id);
+    }
+  );
+}
+
+sub _connect_proxy {
+  my ($self, $loop, $old, $cb) = @_;
+
+  # Start CONNECT request
+  return undef unless my $new = $self->transactor->proxy_connect($old);
+  return $self->_start(
+    ($loop, $new) => sub {
+      my ($self, $tx) = @_;
+
+      # CONNECT failed
+      $old->previous($tx)->req->via_proxy(0);
+      my $id = $tx->connection;
+      if ($tx->error || !$tx->res->is_success || !$tx->keep_alive) {
+        $old->res->error({message => 'Proxy connection failed'});
+        $self->_remove($id) if $id;
+        return $self->$cb($old);
+      }
+
+      # Start real transaction without TLS upgrade
+      return $self->_start($loop, $old->connection($id), $cb)
+        unless $tx->req->url->protocol eq 'https';
+
+      # TLS upgrade before starting the real transaction
+      my $handle = $loop->stream($id)->steal_handle;
+      $self->_remove($id);
+      $id = $self->_connect($loop, 0, $old, $handle,
+        sub { shift->_start($loop, $old->connection($id), $cb) });
+      $self->{connections}{$id} = {cb => $cb, ioloop => $loop, tx => $old};
+    }
+  );
+}
+
+sub _connected {
+  my ($self, $id) = @_;
+
+  my $c      = $self->{connections}{$id};
+  my $stream = $c->{ioloop}->stream($id)->timeout($self->inactivity_timeout)
+    ->request_timeout($self->request_timeout);
+  my $tx = $c->{tx}->connection($id);
+
+  weaken $self;
+  $tx->on(finish => sub { $self->_finish($id) });
+  $stream->process($tx);
+}
+
+sub _connection {
+  my ($self, $loop, $tx, $cb) = @_;
+
+  # Reuse connection
+  my ($proto, $host, $port) = $self->transactor->endpoint($tx);
+  my $id = $tx->connection || $self->_dequeue($loop, "$proto:$host:$port", 1);
+  if ($id) {
+    warn "-- Reusing connection $id ($proto://$host:$port)\n" if DEBUG;
+    @{$self->{connections}{$id}}{qw(cb tx)} = ($cb, $tx);
+    $tx->kept_alive(1) unless $tx->connection;
+    $self->_connected($id);
+    return $id;
+  }
+
+  # CONNECT request to proxy required
+  if (my $id = $self->_connect_proxy($loop, $tx, $cb)) { return $id }
+
+  # New connection
+  $tx->res->error({message => "Unsupported protocol: $proto"})
+    and return $loop->next_tick(sub { $self->$cb($tx) })
+    unless $proto eq 'http' || $proto eq 'https' || $proto eq 'http+unix';
+  $id = $self->_connect($loop, 1, $tx, undef, \&_connected);
+  warn "-- Connect $id ($proto://$host:$port)\n" if DEBUG;
+  $self->{connections}{$id} = {cb => $cb, ioloop => $loop, tx => $tx};
+
+  return $id;
+}
+
+sub _dequeue {
+  my ($self, $loop, $name, $test) = @_;
+
+  my $old = $self->{queue}{$loop} ||= [];
+  my ($found, @new);
+  for my $queued (@$old) {
+    push @new, $queued and next if $found || !grep { $_ eq $name } @$queued;
+
+    # Search for id/name and sort out corrupted connections if necessary
+    next unless my $stream = $loop->stream($queued->[1]);
+    $test && $stream->is_readable ? $stream->close : ($found = $queued->[1]);
+  }
+  @$old = @new;
+
+  return $found;
+}
+
+sub _error {
+  my ($self, $id, $err) = @_;
+  my $tx = $self->{connections}{$id}{tx};
+  $tx->closed->res->finish->error({message => $err}) if $tx;
+  $self->_finish($id, 1);
+}
+
+sub _finish {
+  my ($self, $id, $close) = @_;
+
+  return unless my $c = $self->{connections}{$id};
+  return $self->_reuse($id, $close) unless my $tx = $c->{tx};
+
+  $self->cookie_jar->collect($tx);
+
+  $self->_reuse($id, $close) unless uc $tx->req->method eq 'CONNECT';
+  $c->{cb}($self, $tx) unless $self->_redirect($c, $tx);
+}
+
+sub _redirect {
+  my ($self, $c, $old) = @_;
+  return undef unless my $new = $self->transactor->redirect($old);
+  return undef unless @{$old->redirects} < $self->max_redirects;
+  return $self->_start($c->{ioloop}, $new, delete $c->{cb});
+}
+
+sub _remove {
+  my ($self, $id) = @_;
+  my $c = delete $self->{connections}{$id};
+  $self->_dequeue($c->{ioloop}, $id);
+  $c->{ioloop}->remove($id);
+}
+
+sub _reuse {
+  my ($self, $id, $close) = @_;
+
+  # Connection close
+  my $c   = $self->{connections}{$id};
+  my $tx  = delete $c->{tx};
+  my $max = $self->max_connections;
+  return $self->_remove($id) if $close || !$tx || !$max;
+
+  # Keep connection alive
+  my $queue = $self->{queue}{$c->{ioloop}} ||= [];
+  $self->_remove(shift(@$queue)->[1]) while @$queue && @$queue >= $max;
+  push @$queue, [join(':', $self->transactor->endpoint($tx)), $id];
+}
+
+sub _start {
+  my ($self, $loop, $tx, $cb) = @_;
+
+  # Application serve
+  my $url = $tx->req->url;
+  unless ($url->is_abs) {
+    my $base
+      = $loop == $self->ioloop ? $self->server->url : $self->server->nb_url;
+    $url->scheme($base->scheme)->host($base->host)->port($base->port);
+  }
+
+  $_->prepare($tx) for $self->proxy, $self->cookie_jar;
+  my $max = $self->max_response_size;
+  $tx->res->max_message_size($max) if defined $max;
+
+  $self->emit(start => $tx);
+  return $self->_connection($loop, $tx, $cb);
+}
+
+sub _upgrade {
+  my ($self, $id, $ws) = @_;
+
+  my $c       = delete $self->{connections}{$id};
+  my $loop    = $c->{ioloop};
+  my $timeout = $loop->stream($id)->timeout;
+  my $stream  = $loop->transition($id, 'Mojo::IOLoop::Stream::WebSocketClient');
+  $stream->timeout($timeout);
+
+  weaken $self;
+  $stream->on(timeout => sub { $self->_error($id, 'Inactivity timeout') });
+  $stream->on(close => sub { $self && $self->_remove($id) });
+
+  $self->cookie_jar->collect($ws);
+
+  $c->{cb}($self, $c->{tx} = $ws);
+  $self->{connections}{$id} = $c;
+  $stream->process($ws);
+}
+
+sub _url { shift->req->url->to_abs }
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::UserAgent - Non-blocking I/O HTTP and WebSocket user agent
+
+=head1 SYNOPSIS
+
+  use Mojo::UserAgent;
+
+  # Fine grained response handling (dies on connection errors)
+  my $ua  = Mojo::UserAgent->new;
+  my $res = $ua->get('mojolicious.org/perldoc')->result;
+  if    ($res->is_success)  { say $res->body }
+  elsif ($res->is_error)    { say $res->message }
+  elsif ($res->code == 301) { say $res->headers->location }
+  else                      { say 'Whatever...' }
+
+  # Say hello to the Unicode snowman and include an Accept header
+  say $ua->get('www.â.net?hello=there' => {Accept => '*/*'})->result->body;
+
+  # Extract data from HTML and XML resources with CSS selectors
+  say $ua->get('www.perl.org')->result->dom->at('title')->text;
+
+  # Scrape the latest headlines from a news site
+  say $ua->get('blogs.perl.org')
+    ->result->dom->find('h2 > a')->map('text')->join("\n");
+
+  # IPv6 PUT request with Content-Type header and content
+  my $tx = $ua->put('[::1]:3000' => {'Content-Type' => 'text/plain'} => 'Hi!');
+
+  # Quick JSON API request with Basic authentication
+  my $value = $ua->get('https://sri:t3st@example.com/test.json')->result->json;
+
+  # JSON POST (application/json) with TLS certificate authentication
+  my $tx = $ua->cert('tls.crt')->key('tls.key')
+    ->post('https://example.com' => json => {top => 'secret'});
+
+  # Search DuckDuckGo anonymously through Tor
+  $ua->proxy->http('socks://127.0.0.1:9050');
+  say $ua->get('api.3g2upl4pq6kufc4m.onion/?q=mojolicious&format=json')
+    ->result->json('/Abstract');
+
+  # GET request via UNIX domain socket "/tmp/myapp.sock" (percent encoded slash)
+  say $ua->get('http+unix://%2Ftmp%2Fmyapp.sock/perldoc')->result->body;
+
+  # Follow redirects to download Mojolicious from GitHub
+  $ua->max_redirects(5)
+    ->get('https://www.github.com/kraih/mojo/tarball/master')
+    ->result->content->asset->move_to('/home/sri/mojo.tar.gz');
+
+  # Form POST (application/x-www-form-urlencoded) with manual exception handling
+  my $tx = $ua->post('https://metacpan.org/search' => form => {q => 'mojo'});
+  if (my $res = $tx->success) { say $res->body }
+  else {
+    my $err = $tx->error;
+    die "$err->{code} response: $err->{message}" if $err->{code};
+    die "Connection error: $err->{message}";
+  }
+
+  # Non-blocking request
+  $ua->get('mojolicious.org' => sub {
+    my ($ua, $tx) = @_;
+    say $tx->result->dom->at('title')->text;
+  });
+  Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
+
+  # Concurrent non-blocking requests (synchronized with promises)
+  my $mojo = $ua->get_p('mojolicious.org');
+  my $cpan = $ua->get_p('cpan.org');
+  Mojo::Promise->all($mojo, $cpan)->then(sub {
+    my ($mojo, $cpan) = @_;
+    say $mojo->[0]->result->dom->at('title')->text;
+    say $cpan->[0]->result->dom->at('title')->text;
+  })->wait;
+
+  # WebSocket connection sending and receiving JSON via UNIX domain socket
+  $ua->websocket('ws+unix://%2Ftmp%2Fmyapp.sock/echo.json' => sub {
+    my ($ua, $tx) = @_;
+    say 'WebSocket handshake failed!' and return unless $tx->is_websocket;
+    $tx->on(json => sub {
+      my ($tx, $hash) = @_;
+      say "WebSocket message via JSON: $hash->{msg}";
+      $tx->finish;
+    });
+    $tx->send({json => {msg => 'Hello World!'}});
+  });
+  Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
+
+=head1 DESCRIPTION
+
+L<Mojo::UserAgent> is a full featured non-blocking I/O HTTP and WebSocket user
+agent, with IPv6, TLS, SNI, IDNA, HTTP/SOCKS5 proxy, UNIX domain socket, Comet
+(long polling), Promises/A+, keep-alive, connection pooling, timeout, cookie,
+multipart, gzip compression and multiple event loop support.
+
+All connections will be reset automatically if a new process has been forked,
+this allows multiple processes to share the same L<Mojo::UserAgent> object
+safely.
+
+For better scalability (epoll, kqueue) and to provide non-blocking name
+resolution, SOCKS5 as well as TLS support, the optional modules L<EV> (4.0+),
+L<Net::DNS::Native> (0.15+), L<IO::Socket::Socks> (0.64+) and
+L<IO::Socket::SSL> (2.009+) will be used automatically if possible. Individual
+features can also be disabled with the C<MOJO_NO_NNR>, C<MOJO_NO_SOCKS> and
+C<MOJO_NO_TLS> environment variables.
+
+See L<Mojolicious::Guides::Cookbook/"USER AGENT"> for more.
+
+=head1 EVENTS
+
+L<Mojo::UserAgent> inherits all events from L<Mojo::EventEmitter> and can emit
+the following new ones.
+
+=head2 start
+
+  $ua->on(start => sub {
+    my ($ua, $tx) = @_;
+    ...
+  });
+
+Emitted whenever a new transaction is about to start, this includes
+automatically prepared proxy C<CONNECT> requests and followed redirects.
+
+  $ua->on(start => sub {
+    my ($ua, $tx) = @_;
+    $tx->req->headers->header('X-Bender' => 'Bite my shiny metal ass!');
+  });
+
+=head1 ATTRIBUTES
+
+L<Mojo::UserAgent> implements the following attributes.
+
+=head2 ca
+
+  my $ca = $ua->ca;
+  $ua    = $ua->ca('/etc/tls/ca.crt');
+
+Path to TLS certificate authority file used to verify the peer certificate,
+defaults to the value of the C<MOJO_CA_FILE> environment variable.
+
+  # Show certificate authorities for debugging
+  IO::Socket::SSL::set_defaults(
+    SSL_verify_callback => sub { say "Authority: $_[2]" and return $_[0] });
+
+=head2 cert
+
+  my $cert = $ua->cert;
+  $ua      = $ua->cert('/etc/tls/client.crt');
+
+Path to TLS certificate file, defaults to the value of the C<MOJO_CERT_FILE>
+environment variable.
+
+=head2 connect_timeout
+
+  my $timeout = $ua->connect_timeout;
+  $ua         = $ua->connect_timeout(5);
+
+Maximum amount of time in seconds establishing a connection may take before
+getting canceled, defaults to the value of the C<MOJO_CONNECT_TIMEOUT>
+environment variable or C<10>.
+
+=head2 cookie_jar
+
+  my $cookie_jar = $ua->cookie_jar;
+  $ua            = $ua->cookie_jar(Mojo::UserAgent::CookieJar->new);
+
+Cookie jar to use for requests performed by this user agent, defaults to a
+L<Mojo::UserAgent::CookieJar> object.
+
+  # Ignore all cookies
+  $ua->cookie_jar->ignore(sub { 1 });
+
+  # Ignore cookies for public suffixes
+  my $ps = IO::Socket::SSL::PublicSuffix->default;
+  $ua->cookie_jar->ignore(sub {
+    my $cookie = shift;
+    return undef unless my $domain = $cookie->domain;
+    return ($ps->public_suffix($domain))[0] eq '';
+  });
+
+  # Add custom cookie to the jar
+  $ua->cookie_jar->add(
+    Mojo::Cookie::Response->new(
+      name   => 'foo',
+      value  => 'bar',
+      domain => 'mojolicious.org',
+      path   => '/perldoc'
+    )
+  );
+
+=head2 inactivity_timeout
+
+  my $timeout = $ua->inactivity_timeout;
+  $ua         = $ua->inactivity_timeout(15);
+
+Maximum amount of time in seconds a connection can be inactive before getting
+closed, defaults to the value of the C<MOJO_INACTIVITY_TIMEOUT> environment
+variable or C<20>. Setting the value to C<0> will allow connections to be
+inactive indefinitely.
+
+=head2 insecure
+
+  my $bool = $ua->insecure;
+  $ua      = $ua->insecure($bool);
+
+Do not require a valid TLS certificate to access HTTPS/WSS sites, defaults to
+the value of the C<MOJO_INSECURE> environment variable.
+
+  # Disable TLS certificate verification for testing
+  say $ua->insecure(1)->get('https://127.0.0.1:3000')->result->code;
+
+=head2 ioloop
+
+  my $loop = $ua->ioloop;
+  $ua      = $ua->ioloop(Mojo::IOLoop->new);
+
+Event loop object to use for blocking I/O operations, defaults to a
+L<Mojo::IOLoop> object.
+
+=head2 key
+
+  my $key = $ua->key;
+  $ua     = $ua->key('/etc/tls/client.crt');
+
+Path to TLS key file, defaults to the value of the C<MOJO_KEY_FILE> environment
+variable.
+
+=head2 local_address
+
+  my $address = $ua->local_address;
+  $ua         = $ua->local_address('127.0.0.1');
+
+Local address to bind to.
+
+=head2 max_connections
+
+  my $max = $ua->max_connections;
+  $ua     = $ua->max_connections(5);
+
+Maximum number of keep-alive connections that the user agent will retain before
+it starts closing the oldest ones, defaults to C<5>. Setting the value to C<0>
+will prevent any connections from being kept alive.
+
+=head2 max_redirects
+
+  my $max = $ua->max_redirects;
+  $ua     = $ua->max_redirects(3);
+
+Maximum number of redirects the user agent will follow before it fails,
+defaults to the value of the C<MOJO_MAX_REDIRECTS> environment variable or
+C<0>.
+
+=head2 max_response_size
+
+  my $max = $ua->max_response_size;
+  $ua     = $ua->max_response_size(16777216);
+
+Maximum response size in bytes, defaults to the value of
+L<Mojo::Message::Response/"max_message_size">. Setting the value to C<0> will
+allow responses of indefinite size. Note that increasing this value can also
+drastically increase memory usage, should you for example attempt to parse an
+excessively large response body with the methods L<Mojo::Message/"dom"> or
+L<Mojo::Message/"json">.
+
+=head2 proxy
+
+  my $proxy = $ua->proxy;
+  $ua       = $ua->proxy(Mojo::UserAgent::Proxy->new);
+
+Proxy manager, defaults to a L<Mojo::UserAgent::Proxy> object.
+
+  # Detect proxy servers from environment
+  $ua->proxy->detect;
+
+  # Manually configure HTTP proxy (using CONNECT for HTTPS/WebSockets)
+  $ua->proxy->http('http://127.0.0.1:8080')->https('http://127.0.0.1:8080');
+
+  # Manually configure Tor (SOCKS5)
+  $ua->proxy->http('socks://127.0.0.1:9050')->https('socks://127.0.0.1:9050');
+
+  # Manually configure UNIX domain socket (using CONNECT for HTTPS/WebSockets)
+  $ua->proxy->http('http+unix://%2Ftmp%2Fproxy.sock')
+    ->https('http+unix://%2Ftmp%2Fproxy.sock');
+
+=head2 request_timeout
+
+  my $timeout = $ua->request_timeout;
+  $ua         = $ua->request_timeout(5);
+
+Maximum amount of time in seconds sending the request and receiving a whole
+response may take before getting canceled, defaults to the value of the
+C<MOJO_REQUEST_TIMEOUT> environment variable or C<0>. This does not include
+L</"connect_timeout">. Setting the value to C<0> will allow the user agent to
+wait indefinitely. The timeout will reset for every followed redirect.
+
+  # Allow 3 seconds to establish connection and 5 seconds to process request
+  $ua->max_redirects(0)->connect_timeout(3)->request_timeout(5);
+
+=head2 server
+
+  my $server = $ua->server;
+  $ua        = $ua->server(Mojo::UserAgent::Server->new);
+
+Application server relative URLs will be processed with, defaults to a
+L<Mojo::UserAgent::Server> object.
+
+  # Mock web service
+  $ua->server->app(Mojolicious->new);
+  $ua->server->app->routes->get('/time' => sub {
+    my $c = shift;
+    $c->render(json => {now => time});
+  });
+  my $time = $ua->get('/time')->result->json->{now};
+
+  # Change log level
+  $ua->server->app->log->level('fatal');
+
+  # Port currently used for processing relative URLs blocking
+  say $ua->server->url->port;
+
+  # Port currently used for processing relative URLs non-blocking
+  say $ua->server->nb_url->port;
+
+=head2 transactor
+
+  my $t = $ua->transactor;
+  $ua   = $ua->transactor(Mojo::UserAgent::Transactor->new);
+
+Transaction builder, defaults to a L<Mojo::UserAgent::Transactor> object.
+
+  # Change name of user agent
+  $ua->transactor->name('MyUA 1.0');
+
+=head1 METHODS
+
+L<Mojo::UserAgent> inherits all methods from L<Mojo::EventEmitter> and
+implements the following new ones.
+
+=head2 build_tx
+
+  my $tx = $ua->build_tx(GET => 'example.com');
+  my $tx = $ua->build_tx(
+    PUT => 'http://example.com' => {Accept => '*/*'} => 'Content!');
+  my $tx = $ua->build_tx(
+    PUT => 'http://example.com' => {Accept => '*/*'} => form => {a => 'b'});
+  my $tx = $ua->build_tx(
+    PUT => 'http://example.com' => {Accept => '*/*'} => json => {a => 'b'});
+
+Generate L<Mojo::Transaction::HTTP> object with
+L<Mojo::UserAgent::Transactor/"tx">.
+
+  # Request with custom cookie
+  my $tx = $ua->build_tx(GET => 'https://example.com/account');
+  $tx->req->cookies({name => 'user', value => 'sri'});
+  $tx = $ua->start($tx);
+
+  # Deactivate gzip compression
+  my $tx = $ua->build_tx(GET => 'example.com');
+  $tx->req->headers->remove('Accept-Encoding');
+  $tx = $ua->start($tx);
+
+  # Interrupt response by raising an error
+  my $tx = $ua->build_tx(GET => 'http://example.com');
+  $tx->res->on(progress => sub {
+    my $res = shift;
+    return unless my $server = $res->headers->server;
+    $res->error({message => 'Oh noes, it is IIS!'}) if $server =~ /IIS/;
+  });
+  $tx = $ua->start($tx);
+
+=head2 build_websocket_tx
+
+  my $tx = $ua->build_websocket_tx('ws://example.com');
+  my $tx = $ua->build_websocket_tx(
+    'ws://example.com' => {DNT => 1} => ['v1.proto']);
+
+Generate L<Mojo::Transaction::HTTP> object with
+L<Mojo::UserAgent::Transactor/"websocket">.
+
+  # Custom WebSocket handshake with cookie
+  my $tx = $ua->build_websocket_tx('wss://example.com/echo');
+  $tx->req->cookies({name => 'user', value => 'sri'});
+  $ua->start($tx => sub {
+    my ($ua, $tx) = @_;
+    say 'WebSocket handshake failed!' and return unless $tx->is_websocket;
+    $tx->on(message => sub {
+      my ($tx, $msg) = @_;
+      say "WebSocket message: $msg";
+      $tx->finish;
+    });
+    $tx->send('Hi!');
+  });
+  Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
+
+=head2 delete
+
+  my $tx = $ua->delete('example.com');
+  my $tx = $ua->delete('http://example.com' => {Accept => '*/*'} => 'Content!');
+  my $tx = $ua->delete(
+    'http://example.com' => {Accept => '*/*'} => form => {a => 'b'});
+  my $tx = $ua->delete(
+    'http://example.com' => {Accept => '*/*'} => json => {a => 'b'});
+
+Perform blocking C<DELETE> request and return resulting
+L<Mojo::Transaction::HTTP> object, takes the same arguments as
+L<Mojo::UserAgent::Transactor/"tx"> (except for the C<DELETE> method, which is
+implied). You can also append a callback to perform requests non-blocking.
+
+  $ua->delete('http://example.com' => json => {a => 'b'} => sub {
+    my ($ua, $tx) = @_;
+    say $tx->result->body;
+  });
+  Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
+
+=head2 delete_p
+
+  my $promise = $ua->delete_p('http://example.com');
+
+Same as L</"delete">, but performs all requests non-blocking and returns a
+L<Mojo::Promise> object instead of accepting a callback.
+
+  $ua->delete_p('http://example.com' => json => {a => 'b'})->then(sub {
+    my $tx = shift;
+    say $tx->result->body;
+  })->catch(sub {
+    my $err = shift;
+    warn "Connection error: $err";
+  })->wait;
+
+=head2 get
+
+  my $tx = $ua->get('example.com');
+  my $tx = $ua->get('http://example.com' => {Accept => '*/*'} => 'Content!');
+  my $tx = $ua->get(
+    'http://example.com' => {Accept => '*/*'} => form => {a => 'b'});
+  my $tx = $ua->get(
+    'http://example.com' => {Accept => '*/*'} => json => {a => 'b'});
+
+Perform blocking C<GET> request and return resulting L<Mojo::Transaction::HTTP>
+object, takes the same arguments as L<Mojo::UserAgent::Transactor/"tx"> (except
+for the C<GET> method, which is implied). You can also append a callback to
+perform requests non-blocking.
+
+  $ua->get('http://example.com' => json => {a => 'b'} => sub {
+    my ($ua, $tx) = @_;
+    say $tx->result->body;
+  });
+  Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
+
+=head2 get_p
+
+  my $promise = $ua->get_p('http://example.com');
+
+Same as L</"get">, but performs all requests non-blocking and returns a
+L<Mojo::Promise> object instead of accepting a callback.
+
+  $ua->get_p('http://example.com' => json => {a => 'b'})->then(sub {
+    my $tx = shift;
+    say $tx->result->body;
+  })->catch(sub {
+    my $err = shift;
+    warn "Connection error: $err";
+  })->wait;
+
+=head2 head
+
+  my $tx = $ua->head('example.com');
+  my $tx = $ua->head('http://example.com' => {Accept => '*/*'} => 'Content!');
+  my $tx = $ua->head(
+    'http://example.com' => {Accept => '*/*'} => form => {a => 'b'});
+  my $tx = $ua->head(
+    'http://example.com' => {Accept => '*/*'} => json => {a => 'b'});
+
+Perform blocking C<HEAD> request and return resulting
+L<Mojo::Transaction::HTTP> object, takes the same arguments as
+L<Mojo::UserAgent::Transactor/"tx"> (except for the C<HEAD> method, which is
+implied). You can also append a callback to perform requests non-blocking.
+
+  $ua->head('http://example.com' => json => {a => 'b'} => sub {
+    my ($ua, $tx) = @_;
+    say $tx->result->body;
+  });
+  Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
+
+=head2 head_p
+
+  my $promise = $ua->head_p('http://example.com');
+
+Same as L</"head">, but performs all requests non-blocking and returns a
+L<Mojo::Promise> object instead of accepting a callback.
+
+  $ua->head_p('http://example.com' => json => {a => 'b'})->then(sub {
+    my $tx = shift;
+    say $tx->result->body;
+  })->catch(sub {
+    my $err = shift;
+    warn "Connection error: $err";
+  })->wait;
+
+=head2 options
+
+  my $tx = $ua->options('example.com');
+  my $tx = $ua->options('http://example.com' => {Accept => '*/*'} => 'Content!');
+  my $tx = $ua->options(
+    'http://example.com' => {Accept => '*/*'} => form => {a => 'b'});
+  my $tx = $ua->options(
+    'http://example.com' => {Accept => '*/*'} => json => {a => 'b'});
+
+Perform blocking C<OPTIONS> request and return resulting
+L<Mojo::Transaction::HTTP> object, takes the same arguments as
+L<Mojo::UserAgent::Transactor/"tx"> (except for the C<OPTIONS> method, which is
+implied). You can also append a callback to perform requests non-blocking.
+
+  $ua->options('http://example.com' => json => {a => 'b'} => sub {
+    my ($ua, $tx) = @_;
+    say $tx->result->body;
+  });
+  Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
+
+=head2 options_p
+
+  my $promise = $ua->options_p('http://example.com');
+
+Same as L</"options">, but performs all requests non-blocking and returns a
+L<Mojo::Promise> object instead of accepting a callback.
+
+  $ua->options_p('http://example.com' => json => {a => 'b'})->then(sub {
+    my $tx = shift;
+    say $tx->result->body;
+  })->catch(sub {
+    my $err = shift;
+    warn "Connection error: $err";
+  })->wait;
+
+=head2 patch
+
+  my $tx = $ua->patch('example.com');
+  my $tx = $ua->patch('http://example.com' => {Accept => '*/*'} => 'Content!');
+  my $tx = $ua->patch(
+    'http://example.com' => {Accept => '*/*'} => form => {a => 'b'});
+  my $tx = $ua->patch(
+    'http://example.com' => {Accept => '*/*'} => json => {a => 'b'});
+
+Perform blocking C<PATCH> request and return resulting
+L<Mojo::Transaction::HTTP> object, takes the same arguments as
+L<Mojo::UserAgent::Transactor/"tx"> (except for the C<PATCH> method, which is
+implied). You can also append a callback to perform requests non-blocking.
+
+  $ua->patch('http://example.com' => json => {a => 'b'} => sub {
+    my ($ua, $tx) = @_;
+    say $tx->result->body;
+  });
+  Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
+
+=head2 patch_p
+
+  my $promise = $ua->patch_p('http://example.com');
+
+Same as L</"patch">, but performs all requests non-blocking and returns a
+L<Mojo::Promise> object instead of accepting a callback.
+
+  $ua->patch_p('http://example.com' => json => {a => 'b'})->then(sub {
+    my $tx = shift;
+    say $tx->result->body;
+  })->catch(sub {
+    my $err = shift;
+    warn "Connection error: $err";
+  })->wait;
+
+=head2 post
+
+  my $tx = $ua->post('example.com');
+  my $tx = $ua->post('http://example.com' => {Accept => '*/*'} => 'Content!');
+  my $tx = $ua->post(
+    'http://example.com' => {Accept => '*/*'} => form => {a => 'b'});
+  my $tx = $ua->post(
+    'http://example.com' => {Accept => '*/*'} => json => {a => 'b'});
+
+Perform blocking C<POST> request and return resulting
+L<Mojo::Transaction::HTTP> object, takes the same arguments as
+L<Mojo::UserAgent::Transactor/"tx"> (except for the C<POST> method, which is
+implied). You can also append a callback to perform requests non-blocking.
+
+  $ua->post('http://example.com' => json => {a => 'b'} => sub {
+    my ($ua, $tx) = @_;
+    say $tx->result->body;
+  });
+  Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
+
+=head2 post_p
+
+  my $promise = $ua->post_p('http://example.com');
+
+Same as L</"post">, but performs all requests non-blocking and returns a
+L<Mojo::Promise> object instead of accepting a callback.
+
+  $ua->post_p('http://example.com' => json => {a => 'b'})->then(sub {
+    my $tx = shift;
+    say $tx->result->body;
+  })->catch(sub {
+    my $err = shift;
+    warn "Connection error: $err";
+  })->wait;
+
+=head2 put
+
+  my $tx = $ua->put('example.com');
+  my $tx = $ua->put('http://example.com' => {Accept => '*/*'} => 'Content!');
+  my $tx = $ua->put(
+    'http://example.com' => {Accept => '*/*'} => form => {a => 'b'});
+  my $tx = $ua->put(
+    'http://example.com' => {Accept => '*/*'} => json => {a => 'b'});
+
+Perform blocking C<PUT> request and return resulting L<Mojo::Transaction::HTTP>
+object, takes the same arguments as L<Mojo::UserAgent::Transactor/"tx"> (except
+for the C<PUT> method, which is implied). You can also append a callback to
+perform requests non-blocking.
+
+  $ua->put('http://example.com' => json => {a => 'b'} => sub {
+    my ($ua, $tx) = @_;
+    say $tx->result->body;
+  });
+  Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
+
+=head2 put_p
+
+  my $promise = $ua->put_p('http://example.com');
+
+Same as L</"put">, but performs all requests non-blocking and returns a
+L<Mojo::Promise> object instead of accepting a callback.
+
+  $ua->put_p('http://example.com' => json => {a => 'b'})->then(sub {
+    my $tx = shift;
+    say $tx->result->body;
+  })->catch(sub {
+    my $err = shift;
+    warn "Connection error: $err";
+  })->wait;
+
+=head2 start
+
+  my $tx = $ua->start(Mojo::Transaction::HTTP->new);
+
+Perform blocking request for a custom L<Mojo::Transaction::HTTP> object, which
+can be prepared manually or with L</"build_tx">. You can also append a callback
+to perform requests non-blocking.
+
+  my $tx = $ua->build_tx(GET => 'http://example.com');
+  $ua->start($tx => sub {
+    my ($ua, $tx) = @_;
+    say $tx->result->body;
+  });
+  Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
+
+=head2 start_p
+
+  my $promise = $ua->start_p(Mojo::Transaction::HTTP->new);
+
+Same as L</"start">, but performs all requests non-blocking and returns a
+L<Mojo::Promise> object instead of accepting a callback.
+
+  my $tx = $ua->build_tx(GET => 'http://example.com');
+  $ua->start_p($tx)->then(sub {
+    my $tx = shift;
+    say $tx->result->body;
+  })->catch(sub {
+    my $err = shift;
+    warn "Connection error: $err";
+  })->wait;
+
+=head2 websocket
+
+  $ua->websocket('ws://example.com' => sub {...});
+  $ua->websocket(
+    'ws://example.com' => {DNT => 1} => ['v1.proto'] => sub {...});
+
+Open a non-blocking WebSocket connection with transparent handshake, takes the
+same arguments as L<Mojo::UserAgent::Transactor/"websocket">. The callback will
+receive either a L<Mojo::Transaction::WebSocket> or L<Mojo::Transaction::HTTP>
+object, depending on if the handshake was successful.
+
+  $ua->websocket('wss://example.com/echo' => ['v1.proto'] => sub {
+    my ($ua, $tx) = @_;
+    say 'WebSocket handshake failed!' and return unless $tx->is_websocket;
+    say 'Subprotocol negotiation failed!' and return unless $tx->protocol;
+    $tx->on(finish => sub {
+      my ($tx, $code, $reason) = @_;
+      say "WebSocket closed with status $code.";
+    });
+    $tx->on(message => sub {
+      my ($tx, $msg) = @_;
+      say "WebSocket message: $msg";
+      $tx->finish;
+    });
+    $tx->send('Hi!');
+  });
+  Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
+
+You can activate C<permessage-deflate> compression by setting the
+C<Sec-WebSocket-Extensions> header, this can result in much better performance,
+but also increases memory usage by up to 300KiB per connection.
+
+  $ua->websocket('ws://example.com/foo' => {
+    'Sec-WebSocket-Extensions' => 'permessage-deflate'
+  } => sub {...});
+
+=head2 websocket_p
+
+  my $promise = $ua->websocket_p('ws://example.com');
+
+Same as L</"websocket">, but returns a L<Mojo::Promise> object instead of
+accepting a callback.
+
+  $ua->websocket_p('wss://example.com/echo')->then(sub {
+    my $tx = shift;
+    my $promise = Mojo::Promise->new;
+    $tx->on(finish => sub { $promise->resolve });
+    $tx->on(message => sub {
+      my ($tx, $msg) = @_;
+      say "WebSocket message: $msg";
+      $tx->finish;
+    });
+    $tx->send('Hi!');
+    return $promise;
+  })->catch(sub {
+    my $err = shift;
+    warn "WebSocket error: $err";
+  })->wait;
+
+=head1 DEBUGGING
+
+You can set the C<MOJO_CLIENT_DEBUG> environment variable to get some advanced
+diagnostics information printed to C<STDERR>.
+
+  MOJO_CLIENT_DEBUG=1
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/UserAgent/CookieJar.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/UserAgent/CookieJar.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/UserAgent/CookieJar.pm	(revision 32205)
@@ -0,0 +1,227 @@
+package Mojo::UserAgent::CookieJar;
+use Mojo::Base -base;
+
+use Mojo::Cookie::Request;
+use Mojo::Path;
+use Scalar::Util 'looks_like_number';
+
+has 'ignore';
+has max_cookie_size => 4096;
+
+sub add {
+  my ($self, @cookies) = @_;
+
+  my $size = $self->max_cookie_size;
+  for my $cookie (@cookies) {
+
+    # Convert max age to expires
+    my $age = $cookie->max_age;
+    $cookie->expires($age <= 0 ? 0 : $age + time) if looks_like_number $age;
+
+    # Check cookie size
+    next if length($cookie->value // '') > $size;
+
+    # Replace cookie
+    next unless my $domain = lc($cookie->domain // '');
+    next unless my $path   = $cookie->path;
+    next unless length(my $name = $cookie->name // '');
+    my $jar = $self->{jar}{$domain} ||= [];
+    @$jar = (grep({ _compare($_, $path, $name, $domain) } @$jar), $cookie);
+  }
+
+  return $self;
+}
+
+sub all {
+  my $jar = shift->{jar};
+  return [map { @{$jar->{$_}} } sort keys %$jar];
+}
+
+sub collect {
+  my ($self, $tx) = @_;
+
+  my $url = $tx->req->url;
+  for my $cookie (@{$tx->res->cookies}) {
+
+    # Validate domain
+    my $host = lc $url->ihost;
+    $cookie->domain($host)->host_only(1) unless $cookie->domain;
+    my $domain = lc $cookie->domain;
+    if (my $cb = $self->ignore) { next if $cb->($cookie) }
+    next if $host ne $domain && ($host !~ /\Q.$domain\E$/ || $host =~ /\.\d+$/);
+
+    # Validate path
+    my $path = $cookie->path // $url->path->to_dir->to_abs_string;
+    $path = Mojo::Path->new($path)->trailing_slash(0)->to_abs_string;
+    next unless _path($path, $url->path->to_abs_string);
+    $self->add($cookie->path($path));
+  }
+}
+
+sub empty { delete shift->{jar} }
+
+sub find {
+  my ($self, $url) = @_;
+
+  my @found;
+  my $domain = my $host = lc $url->ihost;
+  my $path = $url->path->to_abs_string;
+  while ($domain) {
+    next unless my $old = $self->{jar}{$domain};
+
+    # Grab cookies
+    my $new = $self->{jar}{$domain} = [];
+    for my $cookie (@$old) {
+      next if $cookie->host_only && $host ne $cookie->domain;
+
+      # Check if cookie has expired
+      if (defined(my $expires = $cookie->expires)) { next if time > $expires }
+      push @$new, $cookie;
+
+      # Taste cookie
+      next if $cookie->secure && $url->protocol ne 'https';
+      next unless _path($cookie->path, $path);
+      my $name  = $cookie->name;
+      my $value = $cookie->value;
+      push @found, Mojo::Cookie::Request->new(name => $name, value => $value);
+    }
+  }
+
+  # Remove another part
+  continue { $domain =~ s/^[^.]*\.*// }
+
+  return \@found;
+}
+
+sub prepare {
+  my ($self, $tx) = @_;
+  return unless keys %{$self->{jar}};
+  my $req = $tx->req;
+  $req->cookies(@{$self->find($req->url)});
+}
+
+sub _compare {
+  my ($cookie, $path, $name, $domain) = @_;
+  return
+       $cookie->path ne $path
+    || $cookie->name ne $name
+    || $cookie->domain ne $domain;
+}
+
+sub _path { $_[0] eq '/' || $_[0] eq $_[1] || index($_[1], "$_[0]/") == 0 }
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::UserAgent::CookieJar - Cookie jar for HTTP user agents
+
+=head1 SYNOPSIS
+
+  use Mojo::UserAgent::CookieJar;
+
+  # Add response cookies
+  my $jar = Mojo::UserAgent::CookieJar->new;
+  $jar->add(
+    Mojo::Cookie::Response->new(
+      name   => 'foo',
+      value  => 'bar',
+      domain => 'localhost',
+      path   => '/test'
+    )
+  );
+
+  # Find request cookies
+  for my $cookie (@{$jar->find(Mojo::URL->new('http://localhost/test'))}) {
+    say $cookie->name;
+    say $cookie->value;
+  }
+
+=head1 DESCRIPTION
+
+L<Mojo::UserAgent::CookieJar> is a minimalistic and relaxed cookie jar used by
+L<Mojo::UserAgent>, based on L<RFC 6265|http://tools.ietf.org/html/rfc6265>.
+
+=head1 ATTRIBUTES
+
+L<Mojo::UserAgent::CookieJar> implements the following attributes.
+
+=head2 ignore
+
+  my $ignore = $jar->ignore;
+  $jar       = $jar->ignore(sub {...});
+
+A callback used to decide if a cookie should be ignored by L</"collect">.
+
+  # Ignore all cookies
+  $jar->ignore(sub { 1 });
+
+  # Ignore cookies for domains "com", "net" and "org"
+  $jar->ignore(sub {
+    my $cookie = shift;
+    return undef unless my $domain = $cookie->domain;
+    return $domain eq 'com' || $domain eq 'net' || $domain eq 'org';
+  });
+
+=head2 max_cookie_size
+
+  my $size = $jar->max_cookie_size;
+  $jar     = $jar->max_cookie_size(4096);
+
+Maximum cookie size in bytes, defaults to C<4096> (4KiB).
+
+=head1 METHODS
+
+L<Mojo::UserAgent::CookieJar> inherits all methods from L<Mojo::Base> and
+implements the following new ones.
+
+=head2 add
+
+  $jar = $jar->add(@cookies);
+
+Add multiple L<Mojo::Cookie::Response> objects to the jar.
+
+=head2 all
+
+  my $cookies = $jar->all;
+
+Return all L<Mojo::Cookie::Response> objects that are currently stored in the
+jar.
+
+  # Names of all cookies
+  say $_->name for @{$jar->all};
+
+=head2 collect
+
+  $jar->collect(Mojo::Transaction::HTTP->new);
+
+Collect response cookies from transaction.
+
+=head2 empty
+
+  $jar->empty;
+
+Empty the jar.
+
+=head2 find
+
+  my $cookies = $jar->find(Mojo::URL->new);
+
+Find L<Mojo::Cookie::Request> objects in the jar for L<Mojo::URL> object.
+
+  # Names of all cookies found
+  say $_->name for @{$jar->find(Mojo::URL->new('http://example.com/foo'))};
+
+=head2 prepare
+
+  $jar->prepare(Mojo::Transaction::HTTP->new);
+
+Prepare request cookies for transaction.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/UserAgent/Proxy.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/UserAgent/Proxy.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/UserAgent/Proxy.pm	(revision 32205)
@@ -0,0 +1,111 @@
+package Mojo::UserAgent::Proxy;
+use Mojo::Base -base;
+
+use Mojo::URL;
+
+has [qw(http https not)];
+
+sub detect {
+  my $self = shift;
+  $self->http($ENV{HTTP_PROXY}   || $ENV{http_proxy});
+  $self->https($ENV{HTTPS_PROXY} || $ENV{https_proxy});
+  return $self->not([split ',', $ENV{NO_PROXY} || $ENV{no_proxy} || '']);
+}
+
+sub is_needed {
+  !grep { $_[1] =~ /\Q$_\E$/ } @{$_[0]->not || []};
+}
+
+sub prepare {
+  my ($self, $tx) = @_;
+
+  $self->detect if $ENV{MOJO_PROXY};
+  my $req = $tx->req;
+  my $url = $req->url;
+  return unless $self->is_needed($url->host);
+
+  # HTTP proxy
+  my $proto = $url->protocol;
+  my $http  = $self->http;
+  $req->proxy(Mojo::URL->new($http)) if $http && $proto eq 'http';
+
+  # HTTPS proxy
+  my $https = $self->https;
+  $req->proxy(Mojo::URL->new($https)) if $https && $proto eq 'https';
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::UserAgent::Proxy - User agent proxy manager
+
+=head1 SYNOPSIS
+
+  use Mojo::UserAgent::Proxy;
+
+  my $proxy = Mojo::UserAgent::Proxy->new;
+  $proxy->detect;
+  say $proxy->http;
+
+=head1 DESCRIPTION
+
+L<Mojo::UserAgent::Proxy> manages proxy servers for L<Mojo::UserAgent>.
+
+=head1 ATTRIBUTES
+
+L<Mojo::UserAgent::Proxy> implements the following attributes.
+
+=head2 http
+
+  my $http = $proxy->http;
+  $proxy   = $proxy->http('socks://sri:secret@127.0.0.1:8080');
+
+Proxy server to use for HTTP and WebSocket requests.
+
+=head2 https
+
+  my $https = $proxy->https;
+  $proxy    = $proxy->https('http://sri:secret@127.0.0.1:8080');
+
+Proxy server to use for HTTPS and WebSocket requests.
+
+=head2 not
+
+  my $not = $proxy->not;
+  $proxy  = $proxy->not(['localhost', 'intranet.mojolicious.org']);
+
+Domains that don't require a proxy server to be used.
+
+=head1 METHODS
+
+L<Mojo::UserAgent::Proxy> inherits all methods from L<Mojo::Base> and
+implements the following new ones.
+
+=head2 detect
+
+  $proxy = $proxy->detect;
+
+Check environment variables C<HTTP_PROXY>, C<http_proxy>, C<HTTPS_PROXY>,
+C<https_proxy>, C<NO_PROXY> and C<no_proxy> for proxy information. Automatic
+proxy detection can be enabled with the C<MOJO_PROXY> environment variable.
+
+=head2 is_needed
+
+  my $bool = $proxy->is_needed('intranet.example.com');
+
+Check if request for domain would use a proxy server.
+
+=head2 prepare
+
+  $proxy->prepare(Mojo::Transaction::HTTP->new);
+
+Prepare proxy server information for transaction.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/UserAgent/Server.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/UserAgent/Server.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/UserAgent/Server.pm	(revision 32205)
@@ -0,0 +1,133 @@
+package Mojo::UserAgent::Server;
+use Mojo::Base -base;
+
+use Mojo::IOLoop;
+use Mojo::Server::Daemon;
+use Scalar::Util 'weaken';
+
+has ioloop => sub { Mojo::IOLoop->singleton };
+
+sub app {
+  my ($self, $app) = @_;
+
+  # Singleton application
+  state $singleton;
+  return $singleton = $app ? $app : $singleton unless ref $self;
+
+  # Default to singleton application
+  return $self->{app} || $singleton unless $app;
+  $self->{app} = $app;
+  return $self;
+}
+
+sub nb_url { shift->_url(1, @_) }
+
+sub restart { delete @{$_[0]}{qw(nb_port nb_server port server)} }
+
+sub url { shift->_url(0, @_) }
+
+sub _url {
+  my ($self, $nb, $proto) = @_;
+
+  if (!$self->{server} || $proto) {
+    $proto = $self->{proto} = $proto || 'http';
+
+    # Blocking
+    my $server = $self->{server}
+      = Mojo::Server::Daemon->new(ioloop => $self->ioloop, silent => 1);
+    weaken $server->app($self->app)->{app};
+    my $port = $self->{port} ? ":$self->{port}" : '';
+    $self->{port}
+      = $server->listen(["$proto://127.0.0.1$port"])->start->ports->[0];
+
+    # Non-blocking
+    $server = $self->{nb_server} = Mojo::Server::Daemon->new(silent => 1);
+    weaken $server->app($self->app)->{app};
+    $port = $self->{nb_port} ? ":$self->{nb_port}" : '';
+    $self->{nb_port}
+      = $server->listen(["$proto://127.0.0.1$port"])->start->ports->[0];
+  }
+
+  my $port = $nb ? $self->{nb_port} : $self->{port};
+  return Mojo::URL->new("$self->{proto}://127.0.0.1:$port/");
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::UserAgent::Server - Application server
+
+=head1 SYNOPSIS
+
+  use Mojo::UserAgent::Server;
+
+  my $server = Mojo::UserAgent::Server->new;
+  say $server->url;
+
+=head1 DESCRIPTION
+
+L<Mojo::UserAgent::Server> is an embedded web server based on
+L<Mojo::Server::Daemon> that processes requests for L<Mojo::UserAgent>.
+
+=head1 ATTRIBUTES
+
+L<Mojo::UserAgent::Server> implements the following attributes.
+
+=head2 ioloop
+
+  my $loop = $server->ioloop;
+  $server  = $server->ioloop(Mojo::IOLoop->new);
+
+Event loop object to use for I/O operations, defaults to the global
+L<Mojo::IOLoop> singleton.
+
+=head1 METHODS
+
+L<Mojo::UserAgent::Server> inherits all methods from L<Mojo::Base> and
+implements the following new ones.
+
+=head2 app
+
+  my $app = Mojo::UserAgent::Server->app;
+            Mojo::UserAgent::Server->app(Mojolicious->new);
+  my $app = $server->app;
+  $server = $server->app(Mojolicious->new);
+
+Application this server handles, instance specific applications override the
+global default.
+
+  # Change application behavior
+  $server->app->defaults(testing => 'oh yea!');
+
+=head2 nb_url
+
+  my $url = $ua->nb_url;
+  my $url = $ua->nb_url('http');
+  my $url = $ua->nb_url('https');
+
+Get absolute L<Mojo::URL> object for server processing non-blocking requests
+with L</"app"> and switch protocol if necessary.
+
+=head2 restart
+
+  $server->restart;
+
+Restart server with new port.
+
+=head2 url
+
+  my $url = $ua->url;
+  my $url = $ua->url('http');
+  my $url = $ua->url('https');
+
+Get absolute L<Mojo::URL> object for server processing blocking requests with
+L</"app"> and switch protocol if necessary.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/UserAgent/Transactor.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/UserAgent/Transactor.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/UserAgent/Transactor.pm	(revision 32205)
@@ -0,0 +1,553 @@
+package Mojo::UserAgent::Transactor;
+use Mojo::Base -base;
+
+use Mojo::Asset::File;
+use Mojo::Asset::Memory;
+use Mojo::Content::MultiPart;
+use Mojo::Content::Single;
+use Mojo::File 'path';
+use Mojo::JSON 'encode_json';
+use Mojo::Parameters;
+use Mojo::Transaction::HTTP;
+use Mojo::Transaction::WebSocket;
+use Mojo::URL;
+use Mojo::Util qw(encode url_escape);
+use Mojo::WebSocket qw(challenge client_handshake);
+
+has generators =>
+  sub { {form => \&_form, json => \&_json, multipart => \&_multipart} };
+has name => 'Mojolicious (Perl)';
+
+sub add_generator { $_[0]->generators->{$_[1]} = $_[2] and return $_[0] }
+
+sub endpoint {
+  my ($self, $tx) = @_;
+
+  # Basic endpoint
+  my $req   = $tx->req;
+  my $url   = $req->url;
+  my $proto = $url->protocol || 'http';
+  my $host  = $url->ihost;
+  my $port  = $url->port // ($proto eq 'https' ? 443 : 80);
+
+  # Proxy for normal HTTP requests
+  my $socks;
+  if (my $proxy = $req->proxy) { $socks = $proxy->protocol eq 'socks' }
+  return _proxy($tx, $proto, $host, $port)
+    if $proto eq 'http' && !$req->is_handshake && !$socks;
+
+  return $proto, $host, $port;
+}
+
+sub peer { _proxy($_[1], $_[0]->endpoint($_[1])) }
+
+sub proxy_connect {
+  my ($self, $old) = @_;
+
+  # Already a CONNECT request
+  my $req = $old->req;
+  return undef if uc $req->method eq 'CONNECT';
+
+  # No proxy
+  return undef unless (my $proxy = $req->proxy) && $req->via_proxy;
+  return undef if $proxy->protocol eq 'socks';
+
+  # WebSocket and/or HTTPS
+  my $url = $req->url;
+  return undef unless $req->is_handshake || $url->protocol eq 'https';
+
+  # CONNECT request (expect a bad response)
+  my $new = $self->tx(CONNECT => $url->clone->userinfo(undef));
+  $new->req->proxy($proxy);
+  $new->res->content->auto_relax(0)->headers->connection('keep-alive');
+
+  return $new;
+}
+
+sub redirect {
+  my ($self, $old) = @_;
+
+  # Commonly used codes
+  my $res  = $old->res;
+  my $code = $res->code // 0;
+  return undef unless grep { $_ == $code } 301, 302, 303, 307, 308;
+
+  # CONNECT requests cannot be redirected
+  my $req = $old->req;
+  return undef if uc $req->method eq 'CONNECT';
+
+  # Fix location without authority and/or scheme
+  return undef
+    unless my $location = $res->headers->every_header('Location')->[0];
+  $location = Mojo::URL->new($location);
+  $location = $location->base($req->url)->to_abs unless $location->is_abs;
+  my $proto = $location->protocol;
+  return undef if ($proto ne 'http' && $proto ne 'https') || !$location->host;
+
+  # Clone request if necessary
+  my $new = Mojo::Transaction::HTTP->new;
+  if ($code == 307 || $code == 308) {
+    return undef unless my $clone = $req->clone;
+    $new->req($clone);
+  }
+  else {
+    my $m = uc $req->method;
+    my $headers = $new->req->method($code == 303 || $m eq 'POST' ? 'GET' : $m)
+      ->content->headers($req->headers->clone)->headers;
+    $headers->remove($_) for grep {/^content-/i} @{$headers->names};
+  }
+  my $headers = $new->req->url($location)->headers;
+  $headers->remove($_) for qw(Authorization Cookie Host Referer);
+  return $new->previous($old);
+}
+
+sub tx {
+  my ($self, $method, $url) = (shift, shift, shift);
+
+  # Method and URL
+  my $tx  = Mojo::Transaction::HTTP->new;
+  my $req = $tx->req->method($method);
+  if   (ref $url) { $req->url($url) }
+  else            { $req->url->parse($url =~ m!^/|://! ? $url : "http://$url") }
+
+  # Headers (we identify ourselves and accept gzip compression)
+  my $headers = $req->headers;
+  $headers->from_hash(shift) if ref $_[0] eq 'HASH';
+  $headers->user_agent($self->name) unless $headers->user_agent;
+  $headers->accept_encoding('gzip') unless $headers->accept_encoding;
+
+  # Generator
+  if (@_ > 1) {
+    my $cb = $self->generators->{shift()};
+    $self->$cb($tx, @_);
+  }
+
+  # Body
+  elsif (@_) { $req->body(shift) }
+
+  return $tx;
+}
+
+sub upgrade {
+  my ($self, $tx) = @_;
+  my $code = $tx->res->code // 0;
+  return undef unless $tx->req->is_handshake && $code == 101;
+  my $ws = Mojo::Transaction::WebSocket->new(handshake => $tx, masked => 1);
+  return challenge($ws) ? $ws->established(1) : undef;
+}
+
+sub websocket {
+  my $self = shift;
+
+  # New WebSocket transaction
+  my $sub = ref $_[-1] eq 'ARRAY' ? pop : [];
+  my $tx = $self->tx(GET => @_);
+  my $req = $tx->req;
+  $req->headers->sec_websocket_protocol(join ', ', @$sub) if @$sub;
+
+  # Handshake protocol
+  my $url   = $req->url;
+  my $proto = $url->protocol // '';
+  if    ($proto eq 'ws')      { $url->scheme('http') }
+  elsif ($proto eq 'wss')     { $url->scheme('https') }
+  elsif ($proto eq 'ws+unix') { $url->scheme('http+unix') }
+
+  return client_handshake $tx;
+}
+
+sub _content { Mojo::Content::MultiPart->new(headers => $_[0], parts => $_[1]) }
+
+sub _form {
+  my ($self, $tx, $form, %options) = @_;
+  $options{charset} = 'UTF-8' unless exists $options{charset};
+
+  # Check for uploads and force multipart if necessary
+  my $req       = $tx->req;
+  my $headers   = $req->headers;
+  my $multipart = ($headers->content_type // '') =~ m!multipart/form-data!i;
+  for my $value (map { ref $_ eq 'ARRAY' ? @$_ : $_ } values %$form) {
+    ++$multipart and last if ref $value eq 'HASH';
+  }
+
+  # Multipart
+  if ($multipart) {
+    $req->content(_content($headers, _form_parts($options{charset}, $form)));
+    _type($headers, 'multipart/form-data');
+    return $tx;
+  }
+
+  # Query parameters or urlencoded
+  my $method = uc $req->method;
+  my @form = map { $_ => $form->{$_} } sort keys %$form;
+  if ($method eq 'GET' || $method eq 'HEAD') { $req->url->query->merge(@form) }
+  else {
+    $req->body(
+      Mojo::Parameters->new(@form)->charset($options{charset})->to_string);
+    _type($headers, 'application/x-www-form-urlencoded');
+  }
+  return $tx;
+}
+
+sub _form_parts {
+  my ($charset, $form) = @_;
+
+  my @parts;
+  for my $name (sort keys %$form) {
+    next unless defined(my $values = $form->{$name});
+    $values = [$values] unless ref $values eq 'ARRAY';
+    push @parts, @{_parts($charset, $name, $values)};
+  }
+
+  return \@parts;
+}
+
+sub _json {
+  my ($self, $tx, $data) = @_;
+  _type($tx->req->body(encode_json $data)->headers, 'application/json');
+  return $tx;
+}
+
+sub _multipart {
+  my ($self, $tx, $parts) = @_;
+  my $req = $tx->req;
+  $req->content(_content($req->headers, _parts(undef, undef, $parts)));
+  return $tx;
+}
+
+sub _parts {
+  my ($charset, $name, $values) = @_;
+
+  my @parts;
+  for my $value (@$values) {
+    push @parts, my $part = Mojo::Content::Single->new;
+
+    my $filename;
+    my $headers = $part->headers;
+    if (ref $value eq 'HASH') {
+
+      # File
+      if (my $file = delete $value->{file}) {
+        $file = Mojo::Asset::File->new(path => $file) unless ref $file;
+        $part->asset($file);
+        $value->{filename} //= path($file->path)->basename
+          if $file->isa('Mojo::Asset::File');
+      }
+
+      # Memory
+      elsif (defined(my $content = delete $value->{content})) {
+        $part->asset(Mojo::Asset::Memory->new->add_chunk($content));
+      }
+
+      # Filename and headers
+      $filename = delete $value->{filename};
+      $headers->from_hash($value);
+      next unless defined $name;
+      $filename = url_escape $filename // $name, '"';
+      $filename = encode $charset, $filename if $charset;
+    }
+
+    # Field
+    else {
+      $value = encode $charset, $value if $charset;
+      $part->asset(Mojo::Asset::Memory->new->add_chunk($value));
+    }
+
+    # Content-Disposition
+    next unless defined $name;
+    $name = url_escape $name, '"';
+    $name = encode $charset, $name if $charset;
+    my $disposition = qq{form-data; name="$name"};
+    $disposition .= qq{; filename="$filename"} if defined $filename;
+    $headers->content_disposition($disposition);
+  }
+
+  return \@parts;
+}
+
+sub _proxy {
+  my ($tx, $proto, $host, $port) = @_;
+
+  my $req = $tx->req;
+  if ($req->via_proxy && (my $proxy = $req->proxy)) {
+    return $proxy->protocol, $proxy->ihost,
+      $proxy->port // ($proto eq 'https' ? 443 : 80);
+  }
+
+  return $proto, $host, $port;
+}
+
+sub _type { $_[0]->content_type($_[1]) unless $_[0]->content_type }
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::UserAgent::Transactor - User agent transactor
+
+=head1 SYNOPSIS
+
+  use Mojo::UserAgent::Transactor;
+
+  # GET request with Accept header
+  my $t = Mojo::UserAgent::Transactor->new;
+  say $t->tx(GET => 'http://example.com' => {Accept => '*/*'})->req->to_string;
+
+  # POST request with form-data
+  say $t->tx(POST => 'example.com' => form => {a => 'b'})->req->to_string;
+
+  # PUT request with JSON data
+  say $t->tx(PUT => 'example.com' => json => {a => 'b'})->req->to_string;
+
+=head1 DESCRIPTION
+
+L<Mojo::UserAgent::Transactor> is the transaction building and manipulation
+framework used by L<Mojo::UserAgent>.
+
+=head1 GENERATORS
+
+These content generators are available by default.
+
+=head2 form
+
+  $t->tx(POST => 'http://example.com' => form => {a => 'b'});
+
+Generate query string, C<application/x-www-form-urlencoded> or
+C<multipart/form-data> content. See L</"tx"> for more.
+
+=head2 json
+
+  $t->tx(PATCH => 'http://example.com' => json => {a => 'b'});
+
+Generate JSON content with L<Mojo::JSON>. See L</"tx"> for more.
+
+=head2 multipart
+
+  $t->tx(PUT => 'http://example.com' => multipart => ['Hello', 'World!']);
+
+Generate multipart content. See L</"tx"> for more.
+
+=head1 ATTRIBUTES
+
+L<Mojo::UserAgent::Transactor> implements the following attributes.
+
+=head2 generators
+
+  my $generators = $t->generators;
+  $t             = $t->generators({foo => sub {...}});
+
+Registered content generators, by default only C<form>, C<json> and C<multipart>
+are already defined.
+
+=head2 name
+
+  my $name = $t->name;
+  $t       = $t->name('Mojolicious');
+
+Value for C<User-Agent> request header of generated transactions, defaults to
+C<Mojolicious (Perl)>.
+
+=head1 METHODS
+
+L<Mojo::UserAgent::Transactor> inherits all methods from L<Mojo::Base> and
+implements the following new ones.
+
+=head2 add_generator
+
+  $t = $t->add_generator(foo => sub {...});
+
+Register a content generator.
+
+  $t->add_generator(foo => sub {
+    my ($t, $tx, @args) = @_;
+    ...
+  });
+
+=head2 endpoint
+
+  my ($proto, $host, $port) = $t->endpoint(Mojo::Transaction::HTTP->new);
+
+Actual endpoint for transaction.
+
+=head2 peer
+
+  my ($proto, $host, $port) = $t->peer(Mojo::Transaction::HTTP->new);
+
+Actual peer for transaction.
+
+=head2 proxy_connect
+
+  my $tx = $t->proxy_connect(Mojo::Transaction::HTTP->new);
+
+Build L<Mojo::Transaction::HTTP> proxy C<CONNECT> request for transaction if
+possible.
+
+=head2 redirect
+
+  my $tx = $t->redirect(Mojo::Transaction::HTTP->new);
+
+Build L<Mojo::Transaction::HTTP> follow-up request for C<301>, C<302>, C<303>,
+C<307> or C<308> redirect response if possible.
+
+=head2 tx
+
+  my $tx = $t->tx(GET  => 'example.com');
+  my $tx = $t->tx(POST => 'http://example.com');
+  my $tx = $t->tx(GET  => 'http://example.com' => {Accept => '*/*'});
+  my $tx = $t->tx(PUT  => 'http://example.com' => 'Content!');
+  my $tx = $t->tx(PUT  => 'http://example.com' => form => {a => 'b'});
+  my $tx = $t->tx(PUT  => 'http://example.com' => json => {a => 'b'});
+  my $tx = $t->tx(PUT  => 'https://example.com' => multipart => ['a', 'b']);
+  my $tx = $t->tx(POST => 'example.com' => {Accept => '*/*'} => 'Content!');
+  my $tx = $t->tx(
+    PUT => 'example.com' => {Accept => '*/*'} => form => {a => 'b'});
+  my $tx = $t->tx(
+    PUT => 'example.com' => {Accept => '*/*'} => json => {a => 'b'});
+  my $tx = $t->tx(
+    PUT => 'example.com' => {Accept => '*/*'} => multipart => ['a', 'b']);
+
+Versatile general purpose L<Mojo::Transaction::HTTP> transaction builder for
+requests, with support for L</"GENERATORS">.
+
+  # Generate and inspect custom GET request with DNT header and content
+  say $t->tx(GET => 'example.com' => {DNT => 1} => 'Bye!')->req->to_string;
+
+  # Stream response content to STDOUT
+  my $tx = $t->tx(GET => 'http://example.com');
+  $tx->res->content->unsubscribe('read')->on(read => sub { say $_[1] });
+
+  # PUT request with content streamed from file
+  my $tx = $t->tx(PUT => 'http://example.com');
+  $tx->req->content->asset(Mojo::Asset::File->new(path => '/foo.txt'));
+
+The C<json> content generator uses L<Mojo::JSON> for encoding and sets the
+content type to C<application/json>.
+
+  # POST request with "application/json" content
+  my $tx = $t->tx(
+    POST => 'http://example.com' => json => {a => 'b', c => [1, 2, 3]});
+
+The C<form> content generator will automatically use query parameters for
+C<GET> and C<HEAD> requests.
+
+  # GET request with query parameters
+  my $tx = $t->tx(GET => 'http://example.com' => form => {a => 'b'});
+
+For all other request methods the C<application/x-www-form-urlencoded> content
+type is used.
+
+  # POST request with "application/x-www-form-urlencoded" content
+  my $tx = $t->tx(
+    POST => 'http://example.com' => form => {a => 'b', c => 'd'});
+
+Parameters may be encoded with the C<charset> option.
+
+  # PUT request with Shift_JIS encoded form values
+  my $tx = $t->tx(
+    PUT => 'example.com' => form => {a => 'b'} => charset => 'Shift_JIS');
+
+An array reference can be used for multiple form values sharing the same name.
+
+  # POST request with form values sharing the same name
+  my $tx = $t->tx(
+    POST => 'http://example.com' => form => {a => ['b', 'c', 'd']});
+
+A hash reference with a C<content> or C<file> value can be used to switch to
+the C<multipart/form-data> content type for file uploads.
+
+  # POST request with "multipart/form-data" content
+  my $tx = $t->tx(
+    POST => 'http://example.com' => form => {mytext => {content => 'lala'}});
+
+  # POST request with multiple files sharing the same name
+  my $tx = $t->tx(POST => 'http://example.com' =>
+    form => {mytext => [{content => 'first'}, {content => 'second'}]});
+
+The C<file> value should contain the path to the file you want to upload or an
+asset object, like L<Mojo::Asset::File> or L<Mojo::Asset::Memory>.
+
+  # POST request with upload streamed from file
+  my $tx = $t->tx(
+    POST => 'http://example.com' => form => {mytext => {file => '/foo.txt'}});
+
+  # POST request with upload streamed from asset
+  my $asset = Mojo::Asset::Memory->new->add_chunk('lalala');
+  my $tx    = $t->tx(
+    POST => 'http://example.com' => form => {mytext => {file => $asset}});
+
+A C<filename> value will be generated automatically, but can also be set
+manually if necessary. All remaining values in the hash reference get merged
+into the C<multipart/form-data> content as headers.
+
+  # POST request with form values and customized upload (filename and header)
+  my $tx = $t->tx(POST => 'http://example.com' => form => {
+    a      => 'b',
+    c      => 'd',
+    mytext => {
+      content        => 'lalala',
+      filename       => 'foo.txt',
+      'Content-Type' => 'text/plain'
+    }
+  });
+
+The C<multipart/form-data> content type can also be enforced by setting the
+C<Content-Type> header manually.
+
+  # Force "multipart/form-data"
+  my $headers = {'Content-Type' => 'multipart/form-data'};
+  my $tx = $t->tx(POST => 'example.com' => $headers => form => {a => 'b'});
+
+The C<multipart> content generator can be used to build custom multipart
+requests and does not set a content type.
+
+  # POST request with multipart content ("foo" and "bar")
+  my $tx = $t->tx(POST => 'http://example.com' => multipart => ['foo', 'bar']);
+
+Similar to the C<form> content generator you can also pass hash references with
+C<content> or C<file> values, as well as headers.
+
+  # POST request with multipart content streamed from file
+  my $tx = $t->tx(
+    POST => 'http://example.com' => multipart => [{file => '/foo.txt'}]);
+
+  # PUT request with multipart content streamed from asset
+  my $headers = {'Content-Type' => 'multipart/custom'};
+  my $asset   = Mojo::Asset::Memory->new->add_chunk('lalala');
+  my $tx      = $t->tx(
+    PUT => 'http://example.com' => $headers => multipart => [{file => $asset}]);
+
+  # POST request with multipart content and custom headers
+  my $tx = $t->tx(POST => 'http://example.com' => multipart => [
+    {
+      content            => 'Hello',
+      'Content-Type'     => 'text/plain',
+      'Content-Language' => 'en-US'
+    },
+    {
+      content            => 'World!',
+      'Content-Type'     => 'text/plain',
+      'Content-Language' => 'en-US'
+    }
+  ]);
+
+=head2 upgrade
+
+  my $tx = $t->upgrade(Mojo::Transaction::HTTP->new);
+
+Build L<Mojo::Transaction::WebSocket> follow-up transaction for WebSocket
+handshake if possible.
+
+=head2 websocket
+
+  my $tx = $t->websocket('ws://example.com');
+  my $tx = $t->websocket('ws://example.com' => {DNT => 1} => ['v1.proto']);
+
+Versatile L<Mojo::Transaction::HTTP> transaction builder for WebSocket
+handshake requests.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojo/Util.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/Util.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/Util.pm	(revision 32205)
@@ -0,0 +1,3129 @@
+package Mojo::Util;
+use Mojo::Base -strict;
+
+use Carp qw(carp croak);
+use Data::Dumper ();
+use Digest::MD5 qw(md5 md5_hex);
+use Digest::SHA qw(hmac_sha1_hex sha1 sha1_hex);
+use Encode 'find_encoding';
+use Exporter 'import';
+use Getopt::Long 'GetOptionsFromArray';
+use IO::Poll qw(POLLIN POLLPRI);
+use List::Util 'min';
+use MIME::Base64 qw(decode_base64 encode_base64);
+use Pod::Usage 'pod2usage';
+use Symbol 'delete_package';
+use Time::HiRes        ();
+use Unicode::Normalize ();
+
+# Check for monotonic clock support
+use constant MONOTONIC =>
+  eval { !!Time::HiRes::clock_gettime(Time::HiRes::CLOCK_MONOTONIC()) };
+
+# Punycode bootstring parameters
+use constant {
+  PC_BASE         => 36,
+  PC_TMIN         => 1,
+  PC_TMAX         => 26,
+  PC_SKEW         => 38,
+  PC_DAMP         => 700,
+  PC_INITIAL_BIAS => 72,
+  PC_INITIAL_N    => 128
+};
+
+# Supported on Perl 5.22+
+my $NAME
+  = eval { require Sub::Util; Sub::Util->can('set_subname') } || sub { $_[1] };
+
+# To generate a new HTML entity table run this command
+# perl examples/entities.pl
+my %ENTITIES;
+for my $line (split "\n", join('', <DATA>)) {
+  next unless $line =~ /^(\S+)\s+U\+(\S+)(?:\s+U\+(\S+))?/;
+  $ENTITIES{$1} = defined $3 ? (chr(hex $2) . chr(hex $3)) : chr(hex $2);
+}
+close DATA;
+
+# Characters that should be escaped in XML
+my %XML = (
+  '&'  => '&amp;',
+  '<'  => '&lt;',
+  '>'  => '&gt;',
+  '"'  => '&quot;',
+  '\'' => '&#39;'
+);
+
+# "Sun, 06 Nov 1994 08:49:37 GMT" and "Sunday, 06-Nov-94 08:49:37 GMT"
+my $EXPIRES_RE = qr/(\w+\W+\d+\W+\w+\W+\d+\W+\d+:\d+:\d+\W*\w+)/;
+
+# HTML entities
+my $ENTITY_RE = qr/&(?:\#((?:[0-9]{1,7}|x[0-9a-fA-F]{1,6}));|(\w+[;=]?))/;
+
+# Encoding and pattern cache
+my (%ENCODING, %PATTERN);
+
+our @EXPORT_OK = (
+  qw(b64_decode b64_encode camelize class_to_file class_to_path decamelize),
+  qw(decode deprecated dumper encode extract_usage getopt hmac_sha1_sum),
+  qw(html_attr_unescape html_unescape md5_bytes md5_sum monkey_patch),
+  qw(punycode_decode punycode_encode quote secure_compare sha1_bytes sha1_sum),
+  qw(slugify split_cookie_header split_header steady_time tablify term_escape),
+  qw(trim unindent unquote url_escape url_unescape xml_escape xor_encode)
+);
+
+# Aliases
+monkey_patch(__PACKAGE__, 'b64_decode',    \&decode_base64);
+monkey_patch(__PACKAGE__, 'b64_encode',    \&encode_base64);
+monkey_patch(__PACKAGE__, 'hmac_sha1_sum', \&hmac_sha1_hex);
+monkey_patch(__PACKAGE__, 'md5_bytes',     \&md5);
+monkey_patch(__PACKAGE__, 'md5_sum',       \&md5_hex);
+monkey_patch(__PACKAGE__, 'sha1_bytes',    \&sha1);
+monkey_patch(__PACKAGE__, 'sha1_sum',      \&sha1_hex);
+
+# Use a monotonic clock if possible
+monkey_patch(__PACKAGE__, 'steady_time',
+  MONOTONIC
+  ? sub () { Time::HiRes::clock_gettime(Time::HiRes::CLOCK_MONOTONIC()) }
+  : \&Time::HiRes::time);
+
+sub camelize {
+  my $str = shift;
+  return $str if $str =~ /^[A-Z]/;
+
+  # CamelCase words
+  return join '::', map {
+    join('', map { ucfirst lc } split '_')
+  } split '-', $str;
+}
+
+sub class_to_file {
+  my $class = shift;
+  $class =~ s/::|'//g;
+  $class =~ s/([A-Z])([A-Z]*)/$1 . lc $2/ge;
+  return decamelize($class);
+}
+
+sub class_to_path { join '.', join('/', split(/::|'/, shift)), 'pm' }
+
+sub decamelize {
+  my $str = shift;
+  return $str if $str !~ /^[A-Z]/;
+
+  # snake_case words
+  return join '-', map {
+    join('_', map {lc} grep {length} split /([A-Z]{1}[^A-Z]*)/)
+  } split '::', $str;
+}
+
+sub decode {
+  my ($encoding, $bytes) = @_;
+  return undef
+    unless eval { $bytes = _encoding($encoding)->decode("$bytes", 1); 1 };
+  return $bytes;
+}
+
+sub deprecated {
+  local $Carp::CarpLevel = 1;
+  $ENV{MOJO_FATAL_DEPRECATIONS} ? croak @_ : carp @_;
+}
+
+sub dumper {
+  Data::Dumper->new([@_])->Indent(1)->Sortkeys(1)->Terse(1)->Useqq(1)->Dump;
+}
+
+sub encode { _encoding($_[0])->encode("$_[1]", 0) }
+
+sub extract_usage {
+  my $file = @_ ? "$_[0]" : (caller)[1];
+
+  open my $handle, '>', \my $output;
+  pod2usage -exitval => 'noexit', -input => $file, -output => $handle;
+  $output =~ s/^.*\n|\n$//;
+  $output =~ s/\n$//;
+
+  return unindent($output);
+}
+
+sub getopt {
+  my ($array, $opts) = map { ref $_[0] eq 'ARRAY' ? shift : $_ } \@ARGV, [];
+  my $save = Getopt::Long::Configure(qw(default no_auto_abbrev no_ignore_case),
+    @$opts);
+  GetOptionsFromArray $array, @_;
+  Getopt::Long::Configure($save);
+}
+
+sub html_attr_unescape { _html(shift, 1) }
+sub html_unescape      { _html(shift, 0) }
+
+sub monkey_patch {
+  my ($class, %patch) = @_;
+  no strict 'refs';
+  no warnings 'redefine';
+  *{"${class}::$_"} = $NAME->("${class}::$_", $patch{$_}) for keys %patch;
+}
+
+# Direct translation of RFC 3492
+sub punycode_decode {
+  my $input = shift;
+  use integer;
+
+  my ($n, $i, $bias, @output) = (PC_INITIAL_N, 0, PC_INITIAL_BIAS);
+
+  # Consume all code points before the last delimiter
+  push @output, split('', $1) if $input =~ s/(.*)\x2d//s;
+
+  while (length $input) {
+    my ($oldi, $w) = ($i, 1);
+
+    # Base to infinity in steps of base
+    for (my $k = PC_BASE; 1; $k += PC_BASE) {
+      my $digit = ord substr $input, 0, 1, '';
+      $digit = $digit < 0x40 ? $digit + (26 - 0x30) : ($digit & 0x1f) - 1;
+      $i += $digit * $w;
+      my $t = $k - $bias;
+      $t = $t < PC_TMIN ? PC_TMIN : $t > PC_TMAX ? PC_TMAX : $t;
+      last if $digit < $t;
+      $w *= PC_BASE - $t;
+    }
+
+    $bias = _adapt($i - $oldi, @output + 1, $oldi == 0);
+    $n += $i / (@output + 1);
+    $i = $i % (@output + 1);
+    splice @output, $i++, 0, chr $n;
+  }
+
+  return join '', @output;
+}
+
+# Direct translation of RFC 3492
+sub punycode_encode {
+  my $output = shift;
+  use integer;
+
+  my ($n, $delta, $bias) = (PC_INITIAL_N, 0, PC_INITIAL_BIAS);
+
+  # Extract basic code points
+  my @input = map {ord} split '', $output;
+  $output =~ s/[^\x00-\x7f]+//gs;
+  my $h = my $basic = length $output;
+  $output .= "\x2d" if $basic > 0;
+
+  for my $m (sort grep { $_ >= PC_INITIAL_N } @input) {
+    next if $m < $n;
+    $delta += ($m - $n) * ($h + 1);
+    $n = $m;
+
+    for my $c (@input) {
+
+      if ($c < $n) { $delta++ }
+      elsif ($c == $n) {
+        my $q = $delta;
+
+        # Base to infinity in steps of base
+        for (my $k = PC_BASE; 1; $k += PC_BASE) {
+          my $t = $k - $bias;
+          $t = $t < PC_TMIN ? PC_TMIN : $t > PC_TMAX ? PC_TMAX : $t;
+          last if $q < $t;
+          my $o = $t + (($q - $t) % (PC_BASE - $t));
+          $output .= chr $o + ($o < 26 ? 0x61 : 0x30 - 26);
+          $q = ($q - $t) / (PC_BASE - $t);
+        }
+
+        $output .= chr $q + ($q < 26 ? 0x61 : 0x30 - 26);
+        $bias = _adapt($delta, $h + 1, $h == $basic);
+        $delta = 0;
+        $h++;
+      }
+    }
+
+    $delta++;
+    $n++;
+  }
+
+  return $output;
+}
+
+sub quote {
+  my $str = shift;
+  $str =~ s/(["\\])/\\$1/g;
+  return qq{"$str"};
+}
+
+sub secure_compare {
+  my ($one, $two) = @_;
+  return undef if length $one != length $two;
+  my $r = 0;
+  $r |= ord(substr $one, $_) ^ ord(substr $two, $_) for 0 .. length($one) - 1;
+  return $r == 0;
+}
+
+sub slugify {
+  my ($value, $allow_unicode) = @_;
+
+  if ($allow_unicode) {
+
+    # Force unicode semantics by upgrading string
+    utf8::upgrade($value = Unicode::Normalize::NFKC($value));
+    $value =~ s/[^\w\s-]+//g;
+  }
+  else {
+    $value = Unicode::Normalize::NFKD($value);
+    $value =~ s/[^a-zA-Z0-9_\p{PosixSpace}-]+//g;
+  }
+  (my $new = lc trim($value)) =~ s/[-\s]+/-/g;
+
+  return $new;
+}
+
+sub split_cookie_header { _header(shift, 1) }
+sub split_header        { _header(shift, 0) }
+
+sub tablify {
+  my $rows = shift;
+
+  my @spec;
+  for my $row (@$rows) {
+    for my $i (0 .. $#$row) {
+      ($row->[$i] //= '') =~ s/[\r\n]//g;
+      my $len = length $row->[$i];
+      $spec[$i] = $len if $len >= ($spec[$i] // 0);
+    }
+  }
+
+  my @fm = (map({"\%-${_}s"} @spec[0 .. $#spec - 1]), '%s');
+  return join '', map { sprintf join('  ', @fm[0 .. $#$_]) . "\n", @$_ } @$rows;
+}
+
+sub term_escape {
+  my $str = shift;
+  $str =~ s/([\x00-\x09\x0b-\x1f\x7f\x80-\x9f])/sprintf '\\x%02x', ord $1/ge;
+  return $str;
+}
+
+sub trim {
+  my $str = shift;
+  $str =~ s/^\s+//;
+  $str =~ s/\s+$//;
+  return $str;
+}
+
+sub unindent {
+  my $str = shift;
+  my $min = min map { m/^([ \t]*)/; length $1 || () } split "\n", $str;
+  $str =~ s/^[ \t]{0,$min}//gm if $min;
+  return $str;
+}
+
+sub unquote {
+  my $str = shift;
+  return $str unless $str =~ s/^"(.*)"$/$1/g;
+  $str =~ s/\\\\/\\/g;
+  $str =~ s/\\"/"/g;
+  return $str;
+}
+
+sub url_escape {
+  my ($str, $pattern) = @_;
+
+  if ($pattern) {
+    unless (exists $PATTERN{$pattern}) {
+      (my $quoted = $pattern) =~ s!([/\$\[])!\\$1!g;
+      $PATTERN{$pattern}
+        = eval "sub { \$_[0] =~ s/([$quoted])/sprintf '%%%02X', ord \$1/ge }"
+        or croak $@;
+    }
+    $PATTERN{$pattern}->($str);
+  }
+  else { $str =~ s/([^A-Za-z0-9\-._~])/sprintf '%%%02X', ord $1/ge }
+
+  return $str;
+}
+
+sub url_unescape {
+  my $str = shift;
+  $str =~ s/%([0-9a-fA-F]{2})/chr hex $1/ge;
+  return $str;
+}
+
+sub xml_escape {
+  return $_[0] if ref $_[0] && ref $_[0] eq 'Mojo::ByteStream';
+  my $str = shift // '';
+  $str =~ s/([&<>"'])/$XML{$1}/ge;
+  return $str;
+}
+
+sub xor_encode {
+  my ($input, $key) = @_;
+
+  # Encode with variable key length
+  my $len = length $key;
+  my $buffer = my $output = '';
+  $output .= $buffer ^ $key
+    while length($buffer = substr($input, 0, $len, '')) == $len;
+  return $output .= $buffer ^ substr($key, 0, length $buffer, '');
+}
+
+sub _adapt {
+  my ($delta, $numpoints, $firsttime) = @_;
+  use integer;
+
+  $delta = $firsttime ? $delta / PC_DAMP : $delta / 2;
+  $delta += $delta / $numpoints;
+  my $k = 0;
+  while ($delta > ((PC_BASE - PC_TMIN) * PC_TMAX) / 2) {
+    $delta /= PC_BASE - PC_TMIN;
+    $k += PC_BASE;
+  }
+
+  return $k + (((PC_BASE - PC_TMIN + 1) * $delta) / ($delta + PC_SKEW));
+}
+
+sub _encoding {
+  $ENCODING{$_[0]} //= find_encoding($_[0]) // croak "Unknown encoding '$_[0]'";
+}
+
+sub _entity {
+  my ($point, $name, $attr) = @_;
+
+  # Code point
+  return chr($point !~ /^x/ ? $point : hex $point) unless defined $name;
+
+  # Named character reference
+  my $rest = my $last = '';
+  while (length $name) {
+    return $ENTITIES{$name} . reverse $rest
+      if exists $ENTITIES{$name}
+      && (!$attr || $name =~ /;$/ || $last !~ /[A-Za-z0-9=]/);
+    $rest .= $last = chop $name;
+  }
+  return '&' . reverse $rest;
+}
+
+# Supported on Perl 5.14+
+sub _global_destruction {
+  defined ${^GLOBAL_PHASE} && ${^GLOBAL_PHASE} eq 'DESTRUCT';
+}
+
+sub _header {
+  my ($str, $cookie) = @_;
+
+  my (@tree, @part);
+  while ($str =~ /\G[,;\s]*([^=;, ]+)\s*/gc) {
+    push @part, $1, undef;
+    my $expires = $cookie && @part > 2 && lc $1 eq 'expires';
+
+    # Special "expires" value
+    if ($expires && $str =~ /\G=\s*$EXPIRES_RE/gco) { $part[-1] = $1 }
+
+    # Quoted value
+    elsif ($str =~ /\G=\s*("(?:\\\\|\\"|[^"])*")/gc) { $part[-1] = unquote $1 }
+
+    # Unquoted value
+    elsif ($str =~ /\G=\s*([^;, ]*)/gc) { $part[-1] = $1 }
+
+    # Separator
+    next unless $str =~ /\G[;\s]*,\s*/gc;
+    push @tree, [@part];
+    @part = ();
+  }
+
+  # Take care of final part
+  return [@part ? (@tree, \@part) : @tree];
+}
+
+sub _html {
+  my ($str, $attr) = @_;
+  $str =~ s/$ENTITY_RE/_entity($1, $2, $attr)/geo;
+  return $str;
+}
+
+sub _options {
+
+  # Hash or name (one)
+  return ref $_[0] eq 'HASH' ? (undef, %{shift()}) : @_ if @_ == 1;
+
+  # Name and values (odd)
+  return shift, @_ if @_ % 2;
+
+  # Name and hash or just values (even)
+  return ref $_[1] eq 'HASH' ? (shift, %{shift()}) : (undef, @_);
+}
+
+# This may break in the future, but is worth it for performance
+sub _readable { !!(IO::Poll::_poll(@_[0, 1], my $m = POLLIN | POLLPRI) > 0) }
+
+sub _stash {
+  my ($name, $object) = (shift, shift);
+
+  # Hash
+  return $object->{$name} ||= {} unless @_;
+
+  # Get
+  return $object->{$name}{$_[0]} unless @_ > 1 || ref $_[0];
+
+  # Set
+  my $values = ref $_[0] ? $_[0] : {@_};
+  @{$object->{$name}}{keys %$values} = values %$values;
+
+  return $object;
+}
+
+sub _teardown {
+  return unless my $class = shift;
+
+  # @ISA has to be cleared first because of circular references
+  no strict 'refs';
+  @{"${class}::ISA"} = ();
+  delete_package $class;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::Util - Portable utility functions
+
+=head1 SYNOPSIS
+
+  use Mojo::Util qw(b64_encode url_escape url_unescape);
+
+  my $str = 'test=23';
+  my $escaped = url_escape $str;
+  say url_unescape $escaped;
+  say b64_encode $escaped, '';
+
+=head1 DESCRIPTION
+
+L<Mojo::Util> provides portable utility functions for L<Mojo>.
+
+=head1 FUNCTIONS
+
+L<Mojo::Util> implements the following functions, which can be imported
+individually.
+
+=head2 b64_decode
+
+  my $bytes = b64_decode $b64;
+
+Base64 decode bytes with L<MIME::Base64>.
+
+=head2 b64_encode
+
+  my $b64 = b64_encode $bytes;
+  my $b64 = b64_encode $bytes, "\n";
+
+Base64 encode bytes with L<MIME::Base64>, the line ending defaults to a newline.
+
+=head2 camelize
+
+  my $camelcase = camelize $snakecase;
+
+Convert C<snake_case> string to C<CamelCase> and replace C<-> with C<::>.
+
+  # "FooBar"
+  camelize 'foo_bar';
+
+  # "FooBar::Baz"
+  camelize 'foo_bar-baz';
+
+  # "FooBar::Baz"
+  camelize 'FooBar::Baz';
+
+=head2 class_to_file
+
+  my $file = class_to_file 'Foo::Bar';
+
+Convert a class name to a file.
+
+  # "foo_bar"
+  class_to_file 'Foo::Bar';
+
+  # "foobar"
+  class_to_file 'FOO::Bar';
+
+  # "foo_bar"
+  class_to_file 'FooBar';
+
+  # "foobar"
+  class_to_file 'FOOBar';
+
+=head2 class_to_path
+
+  my $path = class_to_path 'Foo::Bar';
+
+Convert class name to path, as used by C<%INC>.
+
+  # "Foo/Bar.pm"
+  class_to_path 'Foo::Bar';
+
+  # "FooBar.pm"
+  class_to_path 'FooBar';
+
+=head2 decamelize
+
+  my $snakecase = decamelize $camelcase;
+
+Convert C<CamelCase> string to C<snake_case> and replace C<::> with C<->.
+
+  # "foo_bar"
+  decamelize 'FooBar';
+
+  # "foo_bar-baz"
+  decamelize 'FooBar::Baz';
+
+  # "foo_bar-baz"
+  decamelize 'foo_bar-baz';
+
+=head2 decode
+
+  my $chars = decode 'UTF-8', $bytes;
+
+Decode bytes to characters with L<Encode>, or return C<undef> if decoding
+failed.
+
+=head2 deprecated
+
+  deprecated 'foo is DEPRECATED in favor of bar';
+
+Warn about deprecated feature from perspective of caller. You can also set the
+C<MOJO_FATAL_DEPRECATIONS> environment variable to make them die instead.
+
+=head2 dumper
+
+  my $perl = dumper {some => 'data'};
+
+Dump a Perl data structure with L<Data::Dumper>.
+
+=head2 encode
+
+  my $bytes = encode 'UTF-8', $chars;
+
+Encode characters to bytes with L<Encode>.
+
+=head2 extract_usage
+
+  my $usage = extract_usage;
+  my $usage = extract_usage '/home/sri/foo.pod';
+
+Extract usage message from the SYNOPSIS section of a file containing POD
+documentation, defaults to using the file this function was called from.
+
+  # "Usage: APPLICATION test [OPTIONS]\n"
+  extract_usage;
+
+  =head1 SYNOPSIS
+
+    Usage: APPLICATION test [OPTIONS]
+
+  =cut
+
+=head2 getopt
+
+  getopt
+    'H|headers=s' => \my @headers,
+    't|timeout=i' => \my $timeout,
+    'v|verbose'   => \my $verbose;
+  getopt $array,
+    'H|headers=s' => \my @headers,
+    't|timeout=i' => \my $timeout,
+    'v|verbose'   => \my $verbose;
+  getopt $array, ['pass_through'],
+    'H|headers=s' => \my @headers,
+    't|timeout=i' => \my $timeout,
+    'v|verbose'   => \my $verbose;
+
+Extract options from an array reference with L<Getopt::Long>, but without
+changing its global configuration, defaults to using C<@ARGV>. The configuration
+options C<no_auto_abbrev> and C<no_ignore_case> are enabled by default.
+
+  # Extract "charset" option
+  getopt ['--charset', 'UTF-8'], 'charset=s' => \my $charset;
+  say $charset;
+
+=head2 hmac_sha1_sum
+
+  my $checksum = hmac_sha1_sum $bytes, 'passw0rd';
+
+Generate HMAC-SHA1 checksum for bytes with L<Digest::SHA>.
+
+  # "11cedfd5ec11adc0ec234466d8a0f2a83736aa68"
+  hmac_sha1_sum 'foo', 'passw0rd';
+
+=head2 html_attr_unescape
+
+  my $str = html_attr_unescape $escaped;
+
+Same as L</"html_unescape">, but handles special rules from the
+L<HTML Living Standard|https://html.spec.whatwg.org> for HTML attributes.
+
+  # "foo=bar&ltest=baz"
+  html_attr_unescape 'foo=bar&ltest=baz';
+
+  # "foo=bar<est=baz"
+  html_attr_unescape 'foo=bar&lt;est=baz';
+
+=head2 html_unescape
+
+  my $str = html_unescape $escaped;
+
+Unescape all HTML entities in string.
+
+  # "<div>"
+  html_unescape '&lt;div&gt;';
+
+=head2 md5_bytes
+
+  my $checksum = md5_bytes $bytes;
+
+Generate binary MD5 checksum for bytes with L<Digest::MD5>.
+
+=head2 md5_sum
+
+  my $checksum = md5_sum $bytes;
+
+Generate MD5 checksum for bytes with L<Digest::MD5>.
+
+  # "acbd18db4cc2f85cedef654fccc4a4d8"
+  md5_sum 'foo';
+
+=head2 monkey_patch
+
+  monkey_patch $package, foo => sub {...};
+  monkey_patch $package, foo => sub {...}, bar => sub {...};
+
+Monkey patch functions into package.
+
+  monkey_patch 'MyApp',
+    one   => sub { say 'One!' },
+    two   => sub { say 'Two!' },
+    three => sub { say 'Three!' };
+
+=head2 punycode_decode
+
+  my $str = punycode_decode $punycode;
+
+Punycode decode string as described in
+L<RFC 3492|http://tools.ietf.org/html/rfc3492>.
+
+  # "bÃŒcher"
+  punycode_decode 'bcher-kva';
+
+=head2 punycode_encode
+
+  my $punycode = punycode_encode $str;
+
+Punycode encode string as described in
+L<RFC 3492|http://tools.ietf.org/html/rfc3492>.
+
+  # "bcher-kva"
+  punycode_encode 'bÃŒcher';
+
+=head2 quote
+
+  my $quoted = quote $str;
+
+Quote string.
+
+=head2 secure_compare
+
+  my $bool = secure_compare $str1, $str2;
+
+Constant time comparison algorithm to prevent timing attacks.
+
+=head2 sha1_bytes
+
+  my $checksum = sha1_bytes $bytes;
+
+Generate binary SHA1 checksum for bytes with L<Digest::SHA>.
+
+=head2 sha1_sum
+
+  my $checksum = sha1_sum $bytes;
+
+Generate SHA1 checksum for bytes with L<Digest::SHA>.
+
+  # "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
+  sha1_sum 'foo';
+
+=head2 slugify
+
+  my $slug = slugify $string;
+  my $slug = slugify $string, $bool;
+
+Returns a URL slug generated from the input string. Non-word characters are
+removed, the string is trimmed and lowercased, and whitespace characters are
+replaced by a dash. By default, non-ASCII characters are normalized to ASCII
+word characters or removed, but if a true value is passed as the second
+parameter, all word characters will be allowed in the result according to
+unicode semantics.
+
+  # "joel-is-a-slug"
+  slugify 'Joel is a slug';
+
+  # "this-is-my-resume"
+  slugify 'This is: my - rÃ©sumÃ©! â ';
+
+  # "this-is-my-rÃ©sumÃ©"
+  slugify 'This is: my - rÃ©sumÃ©! â ', 1;
+
+=head2 split_cookie_header
+
+  my $tree = split_cookie_header 'a=b; expires=Thu, 07 Aug 2008 07:07:59 GMT';
+
+Same as L</"split_header">, but handles C<expires> values from
+L<RFC 6265|http://tools.ietf.org/html/rfc6265>.
+
+=head2 split_header
+
+   my $tree = split_header 'foo="bar baz"; test=123, yada';
+
+Split HTTP header value into key/value pairs, each comma separated part gets
+its own array reference, and keys without a value get C<undef> assigned.
+
+  # "one"
+  split_header('one; two="three four", five=six')->[0][0];
+
+  # "two"
+  split_header('one; two="three four", five=six')->[0][2];
+
+  # "three four"
+  split_header('one; two="three four", five=six')->[0][3];
+
+  # "five"
+  split_header('one; two="three four", five=six')->[1][0];
+
+  # "six"
+  split_header('one; two="three four", five=six')->[1][1];
+
+=head2 steady_time
+
+  my $time = steady_time;
+
+High resolution time elapsed from an arbitrary fixed point in the past,
+resilient to time jumps if a monotonic clock is available through
+L<Time::HiRes>.
+
+=head2 tablify
+
+  my $table = tablify [['foo', 'bar'], ['baz', 'yada']];
+
+Row-oriented generator for text tables.
+
+  # "foo   bar\nyada  yada\nbaz   yada\n"
+  tablify [['foo', 'bar'], ['yada', 'yada'], ['baz', 'yada']];
+
+=head2 term_escape
+
+  my $escaped = term_escape $str;
+
+Escape all POSIX control characters except for C<\n>.
+
+  # "foo\\x09bar\\x0d\n"
+  term_escape "foo\tbar\r\n";
+
+=head2 trim
+
+  my $trimmed = trim $str;
+
+Trim whitespace characters from both ends of string.
+
+  # "foo bar"
+  trim '  foo bar  ';
+
+=head2 unindent
+
+  my $unindented = unindent $str;
+
+Unindent multi-line string.
+
+  # "foo\nbar\nbaz\n"
+  unindent "  foo\n  bar\n  baz\n";
+
+=head2 unquote
+
+  my $str = unquote $quoted;
+
+Unquote string.
+
+=head2 url_escape
+
+  my $escaped = url_escape $str;
+  my $escaped = url_escape $str, '^A-Za-z0-9\-._~';
+
+Percent encode unsafe characters in string as described in
+L<RFC 3986|http://tools.ietf.org/html/rfc3986>, the pattern used defaults to
+C<^A-Za-z0-9\-._~>.
+
+  # "foo%3Bbar"
+  url_escape 'foo;bar';
+
+=head2 url_unescape
+
+  my $str = url_unescape $escaped;
+
+Decode percent encoded characters in string as described in
+L<RFC 3986|http://tools.ietf.org/html/rfc3986>.
+
+  # "foo;bar"
+  url_unescape 'foo%3Bbar';
+
+=head2 xml_escape
+
+  my $escaped = xml_escape $str;
+
+Escape unsafe characters C<&>, C<E<lt>>, C<E<gt>>, C<"> and C<'> in string, but
+do not escape L<Mojo::ByteStream> objects.
+
+  # "&lt;div&gt;"
+  xml_escape '<div>';
+
+  # "<div>"
+  use Mojo::ByteStream 'b';
+  xml_escape b('<div>');
+
+=head2 xor_encode
+
+  my $encoded = xor_encode $str, $key;
+
+XOR encode string with variable length key.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
+
+__DATA__
+Aacute; U+000C1
+Aacute U+000C1
+aacute; U+000E1
+aacute U+000E1
+Abreve; U+00102
+abreve; U+00103
+ac; U+0223E
+acd; U+0223F
+acE; U+0223E U+00333
+Acirc; U+000C2
+Acirc U+000C2
+acirc; U+000E2
+acirc U+000E2
+acute; U+000B4
+acute U+000B4
+Acy; U+00410
+acy; U+00430
+AElig; U+000C6
+AElig U+000C6
+aelig; U+000E6
+aelig U+000E6
+af; U+02061
+Afr; U+1D504
+afr; U+1D51E
+Agrave; U+000C0
+Agrave U+000C0
+agrave; U+000E0
+agrave U+000E0
+alefsym; U+02135
+aleph; U+02135
+Alpha; U+00391
+alpha; U+003B1
+Amacr; U+00100
+amacr; U+00101
+amalg; U+02A3F
+AMP; U+00026
+AMP U+00026
+amp; U+00026
+amp U+00026
+And; U+02A53
+and; U+02227
+andand; U+02A55
+andd; U+02A5C
+andslope; U+02A58
+andv; U+02A5A
+ang; U+02220
+ange; U+029A4
+angle; U+02220
+angmsd; U+02221
+angmsdaa; U+029A8
+angmsdab; U+029A9
+angmsdac; U+029AA
+angmsdad; U+029AB
+angmsdae; U+029AC
+angmsdaf; U+029AD
+angmsdag; U+029AE
+angmsdah; U+029AF
+angrt; U+0221F
+angrtvb; U+022BE
+angrtvbd; U+0299D
+angsph; U+02222
+angst; U+000C5
+angzarr; U+0237C
+Aogon; U+00104
+aogon; U+00105
+Aopf; U+1D538
+aopf; U+1D552
+ap; U+02248
+apacir; U+02A6F
+apE; U+02A70
+ape; U+0224A
+apid; U+0224B
+apos; U+00027
+ApplyFunction; U+02061
+approx; U+02248
+approxeq; U+0224A
+Aring; U+000C5
+Aring U+000C5
+aring; U+000E5
+aring U+000E5
+Ascr; U+1D49C
+ascr; U+1D4B6
+Assign; U+02254
+ast; U+0002A
+asymp; U+02248
+asympeq; U+0224D
+Atilde; U+000C3
+Atilde U+000C3
+atilde; U+000E3
+atilde U+000E3
+Auml; U+000C4
+Auml U+000C4
+auml; U+000E4
+auml U+000E4
+awconint; U+02233
+awint; U+02A11
+backcong; U+0224C
+backepsilon; U+003F6
+backprime; U+02035
+backsim; U+0223D
+backsimeq; U+022CD
+Backslash; U+02216
+Barv; U+02AE7
+barvee; U+022BD
+Barwed; U+02306
+barwed; U+02305
+barwedge; U+02305
+bbrk; U+023B5
+bbrktbrk; U+023B6
+bcong; U+0224C
+Bcy; U+00411
+bcy; U+00431
+bdquo; U+0201E
+becaus; U+02235
+Because; U+02235
+because; U+02235
+bemptyv; U+029B0
+bepsi; U+003F6
+bernou; U+0212C
+Bernoullis; U+0212C
+Beta; U+00392
+beta; U+003B2
+beth; U+02136
+between; U+0226C
+Bfr; U+1D505
+bfr; U+1D51F
+bigcap; U+022C2
+bigcirc; U+025EF
+bigcup; U+022C3
+bigodot; U+02A00
+bigoplus; U+02A01
+bigotimes; U+02A02
+bigsqcup; U+02A06
+bigstar; U+02605
+bigtriangledown; U+025BD
+bigtriangleup; U+025B3
+biguplus; U+02A04
+bigvee; U+022C1
+bigwedge; U+022C0
+bkarow; U+0290D
+blacklozenge; U+029EB
+blacksquare; U+025AA
+blacktriangle; U+025B4
+blacktriangledown; U+025BE
+blacktriangleleft; U+025C2
+blacktriangleright; U+025B8
+blank; U+02423
+blk12; U+02592
+blk14; U+02591
+blk34; U+02593
+block; U+02588
+bne; U+0003D U+020E5
+bnequiv; U+02261 U+020E5
+bNot; U+02AED
+bnot; U+02310
+Bopf; U+1D539
+bopf; U+1D553
+bot; U+022A5
+bottom; U+022A5
+bowtie; U+022C8
+boxbox; U+029C9
+boxDL; U+02557
+boxDl; U+02556
+boxdL; U+02555
+boxdl; U+02510
+boxDR; U+02554
+boxDr; U+02553
+boxdR; U+02552
+boxdr; U+0250C
+boxH; U+02550
+boxh; U+02500
+boxHD; U+02566
+boxHd; U+02564
+boxhD; U+02565
+boxhd; U+0252C
+boxHU; U+02569
+boxHu; U+02567
+boxhU; U+02568
+boxhu; U+02534
+boxminus; U+0229F
+boxplus; U+0229E
+boxtimes; U+022A0
+boxUL; U+0255D
+boxUl; U+0255C
+boxuL; U+0255B
+boxul; U+02518
+boxUR; U+0255A
+boxUr; U+02559
+boxuR; U+02558
+boxur; U+02514
+boxV; U+02551
+boxv; U+02502
+boxVH; U+0256C
+boxVh; U+0256B
+boxvH; U+0256A
+boxvh; U+0253C
+boxVL; U+02563
+boxVl; U+02562
+boxvL; U+02561
+boxvl; U+02524
+boxVR; U+02560
+boxVr; U+0255F
+boxvR; U+0255E
+boxvr; U+0251C
+bprime; U+02035
+Breve; U+002D8
+breve; U+002D8
+brvbar; U+000A6
+brvbar U+000A6
+Bscr; U+0212C
+bscr; U+1D4B7
+bsemi; U+0204F
+bsim; U+0223D
+bsime; U+022CD
+bsol; U+0005C
+bsolb; U+029C5
+bsolhsub; U+027C8
+bull; U+02022
+bullet; U+02022
+bump; U+0224E
+bumpE; U+02AAE
+bumpe; U+0224F
+Bumpeq; U+0224E
+bumpeq; U+0224F
+Cacute; U+00106
+cacute; U+00107
+Cap; U+022D2
+cap; U+02229
+capand; U+02A44
+capbrcup; U+02A49
+capcap; U+02A4B
+capcup; U+02A47
+capdot; U+02A40
+CapitalDifferentialD; U+02145
+caps; U+02229 U+0FE00
+caret; U+02041
+caron; U+002C7
+Cayleys; U+0212D
+ccaps; U+02A4D
+Ccaron; U+0010C
+ccaron; U+0010D
+Ccedil; U+000C7
+Ccedil U+000C7
+ccedil; U+000E7
+ccedil U+000E7
+Ccirc; U+00108
+ccirc; U+00109
+Cconint; U+02230
+ccups; U+02A4C
+ccupssm; U+02A50
+Cdot; U+0010A
+cdot; U+0010B
+cedil; U+000B8
+cedil U+000B8
+Cedilla; U+000B8
+cemptyv; U+029B2
+cent; U+000A2
+cent U+000A2
+CenterDot; U+000B7
+centerdot; U+000B7
+Cfr; U+0212D
+cfr; U+1D520
+CHcy; U+00427
+chcy; U+00447
+check; U+02713
+checkmark; U+02713
+Chi; U+003A7
+chi; U+003C7
+cir; U+025CB
+circ; U+002C6
+circeq; U+02257
+circlearrowleft; U+021BA
+circlearrowright; U+021BB
+circledast; U+0229B
+circledcirc; U+0229A
+circleddash; U+0229D
+CircleDot; U+02299
+circledR; U+000AE
+circledS; U+024C8
+CircleMinus; U+02296
+CirclePlus; U+02295
+CircleTimes; U+02297
+cirE; U+029C3
+cire; U+02257
+cirfnint; U+02A10
+cirmid; U+02AEF
+cirscir; U+029C2
+ClockwiseContourIntegral; U+02232
+CloseCurlyDoubleQuote; U+0201D
+CloseCurlyQuote; U+02019
+clubs; U+02663
+clubsuit; U+02663
+Colon; U+02237
+colon; U+0003A
+Colone; U+02A74
+colone; U+02254
+coloneq; U+02254
+comma; U+0002C
+commat; U+00040
+comp; U+02201
+compfn; U+02218
+complement; U+02201
+complexes; U+02102
+cong; U+02245
+congdot; U+02A6D
+Congruent; U+02261
+Conint; U+0222F
+conint; U+0222E
+ContourIntegral; U+0222E
+Copf; U+02102
+copf; U+1D554
+coprod; U+02210
+Coproduct; U+02210
+COPY; U+000A9
+COPY U+000A9
+copy; U+000A9
+copy U+000A9
+copysr; U+02117
+CounterClockwiseContourIntegral; U+02233
+crarr; U+021B5
+Cross; U+02A2F
+cross; U+02717
+Cscr; U+1D49E
+cscr; U+1D4B8
+csub; U+02ACF
+csube; U+02AD1
+csup; U+02AD0
+csupe; U+02AD2
+ctdot; U+022EF
+cudarrl; U+02938
+cudarrr; U+02935
+cuepr; U+022DE
+cuesc; U+022DF
+cularr; U+021B6
+cularrp; U+0293D
+Cup; U+022D3
+cup; U+0222A
+cupbrcap; U+02A48
+CupCap; U+0224D
+cupcap; U+02A46
+cupcup; U+02A4A
+cupdot; U+0228D
+cupor; U+02A45
+cups; U+0222A U+0FE00
+curarr; U+021B7
+curarrm; U+0293C
+curlyeqprec; U+022DE
+curlyeqsucc; U+022DF
+curlyvee; U+022CE
+curlywedge; U+022CF
+curren; U+000A4
+curren U+000A4
+curvearrowleft; U+021B6
+curvearrowright; U+021B7
+cuvee; U+022CE
+cuwed; U+022CF
+cwconint; U+02232
+cwint; U+02231
+cylcty; U+0232D
+Dagger; U+02021
+dagger; U+02020
+daleth; U+02138
+Darr; U+021A1
+dArr; U+021D3
+darr; U+02193
+dash; U+02010
+Dashv; U+02AE4
+dashv; U+022A3
+dbkarow; U+0290F
+dblac; U+002DD
+Dcaron; U+0010E
+dcaron; U+0010F
+Dcy; U+00414
+dcy; U+00434
+DD; U+02145
+dd; U+02146
+ddagger; U+02021
+ddarr; U+021CA
+DDotrahd; U+02911
+ddotseq; U+02A77
+deg; U+000B0
+deg U+000B0
+Del; U+02207
+Delta; U+00394
+delta; U+003B4
+demptyv; U+029B1
+dfisht; U+0297F
+Dfr; U+1D507
+dfr; U+1D521
+dHar; U+02965
+dharl; U+021C3
+dharr; U+021C2
+DiacriticalAcute; U+000B4
+DiacriticalDot; U+002D9
+DiacriticalDoubleAcute; U+002DD
+DiacriticalGrave; U+00060
+DiacriticalTilde; U+002DC
+diam; U+022C4
+Diamond; U+022C4
+diamond; U+022C4
+diamondsuit; U+02666
+diams; U+02666
+die; U+000A8
+DifferentialD; U+02146
+digamma; U+003DD
+disin; U+022F2
+div; U+000F7
+divide; U+000F7
+divide U+000F7
+divideontimes; U+022C7
+divonx; U+022C7
+DJcy; U+00402
+djcy; U+00452
+dlcorn; U+0231E
+dlcrop; U+0230D
+dollar; U+00024
+Dopf; U+1D53B
+dopf; U+1D555
+Dot; U+000A8
+dot; U+002D9
+DotDot; U+020DC
+doteq; U+02250
+doteqdot; U+02251
+DotEqual; U+02250
+dotminus; U+02238
+dotplus; U+02214
+dotsquare; U+022A1
+doublebarwedge; U+02306
+DoubleContourIntegral; U+0222F
+DoubleDot; U+000A8
+DoubleDownArrow; U+021D3
+DoubleLeftArrow; U+021D0
+DoubleLeftRightArrow; U+021D4
+DoubleLeftTee; U+02AE4
+DoubleLongLeftArrow; U+027F8
+DoubleLongLeftRightArrow; U+027FA
+DoubleLongRightArrow; U+027F9
+DoubleRightArrow; U+021D2
+DoubleRightTee; U+022A8
+DoubleUpArrow; U+021D1
+DoubleUpDownArrow; U+021D5
+DoubleVerticalBar; U+02225
+DownArrow; U+02193
+Downarrow; U+021D3
+downarrow; U+02193
+DownArrowBar; U+02913
+DownArrowUpArrow; U+021F5
+DownBreve; U+00311
+downdownarrows; U+021CA
+downharpoonleft; U+021C3
+downharpoonright; U+021C2
+DownLeftRightVector; U+02950
+DownLeftTeeVector; U+0295E
+DownLeftVector; U+021BD
+DownLeftVectorBar; U+02956
+DownRightTeeVector; U+0295F
+DownRightVector; U+021C1
+DownRightVectorBar; U+02957
+DownTee; U+022A4
+DownTeeArrow; U+021A7
+drbkarow; U+02910
+drcorn; U+0231F
+drcrop; U+0230C
+Dscr; U+1D49F
+dscr; U+1D4B9
+DScy; U+00405
+dscy; U+00455
+dsol; U+029F6
+Dstrok; U+00110
+dstrok; U+00111
+dtdot; U+022F1
+dtri; U+025BF
+dtrif; U+025BE
+duarr; U+021F5
+duhar; U+0296F
+dwangle; U+029A6
+DZcy; U+0040F
+dzcy; U+0045F
+dzigrarr; U+027FF
+Eacute; U+000C9
+Eacute U+000C9
+eacute; U+000E9
+eacute U+000E9
+easter; U+02A6E
+Ecaron; U+0011A
+ecaron; U+0011B
+ecir; U+02256
+Ecirc; U+000CA
+Ecirc U+000CA
+ecirc; U+000EA
+ecirc U+000EA
+ecolon; U+02255
+Ecy; U+0042D
+ecy; U+0044D
+eDDot; U+02A77
+Edot; U+00116
+eDot; U+02251
+edot; U+00117
+ee; U+02147
+efDot; U+02252
+Efr; U+1D508
+efr; U+1D522
+eg; U+02A9A
+Egrave; U+000C8
+Egrave U+000C8
+egrave; U+000E8
+egrave U+000E8
+egs; U+02A96
+egsdot; U+02A98
+el; U+02A99
+Element; U+02208
+elinters; U+023E7
+ell; U+02113
+els; U+02A95
+elsdot; U+02A97
+Emacr; U+00112
+emacr; U+00113
+empty; U+02205
+emptyset; U+02205
+EmptySmallSquare; U+025FB
+emptyv; U+02205
+EmptyVerySmallSquare; U+025AB
+emsp; U+02003
+emsp13; U+02004
+emsp14; U+02005
+ENG; U+0014A
+eng; U+0014B
+ensp; U+02002
+Eogon; U+00118
+eogon; U+00119
+Eopf; U+1D53C
+eopf; U+1D556
+epar; U+022D5
+eparsl; U+029E3
+eplus; U+02A71
+epsi; U+003B5
+Epsilon; U+00395
+epsilon; U+003B5
+epsiv; U+003F5
+eqcirc; U+02256
+eqcolon; U+02255
+eqsim; U+02242
+eqslantgtr; U+02A96
+eqslantless; U+02A95
+Equal; U+02A75
+equals; U+0003D
+EqualTilde; U+02242
+equest; U+0225F
+Equilibrium; U+021CC
+equiv; U+02261
+equivDD; U+02A78
+eqvparsl; U+029E5
+erarr; U+02971
+erDot; U+02253
+Escr; U+02130
+escr; U+0212F
+esdot; U+02250
+Esim; U+02A73
+esim; U+02242
+Eta; U+00397
+eta; U+003B7
+ETH; U+000D0
+ETH U+000D0
+eth; U+000F0
+eth U+000F0
+Euml; U+000CB
+Euml U+000CB
+euml; U+000EB
+euml U+000EB
+euro; U+020AC
+excl; U+00021
+exist; U+02203
+Exists; U+02203
+expectation; U+02130
+ExponentialE; U+02147
+exponentiale; U+02147
+fallingdotseq; U+02252
+Fcy; U+00424
+fcy; U+00444
+female; U+02640
+ffilig; U+0FB03
+fflig; U+0FB00
+ffllig; U+0FB04
+Ffr; U+1D509
+ffr; U+1D523
+filig; U+0FB01
+FilledSmallSquare; U+025FC
+FilledVerySmallSquare; U+025AA
+fjlig; U+00066 U+0006A
+flat; U+0266D
+fllig; U+0FB02
+fltns; U+025B1
+fnof; U+00192
+Fopf; U+1D53D
+fopf; U+1D557
+ForAll; U+02200
+forall; U+02200
+fork; U+022D4
+forkv; U+02AD9
+Fouriertrf; U+02131
+fpartint; U+02A0D
+frac12; U+000BD
+frac12 U+000BD
+frac13; U+02153
+frac14; U+000BC
+frac14 U+000BC
+frac15; U+02155
+frac16; U+02159
+frac18; U+0215B
+frac23; U+02154
+frac25; U+02156
+frac34; U+000BE
+frac34 U+000BE
+frac35; U+02157
+frac38; U+0215C
+frac45; U+02158
+frac56; U+0215A
+frac58; U+0215D
+frac78; U+0215E
+frasl; U+02044
+frown; U+02322
+Fscr; U+02131
+fscr; U+1D4BB
+gacute; U+001F5
+Gamma; U+00393
+gamma; U+003B3
+Gammad; U+003DC
+gammad; U+003DD
+gap; U+02A86
+Gbreve; U+0011E
+gbreve; U+0011F
+Gcedil; U+00122
+Gcirc; U+0011C
+gcirc; U+0011D
+Gcy; U+00413
+gcy; U+00433
+Gdot; U+00120
+gdot; U+00121
+gE; U+02267
+ge; U+02265
+gEl; U+02A8C
+gel; U+022DB
+geq; U+02265
+geqq; U+02267
+geqslant; U+02A7E
+ges; U+02A7E
+gescc; U+02AA9
+gesdot; U+02A80
+gesdoto; U+02A82
+gesdotol; U+02A84
+gesl; U+022DB U+0FE00
+gesles; U+02A94
+Gfr; U+1D50A
+gfr; U+1D524
+Gg; U+022D9
+gg; U+0226B
+ggg; U+022D9
+gimel; U+02137
+GJcy; U+00403
+gjcy; U+00453
+gl; U+02277
+gla; U+02AA5
+glE; U+02A92
+glj; U+02AA4
+gnap; U+02A8A
+gnapprox; U+02A8A
+gnE; U+02269
+gne; U+02A88
+gneq; U+02A88
+gneqq; U+02269
+gnsim; U+022E7
+Gopf; U+1D53E
+gopf; U+1D558
+grave; U+00060
+GreaterEqual; U+02265
+GreaterEqualLess; U+022DB
+GreaterFullEqual; U+02267
+GreaterGreater; U+02AA2
+GreaterLess; U+02277
+GreaterSlantEqual; U+02A7E
+GreaterTilde; U+02273
+Gscr; U+1D4A2
+gscr; U+0210A
+gsim; U+02273
+gsime; U+02A8E
+gsiml; U+02A90
+GT; U+0003E
+GT U+0003E
+Gt; U+0226B
+gt; U+0003E
+gt U+0003E
+gtcc; U+02AA7
+gtcir; U+02A7A
+gtdot; U+022D7
+gtlPar; U+02995
+gtquest; U+02A7C
+gtrapprox; U+02A86
+gtrarr; U+02978
+gtrdot; U+022D7
+gtreqless; U+022DB
+gtreqqless; U+02A8C
+gtrless; U+02277
+gtrsim; U+02273
+gvertneqq; U+02269 U+0FE00
+gvnE; U+02269 U+0FE00
+Hacek; U+002C7
+hairsp; U+0200A
+half; U+000BD
+hamilt; U+0210B
+HARDcy; U+0042A
+hardcy; U+0044A
+hArr; U+021D4
+harr; U+02194
+harrcir; U+02948
+harrw; U+021AD
+Hat; U+0005E
+hbar; U+0210F
+Hcirc; U+00124
+hcirc; U+00125
+hearts; U+02665
+heartsuit; U+02665
+hellip; U+02026
+hercon; U+022B9
+Hfr; U+0210C
+hfr; U+1D525
+HilbertSpace; U+0210B
+hksearow; U+02925
+hkswarow; U+02926
+hoarr; U+021FF
+homtht; U+0223B
+hookleftarrow; U+021A9
+hookrightarrow; U+021AA
+Hopf; U+0210D
+hopf; U+1D559
+horbar; U+02015
+HorizontalLine; U+02500
+Hscr; U+0210B
+hscr; U+1D4BD
+hslash; U+0210F
+Hstrok; U+00126
+hstrok; U+00127
+HumpDownHump; U+0224E
+HumpEqual; U+0224F
+hybull; U+02043
+hyphen; U+02010
+Iacute; U+000CD
+Iacute U+000CD
+iacute; U+000ED
+iacute U+000ED
+ic; U+02063
+Icirc; U+000CE
+Icirc U+000CE
+icirc; U+000EE
+icirc U+000EE
+Icy; U+00418
+icy; U+00438
+Idot; U+00130
+IEcy; U+00415
+iecy; U+00435
+iexcl; U+000A1
+iexcl U+000A1
+iff; U+021D4
+Ifr; U+02111
+ifr; U+1D526
+Igrave; U+000CC
+Igrave U+000CC
+igrave; U+000EC
+igrave U+000EC
+ii; U+02148
+iiiint; U+02A0C
+iiint; U+0222D
+iinfin; U+029DC
+iiota; U+02129
+IJlig; U+00132
+ijlig; U+00133
+Im; U+02111
+Imacr; U+0012A
+imacr; U+0012B
+image; U+02111
+ImaginaryI; U+02148
+imagline; U+02110
+imagpart; U+02111
+imath; U+00131
+imof; U+022B7
+imped; U+001B5
+Implies; U+021D2
+in; U+02208
+incare; U+02105
+infin; U+0221E
+infintie; U+029DD
+inodot; U+00131
+Int; U+0222C
+int; U+0222B
+intcal; U+022BA
+integers; U+02124
+Integral; U+0222B
+intercal; U+022BA
+Intersection; U+022C2
+intlarhk; U+02A17
+intprod; U+02A3C
+InvisibleComma; U+02063
+InvisibleTimes; U+02062
+IOcy; U+00401
+iocy; U+00451
+Iogon; U+0012E
+iogon; U+0012F
+Iopf; U+1D540
+iopf; U+1D55A
+Iota; U+00399
+iota; U+003B9
+iprod; U+02A3C
+iquest; U+000BF
+iquest U+000BF
+Iscr; U+02110
+iscr; U+1D4BE
+isin; U+02208
+isindot; U+022F5
+isinE; U+022F9
+isins; U+022F4
+isinsv; U+022F3
+isinv; U+02208
+it; U+02062
+Itilde; U+00128
+itilde; U+00129
+Iukcy; U+00406
+iukcy; U+00456
+Iuml; U+000CF
+Iuml U+000CF
+iuml; U+000EF
+iuml U+000EF
+Jcirc; U+00134
+jcirc; U+00135
+Jcy; U+00419
+jcy; U+00439
+Jfr; U+1D50D
+jfr; U+1D527
+jmath; U+00237
+Jopf; U+1D541
+jopf; U+1D55B
+Jscr; U+1D4A5
+jscr; U+1D4BF
+Jsercy; U+00408
+jsercy; U+00458
+Jukcy; U+00404
+jukcy; U+00454
+Kappa; U+0039A
+kappa; U+003BA
+kappav; U+003F0
+Kcedil; U+00136
+kcedil; U+00137
+Kcy; U+0041A
+kcy; U+0043A
+Kfr; U+1D50E
+kfr; U+1D528
+kgreen; U+00138
+KHcy; U+00425
+khcy; U+00445
+KJcy; U+0040C
+kjcy; U+0045C
+Kopf; U+1D542
+kopf; U+1D55C
+Kscr; U+1D4A6
+kscr; U+1D4C0
+lAarr; U+021DA
+Lacute; U+00139
+lacute; U+0013A
+laemptyv; U+029B4
+lagran; U+02112
+Lambda; U+0039B
+lambda; U+003BB
+Lang; U+027EA
+lang; U+027E8
+langd; U+02991
+langle; U+027E8
+lap; U+02A85
+Laplacetrf; U+02112
+laquo; U+000AB
+laquo U+000AB
+Larr; U+0219E
+lArr; U+021D0
+larr; U+02190
+larrb; U+021E4
+larrbfs; U+0291F
+larrfs; U+0291D
+larrhk; U+021A9
+larrlp; U+021AB
+larrpl; U+02939
+larrsim; U+02973
+larrtl; U+021A2
+lat; U+02AAB
+lAtail; U+0291B
+latail; U+02919
+late; U+02AAD
+lates; U+02AAD U+0FE00
+lBarr; U+0290E
+lbarr; U+0290C
+lbbrk; U+02772
+lbrace; U+0007B
+lbrack; U+0005B
+lbrke; U+0298B
+lbrksld; U+0298F
+lbrkslu; U+0298D
+Lcaron; U+0013D
+lcaron; U+0013E
+Lcedil; U+0013B
+lcedil; U+0013C
+lceil; U+02308
+lcub; U+0007B
+Lcy; U+0041B
+lcy; U+0043B
+ldca; U+02936
+ldquo; U+0201C
+ldquor; U+0201E
+ldrdhar; U+02967
+ldrushar; U+0294B
+ldsh; U+021B2
+lE; U+02266
+le; U+02264
+LeftAngleBracket; U+027E8
+LeftArrow; U+02190
+Leftarrow; U+021D0
+leftarrow; U+02190
+LeftArrowBar; U+021E4
+LeftArrowRightArrow; U+021C6
+leftarrowtail; U+021A2
+LeftCeiling; U+02308
+LeftDoubleBracket; U+027E6
+LeftDownTeeVector; U+02961
+LeftDownVector; U+021C3
+LeftDownVectorBar; U+02959
+LeftFloor; U+0230A
+leftharpoondown; U+021BD
+leftharpoonup; U+021BC
+leftleftarrows; U+021C7
+LeftRightArrow; U+02194
+Leftrightarrow; U+021D4
+leftrightarrow; U+02194
+leftrightarrows; U+021C6
+leftrightharpoons; U+021CB
+leftrightsquigarrow; U+021AD
+LeftRightVector; U+0294E
+LeftTee; U+022A3
+LeftTeeArrow; U+021A4
+LeftTeeVector; U+0295A
+leftthreetimes; U+022CB
+LeftTriangle; U+022B2
+LeftTriangleBar; U+029CF
+LeftTriangleEqual; U+022B4
+LeftUpDownVector; U+02951
+LeftUpTeeVector; U+02960
+LeftUpVector; U+021BF
+LeftUpVectorBar; U+02958
+LeftVector; U+021BC
+LeftVectorBar; U+02952
+lEg; U+02A8B
+leg; U+022DA
+leq; U+02264
+leqq; U+02266
+leqslant; U+02A7D
+les; U+02A7D
+lescc; U+02AA8
+lesdot; U+02A7F
+lesdoto; U+02A81
+lesdotor; U+02A83
+lesg; U+022DA U+0FE00
+lesges; U+02A93
+lessapprox; U+02A85
+lessdot; U+022D6
+lesseqgtr; U+022DA
+lesseqqgtr; U+02A8B
+LessEqualGreater; U+022DA
+LessFullEqual; U+02266
+LessGreater; U+02276
+lessgtr; U+02276
+LessLess; U+02AA1
+lesssim; U+02272
+LessSlantEqual; U+02A7D
+LessTilde; U+02272
+lfisht; U+0297C
+lfloor; U+0230A
+Lfr; U+1D50F
+lfr; U+1D529
+lg; U+02276
+lgE; U+02A91
+lHar; U+02962
+lhard; U+021BD
+lharu; U+021BC
+lharul; U+0296A
+lhblk; U+02584
+LJcy; U+00409
+ljcy; U+00459
+Ll; U+022D8
+ll; U+0226A
+llarr; U+021C7
+llcorner; U+0231E
+Lleftarrow; U+021DA
+llhard; U+0296B
+lltri; U+025FA
+Lmidot; U+0013F
+lmidot; U+00140
+lmoust; U+023B0
+lmoustache; U+023B0
+lnap; U+02A89
+lnapprox; U+02A89
+lnE; U+02268
+lne; U+02A87
+lneq; U+02A87
+lneqq; U+02268
+lnsim; U+022E6
+loang; U+027EC
+loarr; U+021FD
+lobrk; U+027E6
+LongLeftArrow; U+027F5
+Longleftarrow; U+027F8
+longleftarrow; U+027F5
+LongLeftRightArrow; U+027F7
+Longleftrightarrow; U+027FA
+longleftrightarrow; U+027F7
+longmapsto; U+027FC
+LongRightArrow; U+027F6
+Longrightarrow; U+027F9
+longrightarrow; U+027F6
+looparrowleft; U+021AB
+looparrowright; U+021AC
+lopar; U+02985
+Lopf; U+1D543
+lopf; U+1D55D
+loplus; U+02A2D
+lotimes; U+02A34
+lowast; U+02217
+lowbar; U+0005F
+LowerLeftArrow; U+02199
+LowerRightArrow; U+02198
+loz; U+025CA
+lozenge; U+025CA
+lozf; U+029EB
+lpar; U+00028
+lparlt; U+02993
+lrarr; U+021C6
+lrcorner; U+0231F
+lrhar; U+021CB
+lrhard; U+0296D
+lrm; U+0200E
+lrtri; U+022BF
+lsaquo; U+02039
+Lscr; U+02112
+lscr; U+1D4C1
+Lsh; U+021B0
+lsh; U+021B0
+lsim; U+02272
+lsime; U+02A8D
+lsimg; U+02A8F
+lsqb; U+0005B
+lsquo; U+02018
+lsquor; U+0201A
+Lstrok; U+00141
+lstrok; U+00142
+LT; U+0003C
+LT U+0003C
+Lt; U+0226A
+lt; U+0003C
+lt U+0003C
+ltcc; U+02AA6
+ltcir; U+02A79
+ltdot; U+022D6
+lthree; U+022CB
+ltimes; U+022C9
+ltlarr; U+02976
+ltquest; U+02A7B
+ltri; U+025C3
+ltrie; U+022B4
+ltrif; U+025C2
+ltrPar; U+02996
+lurdshar; U+0294A
+luruhar; U+02966
+lvertneqq; U+02268 U+0FE00
+lvnE; U+02268 U+0FE00
+macr; U+000AF
+macr U+000AF
+male; U+02642
+malt; U+02720
+maltese; U+02720
+Map; U+02905
+map; U+021A6
+mapsto; U+021A6
+mapstodown; U+021A7
+mapstoleft; U+021A4
+mapstoup; U+021A5
+marker; U+025AE
+mcomma; U+02A29
+Mcy; U+0041C
+mcy; U+0043C
+mdash; U+02014
+mDDot; U+0223A
+measuredangle; U+02221
+MediumSpace; U+0205F
+Mellintrf; U+02133
+Mfr; U+1D510
+mfr; U+1D52A
+mho; U+02127
+micro; U+000B5
+micro U+000B5
+mid; U+02223
+midast; U+0002A
+midcir; U+02AF0
+middot; U+000B7
+middot U+000B7
+minus; U+02212
+minusb; U+0229F
+minusd; U+02238
+minusdu; U+02A2A
+MinusPlus; U+02213
+mlcp; U+02ADB
+mldr; U+02026
+mnplus; U+02213
+models; U+022A7
+Mopf; U+1D544
+mopf; U+1D55E
+mp; U+02213
+Mscr; U+02133
+mscr; U+1D4C2
+mstpos; U+0223E
+Mu; U+0039C
+mu; U+003BC
+multimap; U+022B8
+mumap; U+022B8
+nabla; U+02207
+Nacute; U+00143
+nacute; U+00144
+nang; U+02220 U+020D2
+nap; U+02249
+napE; U+02A70 U+00338
+napid; U+0224B U+00338
+napos; U+00149
+napprox; U+02249
+natur; U+0266E
+natural; U+0266E
+naturals; U+02115
+nbsp; U+000A0
+nbsp U+000A0
+nbump; U+0224E U+00338
+nbumpe; U+0224F U+00338
+ncap; U+02A43
+Ncaron; U+00147
+ncaron; U+00148
+Ncedil; U+00145
+ncedil; U+00146
+ncong; U+02247
+ncongdot; U+02A6D U+00338
+ncup; U+02A42
+Ncy; U+0041D
+ncy; U+0043D
+ndash; U+02013
+ne; U+02260
+nearhk; U+02924
+neArr; U+021D7
+nearr; U+02197
+nearrow; U+02197
+nedot; U+02250 U+00338
+NegativeMediumSpace; U+0200B
+NegativeThickSpace; U+0200B
+NegativeThinSpace; U+0200B
+NegativeVeryThinSpace; U+0200B
+nequiv; U+02262
+nesear; U+02928
+nesim; U+02242 U+00338
+NestedGreaterGreater; U+0226B
+NestedLessLess; U+0226A
+NewLine; U+0000A
+nexist; U+02204
+nexists; U+02204
+Nfr; U+1D511
+nfr; U+1D52B
+ngE; U+02267 U+00338
+nge; U+02271
+ngeq; U+02271
+ngeqq; U+02267 U+00338
+ngeqslant; U+02A7E U+00338
+nges; U+02A7E U+00338
+nGg; U+022D9 U+00338
+ngsim; U+02275
+nGt; U+0226B U+020D2
+ngt; U+0226F
+ngtr; U+0226F
+nGtv; U+0226B U+00338
+nhArr; U+021CE
+nharr; U+021AE
+nhpar; U+02AF2
+ni; U+0220B
+nis; U+022FC
+nisd; U+022FA
+niv; U+0220B
+NJcy; U+0040A
+njcy; U+0045A
+nlArr; U+021CD
+nlarr; U+0219A
+nldr; U+02025
+nlE; U+02266 U+00338
+nle; U+02270
+nLeftarrow; U+021CD
+nleftarrow; U+0219A
+nLeftrightarrow; U+021CE
+nleftrightarrow; U+021AE
+nleq; U+02270
+nleqq; U+02266 U+00338
+nleqslant; U+02A7D U+00338
+nles; U+02A7D U+00338
+nless; U+0226E
+nLl; U+022D8 U+00338
+nlsim; U+02274
+nLt; U+0226A U+020D2
+nlt; U+0226E
+nltri; U+022EA
+nltrie; U+022EC
+nLtv; U+0226A U+00338
+nmid; U+02224
+NoBreak; U+02060
+NonBreakingSpace; U+000A0
+Nopf; U+02115
+nopf; U+1D55F
+Not; U+02AEC
+not; U+000AC
+not U+000AC
+NotCongruent; U+02262
+NotCupCap; U+0226D
+NotDoubleVerticalBar; U+02226
+NotElement; U+02209
+NotEqual; U+02260
+NotEqualTilde; U+02242 U+00338
+NotExists; U+02204
+NotGreater; U+0226F
+NotGreaterEqual; U+02271
+NotGreaterFullEqual; U+02267 U+00338
+NotGreaterGreater; U+0226B U+00338
+NotGreaterLess; U+02279
+NotGreaterSlantEqual; U+02A7E U+00338
+NotGreaterTilde; U+02275
+NotHumpDownHump; U+0224E U+00338
+NotHumpEqual; U+0224F U+00338
+notin; U+02209
+notindot; U+022F5 U+00338
+notinE; U+022F9 U+00338
+notinva; U+02209
+notinvb; U+022F7
+notinvc; U+022F6
+NotLeftTriangle; U+022EA
+NotLeftTriangleBar; U+029CF U+00338
+NotLeftTriangleEqual; U+022EC
+NotLess; U+0226E
+NotLessEqual; U+02270
+NotLessGreater; U+02278
+NotLessLess; U+0226A U+00338
+NotLessSlantEqual; U+02A7D U+00338
+NotLessTilde; U+02274
+NotNestedGreaterGreater; U+02AA2 U+00338
+NotNestedLessLess; U+02AA1 U+00338
+notni; U+0220C
+notniva; U+0220C
+notnivb; U+022FE
+notnivc; U+022FD
+NotPrecedes; U+02280
+NotPrecedesEqual; U+02AAF U+00338
+NotPrecedesSlantEqual; U+022E0
+NotReverseElement; U+0220C
+NotRightTriangle; U+022EB
+NotRightTriangleBar; U+029D0 U+00338
+NotRightTriangleEqual; U+022ED
+NotSquareSubset; U+0228F U+00338
+NotSquareSubsetEqual; U+022E2
+NotSquareSuperset; U+02290 U+00338
+NotSquareSupersetEqual; U+022E3
+NotSubset; U+02282 U+020D2
+NotSubsetEqual; U+02288
+NotSucceeds; U+02281
+NotSucceedsEqual; U+02AB0 U+00338
+NotSucceedsSlantEqual; U+022E1
+NotSucceedsTilde; U+0227F U+00338
+NotSuperset; U+02283 U+020D2
+NotSupersetEqual; U+02289
+NotTilde; U+02241
+NotTildeEqual; U+02244
+NotTildeFullEqual; U+02247
+NotTildeTilde; U+02249
+NotVerticalBar; U+02224
+npar; U+02226
+nparallel; U+02226
+nparsl; U+02AFD U+020E5
+npart; U+02202 U+00338
+npolint; U+02A14
+npr; U+02280
+nprcue; U+022E0
+npre; U+02AAF U+00338
+nprec; U+02280
+npreceq; U+02AAF U+00338
+nrArr; U+021CF
+nrarr; U+0219B
+nrarrc; U+02933 U+00338
+nrarrw; U+0219D U+00338
+nRightarrow; U+021CF
+nrightarrow; U+0219B
+nrtri; U+022EB
+nrtrie; U+022ED
+nsc; U+02281
+nsccue; U+022E1
+nsce; U+02AB0 U+00338
+Nscr; U+1D4A9
+nscr; U+1D4C3
+nshortmid; U+02224
+nshortparallel; U+02226
+nsim; U+02241
+nsime; U+02244
+nsimeq; U+02244
+nsmid; U+02224
+nspar; U+02226
+nsqsube; U+022E2
+nsqsupe; U+022E3
+nsub; U+02284
+nsubE; U+02AC5 U+00338
+nsube; U+02288
+nsubset; U+02282 U+020D2
+nsubseteq; U+02288
+nsubseteqq; U+02AC5 U+00338
+nsucc; U+02281
+nsucceq; U+02AB0 U+00338
+nsup; U+02285
+nsupE; U+02AC6 U+00338
+nsupe; U+02289
+nsupset; U+02283 U+020D2
+nsupseteq; U+02289
+nsupseteqq; U+02AC6 U+00338
+ntgl; U+02279
+Ntilde; U+000D1
+Ntilde U+000D1
+ntilde; U+000F1
+ntilde U+000F1
+ntlg; U+02278
+ntriangleleft; U+022EA
+ntrianglelefteq; U+022EC
+ntriangleright; U+022EB
+ntrianglerighteq; U+022ED
+Nu; U+0039D
+nu; U+003BD
+num; U+00023
+numero; U+02116
+numsp; U+02007
+nvap; U+0224D U+020D2
+nVDash; U+022AF
+nVdash; U+022AE
+nvDash; U+022AD
+nvdash; U+022AC
+nvge; U+02265 U+020D2
+nvgt; U+0003E U+020D2
+nvHarr; U+02904
+nvinfin; U+029DE
+nvlArr; U+02902
+nvle; U+02264 U+020D2
+nvlt; U+0003C U+020D2
+nvltrie; U+022B4 U+020D2
+nvrArr; U+02903
+nvrtrie; U+022B5 U+020D2
+nvsim; U+0223C U+020D2
+nwarhk; U+02923
+nwArr; U+021D6
+nwarr; U+02196
+nwarrow; U+02196
+nwnear; U+02927
+Oacute; U+000D3
+Oacute U+000D3
+oacute; U+000F3
+oacute U+000F3
+oast; U+0229B
+ocir; U+0229A
+Ocirc; U+000D4
+Ocirc U+000D4
+ocirc; U+000F4
+ocirc U+000F4
+Ocy; U+0041E
+ocy; U+0043E
+odash; U+0229D
+Odblac; U+00150
+odblac; U+00151
+odiv; U+02A38
+odot; U+02299
+odsold; U+029BC
+OElig; U+00152
+oelig; U+00153
+ofcir; U+029BF
+Ofr; U+1D512
+ofr; U+1D52C
+ogon; U+002DB
+Ograve; U+000D2
+Ograve U+000D2
+ograve; U+000F2
+ograve U+000F2
+ogt; U+029C1
+ohbar; U+029B5
+ohm; U+003A9
+oint; U+0222E
+olarr; U+021BA
+olcir; U+029BE
+olcross; U+029BB
+oline; U+0203E
+olt; U+029C0
+Omacr; U+0014C
+omacr; U+0014D
+Omega; U+003A9
+omega; U+003C9
+Omicron; U+0039F
+omicron; U+003BF
+omid; U+029B6
+ominus; U+02296
+Oopf; U+1D546
+oopf; U+1D560
+opar; U+029B7
+OpenCurlyDoubleQuote; U+0201C
+OpenCurlyQuote; U+02018
+operp; U+029B9
+oplus; U+02295
+Or; U+02A54
+or; U+02228
+orarr; U+021BB
+ord; U+02A5D
+order; U+02134
+orderof; U+02134
+ordf; U+000AA
+ordf U+000AA
+ordm; U+000BA
+ordm U+000BA
+origof; U+022B6
+oror; U+02A56
+orslope; U+02A57
+orv; U+02A5B
+oS; U+024C8
+Oscr; U+1D4AA
+oscr; U+02134
+Oslash; U+000D8
+Oslash U+000D8
+oslash; U+000F8
+oslash U+000F8
+osol; U+02298
+Otilde; U+000D5
+Otilde U+000D5
+otilde; U+000F5
+otilde U+000F5
+Otimes; U+02A37
+otimes; U+02297
+otimesas; U+02A36
+Ouml; U+000D6
+Ouml U+000D6
+ouml; U+000F6
+ouml U+000F6
+ovbar; U+0233D
+OverBar; U+0203E
+OverBrace; U+023DE
+OverBracket; U+023B4
+OverParenthesis; U+023DC
+par; U+02225
+para; U+000B6
+para U+000B6
+parallel; U+02225
+parsim; U+02AF3
+parsl; U+02AFD
+part; U+02202
+PartialD; U+02202
+Pcy; U+0041F
+pcy; U+0043F
+percnt; U+00025
+period; U+0002E
+permil; U+02030
+perp; U+022A5
+pertenk; U+02031
+Pfr; U+1D513
+pfr; U+1D52D
+Phi; U+003A6
+phi; U+003C6
+phiv; U+003D5
+phmmat; U+02133
+phone; U+0260E
+Pi; U+003A0
+pi; U+003C0
+pitchfork; U+022D4
+piv; U+003D6
+planck; U+0210F
+planckh; U+0210E
+plankv; U+0210F
+plus; U+0002B
+plusacir; U+02A23
+plusb; U+0229E
+pluscir; U+02A22
+plusdo; U+02214
+plusdu; U+02A25
+pluse; U+02A72
+PlusMinus; U+000B1
+plusmn; U+000B1
+plusmn U+000B1
+plussim; U+02A26
+plustwo; U+02A27
+pm; U+000B1
+Poincareplane; U+0210C
+pointint; U+02A15
+Popf; U+02119
+popf; U+1D561
+pound; U+000A3
+pound U+000A3
+Pr; U+02ABB
+pr; U+0227A
+prap; U+02AB7
+prcue; U+0227C
+prE; U+02AB3
+pre; U+02AAF
+prec; U+0227A
+precapprox; U+02AB7
+preccurlyeq; U+0227C
+Precedes; U+0227A
+PrecedesEqual; U+02AAF
+PrecedesSlantEqual; U+0227C
+PrecedesTilde; U+0227E
+preceq; U+02AAF
+precnapprox; U+02AB9
+precneqq; U+02AB5
+precnsim; U+022E8
+precsim; U+0227E
+Prime; U+02033
+prime; U+02032
+primes; U+02119
+prnap; U+02AB9
+prnE; U+02AB5
+prnsim; U+022E8
+prod; U+0220F
+Product; U+0220F
+profalar; U+0232E
+profline; U+02312
+profsurf; U+02313
+prop; U+0221D
+Proportion; U+02237
+Proportional; U+0221D
+propto; U+0221D
+prsim; U+0227E
+prurel; U+022B0
+Pscr; U+1D4AB
+pscr; U+1D4C5
+Psi; U+003A8
+psi; U+003C8
+puncsp; U+02008
+Qfr; U+1D514
+qfr; U+1D52E
+qint; U+02A0C
+Qopf; U+0211A
+qopf; U+1D562
+qprime; U+02057
+Qscr; U+1D4AC
+qscr; U+1D4C6
+quaternions; U+0210D
+quatint; U+02A16
+quest; U+0003F
+questeq; U+0225F
+QUOT; U+00022
+QUOT U+00022
+quot; U+00022
+quot U+00022
+rAarr; U+021DB
+race; U+0223D U+00331
+Racute; U+00154
+racute; U+00155
+radic; U+0221A
+raemptyv; U+029B3
+Rang; U+027EB
+rang; U+027E9
+rangd; U+02992
+range; U+029A5
+rangle; U+027E9
+raquo; U+000BB
+raquo U+000BB
+Rarr; U+021A0
+rArr; U+021D2
+rarr; U+02192
+rarrap; U+02975
+rarrb; U+021E5
+rarrbfs; U+02920
+rarrc; U+02933
+rarrfs; U+0291E
+rarrhk; U+021AA
+rarrlp; U+021AC
+rarrpl; U+02945
+rarrsim; U+02974
+Rarrtl; U+02916
+rarrtl; U+021A3
+rarrw; U+0219D
+rAtail; U+0291C
+ratail; U+0291A
+ratio; U+02236
+rationals; U+0211A
+RBarr; U+02910
+rBarr; U+0290F
+rbarr; U+0290D
+rbbrk; U+02773
+rbrace; U+0007D
+rbrack; U+0005D
+rbrke; U+0298C
+rbrksld; U+0298E
+rbrkslu; U+02990
+Rcaron; U+00158
+rcaron; U+00159
+Rcedil; U+00156
+rcedil; U+00157
+rceil; U+02309
+rcub; U+0007D
+Rcy; U+00420
+rcy; U+00440
+rdca; U+02937
+rdldhar; U+02969
+rdquo; U+0201D
+rdquor; U+0201D
+rdsh; U+021B3
+Re; U+0211C
+real; U+0211C
+realine; U+0211B
+realpart; U+0211C
+reals; U+0211D
+rect; U+025AD
+REG; U+000AE
+REG U+000AE
+reg; U+000AE
+reg U+000AE
+ReverseElement; U+0220B
+ReverseEquilibrium; U+021CB
+ReverseUpEquilibrium; U+0296F
+rfisht; U+0297D
+rfloor; U+0230B
+Rfr; U+0211C
+rfr; U+1D52F
+rHar; U+02964
+rhard; U+021C1
+rharu; U+021C0
+rharul; U+0296C
+Rho; U+003A1
+rho; U+003C1
+rhov; U+003F1
+RightAngleBracket; U+027E9
+RightArrow; U+02192
+Rightarrow; U+021D2
+rightarrow; U+02192
+RightArrowBar; U+021E5
+RightArrowLeftArrow; U+021C4
+rightarrowtail; U+021A3
+RightCeiling; U+02309
+RightDoubleBracket; U+027E7
+RightDownTeeVector; U+0295D
+RightDownVector; U+021C2
+RightDownVectorBar; U+02955
+RightFloor; U+0230B
+rightharpoondown; U+021C1
+rightharpoonup; U+021C0
+rightleftarrows; U+021C4
+rightleftharpoons; U+021CC
+rightrightarrows; U+021C9
+rightsquigarrow; U+0219D
+RightTee; U+022A2
+RightTeeArrow; U+021A6
+RightTeeVector; U+0295B
+rightthreetimes; U+022CC
+RightTriangle; U+022B3
+RightTriangleBar; U+029D0
+RightTriangleEqual; U+022B5
+RightUpDownVector; U+0294F
+RightUpTeeVector; U+0295C
+RightUpVector; U+021BE
+RightUpVectorBar; U+02954
+RightVector; U+021C0
+RightVectorBar; U+02953
+ring; U+002DA
+risingdotseq; U+02253
+rlarr; U+021C4
+rlhar; U+021CC
+rlm; U+0200F
+rmoust; U+023B1
+rmoustache; U+023B1
+rnmid; U+02AEE
+roang; U+027ED
+roarr; U+021FE
+robrk; U+027E7
+ropar; U+02986
+Ropf; U+0211D
+ropf; U+1D563
+roplus; U+02A2E
+rotimes; U+02A35
+RoundImplies; U+02970
+rpar; U+00029
+rpargt; U+02994
+rppolint; U+02A12
+rrarr; U+021C9
+Rrightarrow; U+021DB
+rsaquo; U+0203A
+Rscr; U+0211B
+rscr; U+1D4C7
+Rsh; U+021B1
+rsh; U+021B1
+rsqb; U+0005D
+rsquo; U+02019
+rsquor; U+02019
+rthree; U+022CC
+rtimes; U+022CA
+rtri; U+025B9
+rtrie; U+022B5
+rtrif; U+025B8
+rtriltri; U+029CE
+RuleDelayed; U+029F4
+ruluhar; U+02968
+rx; U+0211E
+Sacute; U+0015A
+sacute; U+0015B
+sbquo; U+0201A
+Sc; U+02ABC
+sc; U+0227B
+scap; U+02AB8
+Scaron; U+00160
+scaron; U+00161
+sccue; U+0227D
+scE; U+02AB4
+sce; U+02AB0
+Scedil; U+0015E
+scedil; U+0015F
+Scirc; U+0015C
+scirc; U+0015D
+scnap; U+02ABA
+scnE; U+02AB6
+scnsim; U+022E9
+scpolint; U+02A13
+scsim; U+0227F
+Scy; U+00421
+scy; U+00441
+sdot; U+022C5
+sdotb; U+022A1
+sdote; U+02A66
+searhk; U+02925
+seArr; U+021D8
+searr; U+02198
+searrow; U+02198
+sect; U+000A7
+sect U+000A7
+semi; U+0003B
+seswar; U+02929
+setminus; U+02216
+setmn; U+02216
+sext; U+02736
+Sfr; U+1D516
+sfr; U+1D530
+sfrown; U+02322
+sharp; U+0266F
+SHCHcy; U+00429
+shchcy; U+00449
+SHcy; U+00428
+shcy; U+00448
+ShortDownArrow; U+02193
+ShortLeftArrow; U+02190
+shortmid; U+02223
+shortparallel; U+02225
+ShortRightArrow; U+02192
+ShortUpArrow; U+02191
+shy; U+000AD
+shy U+000AD
+Sigma; U+003A3
+sigma; U+003C3
+sigmaf; U+003C2
+sigmav; U+003C2
+sim; U+0223C
+simdot; U+02A6A
+sime; U+02243
+simeq; U+02243
+simg; U+02A9E
+simgE; U+02AA0
+siml; U+02A9D
+simlE; U+02A9F
+simne; U+02246
+simplus; U+02A24
+simrarr; U+02972
+slarr; U+02190
+SmallCircle; U+02218
+smallsetminus; U+02216
+smashp; U+02A33
+smeparsl; U+029E4
+smid; U+02223
+smile; U+02323
+smt; U+02AAA
+smte; U+02AAC
+smtes; U+02AAC U+0FE00
+SOFTcy; U+0042C
+softcy; U+0044C
+sol; U+0002F
+solb; U+029C4
+solbar; U+0233F
+Sopf; U+1D54A
+sopf; U+1D564
+spades; U+02660
+spadesuit; U+02660
+spar; U+02225
+sqcap; U+02293
+sqcaps; U+02293 U+0FE00
+sqcup; U+02294
+sqcups; U+02294 U+0FE00
+Sqrt; U+0221A
+sqsub; U+0228F
+sqsube; U+02291
+sqsubset; U+0228F
+sqsubseteq; U+02291
+sqsup; U+02290
+sqsupe; U+02292
+sqsupset; U+02290
+sqsupseteq; U+02292
+squ; U+025A1
+Square; U+025A1
+square; U+025A1
+SquareIntersection; U+02293
+SquareSubset; U+0228F
+SquareSubsetEqual; U+02291
+SquareSuperset; U+02290
+SquareSupersetEqual; U+02292
+SquareUnion; U+02294
+squarf; U+025AA
+squf; U+025AA
+srarr; U+02192
+Sscr; U+1D4AE
+sscr; U+1D4C8
+ssetmn; U+02216
+ssmile; U+02323
+sstarf; U+022C6
+Star; U+022C6
+star; U+02606
+starf; U+02605
+straightepsilon; U+003F5
+straightphi; U+003D5
+strns; U+000AF
+Sub; U+022D0
+sub; U+02282
+subdot; U+02ABD
+subE; U+02AC5
+sube; U+02286
+subedot; U+02AC3
+submult; U+02AC1
+subnE; U+02ACB
+subne; U+0228A
+subplus; U+02ABF
+subrarr; U+02979
+Subset; U+022D0
+subset; U+02282
+subseteq; U+02286
+subseteqq; U+02AC5
+SubsetEqual; U+02286
+subsetneq; U+0228A
+subsetneqq; U+02ACB
+subsim; U+02AC7
+subsub; U+02AD5
+subsup; U+02AD3
+succ; U+0227B
+succapprox; U+02AB8
+succcurlyeq; U+0227D
+Succeeds; U+0227B
+SucceedsEqual; U+02AB0
+SucceedsSlantEqual; U+0227D
+SucceedsTilde; U+0227F
+succeq; U+02AB0
+succnapprox; U+02ABA
+succneqq; U+02AB6
+succnsim; U+022E9
+succsim; U+0227F
+SuchThat; U+0220B
+Sum; U+02211
+sum; U+02211
+sung; U+0266A
+Sup; U+022D1
+sup; U+02283
+sup1; U+000B9
+sup1 U+000B9
+sup2; U+000B2
+sup2 U+000B2
+sup3; U+000B3
+sup3 U+000B3
+supdot; U+02ABE
+supdsub; U+02AD8
+supE; U+02AC6
+supe; U+02287
+supedot; U+02AC4
+Superset; U+02283
+SupersetEqual; U+02287
+suphsol; U+027C9
+suphsub; U+02AD7
+suplarr; U+0297B
+supmult; U+02AC2
+supnE; U+02ACC
+supne; U+0228B
+supplus; U+02AC0
+Supset; U+022D1
+supset; U+02283
+supseteq; U+02287
+supseteqq; U+02AC6
+supsetneq; U+0228B
+supsetneqq; U+02ACC
+supsim; U+02AC8
+supsub; U+02AD4
+supsup; U+02AD6
+swarhk; U+02926
+swArr; U+021D9
+swarr; U+02199
+swarrow; U+02199
+swnwar; U+0292A
+szlig; U+000DF
+szlig U+000DF
+Tab; U+00009
+target; U+02316
+Tau; U+003A4
+tau; U+003C4
+tbrk; U+023B4
+Tcaron; U+00164
+tcaron; U+00165
+Tcedil; U+00162
+tcedil; U+00163
+Tcy; U+00422
+tcy; U+00442
+tdot; U+020DB
+telrec; U+02315
+Tfr; U+1D517
+tfr; U+1D531
+there4; U+02234
+Therefore; U+02234
+therefore; U+02234
+Theta; U+00398
+theta; U+003B8
+thetasym; U+003D1
+thetav; U+003D1
+thickapprox; U+02248
+thicksim; U+0223C
+ThickSpace; U+0205F U+0200A
+thinsp; U+02009
+ThinSpace; U+02009
+thkap; U+02248
+thksim; U+0223C
+THORN; U+000DE
+THORN U+000DE
+thorn; U+000FE
+thorn U+000FE
+Tilde; U+0223C
+tilde; U+002DC
+TildeEqual; U+02243
+TildeFullEqual; U+02245
+TildeTilde; U+02248
+times; U+000D7
+times U+000D7
+timesb; U+022A0
+timesbar; U+02A31
+timesd; U+02A30
+tint; U+0222D
+toea; U+02928
+top; U+022A4
+topbot; U+02336
+topcir; U+02AF1
+Topf; U+1D54B
+topf; U+1D565
+topfork; U+02ADA
+tosa; U+02929
+tprime; U+02034
+TRADE; U+02122
+trade; U+02122
+triangle; U+025B5
+triangledown; U+025BF
+triangleleft; U+025C3
+trianglelefteq; U+022B4
+triangleq; U+0225C
+triangleright; U+025B9
+trianglerighteq; U+022B5
+tridot; U+025EC
+trie; U+0225C
+triminus; U+02A3A
+TripleDot; U+020DB
+triplus; U+02A39
+trisb; U+029CD
+tritime; U+02A3B
+trpezium; U+023E2
+Tscr; U+1D4AF
+tscr; U+1D4C9
+TScy; U+00426
+tscy; U+00446
+TSHcy; U+0040B
+tshcy; U+0045B
+Tstrok; U+00166
+tstrok; U+00167
+twixt; U+0226C
+twoheadleftarrow; U+0219E
+twoheadrightarrow; U+021A0
+Uacute; U+000DA
+Uacute U+000DA
+uacute; U+000FA
+uacute U+000FA
+Uarr; U+0219F
+uArr; U+021D1
+uarr; U+02191
+Uarrocir; U+02949
+Ubrcy; U+0040E
+ubrcy; U+0045E
+Ubreve; U+0016C
+ubreve; U+0016D
+Ucirc; U+000DB
+Ucirc U+000DB
+ucirc; U+000FB
+ucirc U+000FB
+Ucy; U+00423
+ucy; U+00443
+udarr; U+021C5
+Udblac; U+00170
+udblac; U+00171
+udhar; U+0296E
+ufisht; U+0297E
+Ufr; U+1D518
+ufr; U+1D532
+Ugrave; U+000D9
+Ugrave U+000D9
+ugrave; U+000F9
+ugrave U+000F9
+uHar; U+02963
+uharl; U+021BF
+uharr; U+021BE
+uhblk; U+02580
+ulcorn; U+0231C
+ulcorner; U+0231C
+ulcrop; U+0230F
+ultri; U+025F8
+Umacr; U+0016A
+umacr; U+0016B
+uml; U+000A8
+uml U+000A8
+UnderBar; U+0005F
+UnderBrace; U+023DF
+UnderBracket; U+023B5
+UnderParenthesis; U+023DD
+Union; U+022C3
+UnionPlus; U+0228E
+Uogon; U+00172
+uogon; U+00173
+Uopf; U+1D54C
+uopf; U+1D566
+UpArrow; U+02191
+Uparrow; U+021D1
+uparrow; U+02191
+UpArrowBar; U+02912
+UpArrowDownArrow; U+021C5
+UpDownArrow; U+02195
+Updownarrow; U+021D5
+updownarrow; U+02195
+UpEquilibrium; U+0296E
+upharpoonleft; U+021BF
+upharpoonright; U+021BE
+uplus; U+0228E
+UpperLeftArrow; U+02196
+UpperRightArrow; U+02197
+Upsi; U+003D2
+upsi; U+003C5
+upsih; U+003D2
+Upsilon; U+003A5
+upsilon; U+003C5
+UpTee; U+022A5
+UpTeeArrow; U+021A5
+upuparrows; U+021C8
+urcorn; U+0231D
+urcorner; U+0231D
+urcrop; U+0230E
+Uring; U+0016E
+uring; U+0016F
+urtri; U+025F9
+Uscr; U+1D4B0
+uscr; U+1D4CA
+utdot; U+022F0
+Utilde; U+00168
+utilde; U+00169
+utri; U+025B5
+utrif; U+025B4
+uuarr; U+021C8
+Uuml; U+000DC
+Uuml U+000DC
+uuml; U+000FC
+uuml U+000FC
+uwangle; U+029A7
+vangrt; U+0299C
+varepsilon; U+003F5
+varkappa; U+003F0
+varnothing; U+02205
+varphi; U+003D5
+varpi; U+003D6
+varpropto; U+0221D
+vArr; U+021D5
+varr; U+02195
+varrho; U+003F1
+varsigma; U+003C2
+varsubsetneq; U+0228A U+0FE00
+varsubsetneqq; U+02ACB U+0FE00
+varsupsetneq; U+0228B U+0FE00
+varsupsetneqq; U+02ACC U+0FE00
+vartheta; U+003D1
+vartriangleleft; U+022B2
+vartriangleright; U+022B3
+Vbar; U+02AEB
+vBar; U+02AE8
+vBarv; U+02AE9
+Vcy; U+00412
+vcy; U+00432
+VDash; U+022AB
+Vdash; U+022A9
+vDash; U+022A8
+vdash; U+022A2
+Vdashl; U+02AE6
+Vee; U+022C1
+vee; U+02228
+veebar; U+022BB
+veeeq; U+0225A
+vellip; U+022EE
+Verbar; U+02016
+verbar; U+0007C
+Vert; U+02016
+vert; U+0007C
+VerticalBar; U+02223
+VerticalLine; U+0007C
+VerticalSeparator; U+02758
+VerticalTilde; U+02240
+VeryThinSpace; U+0200A
+Vfr; U+1D519
+vfr; U+1D533
+vltri; U+022B2
+vnsub; U+02282 U+020D2
+vnsup; U+02283 U+020D2
+Vopf; U+1D54D
+vopf; U+1D567
+vprop; U+0221D
+vrtri; U+022B3
+Vscr; U+1D4B1
+vscr; U+1D4CB
+vsubnE; U+02ACB U+0FE00
+vsubne; U+0228A U+0FE00
+vsupnE; U+02ACC U+0FE00
+vsupne; U+0228B U+0FE00
+Vvdash; U+022AA
+vzigzag; U+0299A
+Wcirc; U+00174
+wcirc; U+00175
+wedbar; U+02A5F
+Wedge; U+022C0
+wedge; U+02227
+wedgeq; U+02259
+weierp; U+02118
+Wfr; U+1D51A
+wfr; U+1D534
+Wopf; U+1D54E
+wopf; U+1D568
+wp; U+02118
+wr; U+02240
+wreath; U+02240
+Wscr; U+1D4B2
+wscr; U+1D4CC
+xcap; U+022C2
+xcirc; U+025EF
+xcup; U+022C3
+xdtri; U+025BD
+Xfr; U+1D51B
+xfr; U+1D535
+xhArr; U+027FA
+xharr; U+027F7
+Xi; U+0039E
+xi; U+003BE
+xlArr; U+027F8
+xlarr; U+027F5
+xmap; U+027FC
+xnis; U+022FB
+xodot; U+02A00
+Xopf; U+1D54F
+xopf; U+1D569
+xoplus; U+02A01
+xotime; U+02A02
+xrArr; U+027F9
+xrarr; U+027F6
+Xscr; U+1D4B3
+xscr; U+1D4CD
+xsqcup; U+02A06
+xuplus; U+02A04
+xutri; U+025B3
+xvee; U+022C1
+xwedge; U+022C0
+Yacute; U+000DD
+Yacute U+000DD
+yacute; U+000FD
+yacute U+000FD
+YAcy; U+0042F
+yacy; U+0044F
+Ycirc; U+00176
+ycirc; U+00177
+Ycy; U+0042B
+ycy; U+0044B
+yen; U+000A5
+yen U+000A5
+Yfr; U+1D51C
+yfr; U+1D536
+YIcy; U+00407
+yicy; U+00457
+Yopf; U+1D550
+yopf; U+1D56A
+Yscr; U+1D4B4
+yscr; U+1D4CE
+YUcy; U+0042E
+yucy; U+0044E
+Yuml; U+00178
+yuml; U+000FF
+yuml U+000FF
+Zacute; U+00179
+zacute; U+0017A
+Zcaron; U+0017D
+zcaron; U+0017E
+Zcy; U+00417
+zcy; U+00437
+Zdot; U+0017B
+zdot; U+0017C
+zeetrf; U+02128
+ZeroWidthSpace; U+0200B
+Zeta; U+00396
+zeta; U+003B6
+Zfr; U+02128
+zfr; U+1D537
+ZHcy; U+00416
+zhcy; U+00436
+zigrarr; U+021DD
+Zopf; U+02124
+zopf; U+1D56B
+Zscr; U+1D4B5
+zscr; U+1D4CF
+zwj; U+0200D
+zwnj; U+0200C
Index: main/trunk/greenstone2/perllib/cpan/Mojo/WebSocket.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojo/WebSocket.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojo/WebSocket.pm	(revision 32205)
@@ -0,0 +1,283 @@
+package Mojo::WebSocket;
+use Mojo::Base -strict;
+
+use Config;
+use Exporter 'import';
+use Mojo::Util qw(b64_encode dumper sha1_bytes xor_encode);
+
+use constant DEBUG => $ENV{MOJO_WEBSOCKET_DEBUG} || 0;
+
+# Unique value from RFC 6455
+use constant GUID => '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
+
+# Perl with support for quads
+use constant MODERN =>
+  (($Config{use64bitint} // '') eq 'define' || $Config{longsize} >= 8);
+
+# Opcodes
+use constant {
+  WS_CONTINUATION => 0x0,
+  WS_TEXT         => 0x1,
+  WS_BINARY       => 0x2,
+  WS_CLOSE        => 0x8,
+  WS_PING         => 0x9,
+  WS_PONG         => 0xa
+};
+
+our @EXPORT_OK = (
+  qw(WS_BINARY WS_CLOSE WS_CONTINUATION WS_PING WS_PONG WS_TEXT),
+  qw(build_frame challenge client_handshake parse_frame server_handshake)
+);
+
+sub build_frame {
+  my ($masked, $fin, $rsv1, $rsv2, $rsv3, $op, $payload) = @_;
+  warn "-- Building frame ($fin, $rsv1, $rsv2, $rsv3, $op)\n" if DEBUG;
+
+  # Head
+  my $head = $op + ($fin ? 128 : 0);
+  $head |= 0b01000000 if $rsv1;
+  $head |= 0b00100000 if $rsv2;
+  $head |= 0b00010000 if $rsv3;
+  my $frame = pack 'C', $head;
+
+  # Small payload
+  my $len = length $payload;
+  if ($len < 126) {
+    warn "-- Small payload ($len)\n@{[dumper $payload]}" if DEBUG;
+    $frame .= pack 'C', $masked ? ($len | 128) : $len;
+  }
+
+  # Extended payload (16-bit)
+  elsif ($len < 65536) {
+    warn "-- Extended 16-bit payload ($len)\n@{[dumper $payload]}" if DEBUG;
+    $frame .= pack 'Cn', $masked ? (126 | 128) : 126, $len;
+  }
+
+  # Extended payload (64-bit with 32-bit fallback)
+  else {
+    warn "-- Extended 64-bit payload ($len)\n@{[dumper $payload]}" if DEBUG;
+    $frame .= pack 'C', $masked ? (127 | 128) : 127;
+    $frame .= MODERN ? pack('Q>', $len) : pack('NN', 0, $len & 0xffffffff);
+  }
+
+  # Mask payload
+  if ($masked) {
+    my $mask = pack 'N', int(rand 9 x 7);
+    $payload = $mask . xor_encode($payload, $mask x 128);
+  }
+
+  return $frame . $payload;
+}
+
+sub challenge {
+  my $tx = shift;
+
+  # "permessage-deflate" extension
+  my $headers = $tx->res->headers;
+  $tx->compressed(1)
+    if ($headers->sec_websocket_extensions // '') =~ /permessage-deflate/;
+
+  return _challenge($tx->req->headers->sec_websocket_key) eq
+    $headers->sec_websocket_accept;
+}
+
+sub client_handshake {
+  my $tx = shift;
+
+  my $headers = $tx->req->headers;
+  $headers->upgrade('websocket')      unless $headers->upgrade;
+  $headers->connection('Upgrade')     unless $headers->connection;
+  $headers->sec_websocket_version(13) unless $headers->sec_websocket_version;
+
+  # Generate 16 byte WebSocket challenge
+  my $challenge = b64_encode sprintf('%16u', int(rand 9 x 16)), '';
+  $headers->sec_websocket_key($challenge) unless $headers->sec_websocket_key;
+
+  return $tx;
+}
+
+sub parse_frame {
+  my ($buffer, $max) = @_;
+
+  # Head
+  return undef unless length $$buffer >= 2;
+  my ($first, $second) = unpack 'C2', $$buffer;
+
+  # FIN
+  my $fin = ($first & 0b10000000) == 0b10000000 ? 1 : 0;
+
+  # RSV1-3
+  my $rsv1 = ($first & 0b01000000) == 0b01000000 ? 1 : 0;
+  my $rsv2 = ($first & 0b00100000) == 0b00100000 ? 1 : 0;
+  my $rsv3 = ($first & 0b00010000) == 0b00010000 ? 1 : 0;
+
+  # Opcode
+  my $op = $first & 0b00001111;
+  warn "-- Parsing frame ($fin, $rsv1, $rsv2, $rsv3, $op)\n" if DEBUG;
+
+  # Small payload
+  my ($hlen, $len) = (2, $second & 0b01111111);
+  if ($len < 126) { warn "-- Small payload ($len)\n" if DEBUG }
+
+  # Extended payload (16-bit)
+  elsif ($len == 126) {
+    return undef unless length $$buffer > 4;
+    $hlen = 4;
+    $len = unpack 'x2n', $$buffer;
+    warn "-- Extended 16-bit payload ($len)\n" if DEBUG;
+  }
+
+  # Extended payload (64-bit with 32-bit fallback)
+  elsif ($len == 127) {
+    return undef unless length $$buffer > 10;
+    $hlen = 10;
+    $len = MODERN ? unpack('x2Q>', $$buffer) : unpack('x2x4N', $$buffer);
+    warn "-- Extended 64-bit payload ($len)\n" if DEBUG;
+  }
+
+  # Check message size
+  return 1 if $len > $max;
+
+  # Check if whole packet has arrived
+  $len += 4 if my $masked = $second & 0b10000000;
+  return undef if length $$buffer < ($hlen + $len);
+  substr $$buffer, 0, $hlen, '';
+
+  # Payload
+  my $payload = $len ? substr($$buffer, 0, $len, '') : '';
+  $payload = xor_encode($payload, substr($payload, 0, 4, '') x 128) if $masked;
+  warn dumper $payload if DEBUG;
+
+  return [$fin, $rsv1, $rsv2, $rsv3, $op, $payload];
+}
+
+sub server_handshake {
+  my $tx = shift;
+
+  my $headers = $tx->res->headers;
+  $headers->upgrade('websocket')->connection('Upgrade');
+  $headers->sec_websocket_accept(
+    _challenge($tx->req->headers->sec_websocket_key));
+
+  return $tx;
+}
+
+sub _challenge { b64_encode(sha1_bytes(($_[0] || '') . GUID), '') }
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::WebSocket - The WebSocket protocol
+
+=head1 SYNOPSIS
+
+  use Mojo::WebSocket qw(WS_TEXT build_frame parse_frame);
+
+  my $bytes = build_frame 0, 1, 0, 0, 0, WS_TEXT, 'Hello World!';
+  my $frame = parse_frame \$bytes, 262144;
+
+=head1 DESCRIPTION
+
+L<Mojo::WebSocket> implements the WebSocket protocol as described in
+L<RFC 6455|http://tools.ietf.org/html/rfc6455>. Note that 64-bit frames require
+a Perl with support for quads or they are limited to 32-bit.
+
+=head1 FUNCTIONS
+
+L<Mojo::WebSocket> implements the following functions, which can be imported
+individually.
+
+=head2 build_frame
+
+  my $bytes = build_frame $masked, $fin, $rsv1, $rsv2, $rsv3, $op, $payload;
+
+Build WebSocket frame.
+
+  # Masked binary frame with FIN bit and payload
+  say build_frame 1, 1, 0, 0, 0, WS_BINARY, 'Hello World!';
+
+  # Text frame with payload but without FIN bit
+  say build_frame 0, 0, 0, 0, 0, WS_TEXT, 'Hello ';
+
+  # Continuation frame with FIN bit and payload
+  say build_frame 0, 1, 0, 0, 0, WS_CONTINUATION, 'World!';
+
+  # Close frame with FIN bit and without payload
+  say build_frame 0, 1, 0, 0, 0, WS_CLOSE, '';
+
+  # Ping frame with FIN bit and payload
+  say build_frame 0, 1, 0, 0, 0, WS_PING, 'Test 123';
+
+  # Pong frame with FIN bit and payload
+  say build_frame 0, 1, 0, 0, 0, WS_PONG, 'Test 123';
+
+=head2 challenge
+
+  my $bool = challenge Mojo::Transaction::WebSocket->new;
+
+Check WebSocket handshake challenge.
+
+=head2 client_handshake
+
+  my $tx = client_handshake Mojo::Transaction::HTTP->new;
+
+Perform WebSocket handshake client-side.
+
+=head2 parse_frame
+
+  my $frame = parse_frame \$bytes, $limit;
+
+Parse WebSocket frame.
+
+  # Parse single frame and remove it from buffer
+  my $frame = parse_frame \$buffer, 262144;
+  say "FIN: $frame->[0]";
+  say "RSV1: $frame->[1]";
+  say "RSV2: $frame->[2]";
+  say "RSV3: $frame->[3]";
+  say "Opcode: $frame->[4]";
+  say "Payload: $frame->[5]";
+
+=head2 server_handshake
+
+  my $tx = server_handshake Mojo::Transaction::HTTP->new;
+
+Perform WebSocket handshake server-side.
+
+=head1 CONSTANTS
+
+L<Mojo::WebSocket> implements the following constants, which can be imported
+individually.
+
+=head2 WS_BINARY
+
+Opcode for C<Binary> frames.
+
+=head2 WS_CLOSE
+
+Opcode for C<Close> frames.
+
+=head2 WS_CONTINUATION
+
+Opcode for C<Continuation> frames.
+
+=head2 WS_PING
+
+Opcode for C<Ping> frames.
+
+=head2 WS_PONG
+
+Opcode for C<Pong> frames.
+
+=head2 WS_TEXT
+
+Opcode for C<Text> frames.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious.pm	(revision 32205)
@@ -0,0 +1,1199 @@
+package Mojolicious;
+use Mojo::Base -base;
+
+# "Fry: Shut up and take my money!"
+use Carp ();
+use Mojo::Exception;
+use Mojo::Home;
+use Mojo::Log;
+use Mojo::Util;
+use Mojo::UserAgent;
+use Mojolicious::Commands;
+use Mojolicious::Controller;
+use Mojolicious::Plugins;
+use Mojolicious::Renderer;
+use Mojolicious::Routes;
+use Mojolicious::Sessions;
+use Mojolicious::Static;
+use Mojolicious::Types;
+use Mojolicious::Validator;
+use Scalar::Util ();
+
+has commands => sub {
+  my $commands = Mojolicious::Commands->new(app => shift);
+  Scalar::Util::weaken $commands->{app};
+  return $commands;
+};
+has controller_class => 'Mojolicious::Controller';
+has home             => sub { Mojo::Home->new->detect(ref shift) };
+has log              => sub {
+  my $self = shift;
+
+  # Check if we have a log directory that is writable
+  my $log  = Mojo::Log->new;
+  my $home = $self->home;
+  my $mode = $self->mode;
+  $log->path($home->child('log', "$mode.log"))
+    if -d $home->child('log') && -w _;
+
+  # Reduced log output outside of development mode
+  return $mode eq 'development' ? $log : $log->level('info');
+};
+has 'max_request_size';
+has mode     => sub { $ENV{MOJO_MODE} || $ENV{PLACK_ENV} || 'development' };
+has moniker  => sub { Mojo::Util::decamelize ref shift };
+has plugins  => sub { Mojolicious::Plugins->new };
+has renderer => sub { Mojolicious::Renderer->new };
+has routes   => sub { Mojolicious::Routes->new };
+has secrets  => sub {
+  my $self = shift;
+
+  # Warn developers about insecure default
+  $self->log->debug('Your secret passphrase needs to be changed');
+
+  # Default to moniker
+  return [$self->moniker];
+};
+has sessions => sub { Mojolicious::Sessions->new };
+has static   => sub { Mojolicious::Static->new };
+has types    => sub { Mojolicious::Types->new };
+has ua       => sub {
+  my $ua = Mojo::UserAgent->new;
+  Scalar::Util::weaken $ua->server->app(shift)->{app};
+  return $ua;
+};
+has validator => sub { Mojolicious::Validator->new };
+
+our $CODENAME = 'Doughnut';
+our $VERSION  = '7.84';
+
+sub AUTOLOAD {
+  my $self = shift;
+
+  my ($package, $method) = our $AUTOLOAD =~ /^(.+)::(.+)$/;
+  Carp::croak "Undefined subroutine &${package}::$method called"
+    unless Scalar::Util::blessed $self && $self->isa(__PACKAGE__);
+
+  # Call helper with fresh controller
+  Carp::croak qq{Can't locate object method "$method" via package "$package"}
+    unless my $helper = $self->renderer->get_helper($method);
+  return $self->build_controller->$helper(@_);
+}
+
+sub build_controller {
+  my ($self, $tx) = @_;
+  $tx ||= $self->build_tx;
+
+  # Embedded application
+  my $stash = {};
+  if (my $sub = $tx->can('stash')) { ($stash, $tx) = ($tx->$sub, $tx->tx) }
+
+  # Build default controller
+  my $defaults = $self->defaults;
+  @$stash{keys %$defaults} = values %$defaults;
+  my $c
+    = $self->controller_class->new(app => $self, stash => $stash, tx => $tx);
+  Scalar::Util::weaken $c->{app};
+
+  return $c;
+}
+
+sub build_tx {
+  my $self = shift;
+
+  my $tx  = Mojo::Transaction::HTTP->new;
+  my $max = $self->max_request_size;
+  $tx->req->max_message_size($max) if defined $max;
+  $self->plugins->emit_hook(after_build_tx => $tx, $self);
+
+  return $tx;
+}
+
+sub config   { Mojo::Util::_stash(config   => @_) }
+sub defaults { Mojo::Util::_stash(defaults => @_) }
+
+sub dispatch {
+  my ($self, $c) = @_;
+
+  my $plugins = $self->plugins->emit_hook(before_dispatch => $c);
+
+  # Try to find a static file
+  my $tx = $c->tx;
+  $self->static->dispatch($c) and $plugins->emit_hook(after_static => $c)
+    unless $tx->res->code;
+
+  # Start timer (ignore static files)
+  my $stash = $c->stash;
+  unless ($stash->{'mojo.static'} || $stash->{'mojo.started'}) {
+    my $req    = $c->req;
+    my $method = $req->method;
+    my $path   = $req->url->path->to_abs_string;
+    my $id     = $req->request_id;
+    $self->log->debug(qq{$method "$path" ($id)});
+    $c->helpers->timing->begin('mojo.timer');
+  }
+
+  # Routes
+  $plugins->emit_hook(before_routes => $c);
+  $c->helpers->reply->not_found
+    unless $tx->res->code || $self->routes->dispatch($c) || $tx->res->code;
+}
+
+sub handler {
+  my $self = shift;
+
+  # Dispatcher has to be last in the chain
+  ++$self->{dispatch}
+    and $self->hook(around_action   => sub { $_[2]($_[1]) })
+    and $self->hook(around_dispatch => sub { $_[1]->app->dispatch($_[1]) })
+    unless $self->{dispatch};
+
+  # Process with chain
+  my $c = $self->build_controller(@_);
+  Scalar::Util::weaken $c->{tx};
+  $self->plugins->emit_chain(around_dispatch => $c);
+
+  # Delayed response
+  $self->log->debug('Nothing has been rendered, expecting delayed response')
+    unless $c->stash->{'mojo.rendered'};
+}
+
+sub helper { shift->renderer->add_helper(@_) }
+
+sub hook { shift->plugins->on(@_) }
+
+sub new {
+  my $self = shift->SUPER::new(@_);
+
+  my $home = $self->home;
+  push @{$self->renderer->paths}, $home->child('templates')->to_string;
+  push @{$self->static->paths},   $home->child('public')->to_string;
+
+  # Default to controller and application namespace
+  my $r = $self->routes->namespaces(["@{[ref $self]}::Controller", ref $self]);
+
+  # Hide controller attributes/methods
+  $r->hide(qw(app continue cookie every_cookie every_param));
+  $r->hide(qw(every_signed_cookie finish flash helpers match on param));
+  $r->hide(qw(redirect_to render render_later render_maybe render_to_string));
+  $r->hide(qw(rendered req res respond_to send session signed_cookie stash));
+  $r->hide(qw(tx url_for validation write write_chunk));
+
+  $self->plugin($_)
+    for qw(HeaderCondition DefaultHelpers TagHelpers EPLRenderer EPRenderer);
+
+  # Exception handling should be first in chain
+  $self->hook(around_dispatch => \&_exception);
+
+  $self->startup;
+
+  return $self;
+}
+
+sub plugin {
+  my $self = shift;
+  $self->plugins->register_plugin(shift, $self, @_);
+}
+
+sub server { $_[0]->plugins->emit_hook(before_server_start => @_[1, 0]) }
+
+sub start {
+  my $self = shift;
+  $_->warmup for $self->static, $self->renderer;
+  return $self->commands->run(@_ ? @_ : @ARGV);
+}
+
+sub startup { }
+
+sub _exception {
+  my ($next, $c) = @_;
+  local $SIG{__DIE__}
+    = sub { ref $_[0] ? CORE::die $_[0] : Mojo::Exception->throw(shift) };
+  $c->helpers->reply->exception($@) unless eval { $next->(); 1 };
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious - Real-time web framework
+
+=head1 SYNOPSIS
+
+  # Application
+  package MyApp;
+  use Mojo::Base 'Mojolicious';
+
+  # Route
+  sub startup {
+    my $self = shift;
+    $self->routes->get('/hello')->to('foo#hello');
+  }
+
+  # Controller
+  package MyApp::Controller::Foo;
+  use Mojo::Base 'Mojolicious::Controller';
+
+  # Action
+  sub hello {
+    my $self = shift;
+    $self->render(text => 'Hello World!');
+  }
+
+=head1 DESCRIPTION
+
+An amazing real-time web framework built on top of the powerful L<Mojo> web
+development toolkit. With support for RESTful routes, plugins, commands,
+Perl-ish templates, content negotiation, session management, form validation,
+testing framework, static file server, C<CGI>/C<PSGI> detection, first class
+Unicode support and much more for you to discover.
+
+Take a look at our excellent documentation in L<Mojolicious::Guides>!
+
+=head1 HOOKS
+
+L<Mojolicious> will emit the following hooks in the listed order.
+
+=head2 before_server_start
+
+Emitted right before the application server is started, for web servers that
+support it, which includes all the built-in ones (except for
+L<Mojo::Server::CGI>). Note that this hook is EXPERIMENTAL and might change
+without warning!
+
+  $app->hook(before_server_start => sub {
+    my ($server, $app) = @_;
+    ...
+  });
+
+Useful for reconfiguring application servers dynamically or collecting server
+diagnostics information. (Passed the server and application objects)
+
+=head2 after_build_tx
+
+Emitted right after the transaction is built and before the HTTP request gets
+parsed.
+
+  $app->hook(after_build_tx => sub {
+    my ($tx, $app) = @_;
+    ...
+  });
+
+This is a very powerful hook and should not be used lightly, it makes some
+rather advanced features such as upload progress bars possible. Note that this
+hook will not work for embedded applications, because only the host application
+gets to build transactions. (Passed the transaction and application objects)
+
+=head2 around_dispatch
+
+Emitted right after a new request has been received and wraps around the whole
+dispatch process, so you have to manually forward to the next hook if you want
+to continue the chain. Default exception handling with
+L<Mojolicious::Plugin::DefaultHelpers/"reply-E<gt>exception"> is the first hook
+in the chain and a call to L</"dispatch"> the last, yours will be in between.
+
+  $app->hook(around_dispatch => sub {
+    my ($next, $c) = @_;
+    ...
+    $next->();
+    ...
+  });
+
+This is a very powerful hook and should not be used lightly, it allows you to,
+for example, customize application-wide exception handling, consider it the
+sledgehammer in your toolbox. (Passed a callback leading to the next hook and
+the default controller object)
+
+=head2 before_dispatch
+
+Emitted right before the static file server and router start their work.
+
+  $app->hook(before_dispatch => sub {
+    my $c = shift;
+    ...
+  });
+
+Very useful for rewriting incoming requests and other preprocessing tasks.
+(Passed the default controller object)
+
+=head2 after_static
+
+Emitted after a static file response has been generated by the static file
+server.
+
+  $app->hook(after_static => sub {
+    my $c = shift;
+    ...
+  });
+
+Mostly used for post-processing static file responses. (Passed the default
+controller object)
+
+=head2 before_routes
+
+Emitted after the static file server determined if a static file should be
+served and before the router starts its work.
+
+  $app->hook(before_routes => sub {
+    my $c = shift;
+    ...
+  });
+
+Mostly used for custom dispatchers and collecting metrics. (Passed the default
+controller object)
+
+=head2 around_action
+
+Emitted right before an action gets executed and wraps around it, so you have to
+manually forward to the next hook if you want to continue the chain. Default
+action dispatching is the last hook in the chain, yours will run before it.
+
+  $app->hook(around_action => sub {
+    my ($next, $c, $action, $last) = @_;
+    ...
+    return $next->();
+  });
+
+This is a very powerful hook and should not be used lightly, it allows you for
+example to pass additional arguments to actions or handle return values
+differently. Note that this hook can trigger more than once for the same
+request if there are nested routes. (Passed a callback leading to the next hook,
+the current controller object, the action callback and a flag indicating if this
+action is an endpoint)
+
+=head2 before_render
+
+Emitted before content is generated by the renderer. Note that this hook can
+trigger out of order due to its dynamic nature, and with embedded applications
+will only work for the application that is rendering.
+
+  $app->hook(before_render => sub {
+    my ($c, $args) = @_;
+    ...
+  });
+
+Mostly used for pre-processing arguments passed to the renderer. (Passed the
+current controller object and the render arguments)
+
+=head2 after_render
+
+Emitted after content has been generated by the renderer that will be assigned
+to the response. Note that this hook can trigger out of order due to its
+dynamic nature, and with embedded applications will only work for the
+application that is rendering.
+
+  $app->hook(after_render => sub {
+    my ($c, $output, $format) = @_;
+    ...
+  });
+
+Mostly used for post-processing dynamically generated content. (Passed the
+current controller object, a reference to the content and the format)
+
+=head2 after_dispatch
+
+Emitted in reverse order after a response has been generated. Note that this
+hook can trigger out of order due to its dynamic nature, and with embedded
+applications will only work for the application that is generating the response.
+
+  $app->hook(after_dispatch => sub {
+    my $c = shift;
+    ...
+  });
+
+Useful for rewriting outgoing responses and other post-processing tasks.
+(Passed the current controller object)
+
+=head1 ATTRIBUTES
+
+L<Mojolicious> implements the following attributes.
+
+=head2 commands
+
+  my $commands = $app->commands;
+  $app         = $app->commands(Mojolicious::Commands->new);
+
+Command line interface for your application, defaults to a
+L<Mojolicious::Commands> object.
+
+  # Add another namespace to load commands from
+  push @{$app->commands->namespaces}, 'MyApp::Command';
+
+=head2 controller_class
+
+  my $class = $app->controller_class;
+  $app      = $app->controller_class('Mojolicious::Controller');
+
+Class to be used for the default controller, defaults to
+L<Mojolicious::Controller>. Note that this class needs to have already been
+loaded before the first request arrives.
+
+=head2 home
+
+  my $home = $app->home;
+  $app     = $app->home(Mojo::Home->new);
+
+The home directory of your application, defaults to a L<Mojo::Home> object
+which stringifies to the actual path.
+
+  # Portably generate path relative to home directory
+  my $path = $app->home->child('data', 'important.txt');
+
+=head2 log
+
+  my $log = $app->log;
+  $app    = $app->log(Mojo::Log->new);
+
+The logging layer of your application, defaults to a L<Mojo::Log> object. The
+level will default to C<debug> if the L</mode> is C<development>, or C<info>
+otherwise. All messages will be written to C<STDERR>, or a C<log/$mode.log> file
+if a C<log> directory exists.
+
+  # Log debug message
+  $app->log->debug('It works');
+
+=head2 max_request_size
+
+  my $max = $app->max_request_size;
+  $app    = $app->max_request_size(16777216);
+
+Maximum request size in bytes, defaults to the value of
+L<Mojo::Message/"max_message_size">. Setting the value to C<0> will allow
+requests of indefinite size. Note that increasing this value can also
+drastically increase memory usage, should you for example attempt to parse an
+excessively large request body with the methods L<Mojo::Message/"dom"> or
+L<Mojo::Message/"json">.
+
+=head2 mode
+
+  my $mode = $app->mode;
+  $app     = $app->mode('production');
+
+The operating mode for your application, defaults to a value from the
+C<MOJO_MODE> and C<PLACK_ENV> environment variables or C<development>.
+
+=head2 moniker
+
+  my $moniker = $app->moniker;
+  $app        = $app->moniker('foo_bar');
+
+Moniker of this application, often used as default filename for configuration
+files and the like, defaults to decamelizing the application class with
+L<Mojo::Util/"decamelize">.
+
+=head2 plugins
+
+  my $plugins = $app->plugins;
+  $app        = $app->plugins(Mojolicious::Plugins->new);
+
+The plugin manager, defaults to a L<Mojolicious::Plugins> object. See the
+L</"plugin"> method below if you want to load a plugin.
+
+  # Add another namespace to load plugins from
+  push @{$app->plugins->namespaces}, 'MyApp::Plugin';
+
+=head2 renderer
+
+  my $renderer = $app->renderer;
+  $app         = $app->renderer(Mojolicious::Renderer->new);
+
+Used to render content, defaults to a L<Mojolicious::Renderer> object. For more
+information about how to generate content see
+L<Mojolicious::Guides::Rendering>.
+
+  # Add another "templates" directory
+  push @{$app->renderer->paths}, '/home/sri/templates';
+
+  # Add another "templates" directory with higher precedence
+  unshift @{$app->renderer->paths}, '/home/sri/themes/blue/templates';
+
+  # Add another class with templates in DATA section
+  push @{$app->renderer->classes}, 'Mojolicious::Plugin::Fun';
+
+=head2 routes
+
+  my $routes = $app->routes;
+  $app       = $app->routes(Mojolicious::Routes->new);
+
+The router, defaults to a L<Mojolicious::Routes> object. You use this in your
+startup method to define the url endpoints for your application.
+
+  # Add routes
+  my $r = $app->routes;
+  $r->get('/foo/bar')->to('test#foo', title => 'Hello Mojo!');
+  $r->post('/baz')->to('test#baz');
+
+  # Add another namespace to load controllers from
+  push @{$app->routes->namespaces}, 'MyApp::MyController';
+
+=head2 secrets
+
+  my $secrets = $app->secrets;
+  $app        = $app->secrets([$bytes]);
+
+Secret passphrases used for signed cookies and the like, defaults to the
+L</"moniker"> of this application, which is not very secure, so you should
+change it!!! As long as you are using the insecure default there will be debug
+messages in the log file reminding you to change your passphrase. Only the
+first passphrase is used to create new signatures, but all of them for
+verification. So you can increase security without invalidating all your
+existing signed cookies by rotating passphrases, just add new ones to the front
+and remove old ones from the back.
+
+  # Rotate passphrases
+  $app->secrets(['new_passw0rd', 'old_passw0rd', 'very_old_passw0rd']);
+
+=head2 sessions
+
+  my $sessions = $app->sessions;
+  $app         = $app->sessions(Mojolicious::Sessions->new);
+
+Signed cookie based session manager, defaults to a L<Mojolicious::Sessions>
+object. You can usually leave this alone, see
+L<Mojolicious::Controller/"session"> for more information about working with
+session data.
+
+  # Change name of cookie used for all sessions
+  $app->sessions->cookie_name('mysession');
+
+=head2 static
+
+  my $static = $app->static;
+  $app       = $app->static(Mojolicious::Static->new);
+
+For serving static files from your C<public> directories, defaults to a
+L<Mojolicious::Static> object.
+
+  # Add another "public" directory
+  push @{$app->static->paths}, '/home/sri/public';
+
+  # Add another "public" directory with higher precedence
+  unshift @{$app->static->paths}, '/home/sri/themes/blue/public';
+
+  # Add another class with static files in DATA section
+  push @{$app->static->classes}, 'Mojolicious::Plugin::Fun';
+
+  # Remove built-in favicon
+  delete $app->static->extra->{'favicon.ico'};
+
+=head2 types
+
+  my $types = $app->types;
+  $app      = $app->types(Mojolicious::Types->new);
+
+Responsible for connecting file extensions with MIME types, defaults to a
+L<Mojolicious::Types> object.
+
+  # Add custom MIME type
+  $app->types->type(twt => 'text/tweet');
+
+=head2 ua
+
+  my $ua = $app->ua;
+  $app   = $app->ua(Mojo::UserAgent->new);
+
+A full featured HTTP user agent for use in your applications, defaults to a
+L<Mojo::UserAgent> object.
+
+  # Perform blocking request
+  say $app->ua->get('example.com')->result->body;
+
+=head2 validator
+
+  my $validator = $app->validator;
+  $app          = $app->validator(Mojolicious::Validator->new);
+
+Validate values, defaults to a L<Mojolicious::Validator> object.
+
+  # Add validation check
+  $app->validator->add_check(foo => sub {
+    my ($v, $name, $value) = @_;
+    return $value ne 'foo';
+  });
+
+  # Add validation filter
+  $app->validator->add_filter(quotemeta => sub {
+    my ($v, $name, $value) = @_;
+    return quotemeta $value;
+  });
+
+=head1 METHODS
+
+L<Mojolicious> inherits all methods from L<Mojo::Base> and implements the
+following new ones.
+
+=head2 build_controller
+
+  my $c = $app->build_controller;
+  my $c = $app->build_controller(Mojo::Transaction::HTTP->new);
+  my $c = $app->build_controller(Mojolicious::Controller->new);
+
+Build default controller object with L</"controller_class">.
+
+  # Render template from application
+  my $foo = $app->build_controller->render_to_string(template => 'foo');
+
+=head2 build_tx
+
+  my $tx = $app->build_tx;
+
+Build L<Mojo::Transaction::HTTP> object and emit L</"after_build_tx"> hook.
+
+=head2 config
+
+  my $hash = $app->config;
+  my $foo  = $app->config('foo');
+  $app     = $app->config({foo => 'bar', baz => 23});
+  $app     = $app->config(foo => 'bar', baz => 23);
+
+Application configuration.
+
+  # Remove value
+  my $foo = delete $app->config->{foo};
+
+  # Assign multiple values at once
+  $app->config(foo => 'test', bar => 23);
+
+=head2 defaults
+
+  my $hash = $app->defaults;
+  my $foo  = $app->defaults('foo');
+  $app     = $app->defaults({foo => 'bar', baz => 23});
+  $app     = $app->defaults(foo => 'bar', baz => 23);
+
+Default values for L<Mojolicious::Controller/"stash">, assigned for every new
+request.
+
+  # Remove value
+  my $foo = delete $app->defaults->{foo};
+
+  # Assign multiple values at once
+  $app->defaults(foo => 'test', bar => 23);
+
+=head2 dispatch
+
+  $app->dispatch(Mojolicious::Controller->new);
+
+The heart of every L<Mojolicious> application, calls the L</"static"> and
+L</"routes"> dispatchers for every request and passes them a
+L<Mojolicious::Controller> object.
+
+=head2 handler
+
+  $app->handler(Mojo::Transaction::HTTP->new);
+  $app->handler(Mojolicious::Controller->new);
+
+Sets up the default controller and emits the L</"around_dispatch"> hook for
+every request.
+
+=head2 helper
+
+  $app->helper(foo => sub {...});
+
+Add or replace a helper that will be available as a method of the controller
+object and the application object, as well as a function in C<ep> templates. For
+a full list of helpers that are available by default see
+L<Mojolicious::Plugin::DefaultHelpers> and L<Mojolicious::Plugin::TagHelpers>.
+
+  # Helper
+  $app->helper(cache => sub { state $cache = {} });
+
+  # Application
+  $app->cache->{foo} = 'bar';
+  my $result = $app->cache->{foo};
+
+  # Controller
+  $c->cache->{foo} = 'bar';
+  my $result = $c->cache->{foo};
+
+  # Template
+  % cache->{foo} = 'bar';
+  %= cache->{foo}
+
+=head2 hook
+
+  $app->hook(after_dispatch => sub {...});
+
+Extend L<Mojolicious> with hooks, which allow code to be shared with all
+requests indiscriminately, for a full list of available hooks see L</"HOOKS">.
+
+  # Dispatchers will not run if there's already a response code defined
+  $app->hook(before_dispatch => sub {
+    my $c = shift;
+    $c->render(text => 'Skipped static file server and router!')
+      if $c->req->url->path->to_route =~ /do_not_dispatch/;
+  });
+
+=head2 new
+
+  my $app = Mojolicious->new;
+  my $app = Mojolicious->new(moniker => 'foo_bar');
+  my $app = Mojolicious->new({moniker => 'foo_bar'});
+
+Construct a new L<Mojolicious> application and call L</"startup">. Will
+automatically detect your home directory. Also sets up the renderer, static file
+server, a default set of plugins and an L</"around_dispatch"> hook with the
+default exception handling.
+
+=head2 plugin
+
+  $app->plugin('some_thing');
+  $app->plugin('some_thing', foo => 23);
+  $app->plugin('some_thing', {foo => 23});
+  $app->plugin('SomeThing');
+  $app->plugin('SomeThing', foo => 23);
+  $app->plugin('SomeThing', {foo => 23});
+  $app->plugin('MyApp::Plugin::SomeThing');
+  $app->plugin('MyApp::Plugin::SomeThing', foo => 23);
+  $app->plugin('MyApp::Plugin::SomeThing', {foo => 23});
+
+Load a plugin, for a full list of example plugins included in the
+L<Mojolicious> distribution see L<Mojolicious::Plugins/"PLUGINS">.
+
+=head2 server
+
+  $app->server(Mojo::Server->new);
+
+Emits the L</"before_server_start"> hook. Note that this method is EXPERIMENTAL
+and might change without warning!
+
+=head2 start
+
+  $app->start;
+  $app->start(@ARGV);
+
+Start the command line interface for your application. For a full list of
+commands that are available by default see L<Mojolicious::Commands/"COMMANDS">.
+Note that the options C<-h>/C<--help>, C<--home> and C<-m>/C<--mode>, which are
+shared by all commands, will be parsed from C<@ARGV> during compile time.
+
+  # Always start daemon
+  $app->start('daemon', '-l', 'http://*:8080');
+
+=head2 startup
+
+  $app->startup;
+
+This is your main hook into the application, it will be called at application
+startup. Meant to be overloaded in a subclass.
+
+  sub startup {
+    my $self = shift;
+    ...
+  }
+
+=head1 AUTOLOAD
+
+In addition to the L</"ATTRIBUTES"> and L</"METHODS"> above you can also call
+helpers on L<Mojolicious> objects. This includes all helpers from
+L<Mojolicious::Plugin::DefaultHelpers> and L<Mojolicious::Plugin::TagHelpers>.
+Note that application helpers are always called with a new default controller
+object, so they can't depend on or change controller state, which includes
+request, response and stash.
+
+  # Call helper
+  say $app->dumper({foo => 'bar'});
+
+  # Longer version
+  say $app->build_controller->helpers->dumper({foo => 'bar'});
+
+=head1 BUNDLED FILES
+
+The L<Mojolicious> distribution includes a few files with different licenses
+that have been bundled for internal use.
+
+=head2 Mojolicious Artwork
+
+  Copyright (C) 2010-2018, Sebastian Riedel.
+
+Licensed under the CC-SA License, Version 4.0
+L<http://creativecommons.org/licenses/by-sa/4.0>.
+
+=head2 jQuery
+
+  Copyright (C) jQuery Foundation.
+
+Licensed under the MIT License, L<http://creativecommons.org/licenses/MIT>.
+
+=head2 prettify.js
+
+  Copyright (C) 2006, 2013 Google Inc..
+
+Licensed under the Apache License, Version 2.0
+L<http://www.apache.org/licenses/LICENSE-2.0>.
+
+=head1 CODE NAMES
+
+Every major release of L<Mojolicious> has a code name, these are the ones that
+have been used in the past.
+
+7.0, C<Doughnut> (U+1F369)
+
+6.0, C<Clinking Beer Mugs> (U+1F37B)
+
+5.0, C<Tiger Face> (U+1F42F)
+
+4.0, C<Top Hat> (U+1F3A9)
+
+3.0, C<Rainbow> (U+1F308)
+
+2.0, C<Leaf Fluttering In Wind> (U+1F343)
+
+1.0, C<Snowflake> (U+2744)
+
+=head1 SPONSORS
+
+Some of the work on this distribution has been sponsored by
+L<The Perl Foundation|http://www.perlfoundation.org>, thank you!
+
+=head1 PROJECT FOUNDER
+
+Sebastian Riedel, C<kraih@mojolicious.org>
+
+=head1 CORE DEVELOPERS
+
+Current members of the core team in alphabetical order:
+
+=over 2
+
+Jan Henning Thorsen, C<batman@mojolicious.org>
+
+Joel Berger, C<jberger@mojolicious.org>
+
+Marcus Ramberg, C<marcus@mojolicious.org>
+
+=back
+
+The following members of the core team are currently on hiatus:
+
+=over 2
+
+Abhijit Menon-Sen, C<ams@cpan.org>
+
+Glen Hinkle, C<tempire@cpan.org>
+
+=back
+
+=head1 CREDITS
+
+In alphabetical order:
+
+=over 2
+
+Adam Kennedy
+
+Adriano Ferreira
+
+Al Newkirk
+
+Alex Efros
+
+Alex Salimon
+
+Alexey Likhatskiy
+
+Anatoly Sharifulin
+
+Andre Parker
+
+Andre Vieth
+
+Andreas Jaekel
+
+Andreas Koenig
+
+Andrew Fresh
+
+Andrew Nugged
+
+Andrey Khozov
+
+Andrey Kuzmin
+
+Andy Grundman
+
+Aristotle Pagaltzis
+
+Ashley Dev
+
+Ask Bjoern Hansen
+
+Audrey Tang
+
+Ben Tyler
+
+Ben van Staveren
+
+Benjamin Erhart
+
+Bernhard Graf
+
+Breno G. de Oliveira
+
+Brian Duggan
+
+Brian Medley
+
+Burak Gursoy
+
+Ch Lamprecht
+
+Charlie Brady
+
+Chas. J. Owens IV
+
+Chase Whitener
+
+Christian Hansen
+
+chromatic
+
+Curt Tilmes
+
+Dan Book
+
+Daniel Kimsey
+
+Daniel Mantovani
+
+Danijel Tasov
+
+Danny Thomas
+
+David Davis
+
+David Webb
+
+Diego Kuperman
+
+Dmitriy Shalashov
+
+Dmitry Konstantinov
+
+Dominik Jarmulowicz
+
+Dominique Dumont
+
+Dotan Dimet
+
+Douglas Christopher Wilson
+
+Ettore Di Giacinto
+
+Eugen Konkov
+
+Eugene Toropov
+
+Flavio Poletti
+
+Gisle Aas
+
+Graham Barr
+
+Graham Knop
+
+Henry Tang
+
+Hideki Yamamura
+
+Hiroki Toyokawa
+
+Ian Goodacre
+
+Ilya Chesnokov
+
+James Duncan
+
+Jan Jona Javorsek
+
+Jan Schmidt
+
+Jaroslav Muhin
+
+Jesse Vincent
+
+Johannes Plunien
+
+John Kingsley
+
+Jonathan Yu
+
+Josh Leder
+
+Kazuhiro Shibuya
+
+Kevin Old
+
+Kitamura Akatsuki
+
+Klaus S. Madsen
+
+Knut Arne Bjorndal
+
+Lars Balker Rasmussen
+
+Lee Johnson
+
+Leon Brocard
+
+Magnus Holm
+
+Maik Fischer
+
+Mark Fowler
+
+Mark Grimes
+
+Mark Stosberg
+
+Marty Tennison
+
+Matt S Trout
+
+Matthew Lineen
+
+Maksym Komar
+
+Maxim Vuets
+
+Michael Gregorowicz
+
+Michael Harris
+
+Mike Magowan
+
+Mirko Westermeier
+
+Mons Anderson
+
+Moritz Lenz
+
+Neil Watkiss
+
+Nic Sandfield
+
+Nils Diewald
+
+Oleg Zhelo
+
+Olivier Mengue
+
+Pascal Gaudette
+
+Paul Evans
+
+Paul Robins
+
+Paul Tomlin
+
+Pavel Shaydo
+
+Pedro Melo
+
+Peter Edwards
+
+Pierre-Yves Ritschard
+
+Piotr Roszatycki
+
+Quentin Carbonneaux
+
+Rafal Pocztarski
+
+Randal Schwartz
+
+Richard Elberger
+
+Rick Delaney
+
+Robert Hicks
+
+Robin Lee
+
+Roland Lammel
+
+Roy Storey
+
+Ryan Jendoubi
+
+Salvador Fandino
+
+Santiago Zarate
+
+Sascha Kiefer
+
+Scott Wiersdorf
+
+Sergey Zasenko
+
+Simon Bertrang
+
+Simone Tampieri
+
+Shu Cho
+
+Skye Shaw
+
+Stanis Trendelenburg
+
+Steffen Ullrich
+
+Stephan Kulow
+
+Stephane Este-Gracias
+
+Stevan Little
+
+Steve Atkins
+
+Tatsuhiko Miyagawa
+
+Terrence Brannon
+
+Tianon Gravi
+
+Tomas Znamenacek
+
+Tudor Constantin
+
+Ulrich Habel
+
+Ulrich Kautz
+
+Uwe Voelker
+
+Viacheslav Tykhanovskyi
+
+Victor Engmark
+
+Viliam Pucik
+
+Wes Cravens
+
+William Lindley
+
+Yaroslav Korshak
+
+Yuki Kimoto
+
+Zak B. Elep
+
+Zoffix Znet
+
+=back
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (C) 2008-2018, Sebastian Riedel and others.
+
+This program is free software, you can redistribute it and/or modify it under
+the terms of the Artistic License version 2.0.
+
+=head1 SEE ALSO
+
+L<https://github.com/kraih/mojo>, L<Mojolicious::Guides>,
+L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Command.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Command.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Command.pm	(revision 32205)
@@ -0,0 +1,248 @@
+package Mojolicious::Command;
+use Mojo::Base -base;
+
+use Carp 'croak';
+use Mojo::File 'path';
+use Mojo::Loader 'data_section';
+use Mojo::Server;
+use Mojo::Template;
+
+has app => sub { Mojo::Server->new->build_app('Mojo::HelloWorld') };
+has description => 'No description';
+has 'quiet';
+has usage => "Usage: APPLICATION\n";
+
+sub chmod_file {
+  my ($self, $path, $mod) = @_;
+  chmod $mod, $path or croak qq{Can't chmod file "$path": $!};
+  return $self->_loud("  [chmod] $path " . sprintf('%lo', $mod));
+}
+
+sub chmod_rel_file { $_[0]->chmod_file($_[0]->rel_file($_[1]), $_[2]) }
+
+sub create_dir {
+  my ($self, $path) = @_;
+  return $self->_loud("  [exist] $path") if -d $path;
+  path($path)->make_path;
+  return $self->_loud("  [mkdir] $path");
+}
+
+sub create_rel_dir { $_[0]->create_dir($_[0]->rel_file($_[1])) }
+
+sub extract_usage { Mojo::Util::extract_usage((caller)[1]) }
+
+sub help { print shift->usage }
+
+sub rel_file { path->child(split('/', pop)) }
+
+sub render_data {
+  my ($self, $name) = (shift, shift);
+  Mojo::Template->new->name("template $name from DATA section")
+    ->render(data_section(ref $self, $name), @_);
+}
+
+sub render_to_file {
+  my ($self, $data, $path) = (shift, shift, shift);
+  return $self->write_file($path, $self->render_data($data, @_));
+}
+
+sub render_to_rel_file {
+  my $self = shift;
+  $self->render_to_file(shift, $self->rel_file(shift), @_);
+}
+
+sub run { croak 'Method "run" not implemented by subclass' }
+
+sub write_file {
+  my ($self, $path, $data) = @_;
+  return $self->_loud("  [exist] $path") if -f $path;
+  $self->create_dir(path($path)->dirname);
+  path($path)->spurt($data);
+  return $self->_loud("  [write] $path");
+}
+
+sub write_rel_file { $_[0]->write_file($_[0]->rel_file($_[1]), $_[2]) }
+
+sub _loud {
+  my ($self, $msg) = @_;
+  say $msg unless $self->quiet;
+  return $self;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Command - Command base class
+
+=head1 SYNOPSIS
+
+  # Lowercase command name
+  package Mojolicious::Command::mycommand;
+  use Mojo::Base 'Mojolicious::Command';
+
+  # Short description
+  has description => 'My first Mojo command';
+
+  # Usage message from SYNOPSIS
+  has usage => sub { shift->extract_usage };
+
+  sub run {
+    my ($self, @args) = @_;
+
+    # Magic here! :)
+  }
+
+  1;
+
+  =head1 SYNOPSIS
+
+    Usage: APPLICATION mycommand [OPTIONS]
+
+    Options:
+      -s, --something   Does something
+
+  =cut
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Command> is an abstract base class for L<Mojolicious> commands.
+
+See L<Mojolicious::Commands/"COMMANDS"> for a list of commands that are
+available by default.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Command> implements the following attributes.
+
+=head2 app
+
+  my $app  = $command->app;
+  $command = $command->app(Mojolicious->new);
+
+Application for command, defaults to a L<Mojo::HelloWorld> object.
+
+  # Introspect
+  say "Template path: $_" for @{$command->app->renderer->paths};
+
+=head2 description
+
+  my $description = $command->description;
+  $command        = $command->description('Foo');
+
+Short description of command, used for the command list.
+
+=head2 quiet
+
+  my $bool = $command->quiet;
+  $command = $command->quiet($bool);
+
+Limited command output.
+
+=head2 usage
+
+  my $usage = $command->usage;
+  $command  = $command->usage('Foo');
+
+Usage information for command, used for the help screen.
+
+=head1 METHODS
+
+L<Mojolicious::Command> inherits all methods from L<Mojo::Base> and implements
+the following new ones.
+
+=head2 chmod_file
+
+  $command = $command->chmod_file('/home/sri/foo.txt', 0644);
+
+Change mode of a file.
+
+=head2 chmod_rel_file
+
+  $command = $command->chmod_rel_file('foo/foo.txt', 0644);
+
+Portably change mode of a file relative to the current working directory.
+
+=head2 create_dir
+
+  $command = $command->create_dir('/home/sri/foo/bar');
+
+Create a directory.
+
+=head2 create_rel_dir
+
+  $command = $command->create_rel_dir('foo/bar/baz');
+
+Portably create a directory relative to the current working directory.
+
+=head2 extract_usage
+
+  my $usage = $command->extract_usage;
+
+Extract usage message from the SYNOPSIS section of the file this method was
+called from with L<Mojo::Util/"extract_usage">.
+
+=head2 help
+
+  $command->help;
+
+Print usage information for command.
+
+=head2 rel_file
+
+  my $path = $command->rel_file('foo/bar.txt');
+
+Return a L<Mojo::File> object relative to the current working directory.
+
+=head2 render_data
+
+  my $data = $command->render_data('foo_bar');
+  my $data = $command->render_data('foo_bar', @args);
+
+Render a template from the C<DATA> section of the command class with
+L<Mojo::Loader> and L<Mojo::Template>.
+
+=head2 render_to_file
+
+  $command = $command->render_to_file('foo_bar', '/home/sri/foo.txt');
+  $command = $command->render_to_file('foo_bar', '/home/sri/foo.txt', @args);
+
+Render a template from the C<DATA> section of the command class with
+L<Mojo::Template> to a file and create directory if necessary.
+
+=head2 render_to_rel_file
+
+  $command = $command->render_to_rel_file('foo_bar', 'foo/bar.txt');
+  $command = $command->render_to_rel_file('foo_bar', 'foo/bar.txt', @args);
+
+Portably render a template from the C<DATA> section of the command class with
+L<Mojo::Template> to a file relative to the current working directory and
+create directory if necessary.
+
+=head2 run
+
+  $command->run;
+  $command->run(@ARGV);
+
+Run command. Meant to be overloaded in a subclass.
+
+=head2 write_file
+
+  $command = $command->write_file('/home/sri/foo.txt', 'Hello World!');
+
+Write text to a file and create directory if necessary.
+
+=head2 write_rel_file
+
+  $command = $command->write_rel_file('foo/bar.txt', 'Hello World!');
+
+Portably write text to a file relative to the current working directory and
+create directory if necessary.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/cgi.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/cgi.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/cgi.pm	(revision 32205)
@@ -0,0 +1,83 @@
+package Mojolicious::Command::cgi;
+use Mojo::Base 'Mojolicious::Command';
+
+use Mojo::Server::CGI;
+use Mojo::Util 'getopt';
+
+has description => 'Start application with CGI';
+has usage => sub { shift->extract_usage };
+
+sub run {
+  my ($self, @args) = @_;
+  getopt \@args, nph => \(my $nph = 0);
+  Mojo::Server::CGI->new(app => $self->app, nph => $nph)->run;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Command::cgi - CGI command
+
+=head1 SYNOPSIS
+
+  Usage: APPLICATION cgi [OPTIONS]
+
+    ./myapp.pl cgi
+
+  Options:
+    -h, --help          Show this summary of available options
+        --home <path>   Path to home directory of your application, defaults to
+                        the value of MOJO_HOME or auto-detection
+    -m, --mode <name>   Operating mode for your application, defaults to the
+                        value of MOJO_MODE/PLACK_ENV or "development"
+        --nph           Enable non-parsed-header mode
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Command::cgi> starts applications with the L<Mojo::Server::CGI>
+backend.
+
+This is a core command, that means it is always enabled and its code a good
+example for learning to build new commands, you're welcome to fork it.
+
+See L<Mojolicious::Commands/"COMMANDS"> for a list of commands that are
+available by default.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Command::cgi> inherits all attributes from
+L<Mojolicious::Command> and implements the following new ones.
+
+=head2 description
+
+  my $description = $cgi->description;
+  $cgi            = $cgi->description('Foo');
+
+Short description of this command, used for the command list.
+
+=head2 usage
+
+  my $usage = $cgi->usage;
+  $cgi      = $cgi->usage('Foo');
+
+Usage information for this command, used for the help screen.
+
+=head1 METHODS
+
+L<Mojolicious::Command::cgi> inherits all methods from L<Mojolicious::Command>
+and implements the following new ones.
+
+=head2 run
+
+  $cgi->run(@ARGV);
+
+Run this command.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/cpanify.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/cpanify.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/cpanify.pm	(revision 32205)
@@ -0,0 +1,103 @@
+package Mojolicious::Command::cpanify;
+use Mojo::Base 'Mojolicious::Command';
+
+use Mojo::File 'path';
+use Mojo::Util 'getopt';
+
+has description => 'Upload distribution to CPAN';
+has usage => sub { shift->extract_usage };
+
+sub run {
+  my ($self, @args) = @_;
+
+  getopt \@args,
+    'p|password=s' => \(my $password = ''),
+    'u|user=s'     => \(my $user     = '');
+  die $self->usage unless my $file = shift @args;
+
+  my $tx = $self->app->ua->tap(sub { $_->proxy->detect })->post(
+    "https://$user:$password\@pause.perl.org/pause/authenquery" => form => {
+      HIDDENNAME                        => $user,
+      CAN_MULTIPART                     => 1,
+      pause99_add_uri_upload            => path($file)->basename,
+      SUBMIT_pause99_add_uri_httpupload => ' Upload this file from my disk ',
+      pause99_add_uri_uri               => '',
+      pause99_add_uri_httpupload        => {file => $file},
+    }
+  );
+
+  unless ($tx->success) {
+    my $code = $tx->res->code // 0;
+    my $msg  = $tx->error->{message};
+    if    ($code == 401) { $msg = 'Wrong username or password.' }
+    elsif ($code == 409) { $msg = 'File already exists on CPAN.' }
+    die qq{Problem uploading file "$file": $msg\n};
+  }
+
+  say 'Upload successful!';
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Command::cpanify - CPAN-ify command
+
+=head1 SYNOPSIS
+
+  Usage: APPLICATION cpanify [OPTIONS] [FILE]
+
+    mojo cpanify -u sri -p secr3t Mojolicious-Plugin-MyPlugin-0.01.tar.gz
+
+  Options:
+    -h, --help                  Show this summary of available options
+    -p, --password <password>   PAUSE password
+    -u, --user <name>           PAUSE username
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Command::cpanify> uploads files to CPAN.
+
+This is a core command, that means it is always enabled and its code a good
+example for learning to build new commands, you're welcome to fork it.
+
+See L<Mojolicious::Commands/"COMMANDS"> for a list of commands that are
+available by default.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Command::cpanify> inherits all attributes from
+L<Mojolicious::Command> and implements the following new ones.
+
+=head2 description
+
+  my $description = $cpanify->description;
+  $cpanify        = $cpanify->description('Foo');
+
+Short description of this command, used for the command list.
+
+=head2 usage
+
+  my $usage = $cpanify->usage;
+  $cpanify  = $cpanify->usage('Foo');
+
+Usage information for this command, used for the help screen.
+
+=head1 METHODS
+
+L<Mojolicious::Command::cpanify> inherits all methods from
+L<Mojolicious::Command> and implements the following new ones.
+
+=head2 run
+
+  $cpanify->run(@ARGV);
+
+Run this command.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/daemon.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/daemon.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/daemon.pm	(revision 32205)
@@ -0,0 +1,112 @@
+package Mojolicious::Command::daemon;
+use Mojo::Base 'Mojolicious::Command';
+
+use Mojo::Server::Daemon;
+use Mojo::Util 'getopt';
+
+has description => 'Start application with HTTP and WebSocket server';
+has usage => sub { shift->extract_usage };
+
+sub run {
+  my ($self, @args) = @_;
+
+  my $daemon = Mojo::Server::Daemon->new(app => $self->app);
+  getopt \@args,
+    'b|backlog=i'            => sub { $daemon->backlog($_[1]) },
+    'c|clients=i'            => sub { $daemon->max_clients($_[1]) },
+    'i|inactivity-timeout=i' => sub { $daemon->inactivity_timeout($_[1]) },
+    'l|listen=s'             => \my @listen,
+    'p|proxy'                => sub { $daemon->reverse_proxy(1) },
+    'r|requests=i'           => sub { $daemon->max_requests($_[1]) };
+
+  $daemon->listen(\@listen) if @listen;
+  $daemon->run;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Command::daemon - Daemon command
+
+=head1 SYNOPSIS
+
+  Usage: APPLICATION daemon [OPTIONS]
+
+    ./myapp.pl daemon
+    ./myapp.pl daemon -m production -l http://*:8080
+    ./myapp.pl daemon -l http://127.0.0.1:8080 -l https://[::]:8081
+    ./myapp.pl daemon -l 'https://*:443?cert=./server.crt&key=./server.key'
+    ./myapp.pl daemon -l http+unix://%2Ftmp%2Fmyapp.sock
+
+  Options:
+    -b, --backlog <size>                 Listen backlog size, defaults to
+                                         SOMAXCONN
+    -c, --clients <number>               Maximum number of concurrent
+                                         connections, defaults to 1000
+    -h, --help                           Show this summary of available options
+        --home <path>                    Path to home directory of your
+                                         application, defaults to the value of
+                                         MOJO_HOME or auto-detection
+    -i, --inactivity-timeout <seconds>   Inactivity timeout, defaults to the
+                                         value of MOJO_INACTIVITY_TIMEOUT or 15
+    -l, --listen <location>              One or more locations you want to
+                                         listen on, defaults to the value of
+                                         MOJO_LISTEN or "http://*:3000"
+    -m, --mode <name>                    Operating mode for your application,
+                                         defaults to the value of
+                                         MOJO_MODE/PLACK_ENV or "development"
+    -p, --proxy                          Activate reverse proxy support,
+                                         defaults to the value of
+                                         MOJO_REVERSE_PROXY
+    -r, --requests <number>              Maximum number of requests per
+                                         keep-alive connection, defaults to 100
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Command::daemon> starts applications with the
+L<Mojo::Server::Daemon> backend.
+
+This is a core command, that means it is always enabled and its code a good
+example for learning to build new commands, you're welcome to fork it.
+
+See L<Mojolicious::Commands/"COMMANDS"> for a list of commands that are
+available by default.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Command::daemon> inherits all attributes from
+L<Mojolicious::Command> and implements the following new ones.
+
+=head2 description
+
+  my $description = $daemon->description;
+  $daemon         = $daemon->description('Foo');
+
+Short description of this command, used for the command list.
+
+=head2 usage
+
+  my $usage = $daemon->usage;
+  $daemon   = $daemon->usage('Foo');
+
+Usage information for this command, used for the help screen.
+
+=head1 METHODS
+
+L<Mojolicious::Command::daemon> inherits all methods from
+L<Mojolicious::Command> and implements the following new ones.
+
+=head2 run
+
+  $daemon->run(@ARGV);
+
+Run this command.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/eval.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/eval.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/eval.pm	(revision 32205)
@@ -0,0 +1,93 @@
+package Mojolicious::Command::eval;
+use Mojo::Base 'Mojolicious::Command';
+
+use Mojo::Util 'getopt';
+
+has description => 'Run code against application';
+has usage => sub { shift->extract_usage };
+
+sub run {
+  my ($self, @args) = @_;
+
+  getopt \@args, 'v|verbose' => \my $v1, 'V' => \my $v2;
+  my $code = shift @args || '';
+
+  # Run code against application
+  my $app = $self->app;
+  no warnings;
+  my $result = eval "package main; sub app; local *app = sub { \$app }; $code";
+  return $@ ? die $@ : $result unless defined $result && ($v1 || $v2);
+  $v2 ? print($app->dumper($result)) : say $result;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Command::eval - Eval command
+
+=head1 SYNOPSIS
+
+  Usage: APPLICATION eval [OPTIONS] CODE
+
+    ./myapp.pl eval 'say app->ua->get("/")->result->body'
+    ./myapp.pl eval 'say for sort keys %{app->renderer->helpers}'
+    ./myapp.pl eval -v 'app->home'
+    ./myapp.pl eval -V 'app->renderer->paths'
+
+  Options:
+    -h, --help          Show this summary of available options
+        --home <path>   Path to home directory of your application, defaults to
+                        the value of MOJO_HOME or auto-detection
+    -m, --mode <name>   Operating mode for your application, defaults to the
+                        value of MOJO_MODE/PLACK_ENV or "development"
+    -v, --verbose       Print return value to STDOUT
+    -V                  Print returned data structure to STDOUT
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Command::eval> runs code against applications.
+
+This is a core command, that means it is always enabled and its code a good
+example for learning to build new commands, you're welcome to fork it.
+
+See L<Mojolicious::Commands/"COMMANDS"> for a list of commands that are
+available by default.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Command::eval> inherits all attributes from
+L<Mojolicious::Command> and implements the following new ones.
+
+=head2 description
+
+  my $description = $eval->description;
+  $eval           = $eval->description('Foo');
+
+Short description of this command, used for the command list.
+
+=head2 usage
+
+  my $usage = $eval->usage;
+  $eval     = $eval->usage('Foo');
+
+Usage information for this command, used for the help screen.
+
+=head1 METHODS
+
+L<Mojolicious::Command::eval> inherits all methods from L<Mojolicious::Command>
+and implements the following new ones.
+
+=head2 run
+
+  $eval->run(@ARGV);
+
+Run this command.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/generate.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/generate.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/generate.pm	(revision 32205)
@@ -0,0 +1,89 @@
+package Mojolicious::Command::generate;
+use Mojo::Base 'Mojolicious::Commands';
+
+has description => 'Generate files and directories from templates';
+has hint        => <<EOF;
+
+See 'APPLICATION generate help GENERATOR' for more information on a specific
+generator.
+EOF
+has message    => sub { shift->extract_usage . "\nGenerators:\n" };
+has namespaces => sub { ['Mojolicious::Command::generate'] };
+
+sub help { shift->run(@_) }
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Command::generate - Generator command
+
+=head1 SYNOPSIS
+
+  Usage: APPLICATION generate GENERATOR [OPTIONS]
+
+    mojo generate app
+    mojo generate lite_app
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Command::generate> lists available generators.
+
+This is a core command, that means it is always enabled and its code a good
+example for learning to build new commands, you're welcome to fork it.
+
+See L<Mojolicious::Commands/"COMMANDS"> for a list of commands that are
+available by default.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Command::generate> inherits all attributes from
+L<Mojolicious::Commands> and implements the following new ones.
+
+=head2 description
+
+  my $description = $generator->description;
+  $generator      = $generator->description('Foo');
+
+Short description of this command, used for the command list.
+
+=head2 hint
+
+  my $hint   = $generator->hint;
+  $generator = $generator->hint('Foo');
+
+Short hint shown after listing available generator commands.
+
+=head2 message
+
+  my $msg    = $generator->message;
+  $generator = $generator->message('Bar');
+
+Short usage message shown before listing available generator commands.
+
+=head2 namespaces
+
+  my $namespaces = $generator->namespaces;
+  $generator     = $generator->namespaces(['MyApp::Command::generate']);
+
+Namespaces to search for available generator commands, defaults to
+L<Mojolicious::Command::generate>.
+
+=head1 METHODS
+
+L<Mojolicious::Command::generate> inherits all methods from
+L<Mojolicious::Commands> and implements the following new ones.
+
+=head2 help
+
+  $generator->help('app');
+
+Print usage information for generator command.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/generate/app.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/generate/app.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/generate/app.pm	(revision 32205)
@@ -0,0 +1,221 @@
+package Mojolicious::Command::generate::app;
+use Mojo::Base 'Mojolicious::Command';
+
+use Mojo::Util qw(class_to_file class_to_path decamelize);
+
+has description => 'Generate Mojolicious application directory structure';
+has usage => sub { shift->extract_usage };
+
+sub run {
+  my ($self, $class) = @_;
+  $class ||= 'MyApp';
+
+  # Prevent bad applications
+  die <<EOF unless $class =~ /^[A-Z](?:\w|::)+$/;
+Your application name has to be a well formed (CamelCase) Perl module name
+like "MyApp".
+EOF
+
+  # Script
+  my $name = class_to_file $class;
+  $self->render_to_rel_file('mojo', "$name/script/$name", $class);
+  $self->chmod_rel_file("$name/script/$name", 0744);
+
+  # Application class
+  my $app = class_to_path $class;
+  $self->render_to_rel_file('appclass', "$name/lib/$app", $class);
+
+  # Config file (using the default moniker)
+  $self->render_to_rel_file('config', "$name/@{[decamelize $class]}.conf");
+
+  # Controller
+  my $controller = "${class}::Controller::Example";
+  my $path       = class_to_path $controller;
+  $self->render_to_rel_file('controller', "$name/lib/$path", $controller);
+
+  # Test
+  $self->render_to_rel_file('test', "$name/t/basic.t", $class);
+
+  # Static file
+  $self->render_to_rel_file('static', "$name/public/index.html");
+
+  # Templates
+  $self->render_to_rel_file('layout',
+    "$name/templates/layouts/default.html.ep");
+  $self->render_to_rel_file('welcome',
+    "$name/templates/example/welcome.html.ep");
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Command::generate::app - App generator command
+
+=head1 SYNOPSIS
+
+  Usage: APPLICATION generate app [OPTIONS] [NAME]
+
+    mojo generate app
+    mojo generate app TestApp
+
+  Options:
+    -h, --help   Show this summary of available options
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Command::generate::app> generates application directory
+structures for fully functional L<Mojolicious> applications.
+
+This is a core command, that means it is always enabled and its code a good
+example for learning to build new commands, you're welcome to fork it.
+
+See L<Mojolicious::Commands/"COMMANDS"> for a list of commands that are
+available by default.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Command::generate::app> inherits all attributes from
+L<Mojolicious::Command> and implements the following new ones.
+
+=head2 description
+
+  my $description = $app->description;
+  $app            = $app->description('Foo');
+
+Short description of this command, used for the command list.
+
+=head2 usage
+
+  my $usage = $app->usage;
+  $app      = $app->usage('Foo');
+
+Usage information for this command, used for the help screen.
+
+=head1 METHODS
+
+L<Mojolicious::Command::generate::app> inherits all methods from
+L<Mojolicious::Command> and implements the following new ones.
+
+=head2 run
+
+  $app->run(@ARGV);
+
+Run this command.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
+
+__DATA__
+
+@@ mojo
+% my $class = shift;
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use FindBin;
+BEGIN { unshift @INC, "$FindBin::Bin/../lib" }
+use Mojolicious::Commands;
+
+# Start command line interface for application
+Mojolicious::Commands->start_app('<%= $class %>');
+
+@@ appclass
+% my $class = shift;
+package <%= $class %>;
+use Mojo::Base 'Mojolicious';
+
+# This method will run once at server start
+sub startup {
+  my $self = shift;
+
+  # Load configuration from hash returned by "my_app.conf"
+  my $config = $self->plugin('Config');
+
+  # Documentation browser under "/perldoc"
+  $self->plugin('PODRenderer') if $config->{perldoc};
+
+  # Router
+  my $r = $self->routes;
+
+  # Normal route to controller
+  $r->get('/')->to('example#welcome');
+}
+
+1;
+
+@@ controller
+% my $class = shift;
+package <%= $class %>;
+use Mojo::Base 'Mojolicious::Controller';
+
+# This action will render a template
+sub welcome {
+  my $self = shift;
+
+  # Render template "example/welcome.html.ep" with message
+  $self->render(msg => 'Welcome to the Mojolicious real-time web framework!');
+}
+
+1;
+
+@@ static
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Welcome to the Mojolicious real-time web framework!</title>
+  </head>
+  <body>
+    <h2>Welcome to the Mojolicious real-time web framework!</h2>
+    This is the static document "public/index.html",
+    <a href="/">click here</a> to get back to the start.
+  </body>
+</html>
+
+@@ test
+% my $class = shift;
+use Mojo::Base -strict;
+
+use Test::More;
+use Test::Mojo;
+
+my $t = Test::Mojo->new('<%= $class %>');
+$t->get_ok('/')->status_is(200)->content_like(qr/Mojolicious/i);
+
+done_testing();
+
+@@ layout
+<!DOCTYPE html>
+<html>
+  <head><title><%%= title %></title></head>
+  <body><%%= content %></body>
+</html>
+
+@@ welcome
+%% layout 'default';
+%% title 'Welcome';
+<h2><%%= $msg %></h2>
+<p>
+  This page was generated from the template "templates/example/welcome.html.ep"
+  and the layout "templates/layouts/default.html.ep",
+  <%%= link_to 'click here' => url_for %> to reload the page or
+  <%%= link_to 'here' => '/index.html' %> to move forward to a static page.
+  %% if (config 'perldoc') {
+    To learn more, you can also browse through the documentation
+    <%%= link_to 'here' => '/perldoc' %>.
+  %% }
+</p>
+
+@@ config
+% use Mojo::Util qw(sha1_sum steady_time);
+{
+  perldoc => 1,
+  secrets => ['<%= sha1_sum $$ . steady_time . rand  %>']
+}
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/generate/lite_app.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/generate/lite_app.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/generate/lite_app.pm	(revision 32205)
@@ -0,0 +1,108 @@
+package Mojolicious::Command::generate::lite_app;
+use Mojo::Base 'Mojolicious::Command';
+
+has description => 'Generate Mojolicious::Lite application';
+has usage => sub { shift->extract_usage };
+
+sub run {
+  my ($self, $name) = @_;
+  $name ||= 'myapp.pl';
+  $self->render_to_rel_file('liteapp', $name);
+  $self->chmod_rel_file($name, 0744);
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Command::generate::lite_app - Lite app generator command
+
+=head1 SYNOPSIS
+
+  Usage: APPLICATION generate lite_app [OPTIONS] [NAME]
+
+    mojo generate lite_app
+    mojo generate lite_app foo.pl
+
+  Options:
+    -h, --help   Show this summary of available options
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Command::generate::lite_app> generate fully functional
+L<Mojolicious::Lite> applications.
+
+This is a core command, that means it is always enabled and its code a good
+example for learning to build new commands, you're welcome to fork it.
+
+See L<Mojolicious::Commands/"COMMANDS"> for a list of commands that are
+available by default.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Command::generate::lite_app> inherits all attributes from
+L<Mojolicious::Command> and implements the following new ones.
+
+=head2 description
+
+  my $description = $app->description;
+  $app            = $app->description('Foo');
+
+Short description of this command, used for the command list.
+
+=head2 usage
+
+  my $usage = $app->usage;
+  $app      = $app->usage('Foo');
+
+Usage information for this command, used for the help screen.
+
+=head1 METHODS
+
+L<Mojolicious::Command::generate::lite_app> inherits all methods from
+L<Mojolicious::Command> and implements the following new ones.
+
+=head2 run
+
+  $app->run(@ARGV);
+
+Run this command.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
+
+__DATA__
+
+@@ liteapp
+#!/usr/bin/env perl
+use Mojolicious::Lite;
+
+# Documentation browser under "/perldoc"
+plugin 'PODRenderer';
+
+get '/' => sub {
+  my $c = shift;
+  $c->render(template => 'index');
+};
+
+app->start;
+<% %>__DATA__
+
+<% %>@@ index.html.ep
+%% layout 'default';
+%% title 'Welcome';
+<h1>Welcome to the Mojolicious real-time web framework!</h1>
+To learn more, you can browse through the documentation
+<%%= link_to 'here' => '/perldoc' %>.
+
+<% %>@@ layouts/default.html.ep
+<!DOCTYPE html>
+<html>
+  <head><title><%%= title %></title></head>
+  <body><%%= content %></body>
+</html>
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/generate/makefile.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/generate/makefile.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/generate/makefile.pm	(revision 32205)
@@ -0,0 +1,87 @@
+package Mojolicious::Command::generate::makefile;
+use Mojo::Base 'Mojolicious::Command';
+
+use Mojolicious;
+
+has description => 'Generate "Makefile.PL"';
+has usage => sub { shift->extract_usage };
+
+sub run { shift->render_to_rel_file('makefile', 'Makefile.PL') }
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Command::generate::makefile - Makefile generator command
+
+=head1 SYNOPSIS
+
+  Usage: APPLICATION generate makefile [OPTIONS]
+
+    mojo generate makefile
+
+  Options:
+    -h, --help   Show this summary of available options
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Command::generate::makefile> generates C<Makefile.PL> files for
+applications.
+
+This is a core command, that means it is always enabled and its code a good
+example for learning to build new commands, you're welcome to fork it.
+
+See L<Mojolicious::Commands/"COMMANDS"> for a list of commands that are
+available by default.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Command::generate::makefile> inherits all attributes from
+L<Mojolicious::Command> and implements the following new ones.
+
+=head2 description
+
+  my $description = $makefile->description;
+  $makefile       = $makefile->description('Foo');
+
+Short description of this command, used for the command list.
+
+=head2 usage
+
+  my $usage = $makefile->usage;
+  $makefile = $makefile->usage('Foo');
+
+Usage information for this command, used for the help screen.
+
+=head1 METHODS
+
+L<Mojolicious::Command::generate::makefile> inherits all methods from
+L<Mojolicious::Command> and implements the following new ones.
+
+=head2 run
+
+  $makefile->run(@ARGV);
+
+Run this command.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
+
+__DATA__
+
+@@ makefile
+use strict;
+use warnings;
+
+use ExtUtils::MakeMaker;
+
+WriteMakefile(
+  VERSION   => '0.01',
+  PREREQ_PM => {'Mojolicious' => '<%= $Mojolicious::VERSION %>'},
+  test      => {TESTS => 't/*.t'}
+);
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/generate/plugin.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/generate/plugin.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/generate/plugin.pm	(revision 32205)
@@ -0,0 +1,177 @@
+package Mojolicious::Command::generate::plugin;
+use Mojo::Base 'Mojolicious::Command';
+
+use Mojo::Util qw(camelize class_to_path);
+use Mojolicious;
+
+has description => 'Generate Mojolicious plugin directory structure';
+has usage => sub { shift->extract_usage };
+
+sub run {
+  my ($self, $name) = @_;
+  $name ||= 'MyPlugin';
+
+  # Class
+  my $class = $name =~ /^[a-z]/ ? camelize $name : $name;
+  $class = "Mojolicious::Plugin::$class";
+  my $app = class_to_path $class;
+  my $dir = join '-', split('::', $class);
+  $self->render_to_rel_file('class', "$dir/lib/$app", $class, $name);
+
+  # Test
+  $self->render_to_rel_file('test', "$dir/t/basic.t", $name);
+
+  # Makefile
+  $self->render_to_rel_file('makefile', "$dir/Makefile.PL", $class, $app);
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Command::generate::plugin - Plugin generator command
+
+=head1 SYNOPSIS
+
+  Usage: APPLICATION generate plugin [OPTIONS] [NAME]
+
+    mojo generate plugin
+    mojo generate plugin TestPlugin
+
+  Options:
+    -h, --help   Show this summary of available options
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Command::generate::plugin> generates directory structures for
+fully functional L<Mojolicious> plugins.
+
+This is a core command, that means it is always enabled and its code a good
+example for learning to build new commands, you're welcome to fork it.
+
+See L<Mojolicious::Commands/"COMMANDS"> for a list of commands that are
+available by default.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Command::generate::plugin> inherits all attributes from
+L<Mojolicious::Command> and implements the following new ones.
+
+=head2 description
+
+  my $description = $plugin->description;
+  $plugin         = $plugin->description('Foo');
+
+Short description of this command, used for the command list.
+
+=head2 usage
+
+  my $usage = $plugin->usage;
+  $plugin   = $plugin->usage('Foo');
+
+Usage information for this command, used for the help screen.
+
+=head1 METHODS
+
+L<Mojolicious::Command::generate::plugin> inherits all methods from
+L<Mojolicious::Command> and implements the following new ones.
+
+=head2 run
+
+  $plugin->run(@ARGV);
+
+Run this command.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
+
+__DATA__
+
+@@ class
+% my ($class, $name) = @_;
+package <%= $class %>;
+use Mojo::Base 'Mojolicious::Plugin';
+
+our $VERSION = '0.01';
+
+sub register {
+  my ($self, $app) = @_;
+}
+
+1;
+<% %>__END__
+
+<% %>=encoding utf8
+
+<% %>=head1 NAME
+
+<%= $class %> - Mojolicious Plugin
+
+<% %>=head1 SYNOPSIS
+
+  # Mojolicious
+  $self->plugin('<%= $name %>');
+
+  # Mojolicious::Lite
+  plugin '<%= $name %>';
+
+<% %>=head1 DESCRIPTION
+
+L<<%= $class %>> is a L<Mojolicious> plugin.
+
+<% %>=head1 METHODS
+
+L<<%= $class %>> inherits all methods from
+L<Mojolicious::Plugin> and implements the following new ones.
+
+<% %>=head2 register
+
+  $plugin->register(Mojolicious->new);
+
+Register plugin in L<Mojolicious> application.
+
+<% %>=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+<% %>=cut
+
+@@ test
+% my $name = shift;
+use Mojo::Base -strict;
+
+use Test::More;
+use Mojolicious::Lite;
+use Test::Mojo;
+
+plugin '<%= $name %>';
+
+get '/' => sub {
+  my $c = shift;
+  $c->render(text => 'Hello Mojo!');
+};
+
+my $t = Test::Mojo->new;
+$t->get_ok('/')->status_is(200)->content_is('Hello Mojo!');
+
+done_testing();
+
+@@ makefile
+% my ($class, $path) = @_;
+use strict;
+use warnings;
+
+use ExtUtils::MakeMaker;
+
+WriteMakefile(
+  NAME         => '<%= $class %>',
+  VERSION_FROM => 'lib/<%= $path %>',
+  AUTHOR       => 'A Good Programmer <nospam@cpan.org>',
+  PREREQ_PM    => {'Mojolicious' => '<%= $Mojolicious::VERSION %>'},
+  test         => {TESTS => 't/*.t'}
+);
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/get.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/get.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/get.pm	(revision 32205)
@@ -0,0 +1,235 @@
+package Mojolicious::Command::get;
+use Mojo::Base 'Mojolicious::Command';
+
+use Mojo::DOM;
+use Mojo::IOLoop;
+use Mojo::JSON qw(to_json j);
+use Mojo::JSON::Pointer;
+use Mojo::URL;
+use Mojo::UserAgent;
+use Mojo::Util qw(decode encode getopt);
+use Scalar::Util 'weaken';
+
+has description => 'Perform HTTP request';
+has usage => sub { shift->extract_usage };
+
+sub run {
+  my ($self, @args) = @_;
+
+  # Data from STDIN
+  vec(my $r = '', fileno(STDIN), 1) = 1;
+  my $in = !-t STDIN && select($r, undef, undef, 0) ? join '', <STDIN> : undef;
+
+  my $ua = Mojo::UserAgent->new(ioloop => Mojo::IOLoop->singleton);
+  my %form;
+  getopt \@args,
+    'C|charset=s' => \my $charset,
+    'c|content=s' => \$in,
+    'f|form=s'    => sub { _form(\%form) if $_[1] =~ /^(.+)=(\@?)(.+)$/ },
+    'H|header=s'  => \my @headers,
+    'i|inactivity-timeout=i' => sub { $ua->inactivity_timeout($_[1]) },
+    'k|insecure'             => sub { $ua->insecure(1) },
+    'M|method=s'             => \(my $method = 'GET'),
+    'o|connect-timeout=i'    => sub { $ua->connect_timeout($_[1]) },
+    'r|redirect'             => \my $redirect,
+    'S|response-size=i'      => sub { $ua->max_response_size($_[1]) },
+    'u|user=s'               => \my $user,
+    'v|verbose'              => \my $verbose;
+
+  @args = map { decode 'UTF-8', $_ } @args;
+  die $self->usage unless my $url = shift @args;
+  my $selector = shift @args;
+
+  # Parse header pairs
+  my %headers = map { /^\s*([^:]+)\s*:\s*(.*+)$/ ? ($1, $2) : () } @headers;
+
+  # Detect proxy for absolute URLs
+  $url !~ m!^/! ? $ua->proxy->detect : $ua->server->app($self->app);
+  $url = Mojo::URL->new($url)->userinfo($user) if $user;
+  $ua->max_redirects(10) if $redirect;
+
+  my $buffer = '';
+  $ua->on(
+    start => sub {
+      my ($ua, $tx) = @_;
+
+      # Verbose
+      weaken $tx;
+      $tx->res->content->on(
+        body => sub { warn _header($tx->req), _header($tx->res) })
+        if $verbose;
+
+      # Stream content (ignore redirects)
+      $tx->res->content->unsubscribe('read')->on(
+        read => sub {
+          return if $redirect && $tx->res->is_redirect;
+          defined $selector ? ($buffer .= pop) : print pop;
+        }
+      );
+    }
+  );
+
+  # Switch to verbose for HEAD requests
+  $verbose = 1 if $method eq 'HEAD';
+  STDOUT->autoflush(1);
+  my @content = %form ? (form => \%form) : defined $in ? ($in) : ();
+  my $tx = $ua->start($ua->build_tx($method, $url, \%headers, @content));
+  my $res = $tx->result;
+
+  # JSON Pointer
+  return unless defined $selector;
+  return _json($buffer, $selector) if !length $selector || $selector =~ m!^/!;
+
+  # Selector
+  $charset //= $res->content->charset || $res->default_charset;
+  _select($buffer, $selector, $charset, @args);
+}
+
+sub _form { push @{$_[0]{$1}}, $2 ? {file => $3} : $3 }
+
+sub _header { $_[0]->build_start_line, $_[0]->headers->to_string, "\n\n" }
+
+sub _json {
+  return unless my $data = j(shift);
+  return unless defined($data = Mojo::JSON::Pointer->new($data)->get(shift));
+  _say(ref $data eq 'HASH' || ref $data eq 'ARRAY' ? to_json($data) : $data);
+}
+
+sub _say { length && say encode('UTF-8', $_) for @_ }
+
+sub _select {
+  my ($buffer, $selector, $charset, @args) = @_;
+
+  # Keep a strong reference to the root
+  $buffer = decode($charset, $buffer) // $buffer if $charset;
+  my $dom     = Mojo::DOM->new($buffer);
+  my $results = $dom->find($selector);
+
+  while (defined(my $command = shift @args)) {
+
+    # Number
+    ($results = $results->slice($command)) and next if $command =~ /^\d+$/;
+
+    # Text
+    return _say($results->map('text')->each) if $command eq 'text';
+
+    # All text
+    return _say($results->map('all_text')->each) if $command eq 'all';
+
+    # Attribute
+    return _say($results->map(attr => $args[0] // '')->each)
+      if $command eq 'attr';
+
+    # Unknown
+    die qq{Unknown command "$command".\n};
+  }
+
+  _say($results->each);
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Command::get - Get command
+
+=head1 SYNOPSIS
+
+  Usage: APPLICATION get [OPTIONS] URL [SELECTOR|JSON-POINTER] [COMMANDS]
+
+    ./myapp.pl get /
+    ./myapp.pl get -H 'Accept: text/html' /hello.html 'head > title' text
+    ./myapp.pl get //sri:secr3t@/secrets.json /1/content
+    mojo get mojolicious.org
+    mojo get -v -r -o 25 -i 50 google.com
+    mojo get -v -H 'Host: mojolicious.org' -H 'Accept: */*' mojolicious.org
+    mojo get -u 'sri:s3cret' https://mojolicious.org
+    mojo get mojolicious.org > example.html
+    mojo get -M PUT mojolicious.org < example.html
+    mojo get -f 'q=Mojolicious' -f 'size=5' https://metacpan.org/search
+    mojo get -M POST -f 'upload=@example.html' mojolicious.org
+    mojo get mojolicious.org 'head > title' text
+    mojo get mojolicious.org .footer all
+    mojo get mojolicious.org a attr href
+    mojo get mojolicious.org '*' attr id
+    mojo get mojolicious.org 'h1, h2, h3' 3 text
+    mojo get https://fastapi.metacpan.org/v1/author/SRI /name
+    mojo get -H 'Host: example.com' http+unix://%2Ftmp%2Fmyapp.sock/index.html
+
+  Options:
+    -C, --charset <charset>              Charset of HTML/XML content, defaults
+                                         to auto-detection
+    -c, --content <content>              Content to send with request
+    -f, --form <name=value>              One or more form values and file
+                                         uploads
+    -H, --header <name:value>            One or more additional HTTP headers
+    -h, --help                           Show this summary of available options
+        --home <path>                    Path to home directory of your
+                                         application, defaults to the value of
+                                         MOJO_HOME or auto-detection
+    -i, --inactivity-timeout <seconds>   Inactivity timeout, defaults to the
+                                         value of MOJO_INACTIVITY_TIMEOUT or 20
+    -k, --insecure                       Do not require a valid TLS certificate
+                                         to access HTTPS sites
+    -M, --method <method>                HTTP method to use, defaults to "GET"
+    -m, --mode <name>                    Operating mode for your application,
+                                         defaults to the value of
+                                         MOJO_MODE/PLACK_ENV or "development"
+    -o, --connect-timeout <seconds>      Connect timeout, defaults to the value
+                                         of MOJO_CONNECT_TIMEOUT or 10
+    -r, --redirect                       Follow up to 10 redirects
+    -S, --response-size <size>           Maximum response size in bytes,
+                                         defaults to 2147483648 (2GiB)
+    -u, --user <userinfo>                Alternate mechanism for specifying
+                                         colon-separated username and password
+    -v, --verbose                        Print request and response headers to
+                                         STDERR
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Command::get> is a command line interface for
+L<Mojo::UserAgent>.
+
+This is a core command, that means it is always enabled and its code a good
+example for learning to build new commands, you're welcome to fork it.
+
+See L<Mojolicious::Commands/"COMMANDS"> for a list of commands that are
+available by default.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Command::get> performs requests to remote hosts or local
+applications.
+
+=head2 description
+
+  my $description = $get->description;
+  $get            = $get->description('Foo');
+
+Short description of this command, used for the command list.
+
+=head2 usage
+
+  my $usage = $get->usage;
+  $get      = $get->usage('Foo');
+
+Usage information for this command, used for the help screen.
+
+=head1 METHODS
+
+L<Mojolicious::Command::get> inherits all methods from L<Mojolicious::Command>
+and implements the following new ones.
+
+=head2 run
+
+  $get->run(@ARGV);
+
+Run this command.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/inflate.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/inflate.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/inflate.pm	(revision 32205)
@@ -0,0 +1,97 @@
+package Mojolicious::Command::inflate;
+use Mojo::Base 'Mojolicious::Command';
+
+use Mojo::Loader qw(data_section file_is_binary);
+use Mojo::Util 'encode';
+
+has description => 'Inflate embedded files to real files';
+has usage => sub { shift->extract_usage };
+
+sub run {
+  my $self = shift;
+
+  # Find all embedded files
+  my %all;
+  my $app = $self->app;
+  for my $class (@{$app->renderer->classes}, @{$app->static->classes}) {
+    for my $name (keys %{data_section $class}) {
+      my $data = data_section $class, $name;
+      $data = encode 'UTF-8', $data unless file_is_binary $class, $name;
+      $all{$name} = $data;
+    }
+  }
+
+  # Turn them into real files
+  for my $name (grep {/\.\w+$/} keys %all) {
+    my $prefix = $name =~ /\.\w+\.\w+$/ ? 'templates' : 'public';
+    $self->write_file($self->rel_file("$prefix/$name"), $all{$name});
+  }
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Command::inflate - Inflate command
+
+=head1 SYNOPSIS
+
+  Usage: APPLICATION inflate [OPTIONS]
+
+    ./myapp.pl inflate
+
+  Options:
+    -h, --help          Show this summary of available options
+        --home <path>   Path to home directory of your application, defaults to
+                        the value of MOJO_HOME or auto-detection
+    -m, --mode <name>   Operating mode for your application, defaults to the
+                        value of MOJO_MODE/PLACK_ENV or "development"
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Command::inflate> turns templates and static files embedded in
+the C<DATA> sections of your application into real files.
+
+This is a core command, that means it is always enabled and its code a good
+example for learning to build new commands, you're welcome to fork it.
+
+See L<Mojolicious::Commands/"COMMANDS"> for a list of commands that are
+available by default.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Command::inflate> inherits all attributes from
+L<Mojolicious::Command> and implements the following new ones.
+
+=head2 description
+
+  my $description = $inflate->description;
+  $inflate        = $inflate->description('Foo');
+
+Short description of this command, used for the command list.
+
+=head2 usage
+
+  my $usage = $inflate->usage;
+  $inflate  = $inflate->usage('Foo');
+
+Usage information for this command, used for the help screen.
+
+=head1 METHODS
+
+L<Mojolicious::Command::inflate> inherits all methods from
+L<Mojolicious::Command> and implements the following new ones.
+
+=head2 run
+
+  $inflate->run(@ARGV);
+
+Run this command.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/prefork.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/prefork.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/prefork.pm	(revision 32205)
@@ -0,0 +1,130 @@
+package Mojolicious::Command::prefork;
+use Mojo::Base 'Mojolicious::Command';
+
+use Mojo::Server::Prefork;
+use Mojo::Util 'getopt';
+
+has description =>
+  'Start application with pre-forking HTTP and WebSocket server';
+has usage => sub { shift->extract_usage };
+
+sub run {
+  my ($self, @args) = @_;
+
+  my $prefork = Mojo::Server::Prefork->new(app => $self->app);
+  getopt \@args,
+    'a|accepts=i'            => sub { $prefork->accepts($_[1]) },
+    'b|backlog=i'            => sub { $prefork->backlog($_[1]) },
+    'c|clients=i'            => sub { $prefork->max_clients($_[1]) },
+    'G|graceful-timeout=i'   => sub { $prefork->graceful_timeout($_[1]) },
+    'I|heartbeat-interval=i' => sub { $prefork->heartbeat_interval($_[1]) },
+    'H|heartbeat-timeout=i'  => sub { $prefork->heartbeat_timeout($_[1]) },
+    'i|inactivity-timeout=i' => sub { $prefork->inactivity_timeout($_[1]) },
+    'l|listen=s'             => \my @listen,
+    'P|pid-file=s'           => sub { $prefork->pid_file($_[1]) },
+    'p|proxy'                => sub { $prefork->reverse_proxy(1) },
+    'r|requests=i'           => sub { $prefork->max_requests($_[1]) },
+    's|spare=i'              => sub { $prefork->spare($_[1]) },
+    'w|workers=i'            => sub { $prefork->workers($_[1]) };
+
+  $prefork->listen(\@listen) if @listen;
+  $prefork->run;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Command::prefork - Pre-fork command
+
+=head1 SYNOPSIS
+
+  Usage: APPLICATION prefork [OPTIONS]
+
+    ./myapp.pl prefork
+    ./myapp.pl prefork -m production -l http://*:8080
+    ./myapp.pl prefork -l http://127.0.0.1:8080 -l https://[::]:8081
+    ./myapp.pl prefork -l 'https://*:443?cert=./server.crt&key=./server.key'
+    ./myapp.pl prefork -l http+unix://%2Ftmp%2Fmyapp.sock -w 12
+
+  Options:
+    -a, --accepts <number>               Number of connections for workers to
+                                         accept, defaults to 10000
+    -b, --backlog <size>                 Listen backlog size, defaults to
+                                         SOMAXCONN
+    -c, --clients <number>               Maximum number of concurrent
+                                         connections, defaults to 1000
+    -G, --graceful-timeout <seconds>     Graceful timeout, defaults to 120.
+    -I, --heartbeat-interval <seconds>   Heartbeat interval, defaults to 5
+    -H, --heartbeat-timeout <seconds>    Heartbeat timeout, defaults to 30
+    -h, --help                           Show this summary of available options
+        --home <path>                    Path to home directory of your
+                                         application, defaults to the value of
+                                         MOJO_HOME or auto-detection
+    -i, --inactivity-timeout <seconds>   Inactivity timeout, defaults to the
+                                         value of MOJO_INACTIVITY_TIMEOUT or 15
+    -l, --listen <location>              One or more locations you want to
+                                         listen on, defaults to the value of
+                                         MOJO_LISTEN or "http://*:3000"
+    -m, --mode <name>                    Operating mode for your application,
+                                         defaults to the value of
+                                         MOJO_MODE/PLACK_ENV or "development"
+    -P, --pid-file <path>                Path to process id file, defaults to
+                                         "prefork.pid" in a temporary directory
+    -p, --proxy                          Activate reverse proxy support,
+                                         defaults to the value of
+                                         MOJO_REVERSE_PROXY
+    -r, --requests <number>              Maximum number of requests per
+                                         keep-alive connection, defaults to 100
+    -s, --spare <number>                 Temporarily spawn up to this number of
+                                         additional workers, defaults to 2
+    -w, --workers <number>               Number of workers, defaults to 4
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Command::prefork> starts applications with the
+L<Mojo::Server::Prefork> backend.
+
+This is a core command, that means it is always enabled and its code a good
+example for learning to build new commands, you're welcome to fork it.
+
+See L<Mojolicious::Commands/"COMMANDS"> for a list of commands that are
+available by default.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Command::prefork> inherits all attributes from
+L<Mojolicious::Command> and implements the following new ones.
+
+=head2 description
+
+  my $description = $prefork->description;
+  $prefork        = $prefork->description('Foo');
+
+Short description of this command, used for the command list.
+
+=head2 usage
+
+  my $usage = $prefork->usage;
+  $prefork  = $prefork->usage('Foo');
+
+Usage information for this command, used for the help screen.
+
+=head1 METHODS
+
+L<Mojolicious::Command::prefork> inherits all methods from
+L<Mojolicious::Command> and implements the following new ones.
+
+=head2 run
+
+  $prefork->run(@ARGV);
+
+Run this command.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/psgi.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/psgi.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/psgi.pm	(revision 32205)
@@ -0,0 +1,77 @@
+package Mojolicious::Command::psgi;
+use Mojo::Base 'Mojolicious::Command';
+
+use Mojo::Server::PSGI;
+
+has description => 'Start application with PSGI';
+has usage => sub { shift->extract_usage };
+
+sub run { Mojo::Server::PSGI->new(app => shift->app)->to_psgi_app }
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Command::psgi - PSGI command
+
+=head1 SYNOPSIS
+
+  Usage: APPLICATION psgi [OPTIONS]
+
+    ./myapp.pl psgi
+
+  Options:
+    -h, --help          Show this summary of available options
+        --home <path>   Path to home directory of your application, defaults to
+                        the value of MOJO_HOME or auto-detection
+    -m, --mode <name>   Operating mode for your application, defaults to the
+                        value of MOJO_MODE/PLACK_ENV or "development"
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Command::psgi> starts applications with the L<Mojo::Server::PSGI>
+backend.
+
+This is a core command, that means it is always enabled and its code a good
+example for learning to build new commands, you're welcome to fork it.
+
+See L<Mojolicious::Commands/"COMMANDS"> for a list of commands that are
+available by default.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Command::psgi> inherits all attributes from
+L<Mojolicious::Command> and implements the following new ones.
+
+=head2 description
+
+  my $description = $psgi->description;
+  $psgi           = $psgi->description('Foo');
+
+Short description of this command, used for the command list.
+
+=head2 usage
+
+  my $usage = $psgi->usage;
+  $psgi     = $psgi->usage('Foo');
+
+Usage information for this command, used for the help screen.
+
+=head1 METHODS
+
+L<Mojolicious::Command::psgi> inherits all methods from L<Mojolicious::Command>
+and implements the following new ones.
+
+=head2 run
+
+  my $app = $psgi->run;
+
+Run this command.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/routes.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/routes.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/routes.pm	(revision 32205)
@@ -0,0 +1,122 @@
+package Mojolicious::Command::routes;
+use Mojo::Base 'Mojolicious::Command';
+
+use re 'regexp_pattern';
+use Mojo::Util qw(encode getopt tablify);
+
+has description => 'Show available routes';
+has usage => sub { shift->extract_usage };
+
+sub run {
+  my ($self, @args) = @_;
+
+  getopt \@args, 'v|verbose' => \my $verbose;
+
+  my $rows = [];
+  _walk($_, 0, $rows, $verbose) for @{$self->app->routes->children};
+  print encode('UTF-8', tablify($rows));
+}
+
+sub _walk {
+  my ($route, $depth, $rows, $verbose) = @_;
+
+  # Pattern
+  my $prefix = '';
+  if (my $i = $depth * 2) { $prefix .= ' ' x $i . '+' }
+  push @$rows, my $row = [$prefix . ($route->pattern->unparsed || '/')];
+
+  # Flags
+  my @flags;
+  push @flags, @{$route->over || []} ? 'C' : '.';
+  push @flags, (my $partial = $route->partial) ? 'D' : '.';
+  push @flags, $route->inline       ? 'U' : '.';
+  push @flags, $route->is_websocket ? 'W' : '.';
+  push @$row, join('', @flags) if $verbose;
+
+  # Methods
+  my $via = $route->via;
+  push @$row, !$via ? '*' : uc join ',', @$via;
+
+  # Name
+  my $name = $route->name;
+  push @$row, $route->has_custom_name ? qq{"$name"} : $name;
+
+  # Regex (verbose)
+  my $pattern = $route->pattern;
+  $pattern->match('/', $route->is_endpoint && !$partial);
+  push @$row, (regexp_pattern $pattern->regex)[0] if $verbose;
+
+  $depth++;
+  _walk($_, $depth, $rows, $verbose) for @{$route->children};
+  $depth--;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Command::routes - Routes command
+
+=head1 SYNOPSIS
+
+  Usage: APPLICATION routes [OPTIONS]
+
+    ./myapp.pl routes
+    ./myapp.pl routes -v
+
+  Options:
+    -h, --help          Show this summary of available options
+        --home <path>   Path to home directory of your application, defaults to
+                        the value of MOJO_HOME or auto-detection
+    -m, --mode <name>   Operating mode for your application, defaults to the
+                        value of MOJO_MODE/PLACK_ENV or "development"
+    -v, --verbose       Print additional details about routes, flags indicate
+                        C=Conditions, D=Detour, U=Under and W=WebSocket
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Command::routes> lists all your application routes.
+
+This is a core command, that means it is always enabled and its code a good
+example for learning to build new commands, you're welcome to fork it.
+
+See L<Mojolicious::Commands/"COMMANDS"> for a list of commands that are
+available by default.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Command::routes> inherits all attributes from
+L<Mojolicious::Command> and implements the following new ones.
+
+=head2 description
+
+  my $description = $routes->description;
+  $routes         = $routes->description('Foo');
+
+Short description of this command, used for the command list.
+
+=head2 usage
+
+  my $usage = $routes->usage;
+  $routes   = $routes->usage('Foo');
+
+Usage information for this command, used for the help screen.
+
+=head1 METHODS
+
+L<Mojolicious::Command::routes> inherits all methods from
+L<Mojolicious::Command> and implements the following new ones.
+
+=head2 run
+
+  $routes->run(@ARGV);
+
+Run this command.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/test.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/test.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/test.pm	(revision 32205)
@@ -0,0 +1,90 @@
+package Mojolicious::Command::test;
+use Mojo::Base 'Mojolicious::Command';
+
+use Mojo::Util 'getopt';
+
+has description => 'Run tests';
+has usage => sub { shift->extract_usage };
+
+sub run {
+  my ($self, @args) = @_;
+
+  getopt \@args, 'v|verbose' => \$ENV{HARNESS_VERBOSE};
+
+  if (!@args && (my $tests = $self->app->home->child('t'))) {
+    die "Can't find test directory.\n" unless -d $tests;
+    @args = $tests->list_tree->grep(qr/\.t$/)->map('to_string')->each;
+    say qq{Running tests from "$tests".};
+  }
+
+  $ENV{HARNESS_OPTIONS} //= 'c';
+  require Test::Harness;
+  local $Test::Harness::switches = '';
+  Test::Harness::runtests(sort @args);
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Command::test - Test command
+
+=head1 SYNOPSIS
+
+  Usage: APPLICATION test [OPTIONS] [TESTS]
+
+    ./myapp.pl test
+    ./myapp.pl test t/foo.t
+    ./myapp.pl test -v t/foo/*.t
+
+  Options:
+    -h, --help      Show this summary of available options
+    -v, --verbose   Print verbose debug information to STDERR
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Command::test> runs application tests from the C<t> directory.
+
+This is a core command, that means it is always enabled and its code a good
+example for learning to build new commands, you're welcome to fork it.
+
+See L<Mojolicious::Commands/"COMMANDS"> for a list of commands that are
+available by default.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Command::test> inherits all attributes from
+L<Mojolicious::Command> and implements the following new ones.
+
+=head2 description
+
+  my $description = $test->description;
+  $test           = $test->description('Foo');
+
+Short description of this command, used for the command list.
+
+=head2 usage
+
+  my $usage = $test->usage;
+  $test     = $test->usage('Foo');
+
+Usage information for this command, used for the help screen.
+
+=head1 METHODS
+
+L<Mojolicious::Command::test> inherits all methods from L<Mojolicious::Command>
+and implements the following new ones.
+
+=head2 run
+
+  $test->run(@ARGV);
+
+Run this command.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/version.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/version.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Command/version.pm	(revision 32205)
@@ -0,0 +1,112 @@
+package Mojolicious::Command::version;
+use Mojo::Base 'Mojolicious::Command';
+
+use Mojo::IOLoop::Client;
+use Mojo::IOLoop::TLS;
+use Mojolicious;
+
+has description => 'Show versions of available modules';
+has usage => sub { shift->extract_usage };
+
+sub run {
+  my $self = shift;
+
+  my $ev = eval { require Mojo::Reactor::EV; 1 } ? $EV::VERSION : 'n/a';
+  my $socks
+    = Mojo::IOLoop::Client->can_socks ? $IO::Socket::Socks::VERSION : 'n/a';
+  my $tls = Mojo::IOLoop::TLS->can_tls    ? $IO::Socket::SSL::VERSION  : 'n/a';
+  my $nnr = Mojo::IOLoop::Client->can_nnr ? $Net::DNS::Native::VERSION : 'n/a';
+  my $roles = Mojo::Base->ROLES ? $Role::Tiny::VERSION : 'n/a';
+
+  print <<EOF;
+CORE
+  Perl        ($^V, $^O)
+  Mojolicious ($Mojolicious::VERSION, $Mojolicious::CODENAME)
+
+OPTIONAL
+  EV 4.0+                 ($ev)
+  IO::Socket::Socks 0.64+ ($socks)
+  IO::Socket::SSL 2.009+  ($tls)
+  Net::DNS::Native 0.15+  ($nnr)
+  Role::Tiny 2.000001+    ($roles)
+
+EOF
+
+  # Check latest version on CPAN
+  my $latest = eval {
+    $self->app->ua->max_redirects(10)->tap(sub { $_->proxy->detect })
+      ->get('fastapi.metacpan.org/v1/release/Mojolicious')
+      ->result->json->{version};
+  } or return;
+
+  my $msg = 'This version is up to date, have fun!';
+  $msg = 'Thanks for testing a development release, you are awesome!'
+    if $latest < $Mojolicious::VERSION;
+  $msg = "You might want to update your Mojolicious to $latest!"
+    if $latest > $Mojolicious::VERSION;
+  say $msg;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Command::version - Version command
+
+=head1 SYNOPSIS
+
+  Usage: APPLICATION version [OPTIONS]
+
+    mojo version
+
+  Options:
+    -h, --help   Show this summary of available options
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Command::version> shows version information for available core
+and optional modules.
+
+This is a core command, that means it is always enabled and its code a good
+example for learning to build new commands, you're welcome to fork it.
+
+See L<Mojolicious::Commands/"COMMANDS"> for a list of commands that are
+available by default.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Command::version> inherits all attributes from
+L<Mojolicious::Command> and implements the following new ones.
+
+=head2 description
+
+  my $description = $v->description;
+  $v              = $v->description('Foo');
+
+Short description of this command, used for the command list.
+
+=head2 usage
+
+  my $usage = $v->usage;
+  $v        = $v->usage('Foo');
+
+Usage information for this command, used for the help screen.
+
+=head1 METHODS
+
+L<Mojolicious::Command::version> inherits all methods from
+L<Mojolicious::Command> and implements the following new ones.
+
+=head2 run
+
+  $v->run(@ARGV);
+
+Run this command.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Commands.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Commands.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Commands.pm	(revision 32205)
@@ -0,0 +1,326 @@
+package Mojolicious::Commands;
+use Mojo::Base 'Mojolicious::Command';
+
+use Mojo::Loader qw(find_modules find_packages load_class);
+use Mojo::Server;
+use Mojo::Util qw(getopt tablify);
+
+has hint => <<EOF;
+
+See 'APPLICATION help COMMAND' for more information on a specific command.
+EOF
+has message    => sub { shift->extract_usage . "\nCommands:\n" };
+has namespaces => sub { ['Mojolicious::Command'] };
+
+sub detect {
+
+  # PSGI (Plack only for now)
+  return 'psgi' if defined $ENV{PLACK_ENV};
+
+  # CGI
+  return 'cgi' if defined $ENV{PATH_INFO} || defined $ENV{GATEWAY_INTERFACE};
+
+  # Nothing
+  return undef;
+}
+
+sub run {
+  my ($self, $name, @args) = @_;
+
+  # Application loader
+  return $self->app if defined $ENV{MOJO_APP_LOADER};
+
+  # Try to detect environment
+  if (!$ENV{MOJO_NO_DETECT} && (my $env = $self->detect)) { $name = $env }
+
+  # Run command
+  if ($name && $name =~ /^\w+$/ && ($name ne 'help' || $args[0])) {
+
+    # Help
+    $name = shift @args if my $help = $name eq 'help';
+    $help = $ENV{MOJO_HELP} ||= $help;
+
+    # Remove options shared by all commands before loading the command
+    _args(\@args);
+    my $module;
+    $module = _command("${_}::$name", 1) and last for @{$self->namespaces};
+
+    # Unknown command
+    die qq{Unknown command "$name", maybe you need to install it?\n}
+      unless $module;
+
+    # Run command
+    my $command = $module->new(app => $self->app);
+    return $help ? $command->help(@args) : $command->run(@args);
+  }
+
+  # Hide list for tests
+  return 1 if $ENV{HARNESS_ACTIVE};
+
+  # Find all available commands
+  my %all;
+  for my $ns (@{$self->namespaces}) {
+    $all{substr $_, length "${ns}::"} //= $_->new->description
+      for grep { _command($_) } find_modules($ns), find_packages($ns);
+  }
+
+  my @rows = map { [" $_", $all{$_}] } sort keys %all;
+  return print $self->message, tablify(\@rows), $self->hint;
+}
+
+sub start_app { shift; Mojo::Server->new->build_app(shift)->start(@_) }
+
+# Command line options for MOJO_HELP, MOJO_HOME and MOJO_MODE
+sub _args {
+  getopt shift, ['pass_through'],
+    'h|help'   => \$ENV{MOJO_HELP},
+    'home=s'   => \$ENV{MOJO_HOME},
+    'm|mode=s' => \$ENV{MOJO_MODE}
+    unless __PACKAGE__->detect;
+}
+
+# Do not remove options from @ARGV
+BEGIN { _args([@ARGV]) }
+
+sub _command {
+  my ($module, $fatal) = @_;
+  return $module->isa('Mojolicious::Command') ? $module : undef
+    unless my $e = load_class $module;
+  $fatal && ref $e ? die $e : return undef;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Commands - Command line interface
+
+=head1 SYNOPSIS
+
+  Usage: APPLICATION COMMAND [OPTIONS]
+
+    mojo version
+    mojo generate lite_app
+    ./myapp.pl daemon -m production -l http://*:8080
+    ./myapp.pl get /foo
+    ./myapp.pl routes -v
+
+  Tip: CGI and PSGI environments can be automatically detected very often and
+       work without commands.
+
+  Options (for all commands):
+    -h, --help          Get more information on a specific command
+        --home <path>   Path to home directory of your application, defaults to
+                        the value of MOJO_HOME or auto-detection
+    -m, --mode <name>   Operating mode for your application, defaults to the
+                        value of MOJO_MODE/PLACK_ENV or "development"
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Commands> is the interactive command line interface for the
+L<Mojolicious> framework. It will automatically detect available commands in
+the C<Mojolicious::Command> namespace.
+
+=head1 COMMANDS
+
+These commands are available by default.
+
+=head2 cgi
+
+  $ ./myapp.pl cgi
+
+Use L<Mojolicious::Command::cgi> to start application with CGI backend, usually
+auto detected.
+
+=head2 cpanify
+
+  $ mojo cpanify -u sri -p secr3t Mojolicious-Plugin-Fun-0.1.tar.gz
+
+Use L<Mojolicious::Command::cpanify> for uploading files to CPAN.
+
+=head2 daemon
+
+  $ ./myapp.pl daemon
+
+Use L<Mojolicious::Command::daemon> to start application with standalone HTTP
+and WebSocket server.
+
+=head2 eval
+
+  $ ./myapp.pl eval 'say app->home'
+
+Use L<Mojolicious::Command::eval> to run code against application.
+
+=head2 generate
+
+  $ mojo generate
+  $ mojo generate help
+  $ ./myapp.pl generate help
+
+List available generator commands with short descriptions.
+
+  $ mojo generate help <generator>
+  $ ./myapp.pl generate help <generator>
+
+List available options for generator command with short descriptions.
+
+=head2 generate app
+
+  $ mojo generate app <AppName>
+
+Use L<Mojolicious::Command::generate::app> to generate application directory
+structure for a fully functional L<Mojolicious> application.
+
+=head2 generate lite_app
+
+  $ mojo generate lite_app
+
+Use L<Mojolicious::Command::generate::lite_app> to generate a fully functional
+L<Mojolicious::Lite> application.
+
+=head2 generate makefile
+
+  $ mojo generate makefile
+  $ ./myapp.pl generate makefile
+
+Use L<Mojolicious::Command::generate::makefile> to generate C<Makefile.PL> file
+for application.
+
+=head2 generate plugin
+
+  $ mojo generate plugin <PluginName>
+
+Use L<Mojolicious::Command::generate::plugin> to generate directory structure
+for a fully functional L<Mojolicious> plugin.
+
+=head2 get
+
+  $ mojo get https://mojolicious.org
+  $ ./myapp.pl get /foo
+
+Use L<Mojolicious::Command::get> to perform requests to remote host or local
+application.
+
+=head2 help
+
+  $ mojo
+  $ mojo help
+  $ ./myapp.pl help
+
+List available commands with short descriptions.
+
+  $ mojo help <command>
+  $ ./myapp.pl help <command>
+
+List available options for the command with short descriptions.
+
+=head2 inflate
+
+  $ ./myapp.pl inflate
+
+Use L<Mojolicious::Command::inflate> to turn templates and static files
+embedded in the C<DATA> sections of your application into real files.
+
+=head2 prefork
+
+  $ ./myapp.pl prefork
+
+Use L<Mojolicious::Command::prefork> to start application with standalone
+pre-forking HTTP and WebSocket server.
+
+=head2 psgi
+
+  $ ./myapp.pl psgi
+
+Use L<Mojolicious::Command::psgi> to start application with PSGI backend,
+usually auto detected.
+
+=head2 routes
+
+  $ ./myapp.pl routes
+
+Use L<Mojolicious::Command::routes> to list application routes.
+
+=head2 test
+
+  $ ./myapp.pl test
+  $ ./myapp.pl test t/fun.t
+
+Use L<Mojolicious::Command::test> to run application tests from the C<t>
+directory.
+
+=head2 version
+
+  $ mojo version
+  $ ./myapp.pl version
+
+Use L<Mojolicious::Command::version> to show version information for available
+core and optional modules, very useful for debugging.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Commands> inherits all attributes from L<Mojolicious::Command>
+and implements the following new ones.
+
+=head2 hint
+
+  my $hint  = $commands->hint;
+  $commands = $commands->hint('Foo');
+
+Short hint shown after listing available commands.
+
+=head2 message
+
+  my $msg   = $commands->message;
+  $commands = $commands->message('Hello World!');
+
+Short usage message shown before listing available commands.
+
+=head2 namespaces
+
+  my $namespaces = $commands->namespaces;
+  $commands      = $commands->namespaces(['MyApp::Command']);
+
+Namespaces to load commands from, defaults to C<Mojolicious::Command>.
+
+  # Add another namespace to load commands from
+  push @{$commands->namespaces}, 'MyApp::Command';
+
+=head1 METHODS
+
+L<Mojolicious::Commands> inherits all methods from L<Mojolicious::Command> and
+implements the following new ones.
+
+=head2 detect
+
+  my $env = $commands->detect;
+
+Try to detect environment, or return C<undef> if none could be detected.
+
+=head2 run
+
+  $commands->run;
+  $commands->run(@ARGV);
+
+Load and run commands. Automatic deployment environment detection can be
+disabled with the C<MOJO_NO_DETECT> environment variable.
+
+=head2 start_app
+
+  Mojolicious::Commands->start_app('MyApp');
+  Mojolicious::Commands->start_app(MyApp => @ARGV);
+
+Load application from class and start the command line interface for it. Note
+that the options C<-h>/C<--help>, C<--home> and C<-m>/C<--mode>, which are
+shared by all commands, will be parsed from C<@ARGV> during compile time.
+
+  # Always start daemon for application
+  Mojolicious::Commands->start_app('MyApp', 'daemon', '-l', 'http://*:8080');
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Controller.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Controller.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Controller.pm	(revision 32205)
@@ -0,0 +1,1072 @@
+package Mojolicious::Controller;
+use Mojo::Base -base;
+
+# No imports, for security reasons!
+use Carp ();
+use Mojo::ByteStream;
+use Mojo::URL;
+use Mojo::Util;
+use Mojolicious::Routes::Match;
+use Scalar::Util ();
+
+has [qw(app tx)];
+has match =>
+  sub { Mojolicious::Routes::Match->new(root => shift->app->routes) };
+
+# Reserved stash values
+my %RESERVED = map { $_ => 1 } (
+  qw(action app cb controller data extends format handler inline json layout),
+  qw(namespace path status template text variant)
+);
+
+sub AUTOLOAD {
+  my $self = shift;
+
+  my ($package, $method) = our $AUTOLOAD =~ /^(.+)::(.+)$/;
+  Carp::croak "Undefined subroutine &${package}::$method called"
+    unless Scalar::Util::blessed $self && $self->isa(__PACKAGE__);
+
+  # Call helper with current controller
+  Carp::croak qq{Can't locate object method "$method" via package "$package"}
+    unless my $helper = $self->app->renderer->get_helper($method);
+  return $self->$helper(@_);
+}
+
+sub continue { $_[0]->app->routes->continue($_[0]) }
+
+sub cookie {
+  my ($self, $name) = (shift, shift);
+
+  # Response cookie
+  if (@_) {
+
+    # Cookie too big
+    my $cookie = {name => $name, value => shift, %{shift || {}}};
+    $self->app->log->error(qq{Cookie "$name" is bigger than 4096 bytes})
+      if length $cookie->{value} > 4096;
+
+    $self->res->cookies($cookie);
+    return $self;
+  }
+
+  # Request cookies
+  return undef unless my $cookie = $self->req->cookie($name);
+  return $cookie->value;
+}
+
+sub every_cookie {
+  [map { $_->value } @{shift->req->every_cookie(shift)}];
+}
+
+sub every_param {
+  my ($self, $name) = @_;
+
+  # Captured unreserved values
+  my $captures = $self->stash->{'mojo.captures'} ||= {};
+  if (!$RESERVED{$name} && exists $captures->{$name}) {
+    my $value = $captures->{$name};
+    return ref $value eq 'ARRAY' ? $value : [$value];
+  }
+
+  # Uploads or param values
+  my $req     = $self->req;
+  my $uploads = $req->every_upload($name);
+  return @$uploads ? $uploads : $req->every_param($name);
+}
+
+sub every_signed_cookie {
+  my ($self, $name) = @_;
+
+  my $secrets = $self->app->secrets;
+  my @results;
+  for my $value (@{$self->every_cookie($name)}) {
+
+    # Check signature with rotating secrets
+    if ($value =~ s/--([^\-]+)$//) {
+      my $signature = $1;
+
+      my $valid;
+      for my $secret (@$secrets) {
+        my $check = Mojo::Util::hmac_sha1_sum($value, $secret);
+        ++$valid and last if Mojo::Util::secure_compare($signature, $check);
+      }
+      if ($valid) { push @results, $value }
+
+      else { $self->app->log->debug(qq{Cookie "$name" has a bad signature}) }
+    }
+
+    else { $self->app->log->debug(qq{Cookie "$name" is not signed}) }
+  }
+
+  return \@results;
+}
+
+sub finish {
+  my $self = shift;
+
+  # WebSocket
+  my $tx = $self->tx || Carp::croak 'Connection already closed';
+  $tx->finish(@_) and return $tx->established ? $self : $self->rendered(101)
+    if $tx->is_websocket;
+
+  # Chunked stream
+  return @_ ? $self->write_chunk(@_)->write_chunk('') : $self->write_chunk('')
+    if $tx->res->content->is_chunked;
+
+  # Normal stream
+  return @_ ? $self->write(@_)->write('') : $self->write('');
+}
+
+sub flash {
+  my $self = shift;
+
+  # Check old flash
+  my $session = $self->session;
+  return $session->{flash} ? $session->{flash}{$_[0]} : undef
+    if @_ == 1 && !ref $_[0];
+
+  # Initialize new flash and merge values
+  my $values = ref $_[0] ? $_[0] : {@_};
+  @{$session->{new_flash} ||= {}}{keys %$values} = values %$values;
+
+  return $self;
+}
+
+sub helpers { $_[0]->app->renderer->get_helper('')->($_[0]) }
+
+sub on {
+  my ($self, $name, $cb) = @_;
+  my $tx = $self->tx || Carp::croak 'Connection already closed';
+  $self->rendered(101) if $tx->is_websocket && !$tx->established;
+  return $tx->on($name => sub { shift; $self->$cb(@_) });
+}
+
+sub param {
+  my ($self, $name) = (shift, shift);
+  return $self->every_param($name)->[-1] unless @_;
+  $self->stash->{'mojo.captures'}{$name} = @_ > 1 ? [@_] : $_[0];
+  return $self;
+}
+
+sub redirect_to {
+  my $self = shift;
+
+  # Don't override 3xx status
+  my $res = $self->res;
+  $res->headers->location($self->url_for(@_));
+  return $self->rendered($res->is_redirect ? () : 302);
+}
+
+sub render {
+  my $self = shift;
+
+  # Template may be first argument
+  my ($template, $args) = (@_ % 2 ? shift : undef, {@_});
+  $args->{template} = $template if $template;
+  my $app = $self->app;
+  my $plugins = $app->plugins->emit_hook(before_render => $self, $args);
+
+  # Localize "extends" and "layout" to allow argument overrides
+  my ($maybe, $ts) = @{$args}{'mojo.maybe', 'mojo.string'};
+  my $stash = $self->stash;
+  local $stash->{layout}  = $stash->{layout}  if exists $stash->{layout};
+  local $stash->{extends} = $stash->{extends} if exists $stash->{extends};
+
+  # Rendering to string
+  local @{$stash}{keys %$args} if $ts || $maybe;
+  delete @{$stash}{qw(layout extends)} if $ts;
+
+  # All other arguments just become part of the stash
+  @$stash{keys %$args} = values %$args;
+  my ($output, $format) = $app->renderer->render($self, $args);
+
+  # Maybe no 404
+  return defined $output ? Mojo::ByteStream->new($output) : undef if $ts;
+  return $maybe ? undef : !$self->helpers->reply->not_found
+    unless defined $output;
+
+  $plugins->emit_hook(after_render => $self, \$output, $format);
+  my $headers = $self->res->body($output)->headers;
+  $headers->content_type($app->types->type($format) || 'text/plain')
+    unless $headers->content_type;
+  return !!$self->rendered($stash->{status});
+}
+
+sub render_later { shift->stash('mojo.rendered' => 1) }
+
+sub render_maybe { shift->render(@_, 'mojo.maybe' => 1) }
+
+sub render_to_string { shift->render(@_, 'mojo.string' => 1) }
+
+sub rendered {
+  my ($self, $status) = @_;
+
+  # Make sure we have a status
+  my $res = $self->res;
+  $res->code($status || 200) if $status || !$res->code;
+
+  # Finish transaction
+  my $stash = $self->stash;
+  if (!$stash->{'mojo.finished'} && ++$stash->{'mojo.finished'}) {
+
+    # Disable auto rendering and stop timer
+    my $app    = $self->render_later->app;
+    my $timing = $self->helpers->timing;
+    if (defined(my $elapsed = $timing->elapsed('mojo.timer'))) {
+      my $rps  = $timing->rps($elapsed) // '??';
+      my $code = $res->code;
+      my $msg  = $res->message || $res->default_message($code);
+      $app->log->debug("$code $msg (${elapsed}s, $rps/s)");
+    }
+
+    $app->plugins->emit_hook_reverse(after_dispatch => $self);
+    $app->sessions->store($self);
+  }
+  $self->tx->resume;
+  return $self;
+}
+
+sub req { (shift->tx || Carp::croak 'Connection already closed')->req }
+sub res { (shift->tx || Carp::croak 'Connection already closed')->res }
+
+sub respond_to {
+  my ($self, $args) = (shift, ref $_[0] ? $_[0] : {@_});
+
+  # Find target
+  my $target;
+  my $renderer = $self->app->renderer;
+  my @formats  = @{$renderer->accepts($self)};
+  for my $format (@formats ? @formats : ($renderer->default_format)) {
+    next unless $target = $args->{$format};
+    $self->stash->{format} = $format;
+    last;
+  }
+
+  # Fallback
+  unless ($target) {
+    return $self->rendered(204) unless $target = $args->{any};
+    delete $self->stash->{format};
+  }
+
+  # Dispatch
+  ref $target eq 'CODE' ? $target->($self) : $self->render(%$target);
+
+  return $self;
+}
+
+sub send {
+  my ($self, $msg, $cb) = @_;
+  my $tx = $self->tx || Carp::croak 'Connection already closed';
+  Carp::croak 'No WebSocket connection to send message to'
+    unless $tx->is_websocket;
+  $tx->send($msg, $cb ? sub { shift; $self->$cb(@_) } : ());
+  return $tx->established ? $self : $self->rendered(101);
+}
+
+sub session {
+  my $self = shift;
+
+  my $stash = $self->stash;
+  $self->app->sessions->load($self)
+    unless exists $stash->{'mojo.active_session'};
+
+  # Hash
+  my $session = $stash->{'mojo.session'} ||= {};
+  return $session unless @_;
+
+  # Get
+  return $session->{$_[0]} unless @_ > 1 || ref $_[0];
+
+  # Set
+  my $values = ref $_[0] ? $_[0] : {@_};
+  @$session{keys %$values} = values %$values;
+
+  return $self;
+}
+
+sub signed_cookie {
+  my ($self, $name, $value, $options) = @_;
+
+  # Request cookie
+  return $self->every_signed_cookie($name)->[-1] unless defined $value;
+
+  # Response cookie
+  my $checksum = Mojo::Util::hmac_sha1_sum($value, $self->app->secrets->[0]);
+  return $self->cookie($name, "$value--$checksum", $options);
+}
+
+sub stash { Mojo::Util::_stash(stash => @_) }
+
+sub url_for {
+  my ($self, $target) = (shift, shift // '');
+
+  # Absolute URL
+  return $target if Scalar::Util::blessed $target && $target->isa('Mojo::URL');
+  return Mojo::URL->new($target) if $target =~ m!^(?:[^:/?#]+:|//|#)!;
+
+  # Base
+  my $url  = Mojo::URL->new;
+  my $req  = $self->req;
+  my $base = $url->base($req->url->base->clone)->base->userinfo(undef);
+
+  # Relative URL
+  my $path = $url->path;
+  if ($target =~ m!^/!) {
+    if (defined(my $prefix = $self->stash->{path})) {
+      my $real = $req->url->path->to_route;
+      $real =~ s!/?\Q$prefix\E$!$target!;
+      $target = $real;
+    }
+    $url->parse($target);
+  }
+
+  # Route
+  else {
+    my $generated = $self->match->path_for($target, @_);
+    $path->parse($generated->{path}) if $generated->{path};
+    $base->scheme($base->protocol eq 'https' ? 'wss' : 'ws')
+      if $generated->{websocket};
+  }
+
+  # Make path absolute
+  my $base_path = $base->path;
+  unshift @{$path->parts}, @{$base_path->parts};
+  $base_path->parts([])->trailing_slash(0);
+
+  return $url;
+}
+
+sub validation {
+  my $self = shift;
+
+  my $stash = $self->stash;
+  return $stash->{'mojo.validation'} if $stash->{'mojo.validation'};
+
+  my $req    = $self->req;
+  my $token  = $self->session->{csrf_token};
+  my $header = $req->headers->header('X-CSRF-Token');
+  my $hash   = $req->params->to_hash;
+  $hash->{csrf_token} //= $header if $token && $header;
+  $hash->{$_} = $req->every_upload($_) for map { $_->name } @{$req->uploads};
+  my $v = $self->app->validator->validation->input($hash);
+  return $stash->{'mojo.validation'} = $v->csrf_token($token);
+}
+
+sub write {
+  my ($self, $chunk, $cb) = @_;
+  $self->res->content->write($chunk, $cb ? sub { shift; $self->$cb(@_) } : ());
+  return $self->rendered;
+}
+
+sub write_chunk {
+  my ($self, $chunk, $cb) = @_;
+  my $content = $self->res->content;
+  $content->write_chunk($chunk, $cb ? sub { shift; $self->$cb(@_) } : ());
+  return $self->rendered;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Controller - Controller base class
+
+=head1 SYNOPSIS
+
+  # Controller
+  package MyApp::Controller::Foo;
+  use Mojo::Base 'Mojolicious::Controller';
+
+  # Action
+  sub bar {
+    my $self = shift;
+    my $name = $self->param('name');
+    $self->res->headers->cache_control('max-age=1, no-cache');
+    $self->render(json => {hello => $name});
+  }
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Controller> is the base class for your L<Mojolicious>
+controllers. It is also the default controller class unless you set
+L<Mojolicious/"controller_class">.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Controller> inherits all attributes from L<Mojo::Base> and
+implements the following new ones.
+
+=head2 app
+
+  my $app = $c->app;
+  $c      = $c->app(Mojolicious->new);
+
+A reference back to the application that dispatched to this controller, usually
+a L<Mojolicious> object.
+
+  # Use application logger
+  $c->app->log->debug('Hello Mojo');
+
+  # Generate path
+  my $path = $c->app->home->child('templates', 'foo', 'bar.html.ep');
+
+=head2 match
+
+  my $m = $c->match;
+  $c    = $c->match(Mojolicious::Routes::Match->new);
+
+Router results for the current request, defaults to a
+L<Mojolicious::Routes::Match> object.
+
+  # Introspect
+  my $name   = $c->match->endpoint->name;
+  my $foo    = $c->match->endpoint->pattern->defaults->{foo};
+  my $action = $c->match->stack->[-1]{action};
+
+=head2 tx
+
+  my $tx = $c->tx;
+  $c     = $c->tx(Mojo::Transaction::HTTP->new);
+
+The transaction that is currently being processed, usually a
+L<Mojo::Transaction::HTTP> or L<Mojo::Transaction::WebSocket> object. Note that
+this reference is usually weakened, so the object needs to be referenced
+elsewhere as well when you're performing non-blocking operations and the
+underlying connection might get closed early.
+
+  # Check peer information
+  my $address = $c->tx->remote_address;
+  my $port    = $c->tx->remote_port;
+
+  # Increase size limit for WebSocket messages to 16MiB
+  $c->tx->max_websocket_size(16777216) if $c->tx->is_websocket;
+
+  # Perform non-blocking operation without knowing the connection status
+  my $tx = $c->tx;
+  Mojo::IOLoop->timer(2 => sub {
+    $c->app->log->debug($tx->is_finished ? 'Finished' : 'In progress');
+  });
+
+=head1 METHODS
+
+L<Mojolicious::Controller> inherits all methods from L<Mojo::Base> and
+implements the following new ones.
+
+=head2 continue
+
+  $c->continue;
+
+Continue dispatch chain from an intermediate destination with
+L<Mojolicious::Routes/"continue">.
+
+=head2 cookie
+
+  my $value = $c->cookie('foo');
+  $c        = $c->cookie(foo => 'bar');
+  $c        = $c->cookie(foo => 'bar', {path => '/'});
+
+Access request cookie values and create new response cookies. If there are
+multiple values sharing the same name, and you want to access more than just
+the last one, you can use L</"every_cookie">.
+
+  # Create response cookie with domain and expiration date
+  $c->cookie(user => 'sri', {domain => 'example.com', expires => time + 60});
+
+  # Create secure response cookie
+  $c->cookie(secret => 'I <3 Mojolicious', {secure => 1, httponly => 1});
+
+=head2 every_cookie
+
+  my $values = $c->every_cookie('foo');
+
+Similar to L</"cookie">, but returns all request cookie values sharing the same
+name as an array reference.
+
+  $ Get first cookie value
+  my $first = $c->every_cookie('foo')->[0];
+
+=head2 every_param
+
+  my $values = $c->every_param('foo');
+
+Similar to L</"param">, but returns all values sharing the same name as an
+array reference.
+
+  # Get first value
+  my $first = $c->every_param('foo')->[0];
+
+=head2 every_signed_cookie
+
+  my $values = $c->every_signed_cookie('foo');
+
+Similar to L</"signed_cookie">, but returns all signed request cookie values
+sharing the same name as an array reference.
+
+  # Get first signed cookie value
+  my $first = $c->every_signed_cookie('foo')->[0];
+
+=head2 finish
+
+  $c = $c->finish;
+  $c = $c->finish(1000);
+  $c = $c->finish(1003 => 'Cannot accept data!');
+  $c = $c->finish('Bye!');
+
+Close WebSocket connection or long poll stream gracefully. This method will
+automatically respond to WebSocket handshake requests with a C<101> response
+status, to establish the WebSocket connection.
+
+=head2 flash
+
+  my $foo = $c->flash('foo');
+  $c      = $c->flash({foo => 'bar'});
+  $c      = $c->flash(foo => 'bar');
+
+Data storage persistent only for the next request, stored in the L</"session">.
+
+  # Show message after redirect
+  $c->flash(message => 'User created successfully!');
+  $c->redirect_to('show_user', id => 23);
+
+=head2 helpers
+
+  my $helpers = $c->helpers;
+
+Return a proxy object containing the current controller object and on which
+helpers provided by L</"app"> can be called. This includes all helpers from
+L<Mojolicious::Plugin::DefaultHelpers> and L<Mojolicious::Plugin::TagHelpers>.
+
+  # Make sure to use the "title" helper and not the controller method
+  $c->helpers->title('Welcome!');
+
+  # Use a nested helper instead of the "reply" controller method
+  $c->helpers->reply->not_found;
+
+=head2 on
+
+  my $cb = $c->on(finish => sub {...});
+
+Subscribe to events of L</"tx">, which is usually a L<Mojo::Transaction::HTTP>
+or L<Mojo::Transaction::WebSocket> object. This method will automatically
+respond to WebSocket handshake requests with a C<101> response status, to
+establish the WebSocket connection.
+
+  # Do something after the transaction has been finished
+  $c->on(finish => sub {
+    my $c = shift;
+    $c->app->log->debug('All data has been sent');
+  });
+
+  # Receive WebSocket message
+  $c->on(message => sub {
+    my ($c, $msg) = @_;
+    $c->app->log->debug("Message: $msg");
+  });
+
+  # Receive JSON object via WebSocket message
+  $c->on(json => sub {
+    my ($c, $hash) = @_;
+    $c->app->log->debug("Test: $hash->{test}");
+  });
+
+  # Receive WebSocket "Binary" message
+  $c->on(binary => sub {
+    my ($c, $bytes) = @_;
+    my $len = length $bytes;
+    $c->app->log->debug("Received $len bytes");
+  });
+
+=head2 param
+
+  my $value = $c->param('foo');
+  $c        = $c->param(foo => 'ba;r');
+  $c        = $c->param(foo => 'ba;r', 'baz');
+  $c        = $c->param(foo => ['ba;r', 'baz']);
+
+Access route placeholder values that are not reserved stash values, file
+uploads as well as C<GET> and C<POST> parameters extracted from the query
+string and C<application/x-www-form-urlencoded> or C<multipart/form-data>
+message body, in that order. If there are multiple values sharing the same
+name, and you want to access more than just the last one, you can use
+L</"every_param">. Parts of the request body need to be loaded into memory to
+parse C<POST> parameters, so you have to make sure it is not excessively large.
+There's a 16MiB limit for requests by default.
+
+  # Get first value
+  my $first = $c->every_param('foo')->[0];
+
+For more control you can also access request information directly.
+
+  # Only GET parameters
+  my $foo = $c->req->query_params->param('foo');
+
+  # Only POST parameters
+  my $foo = $c->req->body_params->param('foo');
+
+  # Only GET and POST parameters
+  my $foo = $c->req->param('foo');
+
+  # Only file uploads
+  my $foo = $c->req->upload('foo');
+
+=head2 redirect_to
+
+  $c = $c->redirect_to('named', foo => 'bar');
+  $c = $c->redirect_to('named', {foo => 'bar'});
+  $c = $c->redirect_to('/index.html');
+  $c = $c->redirect_to('http://example.com/index.html');
+
+Prepare a C<302> (if the status code is not already C<3xx>) redirect response
+with C<Location> header, takes the same arguments as L</"url_for">.
+
+  # Moved Permanently
+  $c->res->code(301);
+  $c->redirect_to('some_route');
+
+  # Temporary Redirect
+  $c->res->code(307);
+  $c->redirect_to('some_route');
+
+=head2 render
+
+  my $bool = $c->render;
+  my $bool = $c->render(foo => 'bar', baz => 23);
+  my $bool = $c->render(template => 'foo/index');
+  my $bool = $c->render(template => 'index', format => 'html');
+  my $bool = $c->render(data => $bytes);
+  my $bool = $c->render(text => 'Hello!');
+  my $bool = $c->render(json => {foo => 'bar'});
+  my $bool = $c->render(handler => 'something');
+  my $bool = $c->render('foo/index');
+
+Render content with L<Mojolicious/"renderer"> and emit hooks
+L<Mojolicious/"before_render"> as well as L<Mojolicious/"after_render">, or
+call L<Mojolicious::Plugin::DefaultHelpers/"reply-E<gt>not_found"> if no
+response could be generated, all additional key/value pairs get merged into the
+L</"stash">.
+
+  # Render characters
+  $c->render(text => 'I â¥ Mojolicious!');
+
+  # Render characters (alternative)
+  $c->stash(text => 'I â¥ Mojolicious!')->render;
+
+  # Render binary data
+  use Mojo::JSON 'encode_json';
+  $c->render(data => encode_json({test => 'I â¥ Mojolicious!'}));
+
+  # Render JSON
+  $c->render(json => {test => 'I â¥ Mojolicious!'});
+
+  # Render inline template
+  $c->render(inline => '<%= 1 + 1 %>');
+
+  # Render template "foo/bar.html.ep"
+  $c->render(template => 'foo/bar', format => 'html', handler => 'ep');
+
+  # Render template "test.*.*" with arbitrary values "foo" and "bar"
+  $c->render(template => 'test', foo => 'test', bar => 23);
+
+  # Render template "test.xml.*"
+  $c->render(template => 'test', format => 'xml');
+
+  # Render template "test.xml.*" (alternative)
+  $c->render('test', format => 'xml');
+
+=head2 render_later
+
+  $c = $c->render_later;
+
+Disable automatic rendering to delay response generation, only necessary if
+automatic rendering would result in a response.
+
+  # Delayed rendering
+  $c->render_later;
+  Mojo::IOLoop->timer(2 => sub {
+    $c->render(text => 'Delayed by 2 seconds!');
+  });
+
+=head2 render_maybe
+
+  my $bool = $c->render_maybe;
+  my $bool = $c->render_maybe(foo => 'bar', baz => 23);
+  my $bool = $c->render_maybe('foo/index', format => 'html');
+
+Try to render content, but do not call
+L<Mojolicious::Plugin::DefaultHelpers/"reply-E<gt>not_found"> if no response
+could be generated, all arguments get localized automatically and are only
+available during this render operation, takes the same arguments as
+L</"render">.
+
+  # Render template "index_local" only if it exists
+  $c->render_maybe('index_local') or $c->render('index');
+
+=head2 render_to_string
+
+  my $output = $c->render_to_string('foo/index', format => 'pdf');
+
+Try to render content and return it wrapped in a L<Mojo::ByteStream> object or
+return C<undef>, all arguments get localized automatically and are only
+available during this render operation, takes the same arguments as
+L</"render">.
+
+  # Render inline template
+  my $two = $c->render_to_string(inline => '<%= 1 + 1 %>');
+
+=head2 rendered
+
+  $c = $c->rendered;
+  $c = $c->rendered(302);
+
+Finalize response and emit hook L<Mojolicious/"after_dispatch">, defaults to
+using a C<200> response code.
+
+  # Custom response
+  $c->res->headers->content_type('text/plain');
+  $c->res->body('Hello World!');
+  $c->rendered(200);
+
+=head2 req
+
+  my $req = $c->req;
+
+Get L<Mojo::Message::Request> object from L</"tx">.
+
+  # Longer version
+  my $req = $c->tx->req;
+
+  # Extract request information
+  my $method = $c->req->method;
+  my $url    = $c->req->url->to_abs;
+  my $info   = $c->req->url->to_abs->userinfo;
+  my $host   = $c->req->url->to_abs->host;
+  my $agent  = $c->req->headers->user_agent;
+  my $custom = $c->req->headers->header('Custom-Header');
+  my $bytes  = $c->req->body;
+  my $str    = $c->req->text;
+  my $hash   = $c->req->params->to_hash;
+  my $all    = $c->req->uploads;
+  my $value  = $c->req->json;
+  my $foo    = $c->req->json('/23/foo');
+  my $dom    = $c->req->dom;
+  my $bar    = $c->req->dom('div.bar')->first->text;
+
+=head2 res
+
+  my $res = $c->res;
+
+Get L<Mojo::Message::Response> object from L</"tx">.
+
+  # Longer version
+  my $res = $c->tx->res;
+
+  # Force file download by setting a response header
+  $c->res->headers->content_disposition('attachment; filename=foo.png;');
+
+  # Use a custom response header
+  $c->res->headers->header('Custom-Header' => 'whatever');
+
+  # Make sure response is cached correctly
+  $c->res->headers->cache_control('public, max-age=300');
+  $c->res->headers->append(Vary => 'Accept-Encoding');
+
+=head2 respond_to
+
+  $c = $c->respond_to(
+    json => {json => {message => 'Welcome!'}},
+    html => {template => 'welcome'},
+    any  => sub {...}
+  );
+
+Automatically select best possible representation for resource from C<format>
+C<GET>/C<POST> parameter, C<format> stash value or C<Accept> request header,
+defaults to L<Mojolicious::Renderer/"default_format"> or rendering an empty
+C<204> response. Each representation can be handled with a callback or a hash
+reference containing arguments to be passed to L</"render">.
+
+  # Everything else than "json" and "xml" gets a 204 response
+  $c->respond_to(
+    json => sub { $c->render(json => {just => 'works'}) },
+    xml  => {text => '<just>works</just>'},
+    any  => {data => '', status => 204}
+  );
+
+For more advanced negotiation logic you can also use the helper
+L<Mojolicious::Plugin::DefaultHelpers/"accepts">.
+
+=head2 send
+
+  $c = $c->send({binary => $bytes});
+  $c = $c->send({text   => $bytes});
+  $c = $c->send({json   => {test => [1, 2, 3]}});
+  $c = $c->send([$fin, $rsv1, $rsv2, $rsv3, $op, $payload]);
+  $c = $c->send($chars);
+  $c = $c->send($chars => sub {...});
+
+Send message or frame non-blocking via WebSocket, the optional drain callback
+will be executed once all data has been written. This method will automatically
+respond to WebSocket handshake requests with a C<101> response status, to
+establish the WebSocket connection.
+
+  # Send "Text" message
+  $c->send('I â¥ Mojolicious!');
+
+  # Send JSON object as "Text" message
+  $c->send({json => {test => 'I â¥ Mojolicious!'}});
+
+  # Send JSON object as "Binary" message
+  use Mojo::JSON 'encode_json';
+  $c->send({binary => encode_json({test => 'I â¥ Mojolicious!'})});
+
+  # Send "Ping" frame
+  use Mojo::WebSocket 'WS_PING';
+  $c->send([1, 0, 0, 0, WS_PING, 'Hello World!']);
+
+  # Make sure the first message has been written before continuing
+  $c->send('First message!' => sub {
+    my $c = shift;
+    $c->send('Second message!');
+  });
+
+For mostly idle WebSockets you might also want to increase the inactivity
+timeout with L<Mojolicious::Plugin::DefaultHelpers/"inactivity_timeout">, which
+usually defaults to C<15> seconds.
+
+  # Increase inactivity timeout for connection to 300 seconds
+  $c->inactivity_timeout(300);
+
+=head2 session
+
+  my $session = $c->session;
+  my $foo     = $c->session('foo');
+  $c          = $c->session({foo => 'bar'});
+  $c          = $c->session(foo => 'bar');
+
+Persistent data storage for the next few requests, all session data gets
+serialized with L<Mojo::JSON> and stored Base64 encoded in HMAC-SHA1 signed
+cookies, to prevent tampering. Note that cookies usually have a C<4096> byte
+(4KiB) limit, depending on browser.
+
+  # Manipulate session
+  $c->session->{foo} = 'bar';
+  my $foo = $c->session->{foo};
+  delete $c->session->{foo};
+
+  # Expiration date in seconds from now (persists between requests)
+  $c->session(expiration => 604800);
+
+  # Expiration date as absolute epoch time (only valid for one request)
+  $c->session(expires => time + 604800);
+
+  # Delete whole session by setting an expiration date in the past
+  $c->session(expires => 1);
+
+=head2 signed_cookie
+
+  my $value = $c->signed_cookie('foo');
+  $c        = $c->signed_cookie(foo => 'bar');
+  $c        = $c->signed_cookie(foo => 'bar', {path => '/'});
+
+Access signed request cookie values and create new signed response cookies. If
+there are multiple values sharing the same name, and you want to access more
+than just the last one, you can use L</"every_signed_cookie">. Cookies are
+cryptographically signed with HMAC-SHA1, to prevent tampering, and the ones
+failing signature verification will be automatically discarded.
+
+=head2 stash
+
+  my $hash = $c->stash;
+  my $foo  = $c->stash('foo');
+  $c       = $c->stash({foo => 'bar', baz => 23});
+  $c       = $c->stash(foo => 'bar', baz => 23);
+
+Non-persistent data storage and exchange for the current request, application
+wide default values can be set with L<Mojolicious/"defaults">. Some stash
+values have a special meaning and are reserved, the full list is currently
+C<action>, C<app>, C<cb>, C<controller>, C<data>, C<extends>, C<format>,
+C<handler>, C<inline>, C<json>, C<layout>, C<namespace>, C<path>, C<status>,
+C<template>, C<text> and C<variant>. Note that all stash values with a
+C<mojo.*> prefix are reserved for internal use.
+
+  # Remove value
+  my $foo = delete $c->stash->{foo};
+
+  # Assign multiple values at once
+  $c->stash(foo => 'test', bar => 23);
+
+=head2 url_for
+
+  my $url = $c->url_for;
+  my $url = $c->url_for(name => 'sebastian');
+  my $url = $c->url_for({name => 'sebastian'});
+  my $url = $c->url_for('test', name => 'sebastian');
+  my $url = $c->url_for('test', {name => 'sebastian'});
+  my $url = $c->url_for('/index.html');
+  my $url = $c->url_for('//example.com/index.html');
+  my $url = $c->url_for('http://example.com/index.html');
+  my $url = $c->url_for('mailto:sri@example.com');
+  my $url = $c->url_for('#whatever');
+
+Generate a portable L<Mojo::URL> object with base for a path, URL or route.
+
+  # Rebuild URL for the current route
+  $c->url_for;
+
+  # Rebuild URL for the current route, but replace the "name" placeholder value
+  $c->url_for(name => 'sebastian');
+
+  # Absolute URL for the current route
+  $c->url_for->to_abs;
+
+  # Build URL for route "test" with two placeholder values
+  $c->url_for('test', name => 'sebastian', foo => 'bar');
+
+  # "http://127.0.0.1:3000/index.html" if application was started with Morbo
+  $c->url_for('/index.html')->to_abs;
+
+  # "https://127.0.0.1:443/index.html" if application was started with Morbo
+  $c->url_for('/index.html')->to_abs->scheme('https')->port(443);
+
+  # "/index.html?foo=bar" if application is deployed under "/"
+  $c->url_for('/index.html')->query(foo => 'bar');
+
+  # "/myapp/index.html?foo=bar" if application is deployed under "/myapp"
+  $c->url_for('/index.html')->query(foo => 'bar');
+
+You can also use the helper L<Mojolicious::Plugin::DefaultHelpers/"url_with">
+to inherit query parameters from the current request.
+
+  # "/list?q=mojo&page=2" if current request was for "/list?q=mojo&page=1"
+  $c->url_with->query([page => 2]);
+
+=head2 validation
+
+  my $v = $c->validation;
+
+Get L<Mojolicious::Validator::Validation> object for current request to
+validate file uploads as well as C<GET> and C<POST> parameters extracted from
+the query string and C<application/x-www-form-urlencoded> or
+C<multipart/form-data> message body. Parts of the request body need to be loaded
+into memory to parse C<POST> parameters, so you have to make sure it is not
+excessively large. There's a 16MiB limit for requests by default.
+
+  # Validate GET/POST parameter
+  my $v = $c->validation;
+  $v->required('title', 'trim')->size(3, 50);
+  my $title = $v->param('title');
+
+  # Validate file upload
+  my $v = $c->validation;
+  $v->required('tarball')->upload->size(1, 1048576);
+  my $tarball = $v->param('tarball');
+
+=head2 write
+
+  $c = $c->write;
+  $c = $c->write('');
+  $c = $c->write($bytes);
+  $c = $c->write($bytes => sub {...});
+
+Write dynamic content non-blocking, the optional drain callback will be executed
+once all data has been written. Calling this method without a chunk of data
+will finalize the response headers and allow for dynamic content to be written
+later.
+
+  # Keep connection alive (with Content-Length header)
+  $c->res->headers->content_length(6);
+  $c->write('Hel' => sub {
+    my $c = shift;
+    $c->write('lo!');
+  });
+
+  # Close connection when finished (without Content-Length header)
+  $c->write('Hel' => sub {
+    my $c = shift;
+    $c->write('lo!' => sub {
+      my $c = shift;
+      $c->finish;
+    });
+  });
+
+You can call L</"finish"> or write an empty chunk of data at any time to end
+the stream.
+
+  HTTP/1.1 200 OK
+  Date: Sat, 13 Sep 2014 16:48:29 GMT
+  Content-Length: 6
+  Server: Mojolicious (Perl)
+
+  Hello!
+
+  HTTP/1.1 200 OK
+  Connection: close
+  Date: Sat, 13 Sep 2014 16:48:29 GMT
+  Server: Mojolicious (Perl)
+
+  Hello!
+
+For Comet (long polling) you might also want to increase the inactivity timeout
+with L<Mojolicious::Plugin::DefaultHelpers/"inactivity_timeout">, which usually
+defaults to C<15> seconds.
+
+  # Increase inactivity timeout for connection to 300 seconds
+  $c->inactivity_timeout(300);
+
+=head2 write_chunk
+
+  $c = $c->write_chunk;
+  $c = $c->write_chunk('');
+  $c = $c->write_chunk($bytes);
+  $c = $c->write_chunk($bytes => sub {...});
+
+Write dynamic content non-blocking with chunked transfer encoding, the optional
+drain callback will be executed once all data has been written. Calling this
+method without a chunk of data will finalize the response headers and allow for
+dynamic content to be written later.
+
+  # Make sure previous chunk has been written before continuing
+  $c->write_chunk('H' => sub {
+    my $c = shift;
+    $c->write_chunk('ell' => sub {
+      my $c = shift;
+      $c->finish('o!');
+    });
+  });
+
+You can call L</"finish"> or write an empty chunk of data at any time to end
+the stream.
+
+  HTTP/1.1 200 OK
+  Date: Sat, 13 Sep 2014 16:48:29 GMT
+  Transfer-Encoding: chunked
+  Server: Mojolicious (Perl)
+
+  1
+  H
+  3
+  ell
+  2
+  o!
+  0
+
+=head1 AUTOLOAD
+
+In addition to the L</"ATTRIBUTES"> and L</"METHODS"> above you can also call
+helpers provided by L</"app"> on L<Mojolicious::Controller> objects. This
+includes all helpers from L<Mojolicious::Plugin::DefaultHelpers> and
+L<Mojolicious::Plugin::TagHelpers>.
+
+  # Call helpers
+  $c->layout('green');
+  $c->title('Welcome!');
+
+  # Longer version
+  $c->helpers->layout('green');
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Guides.pod
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Guides.pod	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Guides.pod	(revision 32205)
@@ -0,0 +1,543 @@
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Guides - The Mojolicious Guide to the Galaxy
+
+=head1 DON'T PANIC!
+
+We are constantly working on new documentation. Follow us on
+L<GitHub|http://github.com/kraih/mojo> or join the official IRC channel C<#mojo>
+on C<irc.perl.org> to get all the latest updates.
+
+=head1 BASICS
+
+=over 2
+
+=item Learning Perl
+
+If you are new to Perl, we recommend
+L<Learn Perl in 2 hours 30 minutes|http://qntm.org/files/perl/perl.html> for a
+quick introduction, or the
+L<Modern Perl book|https://pragprog.com/book/swperl/modern-perl-fourth-edition>,
+freely available in many formats. Both are excellent introductions to the
+language. For more books and documentation, check out
+L<learn.perl.org|http://learn.perl.org/>.
+
+=item Learning Web Technologies
+
+All web development starts with HTML, CSS and JavaScript, to learn the basics
+we recommend the
+L<Mozilla Developer Network|https://developer.mozilla.org/en-US/docs/Web>. And
+if you want to know more about how browsers and web servers actually
+communicate, there's also a very nice introduction to
+L<HTTP|https://developer.mozilla.org/en-US/docs/Web/HTTP>.
+
+=back
+
+=head1 CONVENTIONS
+
+=over 2
+
+=item Modern Perl
+
+L<Mojolicious> uses a modern subset of Perl exclusively, and therefore all
+documentation assumes that L<strict>, L<warnings>, L<utf8> and Perl 5.10
+L<features|feature> are enabled, even if examples don't specifically mention it.
+
+  use strict;
+  use warnings;
+  use utf8;
+  use feature ':5.10';
+
+Some modules, like L<Mojo::Base> and L<Mojolicious::Lite>, will enable them for
+you automatically, whenever they are used.
+
+=back
+
+=head1 TUTORIAL
+
+=over 2
+
+=item L<Mojolicious::Guides::Tutorial>
+
+A fast and fun way to get started developing web applications with
+L<Mojolicious>. The tutorial introduces the L<Mojolicious::Lite> micro web
+framework, which is only a thin wrapper around the full web framework. The
+simplified notation introduced in the tutorial is commonly used throughout the
+guides and is therefore considered a prerequisite, you should definitely take a
+look!
+
+=back
+
+=head1 GUIDES
+
+=over 2
+
+=item L<Mojolicious::Guides::Growing>
+
+Starting a L<Mojolicious::Lite> prototype from scratch and growing it into a
+well-structured L<Mojolicious> application.
+
+=item L<Mojolicious::Guides::Routing>
+
+Simple and fun introduction to the L<Mojolicious> router.
+
+=item L<Mojolicious::Guides::Rendering>
+
+Generating content with the L<Mojolicious> renderer.
+
+=item L<Mojolicious::Guides::Testing>
+
+Powerful yet elegant testing techniques and tools for L<Mojolicious> and other
+web applications.
+
+=item L<Mojolicious::Guides::Cookbook>
+
+Cooking with L<Mojolicious>, recipes for every taste.
+
+=item L<Mojolicious::Guides::Contributing>
+
+Become a part of the ongoing L<Mojolicious> development.
+
+=item L<Mojolicious::Guides::FAQ>
+
+Answers to the most frequently asked questions.
+
+=back
+
+=head1 HIGHLIGHTS
+
+L<Mojolicious> and L<Mojolicious::Lite> are the sum of many parts, built on top
+of the L<Mojo> web development toolkit. Small building blocks that can be used
+independently for all kinds of applications, these are the most prominent ones.
+
+=over 2
+
+=item L<Mojo::UserAgent>
+
+Full featured non-blocking I/O HTTP and WebSocket user agent.
+
+=item L<Mojo::DOM>
+
+Very fun and minimalistic HTML/XML DOM parser with CSS selector support.
+
+=item L<Mojo::JSON>
+
+Minimalistic JSON implementation that just works.
+
+=item L<Mojo::Server::Daemon>
+
+Full featured, highly portable non-blocking I/O HTTP and WebSocket server, with
+self-restart support through L<Mojo::Server::Morbo>, perfect for development
+and testing.
+
+=item L<Mojo::Server::Prefork>
+
+Full featured, UNIX optimized, preforking non-blocking I/O HTTP and WebSocket
+server with support for zero downtime software upgrades (hot deployment)
+through L<Mojo::Server::Hypnotoad>.
+
+=item L<Mojo::Server::CGI>, L<Mojo::Server::PSGI>
+
+Transparent CGI and L<PSGI> support out of the box.
+
+=item L<Mojo::IOLoop>
+
+A minimalistic event loop with support for multiple reactor backends.
+
+=item L<Mojo::Template>
+
+Very Perl-ish and minimalistic template system.
+
+=item L<Test::Mojo>
+
+Testing toolkit for web applications.
+
+=item L<ojo>
+
+Fun one-liners using everything above.
+
+=back
+
+=head1 SPIN-OFFS
+
+These modules are not part of the L<Mojolicious> distribution, but have been
+designed to be used with it and are being developed under the same umbrella.
+
+=over 2
+
+=item L<Mojo::Pg>
+
+A tiny wrapper around L<DBD::Pg> that makes
+L<PostgreSQL|http://www.postgresql.org> a lot of fun to use with
+L<Mojolicious>. Perform queries blocking and non-blocking, use all
+L<SQL features|https://www.postgresql.org/docs/current/static/sql.html>
+PostgreSQL has to offer, generate CRUD queries from data structures, manage your
+database schema with migrations and build scalable real-time web applications
+with the publish/subscribe pattern.
+
+And it comes with two great
+L<example applications|https://github.com/kraih/mojo-pg/tree/master/examples/>
+you can use for inspiration. The minimal
+L<chat|https://github.com/kraih/mojo-pg/tree/master/examples/chat.pl>
+application will show you how to scale WebSockets to multiple servers, and the
+well-structured
+L<blog|https://github.com/kraih/mojo-pg/tree/master/examples/blog> application
+how to apply the MVC design pattern in practice.
+
+=item L<Minion>
+
+A full featured job queue for L<Mojolicious> with support for multiple backends
+(such as L<PostgreSQL|http://www.postgresql.org>). Job queues allow you to
+process time and/or computationally intensive tasks in background processes,
+outside of the request/response lifecycle. Among those tasks you'll commonly
+find image resizing, spam filtering, HTTP downloads, building tarballs, warming
+caches and basically everything else you can imagine that's not super fast.
+
+And it comes with a great example application you can use for inspiration. The
+L<link checker|https://github.com/kraih/minion/tree/master/examples/linkcheck>
+will show you how to integrate background jobs into well-structured
+L<Mojolicious> applications.
+
+=back
+
+=head1 REFERENCE
+
+This is the class hierarchy of the L<Mojolicious> distribution.
+
+=over 2
+
+=item * L<Mojo>
+
+=item * L<Mojo::Base>
+
+=over 2
+
+=item * L<Mojolicious>
+
+=over 2
+
+=item * L<Mojo::HelloWorld>
+
+=item * L<Mojolicious::Lite>
+
+=back
+
+=item * L<Mojo::Cache>
+
+=item * L<Mojo::Cookie>
+
+=over 2
+
+=item * L<Mojo::Cookie::Request>
+
+=item * L<Mojo::Cookie::Response>
+
+=back
+
+=item * L<Mojo::DOM::CSS>
+
+=item * L<Mojo::DOM::HTML>
+
+=item * L<Mojo::Date>
+
+=item * L<Mojo::EventEmitter>
+
+=over 2
+
+=item * L<Mojo::Asset>
+
+=over 2
+
+=item * L<Mojo::Asset::File>
+
+=item * L<Mojo::Asset::Memory>
+
+=back
+
+=item * L<Mojo::Content>
+
+=over 2
+
+=item * L<Mojo::Content::MultiPart>
+
+=item * L<Mojo::Content::Single>
+
+=back
+
+=item * L<Mojo::IOLoop>
+
+=item * L<Mojo::IOLoop::Client>
+
+=item * L<Mojo::IOLoop::Server>
+
+=item * L<Mojo::IOLoop::Stream>
+
+=over 2
+
+=item * L<Mojo::IOLoop::Stream::HTTPClient>
+
+=item * L<Mojo::IOLoop::Stream::HTTPServer>
+
+=item * L<Mojo::IOLoop::Stream::WebSocketClient>
+
+=item * L<Mojo::IOLoop::Stream::WebSocketServer>
+
+=back
+
+=item * L<Mojo::IOLoop::TLS>
+
+=item * L<Mojo::Log>
+
+=item * L<Mojo::Message>
+
+=over 2
+
+=item * L<Mojo::Message::Request>
+
+=item * L<Mojo::Message::Response>
+
+=back
+
+=item * L<Mojo::Reactor>
+
+=over 2
+
+=item * L<Mojo::Reactor::Poll>
+
+=over 2
+
+=item * L<Mojo::Reactor::EV>
+
+=back
+
+=back
+
+=item * L<Mojo::Server>
+
+=over 2
+
+=item * L<Mojo::Server::CGI>
+
+=item * L<Mojo::Server::Daemon>
+
+=over 2
+
+=item * L<Mojo::Server::Prefork>
+
+=back
+
+=item * L<Mojo::Server::PSGI>
+
+=back
+
+=item * L<Mojo::Transaction>
+
+=over 2
+
+=item * L<Mojo::Transaction::HTTP>
+
+=item * L<Mojo::Transaction::WebSocket>
+
+=back
+
+=item * L<Mojo::UserAgent>
+
+=item * L<Mojolicious::Plugins>
+
+=back
+
+=item * L<Mojo::Exception>
+
+=item * L<Mojo::Headers>
+
+=item * L<Mojo::IOLoop::Subprocess>
+
+=item * L<Mojo::JSON::Pointer>
+
+=item * L<Mojo::Parameters>
+
+=item * L<Mojo::Path>
+
+=item * L<Mojo::Promise>
+
+=over 2
+
+=item * L<Mojo::IOLoop::Delay>
+
+=back
+
+=item * L<Mojo::Server::Hypnotoad>
+
+=item * L<Mojo::Server::Morbo>
+
+=item * L<Mojo::Server::Morbo::Backend>
+
+=over 2
+
+=item * L<Mojo::Server::Morbo::Backend::Poll>
+
+=back
+
+=item * L<Mojo::Template>
+
+=item * L<Mojo::URL>
+
+=item * L<Mojo::Upload>
+
+=item * L<Mojo::UserAgent::CookieJar>
+
+=item * L<Mojo::UserAgent::Proxy>
+
+=item * L<Mojo::UserAgent::Server>
+
+=item * L<Mojo::UserAgent::Transactor>
+
+=item * L<Mojolicious::Command>
+
+=over 2
+
+=item * L<Mojolicious::Command::cgi>
+
+=item * L<Mojolicious::Command::cpanify>
+
+=item * L<Mojolicious::Command::daemon>
+
+=item * L<Mojolicious::Command::eval>
+
+=item * L<Mojolicious::Command::generate::app>
+
+=item * L<Mojolicious::Command::generate::lite_app>
+
+=item * L<Mojolicious::Command::generate::makefile>
+
+=item * L<Mojolicious::Command::generate::plugin>
+
+=item * L<Mojolicious::Command::get>
+
+=item * L<Mojolicious::Command::inflate>
+
+=item * L<Mojolicious::Command::prefork>
+
+=item * L<Mojolicious::Command::psgi>
+
+=item * L<Mojolicious::Command::routes>
+
+=item * L<Mojolicious::Command::test>
+
+=item * L<Mojolicious::Command::version>
+
+=item * L<Mojolicious::Commands>
+
+=over 2
+
+=item * L<Mojolicious::Command::generate>
+
+=back
+
+=back
+
+=item * L<Mojolicious::Controller>
+
+=item * L<Mojolicious::Plugin>
+
+=over 2
+
+=item * L<Mojolicious::Plugin::Config>
+
+=over 2
+
+=item * L<Mojolicious::Plugin::JSONConfig>
+
+=back
+
+=item * L<Mojolicious::Plugin::DefaultHelpers>
+
+=item * L<Mojolicious::Plugin::EPLRenderer>
+
+=over 2
+
+=item * L<Mojolicious::Plugin::EPRenderer>
+
+=back
+
+=item * L<Mojolicious::Plugin::HeaderCondition>
+
+=item * L<Mojolicious::Plugin::Mount>
+
+=item * L<Mojolicious::Plugin::PODRenderer>
+
+=item * L<Mojolicious::Plugin::TagHelpers>
+
+=back
+
+=item * L<Mojolicious::Renderer>
+
+=item * L<Mojolicious::Routes::Match>
+
+=item * L<Mojolicious::Routes::Pattern>
+
+=item * L<Mojolicious::Routes::Route>
+
+=over 2
+
+=item * L<Mojolicious::Routes>
+
+=back
+
+=item * L<Mojolicious::Sessions>
+
+=item * L<Mojolicious::Static>
+
+=item * L<Mojolicious::Types>
+
+=item * L<Mojolicious::Validator>
+
+=item * L<Mojolicious::Validator::Validation>
+
+=item * L<Test::Mojo>
+
+=back
+
+=item * L<Mojo::ByteStream>
+
+=item * L<Mojo::Collection>
+
+=item * L<Mojo::DOM>
+
+=item * L<Mojo::File>
+
+=over 2
+
+=item * L<Mojo::Home>
+
+=back
+
+=item * L<Mojo::JSON>
+
+=item * L<Mojo::Loader>
+
+=item * L<Mojo::Util>
+
+=item * L<Mojo::WebSocket>
+
+=item * L<ojo>
+
+=back
+
+=head1 MORE
+
+A lot more documentation and examples by many different authors can be found in
+the L<Mojolicious wiki|http://github.com/kraih/mojo/wiki>.
+
+=head1 SUPPORT
+
+If you have any questions the documentation might not yet answer, don't
+hesitate to ask on the
+L<mailing list|http://groups.google.com/group/mojolicious> or the official IRC
+channel C<#mojo> on C<irc.perl.org>
+(L<chat now!|https://chat.mibbit.com/?channel=%23mojo&server=irc.perl.org>).
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Guides/Contributing.pod
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Guides/Contributing.pod	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Guides/Contributing.pod	(revision 32205)
@@ -0,0 +1,314 @@
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Guides::Contributing - Contributing to Mojolicious
+
+=head1 OVERVIEW
+
+There are many ways to contribute to L<Mojolicious>, this guide will show you a
+few of them.
+
+=head1 REPORTING BUGS
+
+We use the L<GitHub issue tracker|https://github.com/kraih/mojo/issues>, so
+you'll need to create a (free) GitHub account to be able to submit issues,
+comments and pull requests.
+
+First of all, make sure you are using the latest version of L<Mojolicious>, it
+is quite likely that your bug has already been fixed. If that doesn't help,
+take a look at the list of currently open issues, perhaps it has already been
+reported by someone else and you can just add a comment confirming it.
+
+If it hasn't been reported yet, try to prepare a test case demonstrating the
+bug, you are not expected to fix it yourself, but you'll have to make sure the
+developers can replicate your problem. Sending in your whole application
+generally does more harm than good, the C<t> directory of this distribution has
+many good examples for how to do it right. Writing a test is usually the
+hardest part of fixing a bug, so the better your test case the faster it can be
+fixed. ;)
+
+And don't forget to add a descriptive title and text, when you create a new
+issue. If your issue does not contain enough information or is unintelligible,
+it might get closed pretty quickly. But don't be disheartened, if there's new
+activity it will get reopened just as quickly.
+
+=head2 Reporting security issues
+
+Please report security issues directly to the pumpkin-holder via email, which is
+currently Sebastian Riedel (C<kraih@mojolicious.org>), and give us a few days to
+develop and release a proper fix.
+
+=head1 RESOLVING ISSUES
+
+There are many ways in which you can help us resolve existing issues on the
+L<GitHub issue tracker|https://github.com/kraih/mojo/issues>.
+
+Can you replicate the problem on your computer? Add a comment saying that
+you're seeing the same. Perhaps you can provide additional information that
+will make it easier for others to replicate the problem, maybe even contribute
+a better test case.
+
+And for all code contributions we very much appreciate additional testing and
+code review, just add a comment to show your approval or to point out flaws
+that need to be addressed.
+
+=head1 CONTRIBUTING DOCUMENTATION
+
+One of the easiest ways to contribute to L<Mojolicious> is through
+documentation improvements. While the L<Mojolicious::Guides> are carefully
+curated by the core team, everybody with a (free) GitHub account can make
+changes and add new information to the
+L<Mojolicious wiki|http://github.com/kraih/mojo/wiki>.
+
+Pull requests with additions or changes to the documentation included in the
+L<Mojolicious> distribution follow the same rules as code contributions. Please
+don't send pull requests for overly simplistic changes, such as the addition of
+a comma or semicolon.
+
+=head1 CONTRIBUTING CODE
+
+All code contributions should be sent as
+L<GitHub pull requests|https://help.github.com/articles/using-pull-requests>.
+
+An expressive title and detailed description are invaluable during the review
+process, which usually ends when members of the community have voiced their
+opinions and the core team voted for or against a change. All code changes
+should emulate the style of the surrounding code, include tests that fail
+without them, and update relevant documentation.
+
+While the L<Mojolicious> distribution covers a wide range of features, we are
+rather conservative when it comes to adding new ones. So if your contribution
+is not a simple bug fix, it is B<strongly recommended> that you discuss it in
+advance on the L<mailing list|http://groups.google.com/group/mojolicious> or the
+official IRC channel C<#mojo> on C<irc.perl.org>
+(L<chat now!|https://chat.mibbit.com/?channel=%23mojo&server=irc.perl.org>), to
+avoid unnecessary work and to increase its chances of getting accepted.
+
+The following mission statement and rules are the foundation of all L<Mojo> and
+L<Mojolicious> development. Please make sure that your contribution aligns well
+with them before sending a pull request.
+
+=head2 Mission statement
+
+L<Mojo> is a web development toolkit, with all the basic tools and helpers
+needed to write simple web applications and higher level web frameworks, such as
+L<Mojolicious>.
+
+All components should be reusable in other projects, and in a UNIXish way only
+loosely coupled.
+
+Especially for people new to Perl it should be as easy as possible to install
+L<Mojolicious> and get started. Writing web applications can be one of the most
+fun ways to learn a language!
+
+For developers of other web frameworks, it should be possible to reuse all the
+infrastructure and just consider the higher levels of the L<Mojolicious>
+distribution an example application.
+
+=head2 Rules
+
+=over 2
+
+Web development should be easy and fun, this is what we optimize for.
+
+The web is a moving target, to stay relevant we have to stay in motion too.
+
+Keep it simple, no magic unless absolutely necessary.
+
+The installation process should be as fast and painless as possible. (Less than
+a minute on most common hardware is a good rule of thumb)
+
+The addition and modification of features is decided by majority vote or the
+pumpkin-holder.
+
+Any core developer may nominate a new one, who must then be accepted by a 2/3
+majority vote.
+
+The pumpkin-holder has veto rights and may select their successor.
+
+It's not a feature without a test and documentation.
+
+A feature is only needed when the majority of the user base benefits from it.
+
+Features may only be changed in a major release, to fix a serious security
+issue, or after being deprecated for at least 3 months.
+
+Refactoring and deprecations should be avoided if there are no substantial
+benefits.
+
+New features can be marked as experimental to be excluded from deprecation
+policies.
+
+A major release is signaled by a new major version number and a unique code
+name based on a Unicode character.
+
+Only add dependencies if absolutely necessary and make them optional if
+possible.
+
+Domain specific languages should be avoided in favor of Perl-ish solutions.
+
+No inline POD.
+
+Documentation belongs to the guides, module POD is just an API reference.
+
+The main focus of the included documentation should be on examples, no walls of
+text. (An example for every one or two sentences is a good rule of thumb)
+
+Everything should be ordered alphabetically if possible, or at least be
+consistent if not.
+
+The master source code repository should always be kept in a stable state, use
+feature branches for actual development.
+
+Code has to be run through L<Perl::Tidy> with the included
+L<.perltidyrc|https://github.com/kraih/mojo/blob/master/.perltidyrc>, and
+everything should look like it was written by a single person.
+
+Functions and methods should be as short as possible, no spaghetti code.
+
+Comments should be correctly capitalized, and funny if possible, punctuation is
+optional if it doesn't increase readability.
+
+No names outside of C<Mojolicious.pm>.
+
+=back
+
+=head1 DONATIONS
+
+L<Mojolicious> is open source and free to use. However, the amount of effort
+needed to maintain the project and develop new features for it is not
+sustainable without proper financial backing. You can support the ongoing
+development of L<Mojolicious> through L<PayPal|https://www.paypal.me/kraih>.
+
+If you run a business and use L<Mojolicious> in a revenue generating product, it
+makes business sense to support L<Mojolicious> development. Because it ensures
+that the project your product relies on stays healthy and actively maintained.
+It can also help your exposure within the community and will make it easier to
+attract L<Mojolicious> developers.
+
+Please email Sebastian Riedel (C<kraih@mojolicious.org>) if you have any
+questions about becoming a sponsor.
+
+=head1 CODE OF CONDUCT
+
+Like the technical community as a whole, the L<Mojolicious> team and community
+is made up of a mixture of professionals and volunteers from all over the world,
+working on every aspect of the mission - including mentorship, teaching, and
+connecting people.
+
+Diversity is one of our huge strengths, but it can also lead to communication
+issues and unhappiness. To that end, we have a few ground rules that we ask
+people to adhere to. This code applies equally to founders, mentors and those
+seeking help and guidance.
+
+This isnât an exhaustive list of things that you canât do. Rather, take it in
+the spirit in which itâs intended - a guide to make it easier to enrich all of
+us and the technical communities in which we participate.
+
+This code of conduct applies to all spaces managed by the L<Mojolicious>
+project. This includes IRC, the mailing lists, the issue tracker, and any other
+forums created by the project team which the community uses for communication.
+In addition, violations of this code outside these spaces may affect a person's
+ability to participate within them.
+
+If you believe someone is violating the code of conduct, we ask that you report
+it by emailing Joel Berger (C<jberger@mojolicious.org>) or other members of
+L<the team|Mojolicious/"CORE DEVELOPERS">.
+
+=over 2
+
+=item * B<Be friendly and patient.>
+
+=item * B<Be welcoming.> We strive to be a community that welcomes and supports
+people of all backgrounds and identities. This includes, but is not limited to
+members of any race, ethnicity, culture, national origin, colour, immigration
+status, social and economic class, educational level, sex, sexual orientation,
+gender identity and expression, age, size, family status, political belief,
+religion, and mental and physical ability.
+
+=item * B<Be considerate.> Your work will be used by other people, and you in
+turn will depend on the work of others. Any decision you take will affect users
+and colleagues, and you should take those consequences into account when making
+decisions. Remember that we're a world-wide community, so you might not be
+communicating in someone else's primary language.
+
+=item * B<Be respectful.> Not all of us will agree all the time, but
+disagreement is no excuse for poor behavior and poor manners. We might all
+experience some frustration now and then, but we cannot allow that frustration
+to turn into a personal attack. Itâs important to remember that a community
+where people feel uncomfortable or threatened is not a productive one. Members
+of the L<Mojolicious> community should be respectful when dealing with other
+members as well as with people outside the L<Mojolicious> community.
+
+=item * B<Be careful in the words that you choose.> We are a community of
+professionals, and we conduct ourselves professionally. Be kind to others. Do
+not insult or put down other participants. Harassment and other exclusionary
+behavior aren't acceptable. This includes, but is not limited to:
+
+=over 2
+
+=item * Violent threats or language directed against another person.
+
+=item * Discriminatory jokes and language.
+
+=item * Posting sexually explicit or violent material.
+
+=item * Posting (or threatening to post) other people's personally identifying
+information ("doxing").
+
+=item * Personal insults, especially those using racist or sexist terms.
+
+=item * Unwelcome sexual attention.
+
+=item * Advocating for, or encouraging, any of the above behavior.
+
+=item * Repeated harassment of others. In general, if someone asks you to stop,
+then stop.
+
+=back
+
+=item * B<When we disagree, try to understand why.> Disagreements, both social
+and technical, happen all the time and L<Mojolicious> is no exception. It is
+important that we resolve disagreements and differing views constructively.
+Remember that weâre different. The strength of L<Mojolicious> comes from its
+varied community, people from a wide range of backgrounds. Different people have
+different perspectives on issues. Being unable to understand why someone holds a
+viewpoint doesnât mean that theyâre wrong. Donât forget that it is human to err
+and blaming each other doesnât get us anywhere. Instead, focus on helping to
+resolve issues and learning from mistakes.
+
+=back
+
+=head1 FORK POLICY
+
+The L<Mojolicious> core team believes that there is a lot of value in the entire
+toolkit being a unified project. Forks drain resources from a project, not just
+mindshare but also very valuable bug reports and patches, which can have very
+serious security implications. Therefore we ask that you please not publically
+fork pieces of the L<Mojolicious> distribution without our consent. As doing so
+is against our express wishes, individuals who engage in unauthorized forking
+may be denied from participating in community sponsored spaces.
+
+For developers considering the use of a forked module, we strongly recommend
+that you make yourself familiar with its history and track record. While many
+parts of L<Mojolicious> have been forked in the past, very few forks have been
+able to keep up with L<Mojolicious> development, and most are missing critical
+bug fixes.
+
+=head1 MORE
+
+You can continue with L<Mojolicious::Guides> now or take a look at the
+L<Mojolicious wiki|http://github.com/kraih/mojo/wiki>, which contains a lot more
+documentation and examples by many different authors.
+
+=head1 SUPPORT
+
+If you have any questions the documentation might not yet answer, don't
+hesitate to ask on the
+L<mailing list|http://groups.google.com/group/mojolicious> or the official IRC
+channel C<#mojo> on C<irc.perl.org>
+(L<chat now!|https://chat.mibbit.com/?channel=%23mojo&server=irc.perl.org>).
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Guides/Cookbook.pod
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Guides/Cookbook.pod	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Guides/Cookbook.pod	(revision 32205)
@@ -0,0 +1,1849 @@
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Guides::Cookbook - Cooking with Mojolicious
+
+=head1 OVERVIEW
+
+This document contains many fun recipes for cooking with L<Mojolicious>.
+
+=head1 CONCEPTS
+
+Essentials every L<Mojolicious> developer should know.
+
+=head2 Blocking and non-blocking operations
+
+A I<blocking> operation is a subroutine that blocks the execution of the
+calling subroutine until the subroutine is finished.
+
+  sub foo {
+    my $result = blocking_subroutine();
+    ...
+  }
+
+A I<non-blocking> operation on the other hand lets the calling subroutine
+continue execution even though the subroutine is not yet finished. Instead of
+waiting, the calling subroutine passes along a callback to be executed once the
+subroutine is finished, this is called continuation-passing style.
+
+  sub foo {
+    non_blocking_subroutine(sub {
+      my $result = shift;
+      ...
+    });
+    ...
+  }
+
+While L<Mojolicious> has been designed from the ground up for non-blocking I/O
+and event loops, it is not possible to magically make Perl code non-blocking.
+You have to use specialized non-blocking code available through modules like
+L<Mojo::IOLoop> and L<Mojo::UserAgent>, or third-party event loops. You can wrap
+your blocking code in L<subprocesses|Mojo::IOLoop/"subprocess"> though to
+prevent it from interfering with your non-blocking code.
+
+=head2 Event loops
+
+An event loop is basically a loop that continually tests for external events
+and executes the appropriate callbacks to handle them, it is often the main
+loop in a program. Non-blocking tests for readability/writability of file
+descriptors and timers are commonly used events for highly scalable network
+servers, because they allow a single process to handle thousands of client
+connections concurrently.
+
+  while (1) {
+    my @readable = test_fds_for_readability();
+    handle_readable_fds(@readable);
+
+    my @writable = test_fds_for_writability();
+    handle_writable_fds(@writable);
+
+    my @expired = test_timers();
+    handle_timers(@expired);
+  }
+
+In L<Mojolicious> this event loop is L<Mojo::IOLoop>.
+
+=head2 Reverse proxy
+
+A reverse proxy architecture is a deployment technique used in many production
+environments, where a I<reverse proxy> server is put in front of your
+application to act as the endpoint accessible by external clients. It can
+provide a lot of benefits, like terminating SSL connections from the outside,
+limiting the number of concurrent open sockets towards the Mojolicious
+application (or even using Unix sockets), balancing load across multiple
+instances, or supporting several applications through the same IP/port.
+
+                   ..........................................
+                   :                                        :
+   +--------+      :  +-----------+      +---------------+  :
+   |        |-------->|           |      |               |  :
+   | client |      :  |  reverse  |----->|  Mojolicious  |  :
+   |        |<--------|   proxy   |      |  application  |  :
+   +--------+      :  |           |<-----|               |  :
+                   :  +-----------+      +---------------+  :
+                   :                                        :
+                   .. system boundary (e.g. same host) ......
+
+This setup introduces some problems, though: the application will receive
+requests from the reverse proxy instead of the original client; the
+address/hostname where your application lives internally will be different from
+the one visible from the outside; and if terminating SSL, the reverse proxy
+exposes services via HTTPS while using HTTP towards the Mojolicious application.
+
+As an example, compare a sample request from the client and what the Mojolicious
+application receives:
+
+   client                       reverse proxy                Mojolicious app
+    __|__              _______________|______________             ____|____
+   /     \            /                              \           /         \
+   1.2.3.4 --HTTPS--> api.example.com      10.20.30.39 --HTTP--> 10.20.30.40
+
+   GET /foo/1 HTTP/1.1                |    GET /foo/1 HTTP/1.1
+   Host: api.example.com              |    Host: 10.20.30.40
+   User-Agent: Firefox                |    User-Agent: ShinyProxy/1.2
+   ...                                |    ...
+
+However, now the client address is no longer available (which might be useful
+for analytics, or Geo-IP) and URLs generated via
+L<Mojolicious::Controller/"url_for"> will look like this:
+
+   http://10.20.30.40/bar/2
+
+instead of something meaningful for the client, like this:
+
+   https://api.example.com/bar/2
+
+To solve these problems, you can configure your reverse proxy to send the
+missing data (see L</Nginx> and L</"Apache/mod_proxy">) and tell your
+application about it by setting the environment variable C<MOJO_REVERSE_PROXY>.
+For finer control, L</Rewriting> includes examples of how the changes could be
+implemented manually.
+
+=head1 DEPLOYMENT
+
+Getting L<Mojolicious> and L<Mojolicious::Lite> applications running on
+different platforms. Note that many real-time web features are based on the
+L<Mojo::IOLoop> event loop, and therefore require one of the built-in web
+servers to be able to use them to their full potential.
+
+=head2 Built-in web server
+
+L<Mojolicious> contains a very portable non-blocking I/O HTTP and WebSocket
+server with L<Mojo::Server::Daemon>. It is usually used during development and
+in the construction of more advanced web servers, but is solid and fast enough
+for small to mid sized applications.
+
+  $ ./script/my_app daemon
+  Server available at http://127.0.0.1:3000
+
+It is available to every application through the command
+L<Mojolicious::Command::daemon>, which has many configuration options and is
+known to work on every platform Perl works on with its single-process
+architecture.
+
+  $ ./script/my_app daemon -h
+  ...List of available options...
+
+Another huge advantage is that it supports TLS and WebSockets out of the box, a
+development certificate for testing purposes is built right in, so it just
+works, but you can specify all listen locations supported by
+L<Mojo::Server::Daemon/"listen">.
+
+  $ ./script/my_app daemon -l https://[::]:3000
+  Server available at https://[::]:3000
+
+To manage the web server with systemd, you can use a unit configuration file
+like this.
+
+  [Unit]
+  Description=My Mojolicious application
+  After=network.target
+
+  [Service]
+  Type=simple
+  ExecStart=/home/sri/myapp/script/my_app daemon -m production -l http://*:8080
+
+  [Install]
+  WantedBy=multi-user.target
+
+=head2 Pre-forking
+
+On UNIX platforms you can also add pre-forking to the built-in web server and
+switch to a multi-process architecture with L<Mojolicious::Command::prefork>, to
+take advantage of multiple CPU cores and copy-on-write memory management.
+
+  $ ./script/my_app prefork
+  Server available at http://127.0.0.1:3000
+
+Since all built-in web servers are based on the L<Mojo::IOLoop> event loop,
+they scale best with non-blocking operations. But if your application for some
+reason needs to perform many blocking operations, you can improve performance
+by increasing the number of worker processes and decreasing the number of
+concurrent connections each worker is allowed to handle (often as low as C<1>).
+
+  $ ./script/my_app prefork -m production -w 10 -c 1
+  Server available at http://127.0.0.1:3000
+
+During startup your application is preloaded in the manager process, which does
+not run an event loop, so you can use L<Mojo::IOLoop/"next_tick"> to run code
+whenever a new worker process has been forked and its event loop gets started.
+
+  use Mojolicious::Lite;
+
+  Mojo::IOLoop->next_tick(sub {
+    app->log->info("Worker $$ star...ALL GLORY TO THE HYPNOTOAD!");
+  });
+
+  get '/' => {text => 'Hello Wor...ALL GLORY TO THE HYPNOTOAD!'};
+
+  app->start;
+
+And to manage the pre-forking web server with systemd, you can use a unit
+configuration file like this.
+
+  [Unit]
+  Description=My Mojolicious application
+  After=network.target
+
+  [Service]
+  Type=simple
+  ExecStart=/home/sri/myapp/script/my_app prefork -m production -l http://*:8080
+
+  [Install]
+  WantedBy=multi-user.target
+
+=head2 Morbo
+
+After reading the L<Mojolicious::Guides::Tutorial>, you should already be
+familiar with L<Mojo::Server::Morbo>.
+
+  Mojo::Server::Morbo
+  +- Mojo::Server::Daemon
+
+It is basically a restarter that forks a new L<Mojo::Server::Daemon> web server
+whenever a file in your project changes, and should therefore only be used
+during development. To start applications with it you can use the L<morbo>
+script.
+
+  $ morbo ./script/my_app
+  Server available at http://127.0.0.1:3000
+
+=head2 Hypnotoad
+
+For bigger applications L<Mojolicious> contains the UNIX optimized pre-forking
+web server L<Hypnotoad|Mojo::Server::Hypnotoad>, which can take advantage of
+multiple CPU cores and copy-on-write memory management to scale up to thousands
+of concurrent client connections.
+
+  Mojo::Server::Hypnotoad
+  |- Mojo::Server::Daemon [1]
+  |- Mojo::Server::Daemon [2]
+  |- Mojo::Server::Daemon [3]
+  +- Mojo::Server::Daemon [4]
+
+It is based on the L<Mojo::Server::Prefork> web server, which adds pre-forking
+to L<Mojo::Server::Daemon>, but optimized specifically for production
+environments out of the box. To start applications with it you can use the
+L<hypnotoad> script, which listens on port C<8080>, automatically daemonizes
+the server process and defaults to C<production> mode for L<Mojolicious> and
+L<Mojolicious::Lite> applications.
+
+  $ hypnotoad ./script/my_app
+
+Many configuration settings can be tweaked right from within your application
+with L<Mojolicious/"config">, for a full list see
+L<Mojo::Server::Hypnotoad/"SETTINGS">.
+
+  use Mojolicious::Lite;
+
+  app->config(hypnotoad => {listen => ['http://*:80']});
+
+  get '/' => {text => 'Hello Wor...ALL GLORY TO THE HYPNOTOAD!'};
+
+  app->start;
+
+Or just add a C<hypnotoad> section to your L<Mojolicious::Plugin::Config> or
+L<Mojolicious::Plugin::JSONConfig> configuration file.
+
+  # myapp.conf
+  {
+    hypnotoad => {
+      listen  => ['https://*:443?cert=/etc/server.crt&key=/etc/server.key'],
+      workers => 10
+    }
+  };
+
+But one of its biggest advantages is the support for effortless zero downtime
+software upgrades (hot deployment). That means you can upgrade L<Mojolicious>,
+Perl or even system libraries at runtime without ever stopping the server or
+losing a single incoming connection, just by running the command above again.
+
+  $ hypnotoad ./script/my_app
+  Starting hot deployment for Hypnotoad server 31841.
+
+You might also want to enable proxy support if you're using
+L<Hypnotoad|Mojo::Server::Hypnotoad> behind a reverse proxy. This allows
+L<Mojolicious> to automatically pick up the C<X-Forwarded-For> and
+C<X-Forwarded-Proto> headers.
+
+  # myapp.conf
+  {hypnotoad => {proxy => 1}};
+
+To manage L<Hypnotoad|Mojo::Server::Hypnotoad> with systemd, you can use a unit
+configuration file like this.
+
+  [Unit]
+  Description=My Mojolicious application
+  After=network.target
+
+  [Service]
+  Type=forking
+  PIDFile=/home/sri/myapp/script/hypnotoad.pid
+  ExecStart=/path/to/hypnotoad /home/sri/myapp/script/my_app
+  ExecReload=/path/to/hypnotoad /home/sri/myapp/script/my_app
+  KillMode=process
+
+  [Install]
+  WantedBy=multi-user.target
+
+=head2 Zero downtime software upgrades
+
+L<Hypnotoad|Mojo::Server::Hypnotoad> makes zero downtime software upgrades (hot
+deployment) very simple, as you can see above, but on modern operating systems
+that support the C<SO_REUSEPORT> socket option, there is also another method
+available that works with all built-in web servers.
+
+  $ ./script/my_app prefork -P /tmp/first.pid -l http://*:8080?reuse=1
+  Server available at http://127.0.0.1:8080
+
+All you have to do, is to start a second web server listening to the same port,
+and stop the first web server gracefully afterwards.
+
+  $ ./script/my_app prefork -P /tmp/second.pid -l http://*:8080?reuse=1
+  Server available at http://127.0.0.1:8080
+  $ kill -s TERM `cat /tmp/first.pid`
+
+Just remember that both web servers need to be started with the C<reuse>
+parameter.
+
+=head2 Nginx
+
+One of the most popular setups these days is
+L<Hypnotoad|Mojo::Server::Hypnotoad> behind an L<Nginx|http://nginx.org> reverse
+proxy, which even supports WebSockets in newer versions.
+
+  upstream myapp {
+    server 127.0.0.1:8080;
+  }
+  server {
+    listen 80;
+    server_name localhost;
+    location / {
+      proxy_pass http://myapp;
+      proxy_http_version 1.1;
+      proxy_set_header Upgrade $http_upgrade;
+      proxy_set_header Connection "upgrade";
+      proxy_set_header Host $host;
+      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+      proxy_set_header X-Forwarded-Proto $scheme;
+    }
+  }
+
+=head2 Apache/mod_proxy
+
+Another good reverse proxy is L<Apache|http://httpd.apache.org> with
+C<mod_proxy>, the configuration looks quite similar to the Nginx one above. And
+if you need WebSocket support, newer versions come with C<mod_proxy_wstunnel>.
+
+  <VirtualHost *:80>
+    ServerName localhost
+    <Proxy *>
+      Require all granted
+    </Proxy>
+    ProxyRequests Off
+    ProxyPreserveHost On
+    ProxyPass /echo ws://localhost:8080/echo
+    ProxyPass / http://localhost:8080/ keepalive=On
+    ProxyPassReverse / http://localhost:8080/
+    RequestHeader set X-Forwarded-Proto "http"
+  </VirtualHost>
+
+=head2 Apache/CGI
+
+C<CGI> is supported out of the box and your L<Mojolicious> application will
+automatically detect that it is executed as a C<CGI> script. Its use in
+production environments is discouraged though, because as a result of how
+C<CGI> works, it is very slow and many web servers are making it exceptionally
+hard to configure properly. Additionally, many real-time web features, such as
+WebSockets, are not available.
+
+  ScriptAlias / /home/sri/my_app/script/my_app/
+
+=head2 PSGI/Plack
+
+L<PSGI> is an interface between Perl web frameworks and web servers, and
+L<Plack> is a Perl module and toolkit that contains L<PSGI> middleware, helpers
+and adapters to web servers. L<PSGI> and L<Plack> are inspired by Python's WSGI
+and Ruby's Rack. L<Mojolicious> applications are ridiculously simple to deploy
+with L<Plack>, but be aware that many real-time web features, such as
+WebSockets, are not available.
+
+  $ plackup ./script/my_app
+
+L<Plack> provides many server and protocol adapters for you to choose from,
+such as C<FCGI>, C<uWSGI> and C<mod_perl>.
+
+  $ plackup ./script/my_app -s FCGI -l /tmp/myapp.sock
+
+The C<MOJO_REVERSE_PROXY> environment variable can be used to enable proxy
+support, this allows L<Mojolicious> to automatically pick up the
+C<X-Forwarded-For> and C<X-Forwarded-Proto> headers.
+
+  $ MOJO_REVERSE_PROXY=1 plackup ./script/my_app
+
+If an older server adapter is unable to correctly detect the application home
+directory, you can simply use the C<MOJO_HOME> environment variable.
+
+  $ MOJO_HOME=/home/sri/my_app plackup ./script/my_app
+
+There is no need for a C<.psgi> file, just point the server adapter at your
+application script, it will automatically act like one if it detects the
+presence of a C<PLACK_ENV> environment variable.
+
+=head2 Plack middleware
+
+Wrapper scripts like C<myapp.fcgi> are a great way to separate deployment and
+application logic.
+
+  #!/usr/bin/env plackup -s FCGI
+  use Plack::Builder;
+
+  builder {
+    enable 'Deflater';
+    require './script/my_app';
+  };
+
+L<Mojo::Server::PSGI> can be used directly to load and customize applications
+in the wrapper script.
+
+  #!/usr/bin/env plackup -s FCGI
+  use Mojo::Server::PSGI;
+  use Plack::Builder;
+
+  builder {
+    enable 'Deflater';
+    my $server = Mojo::Server::PSGI->new;
+    $server->load_app('./script/my_app');
+    $server->app->config(foo => 'bar');
+    $server->to_psgi_app;
+  };
+
+But you could even use middleware right in your application.
+
+  use Mojolicious::Lite;
+  use Plack::Builder;
+
+  get '/welcome' => sub {
+    my $c = shift;
+    $c->render(text => 'Hello Mojo!');
+  };
+
+  builder {
+    enable 'Deflater';
+    app->start;
+  };
+
+=head2 Rewriting
+
+Sometimes you might have to deploy your application in a blackbox environment
+where you can't just change the server configuration or behind a reverse proxy
+that passes along additional information with C<X-Forwarded-*> headers. In such
+cases you can use the hook L<Mojolicious/"before_dispatch"> to rewrite incoming
+requests.
+
+  # Change scheme if "X-Forwarded-HTTPS" header is set
+  $app->hook(before_dispatch => sub {
+    my $c = shift;
+    $c->req->url->base->scheme('https')
+      if $c->req->headers->header('X-Forwarded-HTTPS');
+  });
+
+Since reverse proxies generally don't pass along information about path
+prefixes your application might be deployed under, rewriting the base path of
+incoming requests is also quite common. This allows
+L<Mojolicious::Controller/"url_for"> for example, to generate portable URLs
+based on the current environment.
+
+  # Move first part and slash from path to base path in production mode
+  $app->hook(before_dispatch => sub {
+    my $c = shift;
+    push @{$c->req->url->base->path->trailing_slash(1)},
+      shift @{$c->req->url->path->leading_slash(0)};
+  }) if $app->mode eq 'production';
+
+L<Mojo::URL> objects are very easy to manipulate, just make sure that the URL
+(C<foo/bar?baz=yada>), which represents the routing destination, is always
+relative to the base URL (C<http://example.com/myapp/>), which represents the
+deployment location of your application.
+
+=head2 Application embedding
+
+From time to time you might want to reuse parts of L<Mojolicious> applications
+like configuration files, database connection or helpers for other scripts,
+with this little L<Mojo::Server> based mock server you can just embed them.
+
+  use Mojo::Server;
+
+  # Load application with mock server
+  my $server = Mojo::Server->new;
+  my $app = $server->load_app('./myapp.pl');
+
+  # Access fully initialized application
+  say for @{$app->static->paths};
+  say $app->config->{secret_identity};
+  say $app->dumper({just => 'a helper test'});
+  say $app->build_controller->render_to_string(template => 'foo');
+
+The plugin L<Mojolicious::Plugin::Mount> uses this functionality to allow you
+to combine multiple applications into one and deploy them together.
+
+  use Mojolicious::Lite;
+
+  app->config(hypnotoad => {listen => ['http://*:80']});
+
+  plugin Mount => {'test1.example.com' => '/home/sri/myapp1.pl'};
+  plugin Mount => {'test2.example.com' => '/home/sri/myapp2.pl'};
+
+  app->start;
+
+=head2 Web server embedding
+
+You can also use L<Mojo::IOLoop/"one_tick"> to embed the built-in web server
+L<Mojo::Server::Daemon> into alien environments like foreign event loops that
+for some reason can't just be integrated with a new reactor backend.
+
+  use Mojolicious::Lite;
+  use Mojo::IOLoop;
+  use Mojo::Server::Daemon;
+
+  # Normal action
+  get '/' => {text => 'Hello World!'};
+
+  # Connect application with web server and start accepting connections
+  my $daemon
+    = Mojo::Server::Daemon->new(app => app, listen => ['http://*:8080']);
+  $daemon->start;
+
+  # Call "one_tick" repeatedly from the alien environment
+  Mojo::IOLoop->one_tick while 1;
+
+=head1 REAL-TIME WEB
+
+The real-time web is a collection of technologies that include Comet (long
+polling), EventSource and WebSockets, which allow content to be pushed to
+consumers with long-lived connections as soon as it is generated, instead of
+relying on the more traditional pull model. All built-in web servers use
+non-blocking I/O and are based on the L<Mojo::IOLoop> event loop, which
+provides many very powerful features that allow real-time web applications to
+scale up to thousands of concurrent client connections.
+
+=head2 Backend web services
+
+Since L<Mojo::UserAgent> is also based on the L<Mojo::IOLoop> event loop, it
+won't block the built-in web servers when used non-blocking, even for high
+latency backend web services.
+
+  use Mojolicious::Lite;
+
+  # Search MetaCPAN for "mojolicious"
+  get '/' => sub {
+    my $c = shift;
+    $c->ua->get('fastapi.metacpan.org/v1/module/_search?q=mojolicious' => sub {
+      my ($ua, $tx) = @_;
+      $c->render('metacpan', hits => $tx->result->json->{hits}{hits});
+    });
+  };
+
+  app->start;
+  __DATA__
+
+  @@ metacpan.html.ep
+  <!DOCTYPE html>
+  <html>
+    <head><title>MetaCPAN results for "mojolicious"</title></head>
+    <body>
+      % for my $hit (@$hits) {
+        <p><%= $hit->{_source}{release} %></p>
+      % }
+    </body>
+  </html>
+
+The callback passed to L<Mojo::UserAgent/"get"> will be executed once the
+request to the backend web service has been finished, this is called
+continuation-passing style.
+
+=head2 Synchronizing non-blocking operations
+
+Multiple non-blocking operations, such as concurrent requests, can be easily
+synchronized with promises and L<Mojo::Promise/"all">. You create
+L<Mojo::Promise> objects manually or use methods like L<Mojo::UserAgent/"get_p">
+that create them for you.
+
+  use Mojolicious::Lite;
+  use Mojo::Promise;
+  use Mojo::URL;
+
+  # Search MetaCPAN for "mojo" and "minion"
+  get '/' => sub {
+    my $c = shift;
+
+    # Create two promises
+    my $url   = Mojo::URL->new('fastapi.metacpan.org/v1/module/_search');
+    my $mojo   = $c->ua->get_p($url->clone->query({q => 'mojo'}));
+    my $minion = $c->ua->get_p($url->clone->query({q => 'minion'}));
+
+    # Render a response once both promises have been resolved
+    Mojo::Promise->all($mojo, $minion)->then(sub {
+      my ($mojo, $minion) = @_;
+      $c->render(json => {
+        mojo   => $mojo->[0]->result->json('/hits/hits/0/_source/release'),
+        minion => $minion->[0]->result->json('/hits/hits/0/_source/release')
+      });
+    })->catch(sub {
+      my $err = shift;
+      $c->reply->exception($err);
+    })->wait;
+  };
+
+  app->start;
+
+To create promises manually you just wrap your continuation-passing style APIs
+in functions that return promises. Here's an example for how
+L<Mojo::UserAgent/"get_p"> works internally.
+
+  use Mojo::UserAgent;
+  use Mojo::Promise;
+
+  # Wrap a user agent method with a promise
+  my $ua = Mojo::UserAgent->new;
+  sub get_p {
+    my $promise = Mojo::Promise->new;
+    $ua->get(@_ => sub {
+      my ($ua, $tx) = @_;
+      my $err = $tx->error;
+      $promise->resolve($tx) if !$err || $err->{code};
+      $promise->reject($err->{message});
+    });
+    return $promise;
+  }
+
+  # Use our new promise generating function
+  get_p('https://mojolicious.org')->then(sub {
+    my $tx = shift;
+    say $tx->result->dom->at('title')->text;
+  })->wait;
+
+Promises have three states, they start out as C<pending> and you call
+L<Mojo::Promise/"resolve"> to transition them to C<fulfilled>, or
+L<Mojo::Promise/"reject"> to transition them to C<rejected>.
+
+=head2 Timers
+
+Timers, another primary feature of the event loop, are created with
+L<Mojo::IOLoop/"timer"> and can, for example, be used to delay rendering of a
+response, and unlike C<sleep>, won't block any other requests that might be
+processed concurrently.
+
+  use Mojolicious::Lite;
+  use Mojo::IOLoop;
+
+  # Wait 3 seconds before rendering a response
+  get '/' => sub {
+    my $c = shift;
+    Mojo::IOLoop->timer(3 => sub {
+      $c->render(text => 'Delayed by 3 seconds!');
+    });
+  };
+
+  app->start;
+
+Recurring timers created with L<Mojo::IOLoop/"recurring"> are slightly more
+powerful, but need to be stopped manually, or they would just keep getting
+emitted.
+
+  use Mojolicious::Lite;
+  use Mojo::IOLoop;
+
+  # Count to 5 in 1 second steps
+  get '/' => sub {
+    my $c = shift;
+
+    # Start recurring timer
+    my $i = 1;
+    my $id = Mojo::IOLoop->recurring(1 => sub {
+      $c->write_chunk($i);
+      $c->finish if $i++ == 5;
+    });
+
+    # Stop recurring timer
+    $c->on(finish => sub { Mojo::IOLoop->remove($id) });
+  };
+
+  app->start;
+
+Timers are not tied to a specific request or connection, and can even be
+created at startup time.
+
+  use Mojolicious::Lite;
+  use Mojo::IOLoop;
+
+  # Check title in the background every 10 seconds
+  my $title = 'Got no title yet.';
+  Mojo::IOLoop->recurring(10 => sub {
+    app->ua->get('https://mojolicious.org' => sub {
+      my ($ua, $tx) = @_;
+      $title = $tx->result->dom->at('title')->text;
+    });
+  });
+
+  # Show current title
+  get '/' => sub {
+    my $c = shift;
+    $c->render(json => {title => $title});
+  };
+
+  app->start;
+
+Just remember that all these non-blocking operations are processed
+cooperatively, so your callbacks shouldn't block for too long.
+
+=head2 Subprocesses
+
+You can also use subprocesses, created with L<Mojo::IOLoop/"subprocess">, to
+perform computationally expensive operations without blocking the event loop.
+
+  use Mojolicious::Lite;
+  use Mojo::IOLoop;
+
+  # Operation that would block the event loop for 5 seconds
+  get '/' => sub {
+    my $c = shift;
+    Mojo::IOLoop->subprocess(
+      sub {
+        my $subprocess = shift;
+        sleep 5;
+        return 'â¥', 'Mojolicious';
+      },
+      sub {
+        my ($subprocess, $err, @results) = @_;
+        $c->reply->exception($err) and return if $err;
+        $c->render(text => "I $results[0] $results[1]!");
+      }
+    );
+  };
+
+  app->start;
+
+The first callback will be executed in a child process, without blocking the
+event loop of the parent process. The results of the first callback will then be
+shared between both processes, and the second callback executed in the parent
+process.
+
+=head2 Exceptions in non-blocking operations
+
+Since timers and other non-blocking operations are running solely in the event
+loop, outside of the application, exceptions that get thrown in callbacks can't
+get caught and handled automatically. But you can handle them manually by
+subscribing to the event L<Mojo::Reactor/"error"> or catching them inside the
+callback.
+
+  use Mojolicious::Lite;
+  use Mojo::IOLoop;
+
+  # Forward error messages to the application log
+  Mojo::IOLoop->singleton->reactor->on(error => sub {
+    my ($reactor, $err) = @_;
+    app->log->error($err);
+  });
+
+  # Exception only gets logged (and connection times out)
+  get '/connection_times_out' => sub {
+    my $c = shift;
+    Mojo::IOLoop->timer(2 => sub {
+      die 'This request will not be getting a response';
+    });
+  };
+
+  # Exception gets caught and handled
+  get '/catch_exception' => sub {
+    my $c = shift;
+    Mojo::IOLoop->timer(2 => sub {
+      eval { die 'This request will be getting a response' };
+      $c->reply->exception($@) if $@;
+    });
+  };
+
+  app->start;
+
+A default subscriber that turns all errors into warnings will usually be added
+by L<Mojo::IOLoop> as a fallback.
+
+  Mojo::IOLoop->singleton->reactor->unsubscribe('error');
+
+During development or for applications where crashing is simply preferable, you
+can also make every exception that gets thrown in a callback fatal by removing
+all of its subscribers.
+
+=head2 WebSocket web service
+
+The WebSocket protocol offers full bi-directional low-latency communication
+channels between clients and servers. Receive messages just by subscribing to
+events such as L<Mojo::Transaction::WebSocket/"message"> with
+L<Mojolicious::Controller/"on"> and return them with
+L<Mojolicious::Controller/"send">.
+
+  use Mojolicious::Lite;
+
+  # Template with browser-side code
+  get '/' => 'index';
+
+  # WebSocket echo service
+  websocket '/echo' => sub {
+    my $c = shift;
+
+    # Opened
+    $c->app->log->debug('WebSocket opened');
+
+    # Increase inactivity timeout for connection a bit
+    $c->inactivity_timeout(300);
+
+    # Incoming message
+    $c->on(message => sub {
+      my ($c, $msg) = @_;
+      $c->send("echo: $msg");
+    });
+
+    # Closed
+    $c->on(finish => sub {
+      my ($c, $code, $reason) = @_;
+      $c->app->log->debug("WebSocket closed with status $code");
+    });
+  };
+
+  app->start;
+  __DATA__
+
+  @@ index.html.ep
+  <!DOCTYPE html>
+  <html>
+    <head><title>Echo</title></head>
+    <body>
+      <script>
+        var ws = new WebSocket('<%= url_for('echo')->to_abs %>');
+
+        // Incoming messages
+        ws.onmessage = function (event) {
+          document.body.innerHTML += event.data + '<br/>';
+        };
+
+        // Outgoing messages
+        ws.onopen = function (event) {
+          window.setInterval(function () { ws.send('Hello Mojo!') }, 1000);
+        };
+      </script>
+    </body>
+  </html>
+
+The event L<Mojo::Transaction::WebSocket/"finish"> will be emitted right after
+the WebSocket connection has been closed.
+
+  $c->tx->with_compression;
+
+You can activate C<permessage-deflate> compression with
+L<Mojo::Transaction::WebSocket/"with_compression">, this can result in much
+better performance, but also increases memory usage by up to 300KiB per
+connection.
+
+  my $proto = $c->tx->with_protocols('v2.proto', 'v1.proto');
+
+You can also use L<Mojo::Transaction::WebSocket/"with_protocols"> to negotiate a
+subprotocol.
+
+=head2 EventSource web service
+
+EventSource is a special form of long polling where you can use
+L<Mojolicious::Controller/"write"> to directly send DOM events from servers to
+clients. It is uni-directional, that means you will have to use Ajax requests
+for sending data from clients to servers, the advantage however is low
+infrastructure requirements, since it reuses the HTTP protocol for transport.
+
+  use Mojolicious::Lite;
+
+  # Template with browser-side code
+  get '/' => 'index';
+
+  # EventSource for log messages
+  get '/events' => sub {
+    my $c = shift;
+
+    # Increase inactivity timeout for connection a bit
+    $c->inactivity_timeout(300);
+
+    # Change content type and finalize response headers
+    $c->res->headers->content_type('text/event-stream');
+    $c->write;
+
+    # Subscribe to "message" event and forward "log" events to browser
+    my $cb = $c->app->log->on(message => sub {
+      my ($log, $level, @lines) = @_;
+      $c->write("event:log\ndata: [$level] @lines\n\n");
+    });
+
+    # Unsubscribe from "message" event again once we are done
+    $c->on(finish => sub {
+      my $c = shift;
+      $c->app->log->unsubscribe(message => $cb);
+    });
+  };
+
+  app->start;
+  __DATA__
+
+  @@ index.html.ep
+  <!DOCTYPE html>
+  <html>
+    <head><title>LiveLog</title></head>
+    <body>
+      <script>
+        var events = new EventSource('<%= url_for 'events' %>');
+
+        // Subscribe to "log" event
+        events.addEventListener('log', function (event) {
+          document.body.innerHTML += event.data + '<br/>';
+        }, false);
+      </script>
+    </body>
+  </html>
+
+The event L<Mojo::Log/"message"> will be emitted for every new log message and
+the event L<Mojo::Transaction/"finish"> right after the transaction has been
+finished.
+
+=head2 Streaming multipart uploads
+
+L<Mojolicious> contains a very sophisticated event system based on
+L<Mojo::EventEmitter>, with ready-to-use events on almost all layers, and which
+can be combined to solve some of the hardest problems in web development.
+
+  use Mojolicious::Lite;
+  use Scalar::Util 'weaken';
+
+  # Intercept multipart uploads and log each chunk received
+  hook after_build_tx => sub {
+    my $tx = shift;
+
+    # Subscribe to "upgrade" event to identify multipart uploads
+    weaken $tx;
+    $tx->req->content->on(upgrade => sub {
+      my ($single, $multi) = @_;
+      return unless $tx->req->url->path->contains('/upload');
+
+      # Subscribe to "part" event to find the right one
+      $multi->on(part => sub {
+        my ($multi, $single) = @_;
+
+        # Subscribe to "body" event of part to make sure we have all headers
+        $single->on(body => sub {
+          my $single = shift;
+
+          # Make sure we have the right part and replace "read" event
+          return unless $single->headers->content_disposition =~ /example/;
+          $single->unsubscribe('read')->on(read => sub {
+            my ($single, $bytes) = @_;
+
+            # Log size of every chunk we receive
+            app->log->debug(length($bytes) . ' bytes uploaded');
+          });
+        });
+      });
+    });
+  };
+
+  # Upload form in DATA section
+  get '/' => 'index';
+
+  # Streaming multipart upload
+  post '/upload' => {text => 'Upload was successful.'};
+
+  app->start;
+  __DATA__
+
+  @@ index.html.ep
+  <!DOCTYPE html>
+  <html>
+    <head><title>Streaming multipart upload</title></head>
+    <body>
+      %= form_for upload => (enctype => 'multipart/form-data') => begin
+        %= file_field 'example'
+        %= submit_button 'Upload'
+      % end
+    </body>
+  </html>
+
+=head2 More event loops
+
+Internally, the L<Mojo::IOLoop> event loop can use multiple reactor backends,
+L<EV> for example, will be automatically used if possible. Which in turn allows
+other event loops like L<AnyEvent> to just work.
+
+  use Mojolicious::Lite;
+  use EV;
+  use AnyEvent;
+
+  # Wait 3 seconds before rendering a response
+  get '/' => sub {
+    my $c = shift;
+    my $w;
+    $w = AE::timer 3, 0, sub {
+      $c->render(text => 'Delayed by 3 seconds!');
+      undef $w;
+    };
+  };
+
+  app->start;
+
+Who actually controls the event loop backend is not important.
+
+  use Mojo::UserAgent;
+  use EV;
+  use AnyEvent;
+
+  # Search MetaCPAN for "mojolicious"
+  my $cv = AE::cv;
+  my $ua = Mojo::UserAgent->new;
+  $ua->get('fastapi.metacpan.org/v1/module/_search?q=mojolicious' => sub {
+    my ($ua, $tx) = @_;
+    $cv->send($tx->result->json('/hits/hits/0/_source/release'));
+  });
+  say $cv->recv;
+
+You could, for example, just embed the built-in web server into an L<AnyEvent>
+application.
+
+  use Mojolicious::Lite;
+  use Mojo::Server::Daemon;
+  use EV;
+  use AnyEvent;
+
+  # Normal action
+  get '/' => {text => 'Hello World!'};
+
+  # Connect application with web server and start accepting connections
+  my $daemon
+    = Mojo::Server::Daemon->new(app => app, listen => ['http://*:8080']);
+  $daemon->start;
+
+  # Let AnyEvent take control
+  AE::cv->recv;
+
+=head1 USER AGENT
+
+When we say L<Mojolicious> is a web framework we actually mean it, with
+L<Mojo::UserAgent> there's a full featured HTTP and WebSocket user agent built
+right in.
+
+=head2 REST web services
+
+Requests can be performed very comfortably with methods like
+L<Mojo::UserAgent/"get">, and always result in a L<Mojo::Transaction::HTTP>
+object, which has many useful attributes and methods. You can check for
+connection errors with L<Mojo::Transaction/"result">, or access HTTP request and
+response information directly through L<Mojo::Transaction/"req"> and
+L<Mojo::Transaction/"res">.
+
+  use Mojo::UserAgent;
+
+  # Request a resource and make sure there were no connection errors
+  my $ua = Mojo::UserAgent->new;
+  my $tx = $ua->get('mojolicious.org/perldoc/Mojo' => {Accept => 'text/plain'});
+  my $res = $tx->result;
+
+  # Decide what to do with its representation
+  if    ($res->is_success)  { say $res->body }
+  elsif ($res->is_error)    { say $res->message }
+  elsif ($res->code == 301) { say $res->headers->location }
+  else                      { say 'Whatever...' }
+
+While methods like L<Mojo::Message::Response/"is_success"> and
+L<Mojo::Message::Response/"is_error"> serve as building blocks for more
+sophisticated REST clients.
+
+=head2 Web scraping
+
+Scraping information from websites has never been this much fun before. The
+built-in HTML/XML parser L<Mojo::DOM> is accessible through
+L<Mojo::Message/"dom"> and supports all CSS selectors that make sense for a
+standalone parser, it can be a very powerful tool especially for testing web
+application.
+
+  use Mojo::UserAgent;
+
+  # Fetch website
+  my $ua = Mojo::UserAgent->new;
+  my $res = $ua->get('mojolicious.org/perldoc')->result;
+
+  # Extract title
+  say 'Title: ', $res->dom->at('head > title')->text;
+
+  # Extract headings
+  $res->dom('h1, h2, h3')->each(sub { say 'Heading: ', shift->all_text });
+
+  # Visit all nodes recursively to extract more than just text
+  for my $n ($res->dom->descendant_nodes->each) {
+
+    # Text or CDATA node
+    print $n->content if $n->type eq 'text' || $n->type eq 'cdata';
+
+    # Also include alternate text for images
+    print $n->{alt} if $n->type eq 'tag' && $n->tag eq 'img';
+  }
+
+For a full list of available CSS selectors see L<Mojo::DOM::CSS/"SELECTORS">.
+
+=head2 JSON web services
+
+Most web services these days are based on the JSON data-interchange format.
+That's why L<Mojolicious> comes with the possibly fastest pure-Perl
+implementation L<Mojo::JSON> built right in, which is accessible through
+L<Mojo::Message/"json">.
+
+  use Mojo::UserAgent;
+  use Mojo::URL;
+
+  # Fresh user agent
+  my $ua = Mojo::UserAgent->new;
+
+  # Search MetaCPAN for "mojolicious" and list latest releases
+  my $url = Mojo::URL->new('http://fastapi.metacpan.org/v1/release/_search');
+  $url->query({q => 'mojolicious', sort => 'date:desc'});
+  for my $hit (@{$ua->get($url)->result->json->{hits}{hits}}) {
+    say "$hit->{_source}{name} ($hit->{_source}{author})";
+  }
+
+=head2 Basic authentication
+
+You can just add username and password to the URL, an C<Authorization> header
+will be automatically generated.
+
+  use Mojo::UserAgent;
+
+  my $ua = Mojo::UserAgent->new;
+  say $ua->get('https://sri:secret@example.com/hideout')->result->body;
+
+=head2 Decorating follow-up requests
+
+L<Mojo::UserAgent> can automatically follow redirects, the event
+L<Mojo::UserAgent/"start"> allows you direct access to each transaction right
+after they have been initialized and before a connection gets associated with
+them.
+
+  use Mojo::UserAgent;
+
+  # User agent following up to 10 redirects
+  my $ua = Mojo::UserAgent->new(max_redirects => 10);
+
+  # Add a witty header to every request
+  $ua->on(start => sub {
+    my ($ua, $tx) = @_;
+    $tx->req->headers->header('X-Bender' => 'Bite my shiny metal ass!');
+    say 'Request: ', $tx->req->url->clone->to_abs;
+  });
+
+  # Request that will most likely get redirected
+  say 'Title: ', $ua->get('google.com')->result->dom->at('head > title')->text;
+
+This even works for proxy C<CONNECT> requests.
+
+=head2 Content generators
+
+Content generators can be registered with
+L<Mojo::UserAgent::Transactor/"add_generator"> to generate the same type of
+content repeatedly for multiple requests.
+
+  use Mojo::UserAgent;
+  use Mojo::Asset::File;
+
+  # Add "stream" generator
+  my $ua = Mojo::UserAgent->new;
+  $ua->transactor->add_generator(stream => sub {
+    my ($transactor, $tx, $path) = @_;
+    $tx->req->content->asset(Mojo::Asset::File->new(path => $path));
+  });
+
+  # Send multiple files streaming via PUT and POST
+  $ua->put('http://example.com/upload'  => stream => '/home/sri/mojo.png');
+  $ua->post('http://example.com/upload' => stream => '/home/sri/minion.png');
+
+The C<json>, C<form> and C<multipart> content generators are always available.
+
+  use Mojo::UserAgent;
+
+  # Send "application/json" content via PATCH
+  my $ua = Mojo::UserAgent->new;
+  my $tx = $ua->patch('http://api.example.com' => json => {foo => 'bar'});
+
+  # Send query parameters via GET
+  my $tx2 = $ua->get('search.example.com' => form => {q => 'test'});
+
+  # Send "application/x-www-form-urlencoded" content via POST
+  my $tx3 = $ua->post('http://search.example.com' => form => {q => 'test'});
+
+  # Send "multipart/form-data" content via PUT
+  my $tx4 = $ua->put(
+    'upload.example.com' => form => {test => {content => 'Hello World!'}});
+
+  # Send custom multipart content via PUT
+  my $tx5 = $ua->put('api.example.com' => multipart => ['Hello', 'World!']);
+
+For more information about available content generators see also
+L<Mojo::UserAgent::Transactor/"tx">.
+
+=head2 Large file downloads
+
+When downloading large files with L<Mojo::UserAgent> you don't have to worry
+about memory usage at all, because it will automatically stream everything
+above 250KiB into a temporary file, which can then be moved into a permanent
+file with L<Mojo::Asset::File/"move_to">.
+
+  use Mojo::UserAgent;
+
+  # Fetch the latest Mojolicious tarball
+  my $ua = Mojo::UserAgent->new(max_redirects => 5);
+  my $tx = $ua->get('https://www.github.com/kraih/mojo/tarball/master');
+  $tx->result->content->asset->move_to('mojo.tar.gz');
+
+To protect you from excessively large files there is also a limit of 2GiB by
+default, which you can tweak with the attribute
+L<Mojo::UserAgent/"max_response_size">.
+
+  # Increase limit to 10GiB
+  $ua->max_response_size(10737418240);
+
+=head2 Large file upload
+
+Uploading a large file is even easier.
+
+  use Mojo::UserAgent;
+
+  # Upload file via POST and "multipart/form-data"
+  my $ua = Mojo::UserAgent->new;
+  $ua->post('example.com/upload' =>
+    form => {image => {file => '/home/sri/hello.png'}});
+
+And once again you don't have to worry about memory usage, all data will be
+streamed directly from the file.
+
+=head2 Streaming response
+
+Receiving a streaming response can be really tricky in most HTTP clients, but
+L<Mojo::UserAgent> makes it actually easy.
+
+  use Mojo::UserAgent;
+
+  # Accept responses of indefinite size
+  my $ua = Mojo::UserAgent->new(max_response_size => 0);
+
+  # Build a normal transaction
+  my $tx = $ua->build_tx(GET => 'http://example.com');
+
+  # Replace "read" events to disable default content parser
+  $tx->res->content->unsubscribe('read')->on(read => sub {
+    my ($content, $bytes) = @_;
+    say "Streaming: $bytes";
+  });
+
+  # Process transaction
+  $tx = $ua->start($tx);
+
+The event L<Mojo::Content/"read"> will be emitted for every chunk of data that
+is received, even chunked transfer encoding and gzip content encoding will be
+handled transparently if necessary.
+
+=head2 Streaming request
+
+Sending a streaming request is almost just as easy.
+
+  use Mojo::UserAgent;
+
+  # Build a normal transaction
+  my $ua = Mojo::UserAgent->new;
+  my $tx = $ua->build_tx(GET => 'http://example.com');
+
+  # Prepare body
+  my $body = 'Hello World!';
+  $tx->req->headers->content_length(length $body);
+
+  # Start writing directly with a drain callback
+  my $drain;
+  $drain = sub {
+    my $content = shift;
+    my $chunk = substr $body, 0, 1, '';
+    $drain = undef unless length $body;
+    $content->write($chunk, $drain);
+  };
+  $tx->req->content->$drain;
+
+  # Process transaction
+  $tx = $ua->start($tx);
+
+The drain callback passed to L<Mojo::Content/"write"> will be executed whenever
+the entire previous chunk of data has actually been written.
+
+=head2 Non-blocking
+
+L<Mojo::UserAgent> has been designed from the ground up to be non-blocking, the
+whole blocking API is just a simple convenience wrapper. Especially for high
+latency tasks like web crawling this can be extremely useful, because you can
+keep many concurrent connections active at the same time.
+
+  use Mojo::UserAgent;
+  use Mojo::IOLoop;
+
+  # Concurrent non-blocking requests
+  my $ua = Mojo::UserAgent->new;
+  $ua->get('https://metacpan.org/search?q=mojo' => sub {
+    my ($ua, $mojo) = @_;
+    say $mojo->result->dom->at('title')->text;
+  });
+  $ua->get('https://metacpan.org/search?q=minion' => sub {
+    my ($ua, $minion) = @_;
+    say $minion->result->dom->at('title')->text;
+  });
+
+  # Start event loop if necessary
+  Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
+
+But don't try to open too many connections to one server at the same time, it
+might get overwhelmed. Better use a queue to process requests in smaller
+batches.
+
+  use Mojo::UserAgent;
+  use Mojo::IOLoop;
+
+  my @urls = (
+    'mojolicious.org/perldoc/Mojo/DOM',  'mojolicious.org/perldoc/Mojo',
+    'mojolicious.org/perldoc/Mojo/File', 'mojolicious.org/perldoc/Mojo/URL'
+  );
+
+  # User agent with a custom name, following up to 5 redirects
+  my $ua = Mojo::UserAgent->new(max_redirects => 5);
+  $ua->transactor->name('MyParallelCrawler 1.0');
+
+  # Use a delay to keep the event loop running until we are done
+  my $delay = Mojo::IOLoop->delay;
+  my $fetch;
+  $fetch = sub {
+
+    # Stop if there are no more URLs
+    return unless my $url = shift @urls;
+
+    # Fetch the next title
+    my $end = $delay->begin;
+    $ua->get($url => sub {
+      my ($ua, $tx) = @_;
+      say "$url: ", $tx->result->dom->at('title')->text;
+
+      # Next request
+      $fetch->();
+      $end->();
+    });
+  };
+
+  # Process two requests at a time
+  $fetch->() for 1 .. 2;
+  $delay->wait;
+
+It is also strongly recommended to respect every sites C<robots.txt> file as
+well as terms of service, and to wait a little before reopening connections to
+the same host, or the operators might be forced to block your access.
+
+=head2 Concurrent blocking requests
+
+You might have seen L<Mojo::Promise/"wait"> already in some examples
+above. It is used to make non-blocking operations portable, allowing them to
+work inside an already running event loop or start one on demand.
+
+  use Mojo::UserAgent;
+  use Mojo::Promise;
+
+  # Synchronize non-blocking requests with promises
+  my $ua = Mojo::UserAgent->new;
+  my $mojo   = $ua->get_p('https://metacpan.org/search?q=mojo');
+  my $minion = $ua->get_p('https://metacpan.org/search?q=minion');
+  Mojo::Promise->all($mojo, $minion)->then(sub {
+    my ($mojo, $minion) = @_;
+    say $mojo->[0]->result->dom->at('title')->text;
+    say $minion->[0]->result->dom->at('title')->text;
+  })->wait;
+
+=head2 WebSockets
+
+WebSockets are not just for the server-side, you can use
+L<Mojo::UserAgent/"websocket_p"> to open new connections, which are always
+non-blocking. The WebSocket handshake uses HTTP, and is a normal C<GET> request
+with a few additional headers. It can even contain cookies, and is followed by
+a C<101> response from the server, notifying our user agent that the connection
+has been established and it can start using the bi-directional WebSocket
+protocol.
+
+  use Mojo::UserAgent;
+  use Mojo::Promise;
+
+  # Open WebSocket to echo service
+  my $ua = Mojo::UserAgent->new;
+  $ua->websocket_p('ws://echo.websocket.org')->then(sub {
+    my $tx = shift;
+
+    # Prepare a followup promise so we can wait for messages
+    my $promise = Mojo::Promise->new;
+
+    # Wait for WebSocket to be closed
+    $tx->on(finish => sub {
+      my ($tx, $code, $reason) = @_;
+      say "WebSocket closed with status $code.";
+      $promise->resolve;
+    });
+
+    # Close WebSocket after receiving one message
+    $tx->on(message => sub {
+      my ($tx, $msg) = @_;
+      say "WebSocket message: $msg";
+      $tx->finish;
+    });
+
+    # Send a message to the server
+    $tx->send('Hi!');
+
+    # Insert a new promise into the promise chain
+    return $promise;
+  })->catch(sub {
+    my $err = shift;
+
+    # Handle failed WebSocket handshakes and other exceptions
+    warn "WebSocket error: $err";
+  })->wait;
+
+=head2 UNIX domain sockets
+
+Not just TCP/IP sockets are supported, but also UNIX domain sockets, which can
+have significant security and performance benefits when used for inter-process
+communication. Instead of C<http://> and C<ws://> you can use the
+C<http+unix://> and C<ws+unix://> schemes, and pass along a percent encoded path
+(C</> becomes C<%2F>) instead of a hostname.
+
+  use Mojo::UserAgent;
+  use Mojo::Promise;
+
+  # GET request via UNIX domain socket "/tmp/foo.sock"
+  my $ua = Mojo::UserAgent->new;
+  say $ua->get('http+unix://%2Ftmp%2Ffoo.sock/index.html')->result->body;
+
+  # GET request with HOST header via UNIX domain socket "/tmp/bar.sock"
+  my $tx = $ua->get('http+unix://%2Ftmp%2Fbar.sock' => {Host => 'example.com'});
+  say $tx->result->body;
+
+  # WebSocket connection via UNIX domain socket "/tmp/baz.sock"
+  $ua->websocket_p('ws+unix://%2Ftmp%2Fbaz.sock/echo')->then(sub {
+    my $tx = shift;
+
+    my $promise = Mojo::Promise->new;
+    $tx->on(finish => sub { $promise->resolve });
+
+    $tx->on(message => sub {
+      my ($tx, $msg) = @_;
+      say "WebSocket message: $msg";
+      $tx->finish;
+    });
+    $tx->send('Hi!');
+
+    return $promise;
+  })->catch(sub {
+    my $err = shift;
+    warn "WebSocket error: $err";
+  })->wait;
+
+You can set the C<Host> header manually to pass along a hostname.
+
+=head2 Command line
+
+Don't you hate checking huge HTML files from the command line? Thanks to the
+command L<Mojolicious::Command::get> that is about to change. You can just pick
+the parts that actually matter with the CSS selectors from L<Mojo::DOM> and
+JSON Pointers from L<Mojo::JSON::Pointer>.
+
+  $ mojo get https://mojolicious.org 'head > title'
+
+How about a list of all id attributes?
+
+  $ mojo get https://mojolicious.org '*' attr id
+
+Or the text content of all heading tags?
+
+  $ mojo get https://mojolicious.org 'h1, h2, h3' text
+
+Maybe just the text of the third heading?
+
+  $ mojo get https://mojolicious.org 'h1, h2, h3' 3 text
+
+You can also extract all text from nested child elements.
+
+  $ mojo get https://mojolicious.org '#mojobar' all
+
+The request can be customized as well.
+
+  $ mojo get -M POST -H 'X-Bender: Bite my shiny metal ass!' http://google.com
+
+Store response data by redirecting C<STDOUT>.
+
+  $ mojo get mojolicious.org > example.html
+
+Pass request data by redirecting C<STDIN>.
+
+  $ mojo get -M PUT mojolicious.org < example.html
+
+Or use the output of another program.
+
+  $ echo 'Hello World' | mojo get -M PUT https://mojolicious.org
+
+Submit forms as C<application/x-www-form-urlencoded> content.
+
+  $ mojo get -M POST -f 'q=Mojo' -f 'size=5' https://metacpan.org/search
+
+And upload files as C<multipart/form-data> content.
+
+  $ mojo get -M POST -f 'upload=@example.html' mojolicious.org
+
+You can follow redirects and view the headers for all messages.
+
+  $ mojo get -r -v http://google.com 'head > title'
+
+Extract just the information you really need from JSON data structures.
+
+  $ mojo get https://fastapi.metacpan.org/v1/author/SRI /name
+
+This can be an invaluable tool for testing your applications.
+
+  $ ./myapp.pl get /welcome 'head > title'
+
+=head2 One-liners
+
+For quick hacks and especially testing, L<ojo> one-liners are also a great
+choice.
+
+  $ perl -Mojo -E 'say g("mojolicious.org")->dom->at("title")->text'
+
+=head1 APPLICATIONS
+
+Fun L<Mojolicious> application hacks for all occasions.
+
+=head2 Basic authentication
+
+Basic authentication data will be automatically extracted from the
+C<Authorization> header.
+
+  use Mojolicious::Lite;
+  use Mojo::Util 'secure_compare';
+
+  get '/' => sub {
+    my $c = shift;
+
+    # Check for username "Bender" and password "rocks"
+    return $c->render(text => 'Hello Bender!')
+      if secure_compare $c->req->url->to_abs->userinfo, 'Bender:rocks';
+
+    # Require authentication
+    $c->res->headers->www_authenticate('Basic');
+    $c->render(text => 'Authentication required!', status => 401);
+  };
+
+  app->start;
+
+This can be combined with TLS for a secure authentication mechanism.
+
+  $ ./myapp.pl daemon -l 'https://*:3000?cert=./server.crt&key=./server.key'
+
+=head2 Adding a configuration file
+
+Adding a configuration file to your application is as easy as adding a file to
+its home directory and loading the plugin L<Mojolicious::Plugin::Config>. The
+default name is based on the value of L<Mojolicious/"moniker"> (C<myapp>),
+appended with a C<.conf> extension (C<myapp.conf>).
+
+  $ mkdir myapp
+  $ cd myapp
+  $ touch myapp.pl
+  $ chmod 744 myapp.pl
+  $ echo '{name => "my Mojolicious application"};' > myapp.conf
+
+Configuration files themselves are just Perl scripts that return a hash
+reference with configuration settings of your choice. All those settings are
+then available through the method L<Mojolicious/"config"> and the helper
+L<Mojolicious::Plugin::DefaultHelpers/"config">.
+
+  use Mojolicious::Lite;
+
+  plugin 'Config';
+
+  my $name = app->config('name');
+  app->log->debug("Welcome to $name");
+
+  get '/' => 'with_config';
+
+  app->start;
+  __DATA__
+  @@ with_config.html.ep
+  <!DOCTYPE html>
+  <html>
+    <head><title><%= config 'name' %></title></head>
+    <body>Welcome to <%= config 'name' %></body>
+  </html>
+
+Alternatively you can also use configuration files in the JSON format with
+L<Mojolicious::Plugin::JSONConfig>.
+
+=head2 Adding a plugin to your application
+
+To organize your code better and to prevent helpers from cluttering your
+application, you can use application specific plugins.
+
+  $ mkdir -p lib/MyApp/Plugin
+  $ touch lib/MyApp/Plugin/MyHelpers.pm
+
+They work just like normal plugins and are also subclasses of
+L<Mojolicious::Plugin>. Nested helpers with a prefix based on the plugin name
+are an easy way to avoid conflicts.
+
+  package MyApp::Plugin::MyHelpers;
+  use Mojo::Base 'Mojolicious::Plugin';
+
+  sub register {
+    my ($self, $app) = @_;
+    $app->helper('my_helpers.render_with_header' => sub {
+      my ($c, @args) = @_;
+      $c->res->headers->header('X-Mojo' => 'I <3 Mojolicious!');
+      $c->render(@args);
+    });
+  }
+
+  1;
+
+You can have as many application specific plugins as you like, the only
+difference to normal plugins is that you load them using their full class name.
+
+  use Mojolicious::Lite;
+
+  use lib 'lib';
+
+  plugin 'MyApp::Plugin::MyHelpers';
+
+  get '/' => sub {
+    my $c = shift;
+    $c->my_helpers->render_with_header(text => 'I â¥ Mojolicious!');
+  };
+
+  app->start;
+
+Of course these plugins can contain more than just helpers, take a look at
+L<Mojolicious::Plugins/"PLUGINS"> for a few ideas.
+
+=head2 Adding commands to Mojolicious
+
+By now you've probably used many of the built-in commands described in
+L<Mojolicious::Commands>, but did you know that you can just add new ones and
+that they will be picked up automatically by the command line interface if they
+are placed in a directory from C<@INC>?
+
+  package Mojolicious::Command::spy;
+  use Mojo::Base 'Mojolicious::Command';
+
+  has description => 'Spy on application';
+  has usage       => "Usage: APPLICATION spy [TARGET]\n";
+
+  sub run {
+    my ($self, @args) = @_;
+
+    # Leak secret passphrases
+    if ($args[0] eq 'secrets') { say for @{$self->app->secrets} }
+
+    # Leak mode
+    elsif ($args[0] eq 'mode') { say $self->app->mode }
+  }
+
+  1;
+
+Command line arguments are passed right through and there are many useful
+attributes and methods in L<Mojolicious::Command> that you can use or overload.
+
+  $ mojo spy secrets
+  HelloWorld
+
+  $ ./script/myapp spy secrets
+  secr3t
+
+And to make your commands application specific, just add a custom namespace to
+L<Mojolicious::Commands/"namespaces"> and use a class name like
+C<MyApp::Command::spy> instead of C<Mojolicious::Command::spy>.
+
+  # Application
+  package MyApp;
+  use Mojo::Base 'Mojolicious';
+
+  sub startup {
+    my $self = shift;
+
+    # Add another namespace to load commands from
+    push @{$self->commands->namespaces}, 'MyApp::Command';
+  }
+
+  1;
+
+The options C<-h>/C<--help>, C<--home> and C<-m>/C<--mode> are handled
+automatically by L<Mojolicious::Commands> and are shared by all commands.
+
+  $ ./script/myapp spy -m production mode
+  production
+
+For a full list of shared options see L<Mojolicious::Commands/"SYNOPSIS">.
+
+=head2 Running code against your application
+
+Ever thought about running a quick one-liner against your L<Mojolicious>
+application to test something? Thanks to the command
+L<Mojolicious::Command::eval> you can do just that, the application object
+itself can be accessed via C<app>.
+
+  $ mojo generate lite_app myapp.pl
+  $ ./myapp.pl eval 'say for @{app->static->paths}'
+  $ ./myapp.pl eval 'say for sort keys %{app->renderer->helpers}'
+
+The C<verbose> options will automatically print the return value or returned
+data structure to C<STDOUT>.
+
+  $ ./myapp.pl eval -v 'app->static->paths->[0]'
+  $ ./myapp.pl eval -V 'app->static->paths'
+
+=head2 Making your application installable
+
+Ever thought about releasing your L<Mojolicious> application to CPAN? It's
+actually much easier than you might think.
+
+  $ mojo generate app MyApp
+  $ cd my_app
+  $ mv public lib/MyApp/
+  $ mv templates lib/MyApp/
+
+The trick is to move the C<public> and C<templates> directories so they can get
+automatically installed with the modules.
+
+  # Application
+  package MyApp;
+  use Mojo::Base 'Mojolicious';
+
+  use Mojo::File 'path';
+  use Mojo::Home;
+
+  # Every CPAN module needs a version
+  our $VERSION = '1.0';
+
+  sub startup {
+    my $self = shift;
+
+    # Switch to installable home directory
+    $self->home(Mojo::Home->new(path(__FILE__)->sibling('MyApp')));
+
+    # Switch to installable "public" directory
+    $self->static->paths->[0] = $self->home->child('public');
+
+    # Switch to installable "templates" directory
+    $self->renderer->paths->[0] = $self->home->child('templates');
+
+    $self->plugin('PODRenderer');
+
+    my $r = $self->routes;
+    $r->get('/welcome')->to('example#welcome');
+  }
+
+  1;
+
+Finally there is just one small change to be made to the application script. The
+shebang line becomes the recommended C<#!perl>, which the toolchain can rewrite
+to the proper shebang during installation.
+
+  #!perl
+
+  use strict;
+  use warnings;
+
+  use FindBin;
+  BEGIN { unshift @INC, "$FindBin::Bin/../lib" }
+  use Mojolicious::Commands;
+
+  # Start command line interface for application
+  Mojolicious::Commands->start_app('MyApp');
+
+That's really everything, now you can package your application like any other
+CPAN module.
+
+  $ ./script/my_app generate makefile
+  $ perl Makefile.PL
+  $ make test
+  $ make manifest
+  $ make dist
+
+And if you have a PAUSE account (which can be requested at
+L<http://pause.perl.org>) even upload it.
+
+  $ mojo cpanify -u USER -p PASS MyApp-0.01.tar.gz
+
+=head2 Hello World
+
+If every byte matters this is the smallest C<Hello World> application you can
+write with L<Mojolicious::Lite>.
+
+  use Mojolicious::Lite;
+  any {text => 'Hello World!'};
+  app->start;
+
+It works because all routes without a pattern default to C</> and automatic
+rendering kicks in even if no actual code gets executed by the router. The
+renderer just picks up the C<text> value from the stash and generates a
+response.
+
+=head2 Hello World one-liners
+
+The C<Hello World> example above can get even a little bit shorter in an L<ojo>
+one-liner.
+
+  $ perl -Mojo -E 'a({text => "Hello World!"})->start' daemon
+
+And you can use all the commands from L<Mojolicious::Commands>.
+
+  $ perl -Mojo -E 'a({text => "Hello World!"})->start' get -v /
+
+=head1 MORE
+
+You can continue with L<Mojolicious::Guides> now or take a look at the
+L<Mojolicious wiki|http://github.com/kraih/mojo/wiki>, which contains a lot more
+documentation and examples by many different authors.
+
+=head1 SUPPORT
+
+If you have any questions the documentation might not yet answer, don't
+hesitate to ask on the
+L<mailing list|http://groups.google.com/group/mojolicious> or the official IRC
+channel C<#mojo> on C<irc.perl.org>
+(L<chat now!|https://chat.mibbit.com/?channel=%23mojo&server=irc.perl.org>).
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Guides/FAQ.pod
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Guides/FAQ.pod	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Guides/FAQ.pod	(revision 32205)
@@ -0,0 +1,264 @@
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Guides::FAQ - Frequently Asked Questions
+
+=head1 OVERVIEW
+
+This document contains answers for the most frequently asked questions about
+L<Mojolicious>.
+
+=head1 QUESTIONS
+
+We hope these answers are to your satisfaction.
+
+=head2 How does Mojolicious compare to other Perl web frameworks?
+
+The short answer is "it doesn't", because we interpret the term "web framework"
+much more literally than others. With the emergence of the real-time web and
+new technologies such as WebSockets, we are facing new challenges that go way
+beyond what commonly used modules like L<LWP> were designed for. Because of
+this, L<Mojolicious> contains a whole new HTTP client/server stack called
+L<Mojo>, which was heavily inspired by the original LWPng effort and carefully
+designed with these new requirements in mind. So while some of the higher
+abstraction layers might look similar to other web frameworks, it is more of a
+web toolkit and can even be used as the foundation for more advanced web
+frameworks.
+
+=head2 Why doesn't Mojolicious have any dependencies?
+
+We are optimizing L<Mojolicious> for user-friendliness and development speed,
+without compromises. While there are no rules in
+L<Mojolicious::Guides::Contributing> that forbid dependencies, we do currently
+discourage adding non-optional ones in favor of a faster and more painless
+installation process. And we do in fact already use several optional CPAN
+modules such as L<EV>, L<IO::Socket::Socks>, L<IO::Socket::SSL>,
+L<Net::DNS::Native>, L<Plack> and L<Role::Tiny> to provide advanced
+functionality if possible.
+
+=head2 Why reinvent wheels?
+
+Because we can make them rounder. Components specifically designed for
+user-friendliness and development speed are not easy to come by. We are strong
+believers of the Perl mantra "There is more than one way to do it", and our
+quest is to develop the best possible solutions for these two criteria.
+
+=head2 What about backwards compatibility?
+
+In conformance with L<Mojolicious::Guides::Contributing>, we will always
+deprecate a feature for 3 months, before removing or changing it in
+incompatible ways between major releases. New features can however be marked as
+experimental to explicitly exclude them from these rules. This gives us the
+necessary freedom to ensure a healthy future for L<Mojolicious>. So, as long as
+you are not using anything marked experimental, untested or undocumented, you
+can always count on backwards compatibility, everything else would be
+considered a bug.
+
+=head2 Why not split up Mojolicious into many smaller distributions?
+
+Because there are no advantages, it drastically increases maintenance costs and
+installation times without giving us anything in return. It would only make
+sense if we wanted to pass ownership of a module to a new maintainer, which we
+already have done in the past.
+
+=head2 Where can i discuss my patches for Mojolicious?
+
+We'd love to discuss your contributions to L<Mojolicious> on our official IRC
+channel C<#mojo> on C<irc.perl.org>
+(L<chat now!|https://chat.mibbit.com/?channel=%23mojo&server=irc.perl.org>).
+
+=head2 Which versions of Perl are supported by Mojolicious?
+
+First of all, you need to be aware that according to the L<perlpolicy>, only
+the two most recent stable release series of Perl are supported by the
+community and receive bug fixes, which are currently 5.26.x and 5.24.x.
+L<Mojolicious> follows this model and fully supports these two release series.
+In addition we will also keep the distribution installable up to a certain
+legacy version that we deem worthy of supporting, but not specifically optimize
+for it, this is currently 5.10.1.
+
+=head2 How well is Windows supported by Mojolicious?
+
+Windows is not officially supported by L<Mojolicious>, even though we try to
+keep the distribution installable. There may be serious security and/or
+reliability issues. Some of the more advanced features, such as
+L<subprocesses|Mojo::IOLoop/"subprocess"> and the
+L<Hypnotoad|Mojo::Server::Hypnotoad> web server, will also require the use of
+the L<Windows Subsystem for Linux|https://msdn.microsoft.com/commandline/wsl/>.
+
+=head2 Do I need to clean my environment before testing Mojolicious?
+
+L<Mojolicious> uses many environment variables both internally and externally,
+notably (but not exclusively) those starting with the prefix C<MOJO_*> and
+C<PLACK_ENV>. The test suite expects a clean environment; testing with a
+non-standard environment is unsupported and is unlikely to succeed. Therefore
+when installing or upgrading L<Mojolicious> and when running its tests, we
+highly recommend using an environment which does not set these variables.
+
+=head2 Where did my file extension go?
+
+Standard route placeholders will not match the C<.> character, however
+L<Mojolicious> routes automatically take file extensions like C<.html>, remove
+the leading C<.>, and store the result in the C<format> stash value. This can
+be useful for URL-based content negotiation, such as automatically rendering
+different templates based on the file extension. See
+L<Mojolicious::Guides::Routing/"Formats"> for information on customizing format
+detection, or consider using
+L<relaxed placeholders|Mojolicious::Guides::Routing/"Relaxed placeholders"> to
+allow matching of the C<.> character.
+
+=head2 Can I configure Hypnotoad from the command line?
+
+No, you can't, L<Hypnotoad|Mojo::Server::Hypnotoad> is a bit special in this
+regard. Because when you initiate a zero downtime software upgrade (hot
+deployment), you are only really sending a C<USR2> signal to the already running
+server, and no other information can be passed along. What you can do instead,
+is to use a L<Mojolicious::Plugin::Config> or L<Mojolicious::Plugin::JSONConfig>
+configuration file.
+
+  # myapp.conf
+  {
+    hypnotoad => {
+      listen  => ['http://*:8080'],
+      workers => 10
+    }
+  };
+
+Or if you don't actually need zero downtime software upgrades, just use
+L<Mojolicious::Command::prefork> instead, which is otherwise almost identical to
+Hypnotoad.
+
+  $ ./myapp.pl prefork -m production -l http://*:8080 -w 10
+
+=head2 What does the error "...certificate verify failed" mean?
+
+There are many variations of this error, but most of them mean that TLS
+certificate verification in L<Mojo::UserAgent> failed. This usually happens for
+two reasons. The most common one is that the peer certificate is simply invalid.
+If that's the case and you are certain that no MITM attack is being attempted,
+you can use the attribute L<Mojo::UserAgent/"insecure"> or C<MOJO_INSECURE>
+environment variable to disable certificate verification. And if that's not the
+case you might be missing the L<Mozilla::CA> module, which is often required by
+L<IO::Socket::SSL> to be able to verify certificates.
+
+=head2 What does the error "Maximum message size exceeded" mean?
+
+To protect your applications from excessively large requests and responses, our
+HTTP parser has a cap after which it will automatically stop accepting new
+data, and in most cases force the connection to be closed. The limit is 16MiB
+for requests, and 2GiB for responses by default. You can use the attributes
+L<Mojolicious/"max_request_size"> and L<Mojo::UserAgent/"max_response_size"> to
+change these values.
+
+=head2 What does the error "Maximum start-line size exceeded" mean?
+
+This is a very similar protection mechanism to the one described in the
+previous answer, but a little more specific. It limits the maximum length of
+the start-line for HTTP requests and responses. The limit is 8KiB by default,
+you can use the attribute L<Mojo::Message/"max_line_size"> or
+C<MOJO_MAX_LINE_SIZE> environment variable to change this value.
+
+=head2 What does the error "Maximum header size exceeded" mean?
+
+Almost the same as the previous answer, but this protection mechanism limits
+the number and maximum length of HTTP request and response headers. The limits
+are 100 headers with 8KiB each by default, you can use the attributes
+L<Mojo::Headers/"max_lines"> and L<Mojo::Headers/"max_line_size"> or the
+C<MOJO_MAX_LINES> and C<MOJO_MAX_LINE_SIZE> environment variables to change
+these values.
+
+=head2 What does the error "Maximum buffer size exceeded" mean?
+
+This protection mechanism limits how much content the HTTP parser is allowed to
+buffer when parsing chunked, compressed and multipart messages. The limit is
+around 256KiB by default, you can use the attribute
+L<Mojo::Content/"max_buffer_size"> or C<MOJO_MAX_BUFFER_SIZE> environment
+variable to change this value.
+
+=head2 What does "Your secret passphrase needs to be changed" mean?
+
+L<Mojolicious> uses secret passphrases for security features such as signed
+cookies. It defaults to using L<Mojolicious/"moniker">, which is not very
+secure, so we added this log message as a reminder. You can change the
+passphrase with the attribute L<Mojolicious/"secrets">. Since some plugins also
+depend on it, you should try changing it as early as possible in your
+application.
+
+  $app->secrets(['My very secret passphrase.']);
+
+=head2 What does "Nothing has been rendered, expecting delayed response" mean?
+
+L<Mojolicious> has been designed from the ground up for non-blocking I/O and
+event loops. So when a new request comes in and no response is generated right
+away, it will assume that this was intentional and return control to the web
+server, which can then handle other requests while waiting for events such as
+timers to finally generate a response.
+
+=head2 What does "Inactivity timeout" mean?
+
+To protect your applications from denial-of-service attacks, all connections
+have an inactivity timeout which limits how long a connection may be inactive
+before being closed automatically. It defaults to C<20> seconds for the user
+agent and C<15> seconds for all built-in web servers, and can be changed with
+the attributes L<Mojo::UserAgent/"inactivity_timeout"> and
+L<Mojo::Server::Daemon/"inactivity_timeout"> or the C<MOJO_INACTIVITY_TIMEOUT>
+environment variable. In L<Mojolicious> applications you can also use the helper
+L<Mojolicious::Plugin::DefaultHelpers/"inactivity_timeout"> to change it on
+demand for each connection individually. This timeout always applies, so you
+might have to tweak it for applications that take a long time to process a
+request.
+
+=head2 What does "Premature connection close" mean?
+
+This error message is often related to the one above, and means that the web
+server closed the connection before the user agent could receive the whole
+response or that the user agent got destroyed, which forces all connections to
+be closed immediately.
+
+  # The variable $ua goes out of scope and gets destroyed too early
+  Mojo::IOLoop->timer(5 => sub {
+    my $ua = Mojo::UserAgent->new;
+    $ua->get('https://mojolicious.org' => sub {
+      my ($ua, $tx) = @_;
+      say $tx->result->dom->at('title')->text;
+    });
+  });
+
+=head2 What does "Worker 31842 has no heartbeat (30 seconds), restarting" mean?
+
+As long as they are accepting new connections, worker processes of all built-in
+pre-forking web servers send heartbeat messages to the manager process at
+regular intervals, to signal that they are still responsive. A blocking
+operation such as an infinite loop in your application can prevent this, and
+will force the affected worker to be restarted after a timeout. This timeout
+defaults to C<30> seconds and can be extended with the attribute
+L<Mojo::Server::Prefork/"heartbeat_timeout"> if your application requires it.
+
+=head2 What does "Connection already closed" mean?
+
+This error message usually appears after waiting for the results of a
+non-blocking operation for longer periods of time, because the underlying
+connection has been closed in the meantime and the value of the attribute
+L<Mojolicious::Controller/"tx"> is no longer available. While there might not be
+a way to prevent the connection from getting closed, you can also avoid this
+error message by keeping a reference to the transaction object that is not
+weakened. The helper L<Mojolicious::Plugin::DefaultHelpers/"delay"> will do this
+automatically for you.
+
+=head1 MORE
+
+You can continue with L<Mojolicious::Guides> now or take a look at the
+L<Mojolicious wiki|http://github.com/kraih/mojo/wiki>, which contains a lot more
+documentation and examples by many different authors.
+
+=head1 SUPPORT
+
+If you have any questions the documentation might not yet answer, don't
+hesitate to ask on the
+L<mailing list|http://groups.google.com/group/mojolicious> or the official IRC
+channel C<#mojo> on C<irc.perl.org>
+(L<chat now!|https://chat.mibbit.com/?channel=%23mojo&server=irc.perl.org>).
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Guides/Growing.pod
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Guides/Growing.pod	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Guides/Growing.pod	(revision 32205)
@@ -0,0 +1,835 @@
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Guides::Growing - Growing Mojolicious applications
+
+=head1 OVERVIEW
+
+This document explains the process of starting a L<Mojolicious::Lite> prototype
+from scratch and growing it into a well-structured L<Mojolicious> application.
+
+=head1 CONCEPTS
+
+Essentials every L<Mojolicious> developer should know.
+
+=head2 Model View Controller
+
+MVC is a software architectural pattern for graphical user interface
+programming originating in Smalltalk-80, that separates application logic,
+presentation and input.
+
+           +------------+    +-------+    +------+
+  Input -> | Controller | -> | Model | -> | View | -> Output
+           +------------+    +-------+    +------+
+
+A slightly modified version of the pattern moving some application logic into
+the I<controller> is the foundation of pretty much every web framework these
+days, including L<Mojolicious>.
+
+              +----------------+     +-------+
+  Request  -> |                | <-> | Model |
+              |                |     +-------+
+              |   Controller   |
+              |                |     +-------+
+  Response <- |                | <-> | View  |
+              +----------------+     +-------+
+
+The I<controller> receives a request from a user, passes incoming data to the
+I<model> and retrieves data from it, which then gets turned into an actual
+response by the I<view>. But note that this pattern is just a guideline that
+most of the time results in cleaner more maintainable code, not a rule that
+should be followed at all costs.
+
+=head2 REpresentational State Transfer
+
+REST is a software architectural style for distributed hypermedia systems such
+as the web. While it can be applied to many protocols it is most commonly used
+with HTTP these days. In REST terms, when you are opening a URL like
+C<http://mojolicious.org/foo> with your browser, you are basically asking the
+web server for the HTML I<representation> of the C<http://mojolicious.org/foo>
+I<resource>.
+
+  +--------+                                  +--------+
+  |        | -> http://mojolicious.org/foo -> |        |
+  | Client |                                  | Server |
+  |        | <-  <html>Mojo rocks!</html>  <- |        |
+  +--------+                                  +--------+
+
+The fundamental idea here is that all resources are uniquely addressable with
+URLs and every resource can have different representations such as HTML, RSS or
+JSON. User interface concerns are separated from data storage concerns and all
+session state is kept client-side.
+
+  +---------+                        +------------+
+  |         | ->    PUT /foo      -> |            |
+  |         | ->    Hello World!  -> |            |
+  |         |                        |            |
+  |         | <-    201 CREATED   <- |            |
+  |         |                        |            |
+  |         | ->    GET /foo      -> |            |
+  | Browser |                        | Web Server |
+  |         | <-    200 OK        <- |            |
+  |         | <-    Hello World!  <- |            |
+  |         |                        |            |
+  |         | ->    DELETE /foo   -> |            |
+  |         |                        |            |
+  |         | <-    200 OK        <- |            |
+  +---------+                        +------------+
+
+While HTTP methods such as C<PUT>, C<GET> and C<DELETE> are not directly part
+of REST they go very well with it and are commonly used to manipulate
+I<resources>.
+
+=head2 Sessions
+
+HTTP was designed as a stateless protocol, web servers don't know anything
+about previous requests, which makes user-friendly login systems very tricky.
+Sessions solve this problem by allowing web applications to keep stateful
+information across several HTTP requests.
+
+  GET /login?user=sebastian&pass=s3cret HTTP/1.1
+  Host: mojolicious.org
+
+  HTTP/1.1 200 OK
+  Set-Cookie: sessionid=987654321
+  Content-Length: 10
+  Hello sebastian.
+
+  GET /protected HTTP/1.1
+  Host: mojolicious.org
+  Cookie: sessionid=987654321
+
+  HTTP/1.1 200 OK
+  Set-Cookie: sessionid=987654321
+  Content-Length: 16
+  Hello again sebastian.
+
+Traditionally all session data was stored on the server-side and only session
+ids were exchanged between browser and web server in the form of cookies.
+
+  Set-Cookie: session=hmac-sha1(base64(json($session)))
+
+In L<Mojolicious> however we are taking this concept one step further by
+storing everything JSON serialized and Base64 encoded in HMAC-SHA1 signed
+cookies, which is more compatible with the REST philosophy and reduces
+infrastructure requirements.
+
+=head2 Test-Driven Development
+
+TDD is a software development process where the developer starts writing
+failing test cases that define the desired functionality and then moves on to
+producing code that passes these tests. There are many advantages such as
+always having good test coverage and code being designed for testability, which
+will in turn often prevent future changes from breaking old code. Much of
+L<Mojolicious> was developed using TDD.
+
+=head1 PROTOTYPE
+
+One of the main differences between L<Mojolicious> and other web frameworks is
+that it also includes L<Mojolicious::Lite>, a micro web framework optimized for
+rapid prototyping.
+
+=head2 Differences
+
+You likely know the feeling, you've got a really cool idea and want to try it
+as quickly as possible, that's exactly why L<Mojolicious::Lite> applications
+don't need more than a single file.
+
+  myapp.pl   # Templates and even static files can be inlined
+
+Full L<Mojolicious> applications on the other hand are much closer to a well
+organized CPAN distribution to maximize maintainability.
+
+  myapp                      # Application directory
+  |- script                  # Script directory
+  |  +- my_app               # Application script
+  |- lib                     # Library directory
+  |  |- MyApp.pm             # Application class
+  |  +- MyApp                # Application namespace
+  |     +- Controller        # Controller namespace
+  |        +- Example.pm     # Controller class
+  |- my_app.conf             # Configuration file
+  |- t                       # Test directory
+  |  +- basic.t              # Random test
+  |- log                     # Log directory
+  |  +- development.log      # Development mode log file
+  |- public                  # Static file directory (served automatically)
+  |  +- index.html           # Static HTML file
+  +- templates               # Template directory
+     |- layouts              # Template directory for layouts
+     |  +- default.html.ep   # Layout template
+     +- example              # Template directory for "Example" controller
+        +- welcome.html.ep   # Template for "welcome" action
+
+Both application skeletons can be automatically generated with the commands
+L<Mojolicious::Command::generate::lite_app> and
+L<Mojolicious::Command::generate::app>.
+
+  $ mojo generate lite_app myapp.pl
+  $ mojo generate app MyApp
+
+Feature-wise both are almost equal, the only real differences are
+organizational, so each one can be gradually transformed into the other.
+
+=head2 Foundation
+
+We start our new application with a single executable Perl script.
+
+  $ mkdir myapp
+  $ cd myapp
+  $ touch myapp.pl
+  $ chmod 744 myapp.pl
+
+This will be the foundation for our login manager example application.
+
+  #!/usr/bin/env perl
+  use Mojolicious::Lite;
+
+  get '/' => sub {
+    my $c = shift;
+    $c->render(text => 'Hello World!');
+  };
+
+  app->start;
+
+The built-in development web server makes working on your application a lot of
+fun thanks to automatic reloading.
+
+  $ morbo ./myapp.pl
+  Server available at http://127.0.0.1:3000
+
+Just save your changes and they will be automatically in effect the next time
+you refresh your browser.
+
+=head2 A bird's-eye view
+
+It all starts with an HTTP request like this, sent by your browser.
+
+  GET / HTTP/1.1
+  Host: localhost:3000
+
+Once the request has been received by the web server through the event loop, it
+will be passed on to L<Mojolicious>, where it will be handled in a few simple
+steps.
+
+=over 2
+
+=item  1.
+
+Check if a static file exists that would meet the requirements.
+
+=item 2.
+
+Try to find a route that would meet the requirements.
+
+=item 3.
+
+Dispatch the request to this route, usually reaching one or more actions.
+
+=item 4.
+
+Process the request, maybe generating a response with the renderer.
+
+=item 5.
+
+Return control to the web server, and if no response has been generated yet,
+wait for a non-blocking operation to do so through the event loop.
+
+=back
+
+With our application the router would have found an action in step 2, and
+rendered some text in step 4, resulting in an HTTP response like this being
+sent back to the browser.
+
+  HTTP/1.1 200 OK
+  Content-Length: 12
+  Hello World!
+
+=head2 Model
+
+In L<Mojolicious> we consider web applications simple frontends for existing
+business logic, that means L<Mojolicious> is by design entirely I<model> layer
+agnostic and you just use whatever Perl modules you like most.
+
+  $ mkdir -p lib/MyApp/Model
+  $ touch lib/MyApp/Model/Users.pm
+  $ chmod 644 lib/MyApp/Model/Users.pm
+
+Our login manager will simply use a plain old Perl module abstracting away all
+logic related to matching usernames and passwords. The name
+C<MyApp::Model::Users> is an arbitrary choice, and is simply used to make the
+separation of concerns more visible.
+
+  package MyApp::Model::Users;
+
+  use strict;
+  use warnings;
+
+  use Mojo::Util 'secure_compare';
+
+  my $USERS = {
+    joel      => 'las3rs',
+    marcus    => 'lulz',
+    sebastian => 'secr3t'
+  };
+
+  sub new { bless {}, shift }
+
+  sub check {
+    my ($self, $user, $pass) = @_;
+
+    # Success
+    return 1 if $USERS->{$user} && secure_compare $USERS->{$user}, $pass;
+
+    # Fail
+    return undef;
+  }
+
+  1;
+
+A simple helper can be registered with the function
+L<Mojolicious::Lite/"helper"> to make our model available to all actions and
+templates.
+
+  #!/usr/bin/env perl
+  use Mojolicious::Lite;
+
+  use lib 'lib';
+  use MyApp::Model::Users;
+
+  # Helper to lazy initialize and store our model object
+  helper users => sub { state $users = MyApp::Model::Users->new };
+
+  # /?user=sebastian&pass=secr3t
+  any '/' => sub {
+    my $c = shift;
+
+    # Query parameters
+    my $user = $c->param('user') || '';
+    my $pass = $c->param('pass') || '';
+
+    # Check password
+    return $c->render(text => "Welcome $user.")
+      if $c->users->check($user, $pass);
+
+    # Failed
+    $c->render(text => 'Wrong username or password.');
+  };
+
+  app->start;
+
+The method L<Mojolicious::Controller/"param"> is used to access query
+parameters, C<POST> parameters, file uploads and route placeholders, all at
+once.
+
+=head2 Testing
+
+In L<Mojolicious> we take testing very serious and try to make it a pleasant
+experience.
+
+  $ mkdir t
+  $ touch t/login.t
+  $ chmod 644 t/login.t
+
+L<Test::Mojo> is a scriptable HTTP user agent designed specifically for
+testing, with many fun state of the art features such as CSS selectors based on
+L<Mojo::DOM>.
+
+  use Test::More;
+  use Test::Mojo;
+
+  # Include application
+  use FindBin;
+  require "$FindBin::Bin/../myapp.pl";
+
+  # Allow 302 redirect responses
+  my $t = Test::Mojo->new;
+  $t->ua->max_redirects(1);
+
+  # Test if the HTML login form exists
+  $t->get_ok('/')
+    ->status_is(200)
+    ->element_exists('form input[name="user"]')
+    ->element_exists('form input[name="pass"]')
+    ->element_exists('form input[type="submit"]');
+
+  # Test login with valid credentials
+  $t->post_ok('/' => form => {user => 'sebastian', pass => 'secr3t'})
+    ->status_is(200)
+    ->text_like('html body' => qr/Welcome sebastian/);
+
+  # Test accessing a protected page
+  $t->get_ok('/protected')->status_is(200)->text_like('a' => qr/Logout/);
+
+  # Test if HTML login form shows up again after logout
+  $t->get_ok('/logout')
+    ->status_is(200)
+    ->element_exists('form input[name="user"]')
+    ->element_exists('form input[name="pass"]')
+    ->element_exists('form input[type="submit"]');
+
+  done_testing();
+
+Your application won't pass these tests, but from now on you can use them to
+check your progress with the command L<Mojolicious::Command::test>.
+
+  $ ./myapp.pl test
+  $ ./myapp.pl test t/login.t
+  $ ./myapp.pl test -v t/login.t
+
+Or perform quick requests right from the command line with
+L<Mojolicious::Command::get>.
+
+  $ ./myapp.pl get /
+  Wrong username or password.
+
+  $ ./myapp.pl get -v '/?user=sebastian&pass=secr3t'
+  GET /?user=sebastian&pass=secr3t HTTP/1.1
+  User-Agent: Mojolicious (Perl)
+  Accept-Encoding: gzip
+  Content-Length: 0
+  Host: localhost:59472
+
+  HTTP/1.1 200 OK
+  Date: Sun, 18 Jul 2010 13:09:58 GMT
+  Server: Mojolicious (Perl)
+  Content-Length: 12
+  Content-Type: text/plain
+
+  Welcome sebastian.
+
+=head2 State keeping
+
+Sessions in L<Mojolicious> pretty much just work out of the box once you start
+using the method L<Mojolicious::Controller/"session">, there is no setup
+required, but we suggest setting a more secure passphrase with
+L<Mojolicious/"secrets">.
+
+  $app->secrets(['Mojolicious rocks']);
+
+This passphrase is used by the HMAC-SHA1 algorithm to make signed cookies tamper
+resistant and can be changed at any time to invalidate all existing sessions.
+
+  $c->session(user => 'sebastian');
+  my $user = $c->session('user');
+
+By default all sessions expire after one hour, for more control you can use the
+C<expiration> session value to set an expiration date in seconds from now.
+
+  $c->session(expiration => 3600);
+
+And the whole session can be deleted by using the C<expires> session value to
+set an absolute expiration date in the past.
+
+  $c->session(expires => 1);
+
+For data that should only be visible on the next request, like a confirmation
+message after a C<302> redirect performed with
+L<Mojolicious::Controller/"redirect_to">, you can use the flash, accessible
+through the method L<Mojolicious::Controller/"flash">.
+
+  $c->flash(message => 'Everything is fine.');
+  $c->redirect_to('goodbye');
+
+Just remember that all session data gets serialized with L<Mojo::JSON> and
+stored in HMAC-SHA1 signed cookies, which usually have a C<4096> byte (4KiB)
+limit, depending on browser.
+
+=head2 Final prototype
+
+A final C<myapp.pl> prototype passing all of the tests above could look like
+this.
+
+  #!/usr/bin/env perl
+  use Mojolicious::Lite;
+
+  use lib 'lib';
+  use MyApp::Model::Users;
+
+  # Make signed cookies tamper resistant
+  app->secrets(['Mojolicious rocks']);
+
+  helper users => sub { state $users = MyApp::Model::Users->new };
+
+  # Main login action
+  any '/' => sub {
+    my $c = shift;
+
+    # Query or POST parameters
+    my $user = $c->param('user') || '';
+    my $pass = $c->param('pass') || '';
+
+    # Check password and render "index.html.ep" if necessary
+    return $c->render unless $c->users->check($user, $pass);
+
+    # Store username in session
+    $c->session(user => $user);
+
+    # Store a friendly message for the next page in flash
+    $c->flash(message => 'Thanks for logging in.');
+
+    # Redirect to protected page with a 302 response
+    $c->redirect_to('protected');
+  } => 'index';
+
+  # Make sure user is logged in for actions in this group
+  group {
+    under sub {
+      my $c = shift;
+
+      # Redirect to main page with a 302 response if user is not logged in
+      return 1 if $c->session('user');
+      $c->redirect_to('index');
+      return undef;
+    };
+
+    # A protected page auto rendering "protected.html.ep"
+    get '/protected';
+  };
+
+  # Logout action
+  get '/logout' => sub {
+    my $c = shift;
+
+    # Expire and in turn clear session automatically
+    $c->session(expires => 1);
+
+    # Redirect to main page with a 302 response
+    $c->redirect_to('index');
+  };
+
+  app->start;
+  __DATA__
+
+  @@ index.html.ep
+  % layout 'default';
+  %= form_for index => begin
+    % if (param 'user') {
+      <b>Wrong name or password, please try again.</b><br>
+    % }
+    Name:<br>
+    %= text_field 'user'
+    <br>Password:<br>
+    %= password_field 'pass'
+    <br>
+    %= submit_button 'Login'
+  % end
+
+  @@ protected.html.ep
+  % layout 'default';
+  % if (my $msg = flash 'message') {
+    <b><%= $msg %></b><br>
+  % }
+  Welcome <%= session 'user' %>.<br>
+  %= link_to Logout => 'logout'
+
+  @@ layouts/default.html.ep
+  <!DOCTYPE html>
+  <html>
+    <head><title>Login Manager</title></head>
+    <body><%= content %></body>
+  </html>
+
+And the directory structure should be looking like this now.
+
+  myapp
+  |- myapp.pl
+  |- lib
+  |  +- MyApp
+  |     +- Model
+  |        +- Users.pm
+  +- t
+     +- login.t
+
+Our templates are using quite a few features of the renderer,
+L<Mojolicious::Guides::Rendering> explains them all in great detail.
+
+=head1 WELL-STRUCTURED APPLICATION
+
+Due to the flexibility of L<Mojolicious> there are many variations of the
+actual growing process, but this should give you a good overview of the
+possibilities.
+
+=head2 Inflating templates
+
+All templates and static files inlined in the C<DATA> section can be
+automatically turned into separate files in the C<templates> and C<public>
+directories with the command L<Mojolicious::Command::inflate>.
+
+  $ ./myapp.pl inflate
+
+Those directories have a higher precedence, so inflating can also be a great
+way to allow your users to customize their applications.
+
+=head2 Simplified application class
+
+This is the heart of every full L<Mojolicious> application and always gets
+instantiated during server startup.
+
+  $ touch lib/MyApp.pm
+  $ chmod 644 lib/MyApp.pm
+
+We will start by extracting all actions from C<myapp.pl> and turn them into
+simplified hybrid routes in the L<Mojolicious::Routes> router, none of the
+actual action code needs to be changed.
+
+  package MyApp;
+  use Mojo::Base 'Mojolicious';
+
+  use MyApp::Model::Users;
+
+  sub startup {
+    my $self = shift;
+
+    $self->secrets(['Mojolicious rocks']);
+    $self->helper(users => sub { state $users = MyApp::Model::Users->new });
+
+    my $r = $self->routes;
+
+    $r->any('/' => sub {
+      my $c = shift;
+
+      my $user = $c->param('user') || '';
+      my $pass = $c->param('pass') || '';
+      return $c->render unless $c->users->check($user, $pass);
+
+      $c->session(user => $user);
+      $c->flash(message => 'Thanks for logging in.');
+      $c->redirect_to('protected');
+    } => 'index');
+
+    my $logged_in = $r->under(sub {
+      my $c = shift;
+      return 1 if $c->session('user');
+      $c->redirect_to('index');
+      return undef;
+    });
+    $logged_in->get('/protected');
+
+    $r->get('/logout' => sub {
+      my $c = shift;
+      $c->session(expires => 1);
+      $c->redirect_to('index');
+    });
+  }
+
+  1;
+
+The C<startup> method gets called right after instantiation and is the place
+where the whole application gets set up. Since full L<Mojolicious> applications
+can use nested routes they have no need for C<group> blocks.
+
+=head2 Simplified application script
+
+C<myapp.pl> itself can now be turned into a simplified application script to
+allow running tests again.
+
+  #!/usr/bin/env perl
+
+  use strict;
+  use warnings;
+
+  use lib 'lib';
+  use Mojolicious::Commands;
+
+  # Start command line interface for application
+  Mojolicious::Commands->start_app('MyApp');
+
+And the directory structure of our hybrid application should be looking like
+this.
+
+  myapp
+  |- myapp.pl
+  |- lib
+  |  |- MyApp.pm
+  |  +- MyApp
+  |     +- Model
+  |        +- Users.pm
+  |- t
+  |  +- login.t
+  +- templates
+     |- layouts
+     |  +- default.html.ep
+     |- index.html.ep
+     +- protected.html.ep
+
+=head2 Controller class
+
+Hybrid routes are a nice intermediate step, but to maximize maintainability it
+makes sense to split our action code from its routing information.
+
+  $ mkdir lib/MyApp/Controller
+  $ touch lib/MyApp/Controller/Login.pm
+  $ chmod 644 lib/MyApp/Controller/Login.pm
+
+Once again the actual action code does not need to change, we just rename C<$c>
+to C<$self> since the controller is now the invocant.
+
+  package MyApp::Controller::Login;
+  use Mojo::Base 'Mojolicious::Controller';
+
+  sub index {
+    my $self = shift;
+
+    my $user = $self->param('user') || '';
+    my $pass = $self->param('pass') || '';
+    return $self->render unless $self->users->check($user, $pass);
+
+    $self->session(user => $user);
+    $self->flash(message => 'Thanks for logging in.');
+    $self->redirect_to('protected');
+  }
+
+  sub logged_in {
+    my $self = shift;
+    return 1 if $self->session('user');
+    $self->redirect_to('index');
+    return undef;
+  }
+
+  sub logout {
+    my $self = shift;
+    $self->session(expires => 1);
+    $self->redirect_to('index');
+  }
+
+  1;
+
+All L<Mojolicious::Controller> controllers are plain old Perl classes and get
+instantiated on demand.
+
+=head2 Application class
+
+The application class C<lib/MyApp.pm> can now be reduced to model and routing
+information.
+
+  package MyApp;
+  use Mojo::Base 'Mojolicious';
+
+  use MyApp::Model::Users;
+
+  sub startup {
+    my $self = shift;
+
+    $self->secrets(['Mojolicious rocks']);
+    $self->helper(users => sub { state $users = MyApp::Model::Users->new });
+
+    my $r = $self->routes;
+    $r->any('/')->to('login#index')->name('index');
+
+    my $logged_in = $r->under('/')->to('login#logged_in');
+    $logged_in->get('/protected')->to('login#protected');
+
+    $r->get('/logout')->to('login#logout');
+  }
+
+  1;
+
+The router allows many different route variations,
+L<Mojolicious::Guides::Routing> explains them all in great detail.
+
+=head2 Templates
+
+Templates are our views, and usually bound to controllers, so they need to be
+moved into the appropriate directories.
+
+  $ mkdir templates/login
+  $ mv templates/index.html.ep templates/login/index.html.ep
+  $ mv templates/protected.html.ep templates/login/protected.html.ep
+
+=head2 Script
+
+Finally C<myapp.pl> can be moved into a C<script> directory and renamed to
+C<my_app> to follow the CPAN standard.
+
+  $ mkdir script
+  $ mv myapp.pl script/my_app
+
+Just a few small details change, instead of L<lib> we now use L<FindBin> and
+C<@INC>, allowing us to start the application from outside its home directory.
+
+  #!/usr/bin/env perl
+
+  use strict;
+  use warnings;
+
+  use FindBin;
+  BEGIN { unshift @INC, "$FindBin::Bin/../lib" }
+  use Mojolicious::Commands;
+
+  # Start command line interface for application
+  Mojolicious::Commands->start_app('MyApp');
+
+=head2 Simplified tests
+
+Full L<Mojolicious> applications are a little easier to test, so C<t/login.t>
+can be simplified.
+
+  use Test::More;
+  use Test::Mojo;
+
+  # Load application class
+  my $t = Test::Mojo->new('MyApp');
+  $t->ua->max_redirects(1);
+
+  $t->get_ok('/')
+    ->status_is(200)
+    ->element_exists('form input[name="user"]')
+    ->element_exists('form input[name="pass"]')
+    ->element_exists('form input[type="submit"]');
+
+  $t->post_ok('/' => form => {user => 'sebastian', pass => 'secr3t'})
+    ->status_is(200)
+    ->text_like('html body' => qr/Welcome sebastian/);
+
+  $t->get_ok('/protected')->status_is(200)->text_like('a' => qr/Logout/);
+
+  $t->get_ok('/logout')
+    ->status_is(200)
+    ->element_exists('form input[name="user"]')
+    ->element_exists('form input[name="pass"]')
+    ->element_exists('form input[type="submit"]');
+
+  done_testing();
+
+And our final directory structure should be looking like this.
+
+  myapp
+  |- script
+  |  +- my_app
+  |- lib
+  |  |- MyApp.pm
+  |  +- MyApp
+  |     |- Controller
+  |     |  +- Login.pm
+  |     +- Model
+  |        +- Users.pm
+  |- t
+  |  +- login.t
+  +- templates
+     |- layouts
+     |  +- default.html.ep
+     +- login
+        |- index.html.ep
+        +- protected.html.ep
+
+Test-driven development takes a little getting used to, but can be a very
+powerful tool.
+
+=head1 MORE
+
+You can continue with L<Mojolicious::Guides> now or take a look at the
+L<Mojolicious wiki|http://github.com/kraih/mojo/wiki>, which contains a lot more
+documentation and examples by many different authors.
+
+=head1 SUPPORT
+
+If you have any questions the documentation might not yet answer, don't
+hesitate to ask on the
+L<mailing list|http://groups.google.com/group/mojolicious> or the official IRC
+channel C<#mojo> on C<irc.perl.org>
+(L<chat now!|https://chat.mibbit.com/?channel=%23mojo&server=irc.perl.org>).
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Guides/Rendering.pod
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Guides/Rendering.pod	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Guides/Rendering.pod	(revision 32205)
@@ -0,0 +1,1558 @@
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Guides::Rendering - Rendering content
+
+=head1 OVERVIEW
+
+This document explains content generation with the L<Mojolicious> renderer.
+
+=head1 CONCEPTS
+
+Essentials every L<Mojolicious> developer should know.
+
+=head2 Renderer
+
+The renderer is a tiny black box turning stash data into actual responses
+utilizing multiple template systems and data encoding modules.
+
+  {text => 'Hello.'}                 -> 200 OK, text/html, 'Hello.'
+  {json => {x => 3}}                 -> 200 OK, application/json, '{"x":3}'
+  {text => 'Oops.', status => '410'} -> 410 Gone, text/html, 'Oops.'
+
+Templates can be automatically detected if enough information is provided by
+the developer or routes. Template names are expected to follow the
+C<template.format.handler> scheme, with C<template> defaulting to
+C<controller/action> or the route name, C<format> defaulting to C<html> and
+C<handler> to C<ep>.
+
+  {controller => 'users', action => 'list'} -> 'users/list.html.ep'
+  {template => 'foo', format => 'txt'}      -> 'foo.txt.ep'
+  {template => 'foo', handler => 'epl'}     -> 'foo.html.epl'
+
+The C<controller> value gets converted from C<CamelCase> to C<snake_case> using
+L<Mojo::Util/"decamelize"> and C<-> characters replaced with C</>.
+
+  {controller => 'My::Users', action => 'add'} -> 'my/users/add.html.ep'
+  {controller => 'my-users', action => 'show'} -> 'my/users/show.html.ep'
+
+All templates should be in the C<templates> directories of the application,
+which can be customized with L<Mojolicious::Renderer/"paths">, or one of the
+the C<DATA> sections from L<Mojolicious::Renderer/"classes">.
+
+  __DATA__
+
+  @@ time.html.ep
+  % use Time::Piece;
+  % my $now = localtime;
+  <!DOCTYPE html>
+  <html>
+    <head><title>Time</title></head>
+    <body>The time is <%= $now->hms %>.</body>
+  </html>
+
+  @@ hello.txt.ep
+  ...
+
+The renderer can be easily extended to support additional template systems with
+plugins, but more about that later.
+
+=head2 Embedded Perl
+
+L<Mojolicious> includes a minimalistic but very powerful template system out of
+the box called Embedded Perl or C<ep> for short. It is based on
+L<Mojo::Template> and allows the embedding of Perl code right into actual
+content using a small set of special tags and line start characters. For all
+templates L<strict>, L<warnings>, L<utf8> and Perl 5.10 L<features|feature> are
+automatically enabled.
+
+  <% Perl code %>
+  <%= Perl expression, replaced with XML escaped result %>
+  <%== Perl expression, replaced with result %>
+  <%# Comment, useful for debugging %>
+  <%% Replaced with "<%", useful for generating templates %>
+  % Perl code line, treated as "<% line =%>" (explained later)
+  %= Perl expression line, treated as "<%= line %>"
+  %== Perl expression line, treated as "<%== line %>"
+  %# Comment line, useful for debugging
+  %% Replaced with "%", useful for generating templates
+
+Tags and lines work pretty much the same, but depending on context one will
+usually look a bit better. Semicolons get automatically appended to all
+expressions.
+
+  <% my $i = 10; %>
+  <ul>
+    <% for my $j (1 .. $i) { %>
+      <li>
+        <%= $j %>
+      </li>
+    <% } %>
+  </ul>
+
+  % my $i = 10;
+  <ul>
+    % for my $j (1 .. $i) {
+      <li>
+        %= $j
+      </li>
+    % }
+  </ul>
+
+Aside from differences in whitespace handling, both examples generate similar
+Perl code, a naive translation could look like this.
+
+  my $output = '';
+  my $i = 10;
+  $output .= '<ul>';
+  for my $j (1 .. $i) {
+    $output .= '<li>';
+    $output .= xml_escape scalar + $j;
+    $output .= '</li>';
+  }
+  $output .= '</ul>';
+  return $output;
+
+An additional equal sign can be used to disable escaping of the characters
+C<E<lt>>, C<E<gt>>, C<&>, C<'> and C<"> in results from Perl expressions, which
+is the default to prevent XSS attacks against your application.
+
+  <%= 'I â¥ Mojolicious!' %>
+  <%== '<p>I â¥ Mojolicious!</p>' %>
+
+Only L<Mojo::ByteStream> objects are excluded from automatic escaping.
+
+  <%= b('<p>I â¥ Mojolicious!</p>') %>
+
+Whitespace characters around tags can be trimmed by adding an additional equal
+sign to the end of a tag.
+
+  <% for (1 .. 3) { %>
+    <%= 'Trim all whitespace characters around this expression' =%>
+  <% } %>
+
+Newline characters can be escaped with a backslash.
+
+  This is <%= 1 + 1 %> a\
+  single line
+
+And a backslash in front of a newline character can be escaped with another
+backslash.
+
+  This will <%= 1 + 1 %> result\\
+  in multiple\\
+  lines
+
+A newline character gets appended automatically to every template, unless the
+last character is a backslash. And empty lines at the end of a template are
+ignored.
+
+  There is <%= 1 + 1 %> no newline at the end here\
+
+At the beginning of the template, stash values that don't have invalid
+characters in their name get automatically initialized as normal variables, and
+the controller object as both C<$self> and C<$c>.
+
+  $c->stash(name => 'tester');
+
+  Hello <%= $name %> from <%= $c->tx->remote_address %>.
+
+A prefix like C<myapp.*> is commonly used for stash values that you don't want
+to expose in templates.
+
+  $c->stash('myapp.name' => 'tester');
+
+There are also many helper functions available, but more about that later.
+
+  <%= dumper {foo => 'bar'} %>
+
+=head1 BASICS
+
+Most commonly used features every L<Mojolicious> developer should know about.
+
+=head2 Automatic rendering
+
+The renderer can be manually started by calling the method
+L<Mojolicious::Controller/"render">, but that's usually not necessary, because
+it will get automatically called if nothing has been rendered after the router
+finished its work. This also means you can have routes pointing only to
+templates without actual actions.
+
+  $c->render;
+
+There is one big difference though, by calling it manually you can make sure
+that templates use the current controller object, and not the default
+controller specified with the attribute L<Mojolicious/"controller_class">.
+
+  $c->render_later;
+
+You can also disable automatic rendering with the method
+L<Mojolicious::Controller/"render_later">, which can be very useful to delay
+rendering when a non-blocking operation has to be performed first.
+
+=head2 Rendering templates
+
+The renderer will always try to detect the right template, but you can also use
+the C<template> stash value to render a specific one. Everything before the
+last slash will be interpreted as the subdirectory path in which to find the
+template.
+
+  # foo/bar/baz.*.*
+  $c->render(template => 'foo/bar/baz');
+
+Choosing a specific C<format> and C<handler> is just as easy.
+
+  # foo/bar/baz.txt.epl
+  $c->render(template => 'foo/bar/baz', format => 'txt', handler => 'epl');
+
+Because rendering a specific template is the most common task it also has a
+shortcut.
+
+  $c->render('foo/bar/baz');
+
+If you're not sure in advance if a template actually exists, you can also use
+the method L<Mojolicious::Controller/"render_maybe"> to try multiple
+alternatives.
+
+  $c->render_maybe('localized/baz') or $c->render('foo/bar/baz');
+
+=head2 Rendering to strings
+
+Sometimes you might want to use the rendered result directly instead of
+generating a response, for example, to send emails, this can be done with
+L<Mojolicious::Controller/"render_to_string">.
+
+  my $html = $c->render_to_string('mail');
+
+No encoding will be performed, making it easy to reuse the result in other
+templates or to generate binary data.
+
+  my $pdf = $c->render_to_string('invoice', format => 'pdf');
+  $c->render(data => $pdf, format => 'pdf');
+
+All arguments passed will get localized automatically and are only available
+during this render operation.
+
+=head2 Template variants
+
+To make your application look great on many different devices you can also use
+the C<variant> stash value to choose between different variants of your
+templates.
+
+  # foo/bar/baz.html+phone.ep
+  # foo/bar/baz.html.ep
+  $c->render('foo/bar/baz', variant => 'phone');
+
+This can be done very liberally since it only applies when a template with the
+correct name actually exists and falls back to the generic one otherwise.
+
+=head2 Rendering inline templates
+
+Some renderers such as C<ep> allow templates to be passed C<inline>.
+
+  $c->render(inline => 'The result is <%= 1 + 1 %>.');
+
+Since auto-detection depends on a path you might have to supply a C<handler>
+too.
+
+  $c->render(inline => "<%= shift->param('foo') %>", handler => 'epl');
+
+=head2 Rendering text
+
+Characters can be rendered to bytes with the C<text> stash value, the given
+content will be automatically encoded with L<Mojolicious::Renderer/"encoding">.
+
+  $c->render(text => 'I â¥ Mojolicious!');
+
+=head2 Rendering data
+
+Bytes can be rendered with the C<data> stash value, no encoding will be
+performed.
+
+  $c->render(data => $bytes);
+
+=head2 Rendering JSON
+
+The C<json> stash value allows you to pass Perl data structures to the renderer
+which get directly encoded to JSON with L<Mojo::JSON>.
+
+  $c->render(json => {foo => [1, 'test', 3]});
+
+=head2 Status code
+
+Response status codes can be changed with the C<status> stash value.
+
+  $c->render(text => 'Oops.', status => 500);
+
+=head2 Content type
+
+The C<Content-Type> header of the response is actually based on the MIME type
+mapping of the C<format> stash value.
+
+  # Content-Type: text/plain
+  $c->render(text => 'Hello.', format => 'txt');
+
+  # Content-Type: image/png
+  $c->render(data => $bytes, format => 'png');
+
+These mappings can be easily extended or changed with L<Mojolicious/"types">.
+
+  # Add new MIME type
+  $app->types->type(md => 'text/markdown');
+
+=head2 Stash data
+
+Any of the native Perl data types can be passed to templates as references
+through the L<Mojolicious::Controller/"stash">.
+
+  $c->stash(description => 'web framework');
+  $c->stash(frameworks  => ['Catalyst', 'Mojolicious']);
+  $c->stash(spinoffs    => {minion => 'job queue'});
+
+  %= $description
+  %= $frameworks->[1]
+  %= $spinoffs->{minion}
+
+Since everything is just Perl normal control structures just work.
+
+  % for my $framework (@$frameworks) {
+    <%= $framework %> is a <%= $description %>.
+  % }
+
+  % if (my $description = $spinoffs->{minion}) {
+    Minion is a <%= $description %>.
+  % }
+
+For templates that might get rendered in different ways and where you're not
+sure if a stash value will actually be set, you can just use the helper
+L<Mojolicious::Plugin::DefaultHelpers/"stash">.
+
+  % if (my $spinoffs = stash 'spinoffs') {
+    Minion is a <%= $spinoffs->{minion} %>.
+  % }
+
+=head2 Helpers
+
+Helpers are little functions you can use in templates as well as application
+and controller code.
+
+  # Template
+  %= dumper [1, 2, 3]
+
+  # Application
+  my $serialized = $app->dumper([1, 2, 3]);
+
+  # Controller
+  my $serialized = $c->dumper([1, 2, 3]);
+
+We differentiate between default helpers, which are more general purpose like
+L<Mojolicious::Plugin::DefaultHelpers/"dumper">, and tag helpers like
+L<Mojolicious::Plugin::TagHelpers/"link_to">, which are template specific and
+mostly used to generate HTML tags.
+
+  %= link_to Mojolicious => 'https://mojolicious.org'
+
+In controllers you can also use the method L<Mojolicious::Controller/"helpers">
+to fully qualify helper calls and ensure that they don't conflict with existing
+methods you may already have.
+
+  my $serialized = $c->helpers->dumper([1, 2, 3]);
+
+A list of all built-in helpers can be found in
+L<Mojolicious::Plugin::DefaultHelpers> and L<Mojolicious::Plugin::TagHelpers>.
+
+=head2 Content negotiation
+
+For resources with different representations and that require truly RESTful
+content negotiation you can also use L<Mojolicious::Controller/"respond_to">
+instead of L<Mojolicious::Controller/"render">.
+
+  # /hello (Accept: application/json) -> "json"
+  # /hello (Accept: application/xml)  -> "xml"
+  # /hello.json                       -> "json"
+  # /hello.xml                        -> "xml"
+  # /hello?format=json                -> "json"
+  # /hello?format=xml                 -> "xml"
+  $c->respond_to(
+    json => {json => {hello => 'world'}},
+    xml  => {text => '<hello>world</hello>'}
+  );
+
+The best possible representation will be automatically selected from the
+C<format> C<GET>/C<POST> parameter, C<format> stash value or C<Accept> request
+header and stored in the C<format> stash value. To change MIME type mappings for
+the C<Accept> request header or the C<Content-Type> response header you can use
+L<Mojolicious/"types">.
+
+  $c->respond_to(
+    json => {json => {hello => 'world'}},
+    html => sub {
+      $c->content_for(head => '<meta name="author" content="sri">');
+      $c->render(template => 'hello', message => 'world')
+    }
+  );
+
+Callbacks can be used for representations that are too complex to fit into a
+single render call.
+
+  # /hello (Accept: application/json) -> "json"
+  # /hello (Accept: text/html)        -> "html"
+  # /hello (Accept: image/png)        -> "any"
+  # /hello.json                       -> "json"
+  # /hello.html                       -> "html"
+  # /hello.png                        -> "any"
+  # /hello?format=json                -> "json"
+  # /hello?format=html                -> "html"
+  # /hello?format=png                 -> "any"
+  $c->respond_to(
+    json => {json => {hello => 'world'}},
+    html => {template => 'hello', message => 'world'},
+    any  => {text => '', status => 204}
+  );
+
+And if no viable representation could be found, the C<any> fallback will be
+used or an empty C<204> response rendered automatically.
+
+  # /hello                      -> "html"
+  # /hello (Accept: text/html)  -> "html"
+  # /hello (Accept: text/xml)   -> "xml"
+  # /hello (Accept: text/plain) -> undef
+  # /hello.html                 -> "html"
+  # /hello.xml                  -> "xml"
+  # /hello.txt                  -> undef
+  # /hello?format=html          -> "html"
+  # /hello?format=xml           -> "xml"
+  # /hello?format=txt           -> undef
+  if (my $format = $c->accepts('html', 'xml')) {
+    ...
+  }
+
+For even more advanced negotiation logic you can also use the helper
+L<Mojolicious::Plugin::DefaultHelpers/"accepts">.
+
+=head2 Rendering C<exception> and C<not_found> pages
+
+By now you've probably already encountered the built-in C<404> (Not Found) and
+C<500> (Server Error) pages, that get rendered automatically when you make a
+mistake. Those are fallbacks for when your own exception handling fails, which
+can be especially helpful during development. You can also render them manually
+with the helpers L<Mojolicious::Plugin::DefaultHelpers/"reply-E<gt>exception">
+and L<Mojolicious::Plugin::DefaultHelpers/"reply-E<gt>not_found">.
+
+  use Mojolicious::Lite;
+  use Scalar::Util 'looks_like_number';
+
+  get '/divide/:dividend/by/:divisor' => sub {
+    my $c = shift;
+
+    my $dividend = $c->param('dividend');
+    my $divisor  = $c->param('divisor');
+
+    # 404
+    return $c->reply->not_found
+      unless looks_like_number $dividend && looks_like_number $divisor;
+
+    # 500
+    return $c->reply->exception('Division by zero!') if $divisor == 0;
+
+    # 200
+    $c->render(text => $dividend / $divisor);
+  };
+
+  app->start;
+
+You can also change the templates of those pages, since you most likely want to
+show your users something more closely related to your application in
+production. The renderer will always try to find C<exception.$mode.$format.*>
+or C<not_found.$mode.$format.*> before falling back to the built-in default
+templates.
+
+  use Mojolicious::Lite;
+
+  get '/dies' => sub { die 'Intentional error' };
+
+  app->start;
+  __DATA__
+
+  @@ exception.production.html.ep
+  <!DOCTYPE html>
+  <html>
+    <head><title>Server error</title></head>
+    <body>
+      <h1>Exception</h1>
+      <p><%= $exception->message %></p>
+      <h1>Stash</h1>
+      <pre><%= dumper $snapshot %></pre>
+    </body>
+  </html>
+
+The hook L<Mojolicious/"before_render"> makes even more advanced customizations
+possible by allowing you to intercept and modify the arguments passed to the
+renderer.
+
+  use Mojolicious::Lite;
+
+  hook before_render => sub {
+    my ($c, $args) = @_;
+
+    # Make sure we are rendering the exception template
+    return unless my $template = $args->{template};
+    return unless $template eq 'exception';
+
+    # Switch to JSON rendering if content negotiation allows it
+    $args->{json} = {exception => $args->{exception}} if $c->accepts('json');
+  };
+
+  get '/' => sub { die "This sho...ALL GLORY TO THE HYPNOTOAD!\n" };
+
+  app->start;
+
+=head2 Layouts
+
+Most of the time when using C<ep> templates you will want to wrap your
+generated content in an HTML skeleton, thanks to layouts that's absolutely
+trivial.
+
+  use Mojolicious::Lite;
+
+  get '/' => {template => 'foo/bar'};
+
+  app->start;
+  __DATA__
+
+  @@ foo/bar.html.ep
+  % layout 'mylayout';
+  Hello World!
+
+  @@ layouts/mylayout.html.ep
+  <!DOCTYPE html>
+  <html>
+    <head><title>MyApp</title></head>
+    <body><%= content %></body>
+  </html>
+
+You just select the right layout template with the helper
+L<Mojolicious::Plugin::DefaultHelpers/"layout"> and place the result of the
+current template with the helper
+L<Mojolicious::Plugin::DefaultHelpers/"content">. You can also pass along
+normal stash values to the C<layout> helper.
+
+  use Mojolicious::Lite;
+
+  get '/' => {template => 'foo/bar'};
+
+  app->start;
+  __DATA__
+
+  @@ foo/bar.html.ep
+  % layout 'mylayout', title => 'Hi there';
+  Hello World!
+
+  @@ layouts/mylayout.html.ep
+  <!DOCTYPE html>
+  <html>
+    <head><title><%= $title %></title></head>
+    <body><%= content %></body>
+  </html>
+
+Instead of the C<layout> helper you could also just use the C<layout> stash
+value, or call L<Mojolicious::Controller/"render"> with the C<layout> argument.
+
+  $c->render(template => 'mytemplate', layout => 'mylayout');
+
+To set a C<layout> stash value application-wide you can use
+L<Mojolicious/"defaults">.
+
+  $app->defaults(layout => 'mylayout');
+
+Layouts can also be used with L<Mojolicious::Controller/"render_to_string">,
+but the C<layout> value needs to be passed as a render argument (not a stash
+value).
+
+  my $html = $c->render_to_string('reminder', layout => 'mail');
+
+=head2 Partial templates
+
+You can break up bigger templates into smaller, more manageable chunks. These
+partial templates can also be shared with other templates. Just use the helper
+L<Mojolicious::Plugin::DefaultHelpers/"include"> to include one template into
+another.
+
+  use Mojolicious::Lite;
+
+  get '/' => {template => 'foo/bar'};
+
+  app->start;
+  __DATA__
+
+  @@ foo/bar.html.ep
+  <!DOCTYPE html>
+  <html>
+    %= include '_header', title => 'Howdy'
+    <body>Bar</body>
+  </html>
+
+  @@ _header.html.ep
+  <head><title><%= $title %></title></head>
+
+You can name partial templates however you like, but a leading underscore is a
+commonly used naming convention.
+
+=head2 Reusable template blocks
+
+It's never fun to repeat yourself, that's why you can build reusable template
+blocks in C<ep> that work very similar to normal Perl functions, with the
+C<begin> and C<end> keywords. Just be aware that both keywords are part of the
+surrounding tag and not actual Perl code, so there can only be whitespace after
+C<begin> and before C<end>.
+
+  use Mojolicious::Lite;
+
+  get '/' => 'welcome';
+
+  app->start;
+  __DATA__
+
+  @@ welcome.html.ep
+  <% my $block = begin %>
+    % my $name = shift;
+    Hello <%= $name %>.
+  <% end %>
+  <%= $block->('Wolfgang') %>
+  <%= $block->('Baerbel') %>
+
+A naive translation of the template to Perl code could look like this.
+
+  my $output = '';
+  my $block  = sub {
+    my $name   = shift;
+    my $output = '';
+    $output .= 'Hello ';
+    $output .= xml_escape scalar + $name;
+    $output .= '.';
+    return Mojo::ByteStream->new($output);
+  };
+  $output .= xml_escape scalar + $block->('Wolfgang');
+  $output .= xml_escape scalar + $block->('Baerbel');
+  return $output;
+
+While template blocks cannot be shared between templates, they are most
+commonly used to pass parts of a template to helpers.
+
+=head2 Adding helpers
+
+You should always try to keep your actions small and reuse as much code as
+possible. Helpers make this very easy, they get passed the current controller
+object as first argument, and you can use them to do pretty much anything an
+action could do.
+
+  use Mojolicious::Lite;
+
+  helper debug => sub {
+    my ($c, $str) = @_;
+    $c->app->log->debug($str);
+  };
+
+  get '/' => sub {
+    my $c = shift;
+    $c->debug('Hello from an action!');
+  } => 'index';
+
+  app->start;
+  __DATA__
+
+  @@ index.html.ep
+  % debug 'Hello from a template!';
+
+Helpers can also accept template blocks as last argument, this for example,
+allows very pleasant to use tag helpers and filters. Wrapping the helper result
+into a L<Mojo::ByteStream> object can prevent accidental double escaping.
+
+  use Mojolicious::Lite;
+  use Mojo::ByteStream;
+
+  helper trim_newline => sub {
+    my ($c, $block) = @_;
+    my $result = $block->();
+    $result =~ s/\n//g;
+    return Mojo::ByteStream->new($result);
+  };
+
+  get '/' => 'index';
+
+  app->start;
+  __DATA__
+
+  @@ index.html.ep
+  %= trim_newline begin
+    Some text.
+    %= 1 + 1
+    More text.
+  % end
+
+Similar to stash values, you can use a prefix like C<myapp.*> to keep helpers
+from getting exposed in templates as functions, and to organize them into
+namespaces as your application grows. Every prefix automatically becomes a
+helper that returns a proxy object containing the current controller object and
+on which you can call the nested helpers.
+
+  use Mojolicious::Lite;
+
+  helper 'cache_control.no_caching' => sub {
+    my $c = shift;
+    $c->res->headers->cache_control('private, max-age=0, no-cache');
+  };
+
+  helper 'cache_control.five_minutes' => sub {
+    my $c = shift;
+    $c->res->headers->cache_control('public, max-age=300');
+  };
+
+  get '/news' => sub {
+    my $c = shift;
+    $c->cache_control->no_caching;
+    $c->render(text => 'Always up to date.');
+  };
+
+  get '/some_older_story' => sub {
+    my $c = shift;
+    $c->cache_control->five_minutes;
+    $c->render(text => 'This one can be cached for a bit.');
+  };
+
+  app->start;
+
+While helpers can also be redefined, this should only be done very carefully to
+avoid conflicts.
+
+=head2 Content blocks
+
+The helper L<Mojolicious::Plugin::DefaultHelpers/"content_for"> allows you to
+pass whole blocks of content from one template to another. This can be very
+useful when your layout has distinct sections, such as sidebars, where content
+should be inserted by the template.
+
+  use Mojolicious::Lite;
+
+  get '/' => 'foo';
+
+  app->start;
+  __DATA__
+
+  @@ foo.html.ep
+  % layout 'mylayout';
+  % content_for header => begin
+    <meta http-equiv="Content-Type" content="text/html">
+  % end
+  <div>Hello World!</div>
+  % content_for header => begin
+    <meta http-equiv="Pragma" content="no-cache">
+  % end
+
+  @@ layouts/mylayout.html.ep
+  <!DOCTYPE html>
+  <html>
+    <head><%= content 'header' %></head>
+    <body><%= content %></body>
+  </html>
+
+=head2 Forms
+
+To build HTML forms more efficiently you can use tag helpers like
+L<Mojolicious::Plugin::TagHelpers/"form_for">, which can automatically select a
+request method for you if a route name is provided. And since most browsers
+only allow forms to be submitted with C<GET> and C<POST>, but not request
+methods like C<PUT> or C<DELETE>, they are spoofed with an C<_method> query
+parameter.
+
+  use Mojolicious::Lite;
+
+  get '/' => 'form';
+
+  # PUT  /nothing
+  # POST /nothing?_method=PUT
+  put '/nothing' => sub {
+    my $c = shift;
+
+    # Prevent double form submission with redirect
+    my $value = $c->param('whatever');
+    $c->flash(confirmation => "We did nothing with your value ($value).");
+    $c->redirect_to('form');
+  };
+
+  app->start;
+  __DATA__
+
+  @@ form.html.ep
+  <!DOCTYPE html>
+  <html>
+    <body>
+      % if (my $confirmation = flash 'confirmation') {
+        <p><%= $confirmation %></p>
+      % }
+      %= form_for nothing => begin
+        %= text_field whatever => 'I â¥ Mojolicious!'
+        %= submit_button
+      % end
+    </body>
+  </html>
+
+The methods L<Mojolicious::Controller/"flash"> and
+L<Mojolicious::Controller/"redirect_to"> are often used together to prevent
+double form submission, allowing users to receive a confirmation message that
+will vanish if they decide to reload the page they've been redirected to.
+
+=head2 Form validation
+
+You can use L<Mojolicious::Controller/"validation"> to validate C<GET> and
+C<POST> parameters submitted to your application. All unknown fields will be
+ignored by default, so you have to decide which should be required or optional
+before you can perform checks on their values. Every check is performed right
+away, so you can use the results immediately to build more advanced validation
+logic with methods like L<Mojolicious::Validator::Validation/"is_valid">.
+
+  use Mojolicious::Lite;
+
+  get '/' => sub {
+    my $c = shift;
+
+    # Check if parameters have been submitted
+    my $v = $c->validation;
+    return $c->render unless $v->has_data;
+
+    # Validate parameters ("pass_again" depends on "pass")
+    $v->required('user')->size(1, 20)->like(qr/^[a-z0-9]+$/);
+    $v->required('pass_again')->equal_to('pass')
+      if $v->optional('pass')->size(7, 500)->is_valid;
+
+    # Render confirmation if validation was successful
+    $c->render('thanks') unless $v->has_error;
+  } => 'index';
+
+  app->start;
+  __DATA__
+
+  @@ index.html.ep
+  <!DOCTYPE html>
+  <html>
+    <head>
+      <style>
+        label.field-with-error { color: #dd7e5e }
+        input.field-with-error { background-color: #fd9e7e }
+      </style>
+    </head>
+    <body>
+      %= form_for index => begin
+        %= label_for user => 'Username (required, 1-20 characters, a-z/0-9)'
+        <br>
+        %= text_field 'user', id => 'user'
+        %= submit_button
+        <br>
+        %= label_for pass => 'Password (optional, 7-500 characters)'
+        <br>
+        %= password_field 'pass', id => 'pass'
+        <br>
+        %= label_for pass_again => 'Password again (equal to the value above)'
+        <br>
+        %= password_field 'pass_again', id => 'pass_again'
+      % end
+    </body>
+  </html>
+
+  @@ thanks.html.ep
+  <!DOCTYPE html>
+  <html><body>Thank you <%= validation->param('user') %>.</body></html>
+
+Form elements generated with tag helpers from
+L<Mojolicious::Plugin::TagHelpers> will automatically remember their previous
+values and add the class C<field-with-error> for fields that failed validation
+to make styling with CSS easier.
+
+  <label class="field-with-error" for="user">
+    Username (required, only characters e-t)
+  </label>
+  <input class="field-with-error" type="text" name="user" value="sri">
+
+For a full list of available checks see also
+L<Mojolicious::Validator/"CHECKS">.
+
+=head2 Adding form validation checks
+
+Validation checks can be registered with L<Mojolicious::Validator/"add_check">
+and return a false value if they were successful. A true value may be used to
+pass along additional information which can then be retrieved with
+L<Mojolicious::Validator::Validation/"error">.
+
+  use Mojolicious::Lite;
+
+  # Add "range" check
+  app->validator->add_check(range => sub {
+    my ($v, $name, $value, $min, $max) = @_;
+    return $value < $min || $value > $max;
+  });
+
+  get '/' => 'form';
+
+  post '/test' => sub {
+    my $c = shift;
+
+    # Validate parameters with custom check
+    my $v = $c->validation;
+    $v->required('number')->range(3, 23);
+
+    # Render form again if validation failed
+    return $c->render('form') if $v->has_error;
+
+    # Prevent double form submission with redirect
+    $c->flash(number => $v->param('number'));
+    $c->redirect_to('form');
+  };
+
+  app->start;
+  __DATA__
+
+  @@ form.html.ep
+  <!DOCTYPE html>
+  <html>
+    <body>
+      % if (my $number = flash 'number') {
+        <p>Thanks, the number <%= $number %> was valid.</p>
+      % }
+      %= form_for test => begin
+        % if (my $err = validation->error('number')) {
+          <p>
+            %= 'Value is required.' if $err->[0] eq 'required'
+            %= 'Value needs to be between 3 and 23.' if $err->[0] eq 'range'
+          </p>
+        % }
+        %= text_field 'number'
+        %= submit_button
+      % end
+    </body>
+  </html>
+
+=head2 Cross-site request forgery
+
+CSRF is a very common attack on web applications that trick your logged in
+users to submit forms they did not intend to send, with something as mundane as
+a link. All you have to do, to protect your users from this, is to add an
+additional hidden field to your forms with
+L<Mojolicious::Plugin::TagHelpers/"csrf_field">, and validate it with
+L<Mojolicious::Validator::Validation/"csrf_protect">.
+
+  use Mojolicious::Lite;
+
+  get '/' => {template => 'target'};
+
+  post '/' => sub {
+    my $c = shift;
+
+    # Check CSRF token
+    my $v = $c->validation;
+    return $c->render(text => 'Bad CSRF token!', status => 403)
+      if $v->csrf_protect->has_error('csrf_token');
+
+    my $city = $v->required('city')->param('city');
+    $c->render(text => "Low orbit ion cannon pointed at $city!")
+      unless $v->has_error;
+  } => 'target';
+
+  app->start;
+  __DATA__
+
+  @@ target.html.ep
+  <!DOCTYPE html>
+  <html>
+    <body>
+      %= form_for target => begin
+        %= csrf_field
+        %= label_for city => 'Which city to point low orbit ion cannon at?'
+        %= text_field 'city', id => 'city'
+        %= submit_button
+      %= end
+    </body>
+  </html>
+
+For Ajax requests and the like, you can also generate a token directly with the
+helper L<Mojolicious::Plugin::DefaultHelpers/"csrf_token">, and then pass it
+along with the C<X-CSRF-Token> request header.
+
+=head1 ADVANCED
+
+Less commonly used and more powerful features.
+
+=head2 Template inheritance
+
+Inheritance takes the layout concept above one step further, the helpers
+L<Mojolicious::Plugin::DefaultHelpers/"content"> and
+L<Mojolicious::Plugin::DefaultHelpers/"extends"> allow you to build skeleton
+templates with named blocks that child templates can override.
+
+  use Mojolicious::Lite;
+
+  # first > mylayout
+  get '/first' => {template => 'first', layout => 'mylayout'};
+
+  # third > second > first > mylayout
+  get '/third' => {template => 'third', layout => 'mylayout'};
+
+  app->start;
+  __DATA__
+
+  @@ layouts/mylayout.html.ep
+  <!DOCTYPE html>
+  <html>
+    <head><title>Hello</title></head>
+    <body><%= content %></body>
+  </html>
+
+  @@ first.html.ep
+  %= content header => begin
+    Default header
+  % end
+  <div>Hello World!</div>
+  %= content footer => begin
+    Default footer
+  % end
+
+  @@ second.html.ep
+  % extends 'first';
+  % content header => begin
+    New header
+  % end
+
+  @@ third.html.ep
+  % extends 'second';
+  % content footer => begin
+    New footer
+  % end
+
+This chain could go on and on to allow a very high level of template reuse.
+
+=head2 Serving static files
+
+Static files are automatically served from the C<public> directories of the
+application, which can be customized with L<Mojolicious::Static/"paths">, or one
+of the C<DATA> sections from L<Mojolicious::Static/"classes">. And if that's not
+enough you can also serve them manually with
+L<Mojolicious::Plugin::DefaultHelpers/"reply-E<gt>static"> and
+L<Mojolicious::Plugin::DefaultHelpers/"reply-E<gt>file">.
+
+  use Mojolicious::Lite;
+
+  get '/' => sub {
+    my $c = shift;
+    $c->reply->static('index.html');
+  };
+
+  get '/some_download' => sub {
+    my $c = shift;
+    $c->res->headers->content_disposition('attachment; filename=bar.png;');
+    $c->reply->static('foo/bar.png');
+  };
+
+  get '/leak' => sub {
+    my $c = shift;
+    $c->reply->file('/etc/passwd');
+  };
+
+  app->start;
+
+=head2 Custom responses
+
+Most response content, static as well as dynamic, gets served through
+L<Mojo::Asset::File> and L<Mojo::Asset::Memory> objects. For somewhat static
+content, like cached JSON data or temporary files, you can create your own and
+use the helper L<Mojolicious::Plugin::DefaultHelpers/"reply-E<gt>asset"> to
+serve them while allowing content negotiation to be performed with C<Range>,
+C<If-Modified-Since> and C<If-None-Match> headers.
+
+  use Mojolicious::Lite;
+  use Mojo::Asset::File;
+
+  get '/leak' => sub {
+    my $c = shift;
+    $c->res->headers->content_type('text/plain');
+    $c->reply->asset(Mojo::Asset::File->new(path => '/etc/passwd'));
+  };
+
+  app->start;
+
+For even more control you can also just skip the helper and use
+L<Mojolicious::Controller/"rendered"> to tell the renderer when you're done
+generating a response.
+
+  use Mojolicious::Lite;
+  use Mojo::Asset::File;
+
+  get '/leak' => sub {
+    my $c = shift;
+    $c->res->headers->content_type('text/plain');
+    $c->res->content->asset(Mojo::Asset::File->new(path => '/etc/passwd'));
+    $c->rendered(200);
+  };
+
+  app->start;
+
+=head2 Helper plugins
+
+Some helpers might be useful enough for you to share them between multiple
+applications, plugins make that very simple.
+
+  package Mojolicious::Plugin::DebugHelper;
+  use Mojo::Base 'Mojolicious::Plugin';
+
+  sub register {
+    my ($self, $app) = @_;
+    $app->helper(debug => sub {
+      my ($c, $str) = @_;
+      $c->app->log->debug($str);
+    });
+  }
+
+  1;
+
+The C<register> method will be called when you load the plugin. And to add your
+helper to the application, you can use L<Mojolicious/"helper">.
+
+  use Mojolicious::Lite;
+
+  plugin 'DebugHelper';
+
+  get '/' => sub {
+    my $c = shift;
+    $c->debug('It works!');
+    $c->render(text => 'Hello!');
+  };
+
+  app->start;
+
+A skeleton for a full CPAN compatible plugin distribution can be automatically
+generated.
+
+  $ mojo generate plugin DebugHelper
+
+And if you have a C<PAUSE> account (which can be requested at
+L<http://pause.perl.org>), you are only a few commands away from releasing it
+to CPAN.
+
+  $ perl Makefile.PL
+  $ make test
+  $ make manifest
+  $ make dist
+  $ mojo cpanify -u USER -p PASS Mojolicious-Plugin-DebugHelper-0.01.tar.gz
+
+=head2 Bundling assets with plugins
+
+Assets such as templates and static files can be easily bundled with your
+plugins, even if you plan to release them to CPAN.
+
+  $ mojo generate plugin AlertAssets
+  $ mkdir Mojolicious-Plugin-AlertAssets/lib/Mojolicious/Plugin/AlertAssets
+  $ cd Mojolicious-Plugin-AlertAssets/lib/Mojolicious/Plugin/AlertAssets
+  $ mkdir public
+  $ echo 'alert("Hello World!");' > public/alertassets.js
+  $ mkdir templates
+  $ echo '%= javascript "/alertassets.js"' > templates/alertassets.html.ep
+
+Just give them reasonably unique names, ideally based on the name of your
+plugin, and append their respective directories to the list of search paths
+when C<register> is called.
+
+  package Mojolicious::Plugin::AlertAssets;
+  use Mojo::Base 'Mojolicious::Plugin';
+
+  use Mojo::File 'path';
+
+  sub register {
+    my ($self, $app) = @_;
+
+    # Append "templates" and "public" directories
+    my $base = path(__FILE__)->sibling('AlertAssets');
+    push @{$app->renderer->paths}, $base->child('templates')->to_string;
+    push @{$app->static->paths},   $base->child('public')->to_string;
+  }
+
+  1;
+
+Both will work just like normal C<templates> and C<public> directories once
+you've installed and loaded the plugin, with slightly lower precedence.
+
+  use Mojolicious::Lite;
+
+  plugin 'AlertAssets';
+
+  get '/alert_me';
+
+  app->start;
+  __DATA__
+
+  @@ alert_me.html.ep
+  <!DOCTYPE html>
+  <html>
+    <head>
+      <title>Alert me!</title>
+      %= include 'alertassets'
+    </head>
+    <body>You've been alerted.</body>
+  </html>
+
+And it works just the same for assets bundled in the C<DATA> section of your
+plugin.
+
+  package Mojolicious::Plugin::AlertAssets;
+  use Mojo::Base 'Mojolicious::Plugin';
+
+  sub register {
+    my ($self, $app) = @_;
+
+    # Append class
+    push @{$app->renderer->classes}, __PACKAGE__;
+    push @{$app->static->classes},   __PACKAGE__;
+  }
+
+  1;
+  __DATA__
+
+  @@ alertassets.js
+  alert("Hello World!");
+
+  @@ alertassets.html.ep
+  %= javascript "/alertassets.js"
+
+=head2 Post-processing dynamic content
+
+While post-processing tasks are generally very easy with the hook
+L<Mojolicious/"after_dispatch">, for content generated by the renderer it is a
+lot more efficient to use L<Mojolicious/"after_render">.
+
+  use Mojolicious::Lite;
+  use IO::Compress::Gzip 'gzip';
+
+  hook after_render => sub {
+    my ($c, $output, $format) = @_;
+
+    # Check if "gzip => 1" has been set in the stash
+    return unless $c->stash->{gzip};
+
+    # Check if user agent accepts gzip compression
+    return unless ($c->req->headers->accept_encoding // '') =~ /gzip/i;
+    $c->res->headers->append(Vary => 'Accept-Encoding');
+
+    # Compress content with gzip
+    $c->res->headers->content_encoding('gzip');
+    gzip $output, \my $compressed;
+    $$output = $compressed;
+  };
+
+  get '/' => {template => 'hello', title => 'Hello', gzip => 1};
+
+  app->start;
+  __DATA__
+
+  @@ hello.html.ep
+  <!DOCTYPE html>
+  <html>
+    <head><title><%= title %></title></head>
+    <body>Compressed content.</body>
+  </html>
+
+=head2 Streaming
+
+You don't have to render all content at once, the method
+L<Mojolicious::Controller/"write"> can also be used to stream a series of
+smaller chunks.
+
+  use Mojolicious::Lite;
+
+  get '/' => sub {
+    my $c = shift;
+
+    # Prepare body
+    my $body = 'Hello World!';
+    $c->res->headers->content_length(length $body);
+
+    # Start writing directly with a drain callback
+    my $drain;
+    $drain = sub {
+      my $c = shift;
+      my $chunk = substr $body, 0, 1, '';
+      $drain = undef unless length $body;
+      $c->write($chunk, $drain);
+    };
+    $c->$drain;
+  };
+
+  app->start;
+
+The drain callback will be executed whenever the entire previous chunk of data
+has actually been written.
+
+  HTTP/1.1 200 OK
+  Date: Sat, 13 Sep 2014 16:48:29 GMT
+  Content-Length: 12
+  Server: Mojolicious (Perl)
+
+  Hello World!
+
+Instead of providing a C<Content-Length> header you can also call
+L<Mojolicious::Controller/"finish"> and close the connection manually once you
+are done.
+
+  use Mojolicious::Lite;
+
+  get '/' => sub {
+    my $c = shift;
+
+    # Prepare body
+    my $body = 'Hello World!';
+
+    # Start writing directly with a drain callback
+    my $drain;
+    $drain = sub {
+      my $c = shift;
+      my $chunk = substr $body, 0, 1, '';
+      length $chunk ? $c->write($chunk, $drain) : $c->finish;
+    };
+    $c->$drain;
+  };
+
+  app->start;
+
+While this is rather inefficient, as it prevents keep-alive, it is sometimes
+necessary for EventSource and similar applications.
+
+  HTTP/1.1 200 OK
+  Date: Sat, 13 Sep 2014 16:48:29 GMT
+  Connection: close
+  Server: Mojolicious (Perl)
+
+  Hello World!
+
+=head2 Chunked transfer encoding
+
+For very dynamic content you might not know the response content length in
+advance, that's where the chunked transfer encoding and
+L<Mojolicious::Controller/"write_chunk"> come in handy. A common use would be
+to send the C<head> section of an HTML document to the browser in advance and
+speed up preloading of referenced images and stylesheets.
+
+  use Mojolicious::Lite;
+
+  get '/' => sub {
+    my $c = shift;
+    $c->write_chunk('<html><head><title>Example</title></head>' => sub {
+      my $c = shift;
+      $c->finish('<body>Example</body></html>');
+    });
+  };
+
+  app->start;
+
+The optional drain callback ensures that all previous chunks have been written
+before processing continues. To end the stream you can call
+L<Mojolicious::Controller/"finish"> or write an empty chunk of data.
+
+  HTTP/1.1 200 OK
+  Date: Sat, 13 Sep 2014 16:48:29 GMT
+  Transfer-Encoding: chunked
+  Server: Mojolicious (Perl)
+
+  29
+  <html><head><title>Example</title></head>
+  1b
+  <body>Example</body></html>
+  0
+
+Especially in combination with long inactivity timeouts this can be very useful
+for Comet (long polling). Due to limitations in some web servers this might not
+work perfectly in all deployment environments.
+
+=head2 Encoding
+
+Templates stored in files are expected to be C<UTF-8> by default, but that can
+be easily changed with L<Mojolicious::Renderer/"encoding">.
+
+  $app->renderer->encoding('koi8-r');
+
+All templates from the C<DATA> section are bound to the encoding of the Perl
+script.
+
+  use Mojolicious::Lite;
+
+  get '/heart';
+
+  app->start;
+  __DATA__
+
+  @@ heart.html.ep
+  I â¥ Mojolicious!
+
+=head2 Base64 encoded DATA files
+
+Base64 encoded static files such as images can be easily stored in the C<DATA>
+section of your application, similar to templates.
+
+  use Mojolicious::Lite;
+
+  get '/' => {text => 'I â¥ Mojolicious!'};
+
+  app->start;
+  __DATA__
+
+  @@ favicon.ico (base64)
+  ...base64 encoded image...
+
+=head2 Inflating DATA templates
+
+Templates stored in files get preferred over files from the C<DATA> section,
+this allows you to include a default set of templates in your application that
+the user can later customize. The command L<Mojolicious::Command::inflate> will
+write all templates and static files from the C<DATA> section into actual files
+in the C<templates> and C<public> directories.
+
+  $ ./myapp.pl inflate
+
+=head2 Customizing the template syntax
+
+You can easily change the whole template syntax by loading
+L<Mojolicious::Plugin::EPRenderer> with a custom configuration.
+
+  use Mojolicious::Lite;
+
+  plugin EPRenderer => {
+    name     => 'mustache',
+    template => {
+      tag_start => '{{',
+      tag_end   => '}}'
+    }
+  };
+
+  get '/:name' => {name => 'Anonymous'} => 'index';
+
+  app->start;
+  __DATA__
+
+  @@ index.html.mustache
+  Hello {{= $name }}.
+
+L<Mojo::Template> contains the whole list of available options.
+
+=head2 Adding your favorite template system
+
+Maybe you would prefer a different template system than C<ep>, which is provided
+by L<Mojolicious::Plugin::EPRenderer>, and there is not already a plugin on CPAN
+for your favorite one. All you have to do, is to add a new C<handler> with
+L<Mojolicious::Renderer/"add_handler"> when C<register> is called.
+
+  package Mojolicious::Plugin::MyRenderer;
+  use Mojo::Base 'Mojolicious::Plugin';
+
+  sub register {
+    my ($self, $app) = @_;
+
+    # Add "mine" handler
+    $app->renderer->add_handler(mine => sub {
+      my ($renderer, $c, $output, $options) = @_;
+
+      # Check for one-time use inline template
+      my $inline_template = $options->{inline};
+
+      # Check for appropriate template in "templates" directories
+      my $template_path = $renderer->template_path($options);
+
+      # Check for appropriate template in DATA sections
+      my $data_template = $renderer->get_data_template($options);
+
+      # This part is up to you and your template system :)
+      ...
+
+      # Pass the rendered result back to the renderer
+      $$output = 'Hello World!';
+
+      # Or just die if an error occurs
+      die 'Something went wrong with the template';
+    });
+  }
+
+  1;
+
+An C<inline> template, if provided by the user, will be passed along with the
+options. You can use L<Mojolicious::Renderer/"template_path"> to search the
+C<templates> directories of the application, and
+L<Mojolicious::Renderer/"get_data_template"> to search the C<DATA> sections.
+
+  use Mojolicious::Lite;
+
+  plugin 'MyRenderer';
+
+  # Render an inline template
+  get '/inline' => {inline => '...', handler => 'mine'};
+
+  # Render a template from the DATA section
+  get '/data' => {template => 'test'};
+
+  app->start;
+  __DATA__
+
+  @@ test.html.mine
+  ...
+
+=head2 Adding a handler to generate binary data
+
+By default the renderer assumes that every C<handler> generates characters that
+need to be automatically encoded, but this can be easily disabled if you're
+generating bytes instead.
+
+  use Mojolicious::Lite;
+  use Storable 'nfreeze';
+
+  # Add "storable" handler
+  app->renderer->add_handler(storable => sub {
+    my ($renderer, $c, $output, $options) = @_;
+
+    # Disable automatic encoding
+    delete $options->{encoding};
+
+    # Encode data from stash value
+    $$output = nfreeze delete $c->stash->{storable};
+  });
+
+  # Set "handler" value automatically if "storable" value is set already
+  app->hook(before_render => sub {
+    my ($c, $args) = @_;
+    $args->{handler} = 'storable'
+      if exists $args->{storable} || exists $c->stash->{storable};
+  });
+
+  get '/' => {storable => {i => 'â¥ mojolicious'}};
+
+  app->start;
+
+The hook L<Mojolicious/"before_render"> can be used to make stash values like
+C<storable> special, so that they no longer require a C<handler> value to be set
+explicitly.
+
+  # Explicit "handler" value
+  $c->render(storable => {i => 'â¥ mojolicious'}, handler => 'storable');
+
+  # Implicit "handler" value (with "before_render" hook)
+  $c->render(storable => {i => 'â¥ mojolicious'});
+
+=head1 MORE
+
+You can continue with L<Mojolicious::Guides> now or take a look at the
+L<Mojolicious wiki|http://github.com/kraih/mojo/wiki>, which contains a lot more
+documentation and examples by many different authors.
+
+=head1 SUPPORT
+
+If you have any questions the documentation might not yet answer, don't
+hesitate to ask on the
+L<mailing list|http://groups.google.com/group/mojolicious> or the official IRC
+channel C<#mojo> on C<irc.perl.org>
+(L<chat now!|https://chat.mibbit.com/?channel=%23mojo&server=irc.perl.org>).
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Guides/Routing.pod
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Guides/Routing.pod	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Guides/Routing.pod	(revision 32205)
@@ -0,0 +1,1126 @@
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Guides::Routing - Routing requests
+
+=head1 OVERVIEW
+
+This document contains a simple and fun introduction to the L<Mojolicious>
+router and its underlying concepts.
+
+=head1 CONCEPTS
+
+Essentials every L<Mojolicious> developer should know.
+
+=head2 Dispatcher
+
+The foundation of every web framework is a tiny black box connecting incoming
+requests with code generating the appropriate response.
+
+  GET /user/show/1 -> $c->render(text => 'Daniel');
+
+This black box is usually called a dispatcher. There are many implementations
+using different strategies to establish these connections, but pretty much all
+are based around mapping the path part of the request URL to some kind of
+response generator.
+
+  /user/show/2 -> $c->render(text => 'Isabell');
+  /user/show/3 -> $c->render(text => 'Sara');
+  /user/show/4 -> $c->render(text => 'Stefan');
+  /user/show/5 -> $c->render(text => 'Fynn');
+
+While it is very well possible to make all these connections static, it is also
+rather inefficient. That's why regular expressions are commonly used to make
+the dispatch process more dynamic.
+
+  qr!/user/show/(\d+)! -> $c->render(text => $users{$1});
+
+Modern dispatchers have pretty much everything HTTP has to offer at their
+disposal and can use many more variables than just the request path, such as
+request method and headers like C<Host>, C<User-Agent> and C<Accept>.
+
+  GET /user/show/23 HTTP/1.1
+  Host: mojolicious.org
+  User-Agent: Mojolicious (Perl)
+  Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
+
+=head2 Routes
+
+While regular expressions are quite powerful they also tend to be unpleasant to
+look at and are generally overkill for ordinary path matching.
+
+  qr!/user/show/(\d+)! -> $c->render(text => $users{$1});
+
+This is where routes come into play, they have been designed from the ground up
+to represent paths with placeholders.
+
+  /user/show/:id -> $c->render(text => $users{$id});
+
+The only difference between a static path and the route above is the C<:id>
+placeholder. One or more placeholders can be anywhere in the route.
+
+  /user/:action/:id
+
+A fundamental concept of the L<Mojolicious> router is that extracted
+placeholder values are turned into a hash.
+
+  /user/show/23 -> /user/:action/:id -> {action => 'show', id => 23}
+
+This hash is basically the center of every L<Mojolicious> application, you will
+learn more about this later on. Internally, routes get compiled to regular
+expressions, so you can get the best of both worlds with a little bit of
+experience.
+
+  /user/show/:id -> qr/(?-xism:^\/user\/show\/([^\/.]+))/
+
+A trailing slash in the path is always optional.
+
+  /user/show/23/ -> /user/:action/:id -> {action => 'show', id => 23}
+
+=head2 Reversibility
+
+One more huge advantage routes have over regular expressions is that they are
+easily reversible, extracted placeholders can be turned back into a path at any
+time.
+
+  /sebastian -> /:name -> {name => 'sebastian'}
+  {name => 'sebastian'} -> /:name -> /sebastian
+
+Every placeholder has a name, even if it's just an empty string.
+
+=head2 Standard placeholders
+
+Standard placeholders are the simplest form of placeholders, they use a colon
+prefix and match all characters except C</> and C<.>, similar to the regular
+expression C<([^/.]+)>.
+
+  /hello              -> /:name/hello -> undef
+  /sebastian/23/hello -> /:name/hello -> undef
+  /sebastian.23/hello -> /:name/hello -> undef
+  /sebastian/hello    -> /:name/hello -> {name => 'sebastian'}
+  /sebastian23/hello  -> /:name/hello -> {name => 'sebastian23'}
+  /sebastian 23/hello -> /:name/hello -> {name => 'sebastian 23'}
+
+All placeholders can be surrounded by C<E<lt>> and C<E<gt>> to separate them
+from the surrounding text.
+
+  /hello             -> /<:name>hello -> undef
+  /sebastian/23hello -> /<:name>hello -> undef
+  /sebastian.23hello -> /<:name>hello -> undef
+  /sebastianhello    -> /<:name>hello -> {name => 'sebastian'}
+  /sebastian23hello  -> /<:name>hello -> {name => 'sebastian23'}
+  /sebastian 23hello -> /<:name>hello -> {name => 'sebastian 23'}
+
+The colon prefix is optional for standard placeholders that are surrounded by
+C<E<lt>> and C<E<gt>>.
+
+  /iâ¥mojolicious -> /<one>â¥<two> -> {one => 'i', two => 'mojolicious'}
+
+=head2 Relaxed placeholders
+
+Relaxed placeholders are just like standard placeholders, but use a hash prefix
+and match all characters except C</>, similar to the regular expression
+C<([^/]+)>.
+
+  /hello              -> /#name/hello -> undef
+  /sebastian/23/hello -> /#name/hello -> undef
+  /sebastian.23/hello -> /#name/hello -> {name => 'sebastian.23'}
+  /sebastian/hello    -> /#name/hello -> {name => 'sebastian'}
+  /sebastian23/hello  -> /#name/hello -> {name => 'sebastian23'}
+  /sebastian 23/hello -> /#name/hello -> {name => 'sebastian 23'}
+
+They can be especially useful for manually matching file names with extensions,
+rather than using L<format detection|/"Formats">.
+
+  /music/song.mp3 -> /music/#filename -> {filename => 'song.mp3'}
+
+=head2 Wildcard placeholders
+
+Wildcard placeholders are just like the two types of placeholders above, but use
+an asterisk prefix and match absolutely everything, including C</> and C<.>,
+similar to the regular expression C<(.+)>.
+
+  /hello              -> /*name/hello -> undef
+  /sebastian/23/hello -> /*name/hello -> {name => 'sebastian/23'}
+  /sebastian.23/hello -> /*name/hello -> {name => 'sebastian.23'}
+  /sebastian/hello    -> /*name/hello -> {name => 'sebastian'}
+  /sebastian23/hello  -> /*name/hello -> {name => 'sebastian23'}
+  /sebastian 23/hello -> /*name/hello -> {name => 'sebastian 23'}
+
+They can be useful for manually matching entire file paths.
+
+  /music/rock/song.mp3 -> /music/*filepath -> {filepath => 'rock/song.mp3'}
+
+=head1 BASICS
+
+Most commonly used features every L<Mojolicious> developer should know about.
+
+=head2 Minimal route
+
+The attribute L<Mojolicious/"routes"> contains a router you can use to generate
+route structures.
+
+  # Application
+  package MyApp;
+  use Mojo::Base 'Mojolicious';
+
+  sub startup {
+    my $self = shift;
+
+    # Router
+    my $r = $self->routes;
+
+    # Route
+    $r->get('/welcome')->to(controller => 'foo', action => 'welcome');
+  }
+
+  1;
+
+The minimal route above will load and instantiate the class
+C<MyApp::Controller::Foo> and call its C<welcome> method. Routes are usually
+configured in the C<startup> method of the application class, but the router can
+be accessed from everywhere (even at runtime).
+
+  # Controller
+  package MyApp::Controller::Foo;
+  use Mojo::Base 'Mojolicious::Controller';
+
+  # Action
+  sub welcome {
+    my $self = shift;
+
+    # Render response
+    $self->render(text => 'Hello there.');
+  }
+
+  1;
+
+All routes match in the same order in which they were defined, and matching
+stops as soon as a suitable route has been found. So you can improve the routing
+performance by declaring your most frequently accessed routes first. A routing
+cache will also be used automatically to handle sudden traffic spikes more
+gracefully.
+
+=head2 Routing destination
+
+After you start a new route with methods like
+L<Mojolicious::Routes::Route/"get">, you can also give it a destination in the
+form of a hash using the chained method L<Mojolicious::Routes::Route/"to">.
+
+  # /welcome -> {controller => 'foo', action => 'welcome'}
+  $r->get('/welcome')->to(controller => 'foo', action => 'welcome');
+
+Now if the route matches an incoming request it will use the content of this
+hash to try and find appropriate code to generate a response.
+
+=head2 HTTP methods
+
+There are already shortcuts for the most common HTTP request methods like
+L<Mojolicious::Routes::Route/"post">, and for more control
+L<Mojolicious::Routes::Route/"any"> accepts an optional array reference with
+arbitrary request methods as first argument.
+
+  # PUT /hello  -> undef
+  # GET /hello  -> {controller => 'foo', action => 'hello'}
+  $r->get('/hello')->to(controller => 'foo', action => 'hello');
+
+  # PUT /hello -> {controller => 'foo', action => 'hello'}
+  $r->put('/hello')->to(controller => 'foo', action => 'hello');
+
+  # POST /hello -> {controller => 'foo', action => 'hello'}
+  $r->post('/hello')->to(controller => 'foo', action => 'hello');
+
+  # GET|POST /bye  -> {controller => 'foo', action => 'bye'}
+  $r->any(['GET', 'POST'] => '/bye')->to(controller => 'foo', action => 'bye');
+
+  # * /whatever -> {controller => 'foo', action => 'whatever'}
+  $r->any('/whatever')->to(controller => 'foo', action => 'whatever');
+
+There is one small exception, C<HEAD> requests are considered equal to C<GET>,
+but content will not be sent with the response even if it is present.
+
+  # GET /test  -> {controller => 'bar', action => 'test'}
+  # HEAD /test -> {controller => 'bar', action => 'test'}
+  $r->get('/test')->to(controller => 'bar', action => 'test');
+
+You can also use the C<_method> query parameter to override the request method.
+This can be very useful when submitting forms with browsers that only support
+C<GET> and C<POST>.
+
+  # PUT  /stuff             -> {controller => 'baz', action => 'stuff'}
+  # POST /stuff?_method=PUT -> {controller => 'baz', action => 'stuff'}
+  $r->put('/stuff')->to(controller => 'baz', action => 'stuff');
+
+=head2 IRIs
+
+IRIs are handled transparently, that means paths are guaranteed to be unescaped
+and decoded from bytes to characters.
+
+  # GET /â (Unicode snowman) -> {controller => 'foo', action => 'snowman'}
+  $r->get('/â')->to(controller => 'foo', action => 'snowman');
+
+=head2 Stash
+
+The generated hash of a matching route is actually the center of the whole
+L<Mojolicious> request cycle. We call it the stash, and it persists until a
+response has been generated.
+
+  # /bye -> {controller => 'foo', action => 'bye', mymessage => 'Bye'}
+  $r->get('/bye')
+    ->to(controller => 'foo', action => 'bye', mymessage => 'Bye');
+
+There are a few stash values with special meaning, such as C<controller> and
+C<action>, but you can generally fill it with whatever data you need to
+generate a response. Once dispatched the whole stash content can be changed at
+any time.
+
+  sub bye {
+    my $self = shift;
+
+    # Get message from stash
+    my $msg = $self->stash('mymessage');
+
+    # Change message in stash
+    $self->stash(mymessage => 'Welcome');
+  }
+
+For a full list of reserved stash values see
+L<Mojolicious::Controller/"stash">.
+
+=head2 Nested routes
+
+It is also possible to build tree structures from routes to remove repetitive
+code. A route with children can't match on its own though, only the actual
+endpoints of these nested routes can.
+
+  # /foo     -> undef
+  # /foo/bar -> {controller => 'foo', action => 'bar'}
+  my $foo = $r->any('/foo')->to(controller => 'foo');
+  $foo->get('/bar')->to(action => 'bar');
+
+The stash is simply inherited from route to route and newer values override old
+ones.
+
+  # /cats      -> {controller => 'cats', action => 'index'}
+  # /cats/nyan -> {controller => 'cats', action => 'nyan'}
+  # /cats/lol  -> {controller => 'cats', action => 'default'}
+  my $cats = $r->any('/cats')->to(controller => 'cats', action => 'default');
+  $cats->get('/')->to(action => 'index');
+  $cats->get('/nyan')->to(action => 'nyan');
+  $cats->get('/lol');
+
+With a few common prefixes you can also greatly improve the routing performance
+of applications with many routes, because children are only tried if the prefix
+matched first.
+
+=head2 Special stash values
+
+When the dispatcher sees C<controller> and C<action> values in the stash it
+will always try to turn them into a class and method to dispatch to. The
+C<controller> value gets converted from C<snake_case> to C<CamelCase> using
+L<Mojo::Util/"camelize"> and appended to one or more namespaces, defaulting to
+a controller namespace based on the application class (C<MyApp::Controller>),
+as well as the bare application class (C<MyApp>), and these namespaces are
+searched in that order. The action value is not changed at all, so both values
+are case-sensitive.
+
+  # Application
+  package MyApp;
+  use Mojo::Base 'Mojolicious';
+
+  sub startup {
+    my $self = shift;
+
+    # /bye -> MyApp::Controller::Foo->bye
+    $self->routes->get('/bye')->to(controller => 'foo', action => 'bye');
+  }
+
+  1;
+
+  # Controller
+  package MyApp::Controller::Foo;
+  use Mojo::Base 'Mojolicious::Controller';
+
+  # Action
+  sub bye {
+    my $self = shift;
+
+    # Render response
+    $self->render(text => 'Good bye.');
+  }
+
+  1;
+
+Controller classes are perfect for organizing code in larger projects. There
+are more dispatch strategies, but because controllers are the most commonly
+used ones they also got a special shortcut in the form of C<controller#action>.
+
+  # /bye -> {controller => 'foo', action => 'bye', mymessage => 'Bye'}
+  $r->get('/bye')->to('foo#bye', mymessage => 'Bye');
+
+During camelization C<-> characters get replaced with C<::>, this allows
+multi-level C<controller> hierarchies.
+
+  # / -> MyApp::Controller::Foo::Bar->hi
+  $r->get('/')->to('foo-bar#hi');
+
+You can also just specify the C<controller> in CamelCase form instead of
+snake_case.
+
+  # / -> MyApp::Controller::Foo::Bar->hi
+  $r->get('/')->to('Foo::Bar#hi');
+
+For security reasons the dispatcher will always check if the C<controller> is
+actually a subclass of L<Mojolicious::Controller> or L<Mojo> before dispatching
+to it.
+
+=head2 Namespaces
+
+You can use the C<namespace> stash value to change the namespace of a whole
+route with all its children.
+
+  # /bye -> MyApp::MyController::Foo::Bar->bye
+  $r->get('/bye')
+    ->to(namespace => 'MyApp::MyController::Foo::Bar', action => 'bye');
+
+The C<controller> is always converted from C<snake_case> to C<CamelCase> with
+L<Mojo::Util/"camelize">, and then appended to this C<namespace>.
+
+  # /bye -> MyApp::MyController::Foo::Bar->bye
+  $r->get('/bye')->to('foo-bar#bye', namespace => 'MyApp::MyController');
+
+  # /hey -> MyApp::MyController::Foo::Bar->hey
+  $r->get('/hey')->to('Foo::Bar#hey', namespace => 'MyApp::MyController');
+
+You can also change the default namespaces for all routes in the application
+with the router attribute L<Mojolicious::Routes/"namespaces">, which usually
+defaults to a namespace based on the application class (C<MyApp::Controller>),
+as well as the bare application class (C<MyApp>).
+
+  $r->namespaces(['MyApp::MyController']);
+
+=head2 Route to callback
+
+The C<cb> stash value, which won't be inherited by nested routes, can be used
+to bypass controllers and execute a callback instead.
+
+  $r->get('/bye')->to(cb => sub {
+    my $c = shift;
+    $c->render(text => 'Good bye.');
+  });
+
+But just like in L<Mojolicious::Lite> you can also pass the callback directly,
+which usually looks much better.
+
+  $r->get('/bye' => sub {
+    my $c = shift;
+    $c->render(text => 'Good bye.');
+  });
+
+=head2 Named routes
+
+Naming your routes will allow backreferencing in many methods and helpers
+throughout the whole framework, most of which internally rely on
+L<Mojolicious::Controller/"url_for"> for this.
+
+  # /foo/marcus -> {controller => 'foo', action => 'bar', user => 'marcus'}
+  $r->get('/foo/:user')->to('foo#bar')->name('baz');
+
+  # Generate URL "/foo/marcus" for route "baz"
+  my $url = $c->url_for('baz');
+
+  # Generate URL "/foo/jan" for route "baz"
+  my $url = $c->url_for('baz', user => 'jan');
+
+  # Generate URL "http://127.0.0.1:3000/foo/jan" for route "baz"
+  my $url = $c->url_for('baz', user => 'jan')->to_abs;
+
+You can assign a name with L<Mojolicious::Routes::Route/"name">, or let the
+router generate one automatically, which would be equal to the route itself
+without non-word characters, custom names have a higher precedence though.
+
+  # /foo/bar ("foobar")
+  $r->get('/foo/bar')->to('test#stuff');
+
+  # Generate URL "/foo/bar"
+  my $url = $c->url_for('foobar');
+
+To refer to the current route you can use the reserved name C<current> or no
+name at all.
+
+  # Generate URL for current route
+  my $url = $c->url_for('current');
+  my $url = $c->url_for;
+
+To check or get the name of the current route you can use the helper
+L<Mojolicious::Plugin::DefaultHelpers/"current_route">.
+
+  # Name for current route
+  my $name = $c->current_route;
+
+  # Check route name in code shared by multiple routes
+  $c->stash(button => 'green') if $c->current_route('login');
+
+=head2 Optional placeholders
+
+Extracted placeholder values will simply redefine older stash values if they
+already exist.
+
+  # /bye -> {controller => 'foo', action => 'bar', mymessage => 'bye'}
+  # /hey -> {controller => 'foo', action => 'bar', mymessage => 'hey'}
+  $r->get('/:mymessage')->to('foo#bar', mymessage => 'hi');
+
+One more interesting effect, a placeholder automatically becomes optional if
+there is already a stash value of the same name present, this works similar to
+the regular expression C<([^/.]+)?>.
+
+  # / -> {controller => 'foo', action => 'bar', mymessage => 'hi'}
+  $r->get('/:mymessage')->to('foo#bar', mymessage => 'hi');
+
+  # /test/123     -> {controller => 'foo', action => 'bar', mymessage => 'hi'}
+  # /test/bye/123 -> {controller => 'foo', action => 'bar', mymessage => 'bye'}
+  $r->get('/test/:mymessage/123')->to('foo#bar', mymessage => 'hi');
+
+And if two optional placeholders are only separated by a slash, that slash can
+become optional as well.
+
+  # /           -> {controller => 'foo',   action => 'bar'}
+  # /users      -> {controller => 'users', action => 'bar'}
+  # /users/list -> {controller => 'users', action => 'list'}
+  $r->get('/:controller/:action')->to('foo#bar');
+
+Special stash values like C<controller> and C<action> can also be placeholders,
+which is very convenient especially during development, but should only be used
+very carefully, because every controller method becomes a potential route. All
+uppercase methods as well as those starting with an underscore are
+automatically hidden from the router and you can use
+L<Mojolicious::Routes/"hide"> to add additional ones.
+
+  # Hide "create" method in all controllers
+  $r->hide('create');
+
+This has already been done for all attributes and methods from
+L<Mojolicious::Controller>.
+
+=head2 Restrictive placeholders
+
+A very easy way to make placeholders more restrictive are alternatives, you
+just make a list of possible values, which then work similar to the regular
+expression C<(bender|leela)>.
+
+  # /fry    -> undef
+  # /bender -> {controller => 'foo', action => 'bar', name => 'bender'}
+  # /leela  -> {controller => 'foo', action => 'bar', name => 'leela'}
+  $r->get('/:name' => [name => ['bender', 'leela']])->to('foo#bar');
+
+You can also adjust the regular expressions behind placeholders directly, just
+make sure not to use C<^> and C<$> or capturing groups C<(...)>, because
+placeholders become part of a larger regular expression internally,
+non-capturing groups C<(?:...)> are fine though.
+
+  # /23   -> {controller => 'foo', action => 'bar', number => 23}
+  # /test -> undef
+  $r->get('/:number' => [number => qr/\d+/])->to('foo#bar');
+
+  # /23   -> undef
+  # /test -> {controller => 'foo', action => 'bar', name => 'test'}
+  $r->get('/:name' => [name => qr/[a-zA-Z]+/])->to('foo#bar');
+
+This way you get easily readable routes and the raw power of regular
+expressions.
+
+=head2 Placeholder types
+
+And if you have multiple routes using restrictive placeholders you can also turn
+them into placeholder types with L<Mojolicious::Routes/"add_type">.
+
+  # A type with alternatives
+  $r->add_type(futurama_name => ['bender', 'leela']);
+
+  # /fry    -> undef
+  # /bender -> {controller => 'foo', action => 'bar', name => 'bender'}
+  # /leela  -> {controller => 'foo', action => 'bar', name => 'leela'}
+  $r->get('/<name:futurama_name>')->to('foo#bar');
+
+Placeholder types work just like restrictive placeholders, they are just
+reusable with the C<E<lt>placeholder:typeE<gt>> notation.
+
+  # A type adjusting the regular expression
+  $r->add_type(upper => qr/[A-Z]+/);
+
+  # /user/ROOT -> {controller => 'users', action => 'show', name => 'ROOT'}
+  # /user/root -> undef
+  # /user/23   -> undef
+  $r->get('/user/<name:upper>')->to('users#show');
+
+Some types like C<num> are used so commonly that they are available by default.
+
+  # /article/12   -> {controller => 'article', action => 'show', id => 12}
+  # /article/test -> undef
+  $r->get('/article/<id:num>')->to('articles#show');
+
+For a full list of available placeholder types see also
+L<Mojolicious::Routes/"TYPES">.
+
+=head2 Introspection
+
+The command L<Mojolicious::Command::routes> can be used from the command line
+to list all available routes together with names and underlying regular
+expressions.
+
+  $ ./myapp.pl routes -v
+  /foo/:name  ....  POST  fooname  ^/foo/([^/.]+)/?(?:\.([^/]+))?$
+  /bar        ..U.  *     bar      ^/bar
+    +/baz     ...W  GET   baz      ^/baz/?(?:\.([^/]+))?$
+  /yada       ....  *     yada     ^/yada/?(?:\.([^/]+))?$
+
+=head2 Under
+
+To share code with multiple nested routes you can use
+L<Mojolicious::Routes::Route/"under">, because unlike normal nested routes, the
+routes generated with it have their own intermediate destination and result in
+additional dispatch cycles when they match.
+
+  # /foo     -> undef
+  # /foo/bar -> {controller => 'foo', action => 'baz'}
+  #             {controller => 'foo', action => 'bar'}
+  my $foo = $r->under('/foo')->to('foo#baz');
+  $foo->get('/bar')->to('#bar');
+
+The actual action code for this destination needs to return a true value or the
+dispatch chain will be broken, this can be a very powerful tool for
+authentication.
+
+  # /blackjack -> {cb => sub {...}}
+  #               {controller => 'hideout', action => 'blackjack'}
+  my $auth = $r->under('/' => sub {
+    my $c = shift;
+
+    # Authenticated
+    return 1 if $c->req->headers->header('X-Bender');
+
+    # Not authenticated
+    $c->render(text => "You're not Bender.", status => 401);
+    return undef;
+  });
+  $auth->get('/blackjack')->to('hideout#blackjack');
+
+Broken dispatch chains can be continued by calling the method
+L<Mojolicious::Controller/"continue">, this allows for example, non-blocking
+operations to finish before reaching the next dispatch cycle.
+
+  my $maybe = $r->under('/maybe' => sub {
+    my $c = shift;
+
+    # Wait 3 seconds and then give visitors a 50% chance to continue
+    Mojo::IOLoop->timer(3 => sub {
+
+      # Loser
+      return $c->render(text => 'No luck.') unless int rand 2;
+
+      # Winner
+      $c->continue;
+    });
+
+    return undef;
+  });
+  $maybe->get('/')->to('maybe#winner');
+
+Every destination is just a snapshot of the stash at the time the route
+matched, and only the C<format> value is shared by all of them. For a little
+more power you can introspect the preceding and succeeding destinations with
+L<Mojolicious::Controller/"match">.
+
+  # Action of the fourth dispatch cycle
+  my $action = $c->match->stack->[3]{action};
+
+=head2 Formats
+
+File extensions like C<.html> and C<.txt> at the end of a route are
+automatically detected and stored in the stash value C<format>.
+
+  # /foo      -> {controller => 'foo', action => 'bar'}
+  # /foo.html -> {controller => 'foo', action => 'bar', format => 'html'}
+  # /foo.txt  -> {controller => 'foo', action => 'bar', format => 'txt'}
+  $r->get('/foo')->to('foo#bar');
+
+This for example, allows multiple templates in different formats to share the
+same action code. Restrictive placeholders can also be used to limit the
+allowed formats.
+
+  # /foo.txt -> undef
+  # /foo.rss -> {controller => 'foo', action => 'bar', format => 'rss'}
+  # /foo.xml -> {controller => 'foo', action => 'bar', format => 'xml'}
+  $r->get('/foo' => [format => ['rss', 'xml']])->to('foo#bar');
+
+A C<format> value can also be passed to L<Mojolicious::Controller/"url_for">.
+
+  # /foo/bar.txt -> {controller => 'foo', action => 'bar', format => 'txt'}
+  $r->get('/foo/:action')->to('foo#')->name('baz');
+
+  # Generate URL "/foo/bar.txt" for route "baz"
+  my $url = $c->url_for('baz', action => 'bar', format => 'txt');
+
+Or you can just disable format detection with a special type of restrictive
+placeholder, which gets inherited by nested routes, and then re-enable it on
+demand.
+
+  # /foo      -> {controller => 'foo', action => 'bar'}
+  # /foo.html -> undef
+  $r->get('/foo' => [format => 0])->to('foo#bar');
+
+  # /foo      -> {controller => 'foo', action => 'bar'}
+  # /foo.html -> undef
+  # /baz      -> undef
+  # /baz.txt  -> {controller => 'baz', action => 'yada', format => 'txt'}
+  # /baz.html -> {controller => 'baz', action => 'yada', format => 'html'}
+  # /baz.xml  -> undef
+  my $inactive = $r->under([format => 0]);
+  $inactive->get('/foo')->to('foo#bar');
+  $inactive->get('/baz' => [format => ['txt', 'html']])->to('baz#yada');
+
+=head2 WebSockets
+
+With the method L<Mojolicious::Routes::Route/"websocket"> you can restrict
+access to WebSocket handshakes, which are normal C<GET> requests with some
+additional information.
+
+  # /echo (WebSocket handshake)
+  $r->websocket('/echo')->to('foo#echo');
+
+  # Controller
+  package MyApp::Controller::Foo;
+  use Mojo::Base 'Mojolicious::Controller';
+
+  # Action
+  sub echo {
+    my $self = shift;
+    $self->on(message => sub {
+      my ($self, $msg) = @_;
+      $self->send("echo: $msg");
+    });
+  }
+
+  1;
+
+The connection gets established when you respond to the WebSocket handshake
+request with a C<101> response status, which happens automatically if you
+subscribe to an event with L<Mojolicious::Controller/"on"> or send a message
+with L<Mojolicious::Controller/"send"> right away.
+
+  GET /echo HTTP/1.1
+  Host: mojolicious.org
+  User-Agent: Mojolicious (Perl)
+  Connection: Upgrade
+  Upgrade: websocket
+  Sec-WebSocket-Key: IDM3ODE4NDk2MjA1OTcxOQ==
+  Sec-WebSocket-Version: 13
+
+  HTTP/1.1 101 Switching Protocols
+  Server: Mojolicious (Perl)
+  Date: Tue, 03 Feb 2015 17:08:24 GMT
+  Connection: Upgrade
+  Upgrade: websocket
+  Sec-WebSocket-Accept: SWsp5N2iNxPbHlcOTIw8ERvyVPY=
+
+=head2 Catch-all route
+
+Since routes match in the order in which they were defined, you can catch all
+requests that did not match in your last route with an optional wildcard
+placeholder.
+
+  # * /*
+  $r->any('/*whatever' => {whatever => ''} => sub {
+    my $c        = shift;
+    my $whatever = $c->param('whatever');
+    $c->render(text => "/$whatever did not match.", status => 404);
+  });
+
+=head2 Conditions
+
+Conditions such as C<headers>, C<agent> and C<host> from
+L<Mojolicious::Plugin::HeaderCondition> can be applied to any route with the
+method L<Mojolicious::Routes::Route/"over">, and allow even more powerful route
+constructs.
+
+  # / (Origin: http://perl.org)
+  $r->get('/')->over(headers => {Origin => qr/perl\.org/})->to('foo#bar');
+
+  # / (Firefox)
+  $r->get('/')->over(agent => qr/Firefox/)->to('browser-test#firefox');
+
+  # / (Internet Explorer)
+  $r->get('/')->over(agent => qr/Internet Explorer/)->to('browser-test#ie');
+
+  # http://mojolicious.org/perldoc
+  $r->get('/perldoc')->over(host => 'mojolicious.org')->to('perldoc#index');
+
+Just be aware that conditions are too complex for the routing cache, which
+normally speeds up recurring requests, and can therefore reduce performance.
+
+=head2 Hooks
+
+Hooks operate outside the routing system and allow you to extend the framework
+itself by sharing code with all requests indiscriminately through
+L<Mojolicious/"hook">, which makes them a very powerful tool especially for
+plugins.
+
+  # Application
+  package MyApp;
+  use Mojo::Base 'Mojolicious';
+
+  sub startup {
+    my $self = shift;
+
+    # Check all requests for a "/test" prefix
+    $self->hook(before_dispatch => sub {
+      my $c = shift;
+      $c->render(text => 'This request did not reach the router.')
+        if $c->req->url->path->contains('/test');
+    });
+
+    # These will not be reached if the hook above renders a response
+    my $r = $self->routes;
+    $r->get('/welcome')->to('foo#welcome');
+    $r->post('/bye')->to('foo#bye');
+  }
+
+  1;
+
+Post-processing the response to add or remove headers is a very common use.
+
+  # Make sure static files are cached
+  $app->hook(after_static => sub {
+    my $c = shift;
+    $c->res->headers->cache_control('max-age=3600, must-revalidate');
+  });
+
+  # Remove a default header
+  $app->hook(after_dispatch => sub {
+    my $c = shift;
+    $c->res->headers->remove('Server');
+  });
+
+Same for pre-processing the request.
+
+  # Choose template variant based on request headers
+  $app->hook(before_dispatch => sub {
+    my $c = shift;
+    return unless my $agent = $c->req->headers->user_agent;
+    $c->stash(variant => 'ie') if $agent =~ /Internet Explorer/;
+  });
+
+Or more advanced extensions to add monitoring to your application.
+
+  # Forward exceptions to a web service
+  $app->hook(after_dispatch => sub {
+    my $c = shift;
+    return unless my $e = $c->stash('exception');
+    $c->ua->post('https://example.com/bugs' => form => {exception => $e});
+  });
+
+You can even extend much of the core functionality.
+
+  # Make controller object available to actions as $_
+  $app->hook(around_action => sub {
+    my ($next, $c, $action, $last) = @_;
+    local $_ = $c;
+    return $next->();
+  });
+
+  # Pass route name as argument to actions
+  $app->hook(around_action => sub {
+    my ($next, $c, $action, $last) = @_;
+    return $c->$action($c->current_route);
+  });
+
+For a full list of available hooks see L<Mojolicious/"HOOKS">.
+
+=head1 ADVANCED
+
+Less commonly used and more powerful features.
+
+=head2 Shortcuts
+
+To make route generation more expressive, you can also add your own shortcuts
+with L<Mojolicious::Routes/"add_shortcut">.
+
+  # Simple "resource" shortcut
+  $r->add_shortcut(resource => sub {
+    my ($r, $name) = @_;
+
+    # Prefix for resource
+    my $resource = $r->any("/$name")->to("$name#");
+
+    # Render a list of resources
+    $resource->get->to('#index')->name($name);
+
+    # Render a form to create a new resource (submitted to "store")
+    $resource->get('/create')->to('#create')->name("create_$name");
+
+    # Store newly created resource (submitted by "create")
+    $resource->post->to('#store')->name("store_$name");
+
+    # Render a specific resource
+    $resource->get('/:id')->to('#show')->name("show_$name");
+
+    # Render a form to edit a resource (submitted to "update")
+    $resource->get('/:id/edit')->to('#edit')->name("edit_$name");
+
+    # Store updated resource (submitted by "edit")
+    $resource->put('/:id')->to('#update')->name("update_$name");
+
+    # Remove a resource
+    $resource->delete('/:id')->to('#remove')->name("remove_$name");
+
+    return $resource;
+  });
+
+  # GET /users         -> {controller => 'users', action => 'index'}
+  # GET /users/create  -> {controller => 'users', action => 'create'}
+  # POST /users        -> {controller => 'users', action => 'store'}
+  # GET /users/23      -> {controller => 'users', action => 'show', id => 23}
+  # GET /users/23/edit -> {controller => 'users', action => 'edit', id => 23}
+  # PUT /users/23      -> {controller => 'users', action => 'update', id => 23}
+  # DELETE /users/23   -> {controller => 'users', action => 'remove', id => 23}
+  $r->resource('users');
+
+=head2 Rearranging routes
+
+From application startup until the first request has arrived, all routes can
+still be moved around or even removed with methods like
+L<Mojolicious::Routes::Route/"add_child"> and
+L<Mojolicious::Routes::Route/"remove">.
+
+  # GET /example/show -> {controller => 'example', action => 'show'}
+  my $show = $r->get('/show')->to('example#show');
+  $r->any('/example')->add_child($show);
+
+  # Nothing
+  $r->get('/secrets/show')->to('secrets#show')->name('show_secrets');
+  $r->find('show_secrets')->remove;
+
+Especially for rearranging routes created by plugins this can be very useful,
+to find routes by their name you can use L<Mojolicious::Routes::Route/"find">.
+
+  # GET /example/test -> {controller => 'example', action => 'test'}
+  $r->get('/something/else')->to('something#else')->name('test');
+  my $test = $r->find('test');
+  $test->pattern->parse('/example/test');
+  $test->pattern->defaults({controller => 'example', action => 'test'});
+
+Even the route pattern and destination can still be changed with
+L<Mojolicious::Routes::Pattern/"parse"> and
+L<Mojolicious::Routes::Pattern/"defaults">.
+
+=head2 Adding conditions
+
+You can also add your own conditions with the method
+L<Mojolicious::Routes/"add_condition">. All conditions are basically router
+plugins that run every time a new request arrives, and which need to return a
+true value for the route to match.
+
+  # A condition that randomly allows a route to match
+  $r->add_condition(random => sub {
+    my ($route, $c, $captures, $num) = @_;
+
+    # Loser
+    return undef if int rand $num;
+
+    # Winner
+    return 1;
+  });
+
+  # /maybe (25% chance)
+  $r->get('/maybe')->over(random => 4)->to('foo#bar');
+
+Use whatever request information you need.
+
+  # A condition to check query parameters (useful for mock web services)
+  $r->add_condition(query => sub {
+    my ($route, $c, $captures, $hash) = @_;
+
+    for my $key (keys %$hash) {
+      my $param = $c->req->url->query->param($key);
+      return undef unless defined $param && $param eq $hash->{$key};
+    }
+
+    return 1;
+  });
+
+  # /hello?to=world&test=1
+  $r->get('/hello')->over(query => {test => 1, to => 'world'})->to('foo#bar');
+
+=head2 Condition plugins
+
+You can also package your conditions as reusable plugins.
+
+  # Plugin
+  package Mojolicious::Plugin::WerewolfCondition;
+  use Mojo::Base 'Mojolicious::Plugin';
+
+  use Astro::MoonPhase;
+
+  sub register {
+    my ($self, $app) = @_;
+
+    # Add "werewolf" condition
+    $app->routes->add_condition(werewolf => sub {
+      my ($route, $c, $captures, $days) = @_;
+
+      # Keep the werewolves out!
+      return undef if abs(14 - (phase(time))[2]) > ($days / 2);
+
+      # It's ok, no werewolf
+      return 1;
+    });
+  }
+
+  1;
+
+Now just load the plugin and you are ready to use the condition in all your
+applications.
+
+  # Application
+  package MyApp;
+  use Mojo::Base 'Mojolicious';
+
+  sub startup {
+    my $self = shift;
+
+    # Plugin
+    $self->plugin('WerewolfCondition');
+
+    # /hideout (keep them out for 4 days after full moon)
+    $self->routes->get('/hideout')->over(werewolf => 4)
+      ->to(controller => 'foo', action => 'bar');
+  }
+
+  1;
+
+=head2 Mount applications
+
+The easiest way to embed one application into another is
+L<Mojolicious::Plugin::Mount>, which allows you to mount whole self-contained
+applications under a domain and/or prefix.
+
+  use Mojolicious::Lite;
+
+  # Whole application mounted under "/prefix"
+  plugin Mount => {'/prefix' => '/home/sri/myapp.pl'};
+
+  # Mount application with subdomain
+  plugin Mount => {'test.example.com' => '/home/sri/myapp2.pl'};
+
+  # Normal route
+  get '/' => sub { shift->render(text => 'Hello World!') };
+
+  app->start;
+
+=head2 Embed applications
+
+For a little more power you can also embed applications by using them instead of
+a controller. This allows for example, the use of the L<Mojolicious::Lite>
+domain specific language in normal L<Mojolicious> controllers.
+
+  # Controller
+  package MyApp::Controller::Bar;
+  use Mojolicious::Lite;
+
+  # /hello
+  get '/hello' => sub {
+    my $c    = shift;
+    my $name = $c->param('name');
+    $c->render(text => "Hello $name.");
+  };
+
+  1;
+
+With the method L<Mojolicious::Routes::Route/"detour"> which is very similar to
+L<Mojolicious::Routes::Route/"to">, you can allow the route to partially match
+and use only the remaining path in the embedded application, the base path will
+be passed along in the C<path> stash value.
+
+  # /foo/*
+  $r->any('/foo')->detour('bar#', name => 'Mojo');
+
+A minimal embeddable application is nothing more than a subclass of
+L<Mojolicious>, containing a C<handler> method accepting
+L<Mojolicious::Controller> objects.
+
+  package MyApp::Controller::Bar;
+  use Mojo::Base 'Mojolicious';
+
+  sub handler {
+    my ($self, $c) = @_;
+    $c->res->code(200);
+    my $name = $c->param('name');
+    $c->res->body("Hello $name.");
+  }
+
+  1;
+
+The host application will only share very little information with the embedded
+application through the stash. So you cannot currently use route placeholders in
+routes leading to embedded applications, since that would cause problems with
+L<Mojolicious::Controller/"url_for">.
+
+=head2 Application plugins
+
+You can even package applications as self-contained reusable plugins.
+
+  # Plugin
+  package Mojolicious::Plugin::MyEmbeddedApp;
+  use Mojo::Base 'Mojolicious::Plugin';
+
+  sub register {
+    my ($self, $app) = @_;
+
+    # Automatically add route
+    $app->routes->any('/foo')->detour(app => EmbeddedApp::app());
+  }
+
+  package EmbeddedApp;
+  use Mojolicious::Lite;
+
+  get '/bar' => 'bar';
+
+  1;
+  __DATA__
+  @@ bar.html.ep
+  Hello World!
+
+The C<app> stash value, which won't be inherited by nested routes, can be used
+for already instantiated applications. Now just load the plugin and you're
+done.
+
+  # Application
+  package MyApp;
+  use Mojo::Base 'Mojolicious';
+
+  sub startup {
+    my $self = shift;
+
+    # Plugin
+    $self->plugin('MyEmbeddedApp');
+  }
+
+  1;
+
+=head1 MORE
+
+You can continue with L<Mojolicious::Guides> now or take a look at the
+L<Mojolicious wiki|http://github.com/kraih/mojo/wiki>, which contains a lot more
+documentation and examples by many different authors.
+
+=head1 SUPPORT
+
+If you have any questions the documentation might not yet answer, don't
+hesitate to ask on the
+L<mailing list|http://groups.google.com/group/mojolicious> or the official IRC
+channel C<#mojo> on C<irc.perl.org>
+(L<chat now!|https://chat.mibbit.com/?channel=%23mojo&server=irc.perl.org>).
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Guides/Testing.pod
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Guides/Testing.pod	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Guides/Testing.pod	(revision 32205)
@@ -0,0 +1,718 @@
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Guides::Testing - Web Application Testing Made Easy
+
+=head1 OVERVIEW
+
+This document is an introduction to testing web applications with L<Test::Mojo>.
+L<Test::Mojo> can be thought of as a module that provides all of the tools and
+testing assertions needed to test web applications in a Perl-ish way.
+
+While L<Test::Mojo> can be used to test any web application, it has shortcuts
+designed to make testing L<Mojolicious> web applications easy and pain-free.
+
+Please refer to the L<Test::Mojo> documentation for a complete reference to many
+of the ideas and syntax introduced in this document.
+
+A test file for a simple web application might look like:
+
+  use Mojo::Base -strict;
+
+  use Test::Mojo;
+  use Test::More;
+
+  # Start a Mojolicious app named "Celestial"
+  my $t = Test::Mojo->new('Celestial');
+
+  # Post a JSON document
+  $t->post_ok('/notifications' => json => {event => 'full moon'})
+    ->status_is(201)
+    ->json_is('/message' => 'notification created');
+
+  # Perform GET requests and look at the responses
+  $t->get_ok('/sunrise')
+    ->status_is(200)
+    ->content_like(qr/ am$/);
+  $t->get_ok('/sunset')
+    ->status_is(200)
+    ->content_like(qr/ pm$/);
+
+  # Post a URL-encoded form
+  $t->post_ok('/insurance' => form => {name => 'Jimmy', amount => 'â¬3.000.000'})
+    ->status_is(200);
+
+  # Use Test::More's like() to check the response
+  like $t->tx->res->dom->at('div#thanks')->text, qr/thank you/, 'thanks';
+
+  done_testing();
+
+In the rest of this document we'll explore these concepts and others related to
+L<Test::Mojo>.
+
+=head1 CONCEPTS
+
+Essentials every L<Mojolicious> developer should know.
+
+=head2 L<Test::Mojo> at a glance
+
+The L<Test::More> module bundled with Perl includes several primitive test
+assertions, such as C<ok>, C<is>, C<isnt>, C<like>, C<unlike>, C<cmp_ok>, etc.
+An assertion "passes" if its expression returns a true value. The assertion
+method prints "ok" or "not ok" if an assertion passes or fails (respectively).
+
+L<Test::Mojo> supplies additional test assertions organized around the web
+application request/response transaction (transport, response headers, response
+bodies, etc.), and WebSocket communications.
+
+One interesting thing of note: the return value of L<Test::Mojo> object
+assertions is always the test object itself, allowing us to "chain" test
+assertion methods. So rather than grouping related test statements like this:
+
+  $t->get_ok('/frogs');
+  $t->status_is(200);
+  $t->content_like(qr/bullfrog/);
+  $t->content_like(qr/hypnotoad/);
+
+Method chaining allows us to connect test assertions that belong together:
+
+  $t->get_ok('/frogs')
+    ->status_is(200)
+    ->content_like(qr/bullfrog/)
+    ->content_like(qr/hypnotoad/);
+
+This makes for a much more I<concise> and I<coherent> testing experience:
+concise because we are not repeating the invocant for each test, and coherent
+because assertions that belong to the same request are syntactically bound in
+the same method chain.
+
+Occasionally it makes sense to break up a test to perform more complex
+assertions on a response. L<Test::Mojo> exposes the entire transaction object so
+you can get all the data you need from a response:
+
+  $t->put_ok('/bees' => json => {type => 'worker', name => 'Karl'})
+    ->status_is(202)
+    ->json_has('/id');
+
+  # Pull out the id from the response
+  my $newbee = $t->tx->res->json('/id');
+
+  # Make a new request with data from the previous response
+  $t->get_ok("/bees/$newbee")
+    ->status_is(200)
+    ->json_is('/name' => 'Karl');
+
+The L<Test::Mojo> object is I<stateful>. As long as we haven't started a new
+transaction by invoking one of the C<*_ok> methods, the request and response
+objects from the previous transaction are available in the L<Test::Mojo>
+object:
+
+  # First transaction
+  $t->get_ok('/frogs?q=bullfrog' => {'Content-Type' => 'application/json'})
+    ->status_is(200)
+    ->json_like('/0/species' => qr/catesbeianus/i);
+
+  # Still first transaction
+  $t->content_type_is('application/json');
+
+  # Second transaction
+  $t->get_ok('/frogs?q=banjo' => {'Content-Type' => 'text/html'})
+    ->status_is(200)
+    ->content_like(qr/interioris/i);
+
+  # Still second transaction
+  $t->content_type_is('text/html');
+
+This statefulness also enables L<Test::Mojo> to handle sessions, follow
+redirects, and inspect past responses during a redirect.
+
+=head2 The L<Test::Mojo> object
+
+The L<Test::Mojo> object manages the Mojolicious application lifecycle (if a
+Mojolicious application class is supplied) as well as exposes the built-in
+L<Mojo::UserAgent> object. To create a bare L<Test::Mojo> object:
+
+  my $t = Test::Mojo->new;
+
+This object initializes a L<Mojo::UserAgent> object and provides a variety of
+test assertion methods for accessing a web application. For example, with this
+object, we could test any running web application:
+
+  $t->get_ok('https://www.google.com/')
+    ->status_is(200)
+    ->content_like(qr/search/i);
+
+You can access the user agent directly if you want to make web requests without
+triggering test assertions:
+
+  my $tx = $t->ua->post(
+    'https://duckduckgo.com/html' => form => {q => 'hypnotoad'});
+  $tx->result->dom->find('a.result__a')->each(sub { say $_->text });
+
+See L<Mojo::UserAgent> for the complete API and return values.
+
+=head2 Testing Mojolicious applications
+
+If you pass the name of a L<Mojolicious> application class (e.g., 'MyApp') to
+the L<Test::Mojo> constructor, L<Test::Mojo> will instantiate the class and
+start it, and cause it to listen on a random (unused) port number. Testing a
+Mojolicious application using L<Test::Mojo> will never conflict with running
+applications, including the application you're testing.
+
+The L<Mojo::UserAgent> object in L<Test::Mojo> will know where the application
+is running and make requests to it. Once the tests have completed, the
+L<Mojolicious> application will be torn down.
+
+  # Listens on localhost:32114 (some unused TCP port)
+  my $t = Test::Mojo->new('Frogs');
+
+This object initializes a L<Mojo::UserAgent> object, loads the Mojolicious
+application C<Frogs>, binds and listens on a free TCP port (e.g., 32114), and
+starts the application event loop. When the L<Test::Mojo> object (C<$t>) goes
+out of scope, the application is stopped.
+
+Relative URLs in the test object method assertions (C<get_ok>, C<post_ok>, etc.)
+will be sent to the Mojolicious application started by L<Test::Mojo>:
+
+  # Rewritten to "http://localhost:32114/frogs"
+  $t->get_ok('/frogs');
+
+L<Test::Mojo> has a lot of handy shortcuts built into it to make testing
+L<Mojolicious> or L<Mojolicious::Lite> applications enjoyable.
+
+=head3 An example
+
+Let's spin up a Mojolicious application using C<mojo generate app MyApp>. The
+C<mojo> utility will create a working application and a C<t> directory with a
+working test file:
+
+  $ mojo generate app MyApp
+  [mkdir] /my_app/script
+  [write] /my_app/script/my_app
+  [chmod] /my_app/script/my_app 744
+  ...
+  [mkdir] /my_app/t
+  [write] /my_app/t/basic.t
+  ...
+
+Let's run the tests (we'll create the C<log> directory to quiet the application
+output):
+
+  $ cd my_app
+  $ mkdir log
+  $ prove -lv t
+  t/basic.t ..
+  ok 1 - GET /
+  ok 2 - 200 OK
+  ok 3 - content is similar
+  1..3
+  ok
+  All tests successful.
+  Files=1, Tests=3,  0 wallclock secs ( 0.03 usr  0.01 sys +  0.33 cusr  0.07
+   csys =  0.44 CPU)
+  Result: PASS
+
+The boilerplate test file looks like this:
+
+  use Mojo::Base -strict;
+
+  use Test::More;
+  use Test::Mojo;
+
+  my $t = Test::Mojo->new('MyApp');
+  $t->get_ok('/')->status_is(200)->content_like(qr/Mojolicious/i);
+
+  done_testing();
+
+Here we can see our application class name C<MyApp> is passed to the
+L<Test::Mojo> constructor. Under the hood, L<Test::Mojo> creates a new
+L<Mojo::Server> instance, loads C<MyApp> (which we just created), and runs the
+application. We write our tests with relative URLs because L<Test::Mojo> takes
+care of getting the request to the running test application (since its port may
+change between runs).
+
+=head3 Testing with configuration data
+
+We can alter the behavior of our application using environment variables (such
+as C<MOJO_MODE>) and through configuration values. One nice feature of
+L<Test::Mojo> is its ability to pass configuration values directly from its
+constructor.
+
+Let's modify our application and add a "feature flag" to enable a new feature
+when the C<enable_weather> configuration value is set:
+
+  # Load configuration from hash returned by "my_app.conf"
+  my $config = $self->plugin('Config');
+
+  # Normal route to controller
+  $r->get('/')->to('example#welcome');
+
+  # NEW: this route only exists if "enable_weather" is set in the configuration
+  if ($config->{enable_weather}) {
+    $r->get('/weather' => sub { shift->render(text => "It's hot! ð¥") }
+  }
+
+To test this new feature, we don't even need to create a configuration fileâwe
+can simply pass the configuration data to the application directly via
+L<Test::Mojo>'s constructor:
+
+  my $t = Test::Mojo->new(MyApp => {enable_weather => 1});
+  $t->get_ok('/')->status_is(200)->content_like(qr/Mojolicious/i);
+  $t->get_ok('/weather')->status_is(200)->content_like(qr/ð¥/);
+
+When we run these tests, L<Test::Mojo> will pass this configuration data to the
+application, which will cause it to create a special C</weather> route that we
+can access in our tests. Unless C<enable_weather> is set in a configuration
+file, this route will not exist when the application runs. Feature flags like
+this allow us to do soft rollouts of features, targeting a small audience for a
+period of time. Once the feature has been proven, we can refactor the
+conditional and make it a full release.
+
+This example shows how easy it is to start testing a Mojolicious application and
+how to set specific application configuration directives from a test file.
+
+=head3 Testing application helpers
+
+Let's say we register a helper in our application to generate an HTTP Basic
+Authorization header:
+
+  use Mojo::Util 'b64_encode';
+
+  app->helper(basic_auth => sub {
+    my ($c, @values) = @_;
+    return {Authorization => 'Basic ' . b64_encode join(':' => @values), ''};
+  });
+
+How do we test application helpers like this? L<Test::Mojo> has access to the
+application object, which allows us to invoke helpers from our test file:
+
+  my $t = Test::Mojo->new('MyApp');
+
+  is_deeply $t->app->basic_auth(bif => "Bif's Passwerdd"),
+    {Authorization => 'Basic YmlmOkJpZidzIFBhc3N3ZXJkZA=='},
+    'correct header value';
+
+Any aspect of the application (helpers, plugins, routes, etc.) can be
+introspected from L<Test::Mojo> through the application object. This enables us
+to get deep test coverage of L<Mojolicious>-based applications.
+
+=head1 ASSERTIONS
+
+This section describes the basic test assertions supplied by L<Test::Mojo>.
+There are four broad categories of assertions for HTTP requests:
+
+=over 2
+
+=item * HTTP requests
+
+=item * HTTP response status
+
+=item * HTTP response headers
+
+=item * HTTP response content/body
+
+=back
+
+WebSocket test assertions are covered in L</Testing WebSocket web services>.
+
+=head2 HTTP request assertions
+
+L<Test::Mojo> has a L<Mojo::UserAgent> object that allows it to make HTTP
+requests and check for HTTP transport errors. HTTP request assertions include
+C<get_ok>, C<post_ok>, etc. These assertions do not test whether the request
+was handled I<successfully>, only that the web application handled the request
+in an HTTP compliant way.
+
+You may also make HTTP requests using custom verbs (beyond C<GET>, C<POST>,
+C<PUT>, etc.) by building your own transaction object. See
+L</"Custom transactions"> below.
+
+=head3 Using HTTP request assertions
+
+To post a URL-encoded form to the C</calls> endpoint of an application, we
+simply use the C<form> content type shortcut:
+
+  $t->post_ok('/calls' => form => {to => '+43.55.555.5555'});
+
+Which will create the following HTTP request:
+
+  POST /calls HTTP/1.1
+  Content-Length: 20
+  Content-Type: application/x-www-form-urlencoded
+
+  to=%2B43.55.555.5555
+
+The C<*_ok> HTTP request assertion methods accept the same arguments as their
+corresponding L<Mojo::UserAgent> methods (except for the callback argument).
+This allows us to set headers and build query strings for authentic test
+situations:
+
+  $t->get_ok('/internal/personnel' => {Authorization => 'Token secret-password'}
+    => form => {q => 'Professor Plum'});
+
+which generates the following request:
+
+  GET /internal/personnel?q=Professor+Plum HTTP/1.1
+  Content-Length: 0
+  Authorization: Token secret-password
+
+The C<form> content generator (see L<Mojo::UserAgent::Transactor>) will generate
+a query string for C<GET> requests and C<application/x-www-form-urlencoded> or
+C<multipart/form-data> for POST requests.
+
+While these C<*_ok> assertions make the HTTP I<requests> we expect, they tell us
+little about I<how well> the application handled the request. The application
+we're testing might have returned any content-type, body, or HTTP status code
+(200, 302, 400, 404, 500, etc.) and we wouldn't know it.
+
+L<Test::Mojo> provides assertions to test almost every aspect of the HTTP
+response, including the HTTP response status code, the value of the
+C<Content-Type> header, and other arbitrary HTTP header information.
+
+=head2 HTTP response status code
+
+While not technically an HTTP header, the status line is the first line in an
+HTTP response and is followed by the response headers. Testing the response
+status code is common in REST-based and other web applications that use the HTTP
+status codes to broadly indicate the type of response the server is returning.
+
+Testing the status code is as simple as adding the C<status_is> assertion:
+
+  $t->post_ok('/doorbell' => form => {action => 'ring once'})
+    ->status_is(200);
+
+Along with C<status_isnt>, this will cover most needs. For more elaborate status
+code testing, you can access the response internals directly:
+
+  $t->post_ok('/doorbell' => form => {action => 'ring once'});
+  is $t->tx->res->message, 'Moved Permanently', 'try next door';
+
+=head2 HTTP response headers
+
+L<Test::Mojo> allows us to inspect and make assertions about HTTP response
+headers. The C<Content-Type> header is commonly tested and has its own
+assertion:
+
+  $t->get_ok('/map-of-the-world.pdf')
+    ->content_type_is('application/pdf');
+
+This is equivalent to the more verbose:
+
+  $t->get_ok('/map-of-the-world.pdf')
+    ->header_is('Content-Type' => 'application/pdf');
+
+We can test for multiple headers in a single response using method chains:
+
+  $t->get_ok('/map-of-the-world.pdf')
+    ->content_type_is('application/pdf')
+    ->header_isnt('Compression' => 'gzip')
+    ->header_unlike('Server' => qr/IIS/i);
+
+=head2 HTTP response content assertions
+
+L<Test::Mojo> also exposes a rich set of assertions for testing the body of a
+response, whether that body be HTML, plain-text, or JSON. The C<content_*>
+methods look at the body of the response as plain text (as defined by the
+response's character set):
+
+  $t->get_ok('/scary-things/spiders.json')
+    ->content_is('{"arachnid":"brown recluse"}');
+
+Although this is a JSON document, C<content_is> treats it as if it were a text
+document. This may be useful for situations where we're looking for a particular
+string and not concerned with the structure of the document. For example, we can
+do the same thing with an HTML document:
+
+  $t->get_ok('/scary-things/spiders.html')
+    ->content_like(qr{<title>All The Spiders</title>});
+
+But because L<Test::Mojo> has access to everything that L<Mojo::UserAgent> does,
+we can introspect JSON documents as well as DOM-based documents (HTML, XML) with
+assertions that allow us to check for the existence of elements as well as
+inspect the content of text nodes.
+
+=head3 JSON response assertions
+
+L<Test::Mojo>'s L<Mojo::UserAgent> has access to a JSON parser, which allows us
+to test to see if a JSON response contains a value at a location in the document
+using JSON pointer syntax:
+
+  $t->get_ok('/animals/friendly.json')
+    ->json_has('/beings/jeremiah/age');
+
+This assertion tells us that the C<friendly.json> document contains a value at
+the C</beings/jeremiah/age> JSON pointer location. We can also inspect the value
+at JSON pointer locations:
+
+  $t->get_ok('/animals/friendly.json')
+    ->json_has('/beings/jeremiah/age')
+    ->json_is('/beings/jeremiah/age' => 42)
+    ->json_like('/beings/jeremiah/species' => qr/bullfrog/i);
+
+JSON pointer syntax makes testing JSON responses simple and readable.
+
+=head3 DOM response assertions
+
+We can also inspect HTML and XML responses using the L<Mojo::DOM> parser in the
+user agent. Here are a few examples from the L<Test::Mojo> documentation:
+
+  $t->text_is('div.foo[x=y]' => 'Hello!');
+  $t->text_is('html head title' => 'Hello!', 'right title');
+
+The L<Mojo::DOM> parser uses the CSS selector syntax described in
+L<Mojo::DOM::CSS>, allowing us to test for values in HTML and XML documents
+without resorting to typically verbose and inflexible DOM traversal methods.
+
+=head1 ADVANCED TOPICS
+
+This section describes some complex (but common) testing situations that
+L<Test::Mojo> excels in making simple.
+
+=head2 Redirects
+
+The L<Mojo::UserAgent> object in L<Test::Mojo> can handle HTTP redirections
+internally to whatever level you need. Let's say we have a web service that
+redirects C</1> to C</2>, C</2> redirects to C</3>, C</3> redirects to C</4>,
+and C</4> redirects to C</5>:
+
+  GET /1
+
+returns:
+
+  302 Found
+  Location: /2
+
+and:
+
+  GET /2
+
+returns:
+
+  302 Found
+  Location: /3
+
+and so forth, up to C</5>:
+
+  GET /5
+
+which returns the data we wanted:
+
+  200 OK
+
+  {"message":"this is five"}
+
+We can tell the user agent in L<Test::Mojo> how to deal with redirects. Each
+test is making a request to C<GET /1>, but we vary the number of redirects the
+user agent should follow with each test:
+
+  my $t = Test::Mojo->new;
+
+  $t->get_ok('/1')
+    ->header_is(location => '/2');
+
+  $t->ua->max_redirects(1);
+  $t->get_ok('/1')
+    ->header_is(location => '/3');
+
+  $t->ua->max_redirects(2);
+  $t->get_ok('/1')
+    ->header_is(location => '/4');
+
+  # Look at the previous hop
+  is $t->tx->previous->res->headers->location, '/3', 'previous redirect';
+
+  $t->ua->max_redirects(3);
+  $t->get_ok('/1')
+    ->header_is(location => '/5');
+
+  $t->ua->max_redirects(4);
+  $t->get_ok('/1')
+    ->json_is('/message' => 'this is five');
+
+When we set C<max_redirects>, it stays set for the life of the test object until
+we change it.
+
+L<Test::Mojo>'s handling of HTTP redirects eliminates the need for making many,
+sometimes an unknown number, of redirections to keep testing precise and easy to
+follow (ahem).
+
+=head2 Cookies and session management
+
+We can use L<Test::Mojo> to test applications that keep session state in
+cookies. By default, the L<Mojo::UserAgent> object in L<Test::Mojo> will manage
+session for us by saving and sending cookies automatically, just like common web
+browsers:
+
+  use Mojo::Base -strict;
+
+  use Test::More;
+  use Test::Mojo;
+
+  my $t = Test::Mojo->new('MyApp');
+
+  # No authorization cookie
+  $t->get_ok('/')
+    ->status_is(401)
+    ->content_is('Please log in');
+
+  # Application sets an authorization cookie
+  $t->post_ok('/login' => form => {password => 'let me in'})
+    ->status_is(200)
+    ->content_is('You are logged in');
+
+  # Sends the cookie from the previous transaction
+  $t->get_ok('/')
+    ->status_is(200)
+    ->content_like(qr/You logged in at \d+/);
+
+  # Clear the cookies
+  $t->reset_session;
+
+  # No authorization cookie again
+  $t->get_ok('/')
+    ->status_is(401)
+    ->content_is('Please log in');
+
+We can also inspect cookies in responses for special values through the
+transaction's response (L<Mojo::Message::Response>) object:
+
+  $t->get_ok('/');
+  like $t->tx->res->cookie('smarty'), qr/smarty=pants/, 'cookie found';
+
+=head2 Custom transactions
+
+Let's say we have an application that responds to a new HTTP verb C<RING> and to
+use it we must also pass in a secret cookie value. This is not a problem. We can
+test the application by creating a L<Mojo::Transaction> object, setting the
+cookie (see L<Mojo::Message::Request>), then passing the transaction object to
+C<request_ok>:
+
+  # Use custom "RING" verb
+  my $tx = $t->ua->build_tx(RING => '/doorbell');
+
+  # Set a special cookie
+  $tx->req->cookies({name => 'Secret', value => "don't tell anybody"});
+
+  # Make the request
+  $t->request_ok($tx)
+    ->status_is(200)
+    ->json_is('/status' => 'ding dong');
+
+=head2 Testing WebSocket web services
+
+While the message flow on WebSocket connections can be rather dynamic, it more
+often than not is quite predictable, which allows this rather pleasant
+L<Test::Mojo> WebSocket API to be used:
+
+  use Mojo::Base -strict;
+
+  use Test::More;
+  use Test::Mojo;
+
+  # Test echo web service
+  my $t = Test::Mojo->new('EchoService');
+  $t->websocket_ok('/echo')
+    ->send_ok('Hello Mojo!')
+    ->message_ok
+    ->message_is('echo: Hello Mojo!')
+    ->finish_ok;
+
+  # Test JSON web service
+  $t->websocket_ok('/echo.json')
+    ->send_ok({json => {test => [1, 2, 3]}})
+    ->message_ok
+    ->json_message_is('/test' => [1, 2, 3])
+    ->finish_ok;
+
+  done_testing();
+
+Because of their inherent asynchronous nature, testing WebSocket communications
+can be tricky. The L<Test::Mojo> WebSocket assertions serialize messages via
+event loop primitives. This enables us to treat WebSocket messages as if they
+were using the same request-response communication pattern we're accustomed to
+with HTTP.
+
+To illustrate, let's walk through these tests. In the first test, we use the
+C<websocket_ok> assertion to ensure that we can connect to our application's
+WebSocket route at C</echo> and that it's "speaking" WebSocket protocol to us.
+The next C<send_ok> assertion tests the connection again (in case it closed, for
+example) and attempts to send the message C<Hello Mojo!>. The next assertion,
+C<message_ok>, blocks (using the L<Mojo::IOLoop> singleton in the application)
+and waits for a response from the server. The response is then compared with
+C<'echo: Hello Mojo!'> in the C<message_is> assertion, and finally we close and
+test our connection status again with C<finish_ok>.
+
+The second test is like the first, but now we're sending and expecting JSON
+documents at C</echo.json>. In the C<send_ok> assertion we take advantage of
+L<Mojo::UserAgent>'s JSON content generator (see L<Mojo::UserAgent::Transactor>)
+to marshal hash and array references into JSON documents, and then send them as
+a WebSocket message. We wait (block) for a response from the server with
+C<message_ok>. Then because we're expecting a JSON document back, we can
+leverage C<json_message_ok> which parses the WebSocket response body and returns
+an object we can access through L<Mojo::JSON::Pointer> syntax. Then we close
+(and test) our WebSocket connection.
+
+Testing WebSocket servers does not get any simpler than with L<Test::Mojo>.
+
+=head2 Extending L<Test::Mojo>
+
+If you see that you're writing a lot of test assertions that aren't chainable,
+you may benefit from writing your own test assertions. Let's say we want to test
+the C<Location> header after a redirect. We'll create a new class with
+L<Role::Tiny> that implements a test assertion named C<location_is>:
+
+  package Test::Mojo::Role::Location;
+  use Mojo::Base -role;
+
+  use Test::More;
+
+  sub location_is {
+    my ($t, $value, $desc) = @_;
+    $desc ||= "Location: $value";
+    local $Test::Builder::Level = $Test::Builder::Level + 1;
+    return $t->success(is($t->tx->res->headers->location, $value, $desc));
+  }
+
+  1;
+
+When we make new test assertions using roles, we want to use method signatures
+that match other C<*_is> methods in L<Test::Mojo>, so here we accept the test
+object, the value to compare, and an optional description.
+
+We assign a default description value (C<$desc>), set the L<Test::Builder>
+C<Level> global variable one level higher (which tells L<Test::Builder> how far
+up the call stack to look when something fails), then we use L<Test::More>'s
+C<is> function to compare the location header with the expected header value. We
+wrap that in the C<success> attribute, which records the boolean test result and
+propagates the L<Test::Mojo> object for method chaining.
+
+With this new package, we're ready to compose a new test object that uses the
+role:
+
+  my $t = Test::Mojo->with_roles('+Location')->new('MyApp');
+
+  $t->post_ok('/redirect/mojo' => json => {message => 'Mojo, here I come!'})
+    ->status_is(302)
+    ->location_is('http://mojolicious.org')
+    ->or(sub { diag 'I miss tempire.' });
+
+In this section we've covered how to add custom test assertions to L<Test::Mojo>
+with roles and how to use those roles to simplify testing.
+
+=head1 MORE
+
+You can continue with L<Mojolicious::Guides> now or take a look at the
+L<Mojolicious wiki|http://github.com/kraih/mojo/wiki>, which contains a lot more
+documentation and examples by many different authors.
+
+=head1 SUPPORT
+
+If you have any questions the documentation might not yet answer, don't
+hesitate to ask on the
+L<mailing list|http://groups.google.com/group/mojolicious> or the official IRC
+channel C<#mojo> on C<irc.perl.org>
+(L<chat now!|https://chat.mibbit.com/?channel=%23mojo&server=irc.perl.org>).
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Guides/Tutorial.pod
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Guides/Tutorial.pod	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Guides/Tutorial.pod	(revision 32205)
@@ -0,0 +1,1035 @@
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Guides::Tutorial - Get started with Mojolicious
+
+=head1 TUTORIAL
+
+A quick example-driven introduction to the wonders of L<Mojolicious::Lite>.
+Almost everything you'll learn here also applies to full L<Mojolicious>
+applications.
+
+This is only the first of the L<Mojolicious::Guides>. Other guides delve deeper
+into topics like L<growing|Mojolicious::Guides::Growing> a L<Mojolicious::Lite>
+prototype into a well-structured L<Mojolicious> application,
+L<routing|Mojolicious::Guides::Routing>,
+L<rendering|Mojolicious::Guides::Rendering> and more. It is highly encouraged
+that readers continue on to the remaining guides after reading this one.
+
+=head2 Hello World
+
+A simple Hello World application can look like this, L<strict>, L<warnings>,
+L<utf8> and Perl 5.10 L<features|feature> are automatically enabled and a few
+L<functions|Mojolicious::Lite/"FUNCTIONS"> imported, when you use
+L<Mojolicious::Lite>, turning your script into a full featured web application.
+
+  #!/usr/bin/env perl
+  use Mojolicious::Lite;
+
+  get '/' => sub {
+    my $c = shift;
+    $c->render(text => 'Hello World!');
+  };
+
+  app->start;
+
+With L<Mojolicious::Command::generate::lite_app> there is also a helper command
+to generate a small example application.
+
+  $ mojo generate lite_app myapp.pl
+
+=head2 Commands
+
+Many different L<commands|Mojolicious::Commands/"COMMANDS"> are automatically
+available from the command line. CGI and L<PSGI> environments can even be
+detected and will usually just work without commands.
+
+  $ ./myapp.pl daemon
+  Server available at http://127.0.0.1:3000
+
+  $ ./myapp.pl daemon -l http://*:8080
+  Server available at http://127.0.0.1:8080
+
+  $ ./myapp.pl cgi
+  ...CGI output...
+
+  $ ./myapp.pl get /
+  Hello World!
+
+  $ ./myapp.pl
+  ...List of available commands (or automatically detected environment)...
+
+A call to L<Mojolicious/"start"> (C<app-E<gt>start>), which starts the command
+system, should be the last expression in your application, because its return
+value can be significant.
+
+  # Use @ARGV to pick a command
+  app->start;
+
+  # Start the "daemon" command
+  app->start('daemon', '-l', 'http://*:8080');
+
+=head2 Reloading
+
+Your application will automatically reload itself if you start it with the
+L<morbo> development web server, so you don't have to restart the server after
+every change.
+
+  $ morbo ./myapp.pl
+  Server available at http://127.0.0.1:3000
+
+For more information about how to deploy your application see also
+L<Mojolicious::Guides::Cookbook/"DEPLOYMENT">.
+
+=head2 Routes
+
+Routes are basically just fancy paths that can contain different kinds of
+placeholders and usually lead to an action, if they match the path part of the
+request URL. The first argument passed to all actions (C<$c>) is a
+L<Mojolicious::Controller> object, containing both the HTTP request and
+response.
+
+  use Mojolicious::Lite;
+
+  # Route leading to an action that renders some text
+  get '/foo' => sub {
+    my $c = shift;
+    $c->render(text => 'Hello World!');
+  };
+
+  app->start;
+
+Response content is often generated by actions with
+L<Mojolicious::Controller/"render">, but more about that later.
+
+=head2 GET/POST parameters
+
+All C<GET> and C<POST> parameters sent with the request are accessible via
+L<Mojolicious::Controller/"param">.
+
+  use Mojolicious::Lite;
+
+  # /foo?user=sri
+  get '/foo' => sub {
+    my $c    = shift;
+    my $user = $c->param('user');
+    $c->render(text => "Hello $user.");
+  };
+
+  app->start;
+
+=head2 Stash and templates
+
+The L<Mojolicious::Controller/"stash"> is used to pass data to templates, which
+can be inlined in the C<DATA> section. A few stash values like C<template>,
+C<text> and C<data> are reserved and will be used by
+L<Mojolicious::Controller/"render"> to decide how a response should be
+generated.
+
+  use Mojolicious::Lite;
+
+  # Route leading to an action that renders a template
+  get '/foo' => sub {
+    my $c = shift;
+    $c->stash(one => 23);
+    $c->render(template => 'magic', two => 24);
+  };
+
+  app->start;
+  __DATA__
+
+  @@ magic.html.ep
+  The magic numbers are <%= $one %> and <%= $two %>.
+
+For more information about templates see also
+L<Mojolicious::Guides::Rendering/"Embedded Perl">.
+
+=head2 HTTP
+
+L<Mojolicious::Controller/"req"> and L<Mojolicious::Controller/"res"> give you
+full access to all HTTP features and information.
+
+  use Mojolicious::Lite;
+
+  # Access request information
+  get '/agent' => sub {
+    my $c    = shift;
+    my $host = $c->req->url->to_abs->host;
+    my $ua   = $c->req->headers->user_agent;
+    $c->render(text => "Request by $ua reached $host.");
+  };
+
+  # Echo the request body and send custom header with response
+  post '/echo' => sub {
+    my $c = shift;
+    $c->res->headers->header('X-Bender' => 'Bite my shiny metal ass!');
+    $c->render(data => $c->req->body);
+  };
+
+  app->start;
+
+You can test the more advanced examples right from the command line with
+L<Mojolicious::Command::get>.
+
+  $ ./myapp.pl get -v -M POST -c 'test' /echo
+
+=head2 JSON
+
+JSON is the most commonly used data-interchange format for web services.
+L<Mojolicious> loves JSON and comes with the possibly fastest pure-Perl
+implementation L<Mojo::JSON> built right in, which is accessible through
+L<Mojo::Message/"json"> as well as the reserved stash value C<json>.
+
+  use Mojolicious::Lite;
+
+  # Modify the received JSON document and return it
+  put '/reverse' => sub {
+    my $c    = shift;
+    my $hash = $c->req->json;
+    $hash->{message} = reverse $hash->{message};
+    $c->render(json => $hash);
+  };
+
+  app->start;
+
+You can send JSON documents from the command line with
+L<Mojolicious::Command::get>.
+
+  $ ./myapp.pl get -M PUT -c '{"message":"Hello Mojo!"}' /reverse
+
+=head2 Built-in C<exception> and C<not_found> pages
+
+During development you will encounter these pages whenever you make a mistake,
+they are gorgeous and contain a lot of valuable information that will aid you
+in debugging your application.
+
+  use Mojolicious::Lite;
+
+  # Not found (404)
+  get '/missing' => sub { shift->render(template => 'does_not_exist') };
+
+  # Exception (500)
+  get '/dies' => sub { die 'Intentional error' };
+
+  app->start;
+
+You can even use CSS selectors with L<Mojolicious::Command::get> to extract only
+the information you're actually interested in.
+
+  $ ./myapp.pl get /dies '#error'
+
+And don't worry about revealing too much information on these pages, they are
+only available during development, and will be replaced automatically with pages
+that don't reveal any sensitive information in a production environment.
+
+=head2 Route names
+
+All routes can have a name associated with them, this allows automatic template
+detection and backreferencing with L<Mojolicious::Controller/"url_for">, on
+which many methods and helpers like
+L<Mojolicious::Plugin::TagHelpers/"link_to"> rely.
+
+  use Mojolicious::Lite;
+
+  # Render the template "index.html.ep"
+  get '/' => sub {
+    my $c = shift;
+    $c->render;
+  } => 'index';
+
+  # Render the template "hello.html.ep"
+  get '/hello';
+
+  app->start;
+  __DATA__
+
+  @@ index.html.ep
+  <%= link_to Hello  => 'hello' %>.
+  <%= link_to Reload => 'index' %>.
+
+  @@ hello.html.ep
+  Hello World!
+
+Nameless routes get an automatically generated one assigned that is simply
+equal to the route itself without non-word characters.
+
+=head2 Layouts
+
+Templates can have layouts too, you just select one with the helper
+L<Mojolicious::Plugin::DefaultHelpers/"layout"> and place the result of the
+current template with the helper
+L<Mojolicious::Plugin::DefaultHelpers/"content">.
+
+  use Mojolicious::Lite;
+
+  get '/with_layout';
+
+  app->start;
+  __DATA__
+
+  @@ with_layout.html.ep
+  % title 'Green';
+  % layout 'green';
+  Hello World!
+
+  @@ layouts/green.html.ep
+  <!DOCTYPE html>
+  <html>
+    <head><title><%= title %></title></head>
+    <body><%= content %></body>
+  </html>
+
+The stash or helpers like L<Mojolicious::Plugin::DefaultHelpers/"title"> can be
+used to pass additional data to the layout.
+
+=head2 Blocks
+
+Template blocks can be used like normal Perl functions and are always delimited
+by the C<begin> and C<end> keywords, they are the foundation for many helpers.
+
+  use Mojolicious::Lite;
+
+  get '/with_block' => 'block';
+
+  app->start;
+  __DATA__
+
+  @@ block.html.ep
+  % my $link = begin
+    % my ($url, $name) = @_;
+    Try <%= link_to $url => begin %><%= $name %><% end %>.
+  % end
+  <!DOCTYPE html>
+  <html>
+    <head><title>Sebastians frameworks</title></head>
+    <body>
+      %= $link->('http://mojolicious.org', 'Mojolicious')
+      %= $link->('http://catalystframework.org', 'Catalyst')
+    </body>
+  </html>
+
+=head2 Helpers
+
+Helpers are little functions you can create with the keyword
+L<Mojolicious::Lite/"helper"> and reuse throughout your whole application, from
+actions to templates.
+
+  use Mojolicious::Lite;
+
+  # A helper to identify visitors
+  helper whois => sub {
+    my $c     = shift;
+    my $agent = $c->req->headers->user_agent || 'Anonymous';
+    my $ip    = $c->tx->remote_address;
+    return "$agent ($ip)";
+  };
+
+  # Use helper in action and template
+  get '/secret' => sub {
+    my $c    = shift;
+    my $user = $c->whois;
+    $c->app->log->debug("Request from $user");
+  };
+
+  app->start;
+  __DATA__
+
+  @@ secret.html.ep
+  We know who you are <%= whois %>.
+
+A list of all built-in ones can be found in
+L<Mojolicious::Plugin::DefaultHelpers> and L<Mojolicious::Plugin::TagHelpers>.
+
+=head2 Placeholders
+
+Route placeholders allow capturing parts of a request path until a C</> or C<.>
+separator occurs, similar to the regular expression C<([^/.]+)>. Results are
+accessible via L<Mojolicious::Controller/"stash"> and
+L<Mojolicious::Controller/"param">.
+
+  use Mojolicious::Lite;
+
+  # /foo/test
+  # /foo/test123
+  get '/foo/:bar' => sub {
+    my $c   = shift;
+    my $bar = $c->stash('bar');
+    $c->render(text => "Our :bar placeholder matched $bar");
+  };
+
+  # /testsomething/foo
+  # /test123something/foo
+  get '/<:bar>something/foo' => sub {
+    my $c   = shift;
+    my $bar = $c->param('bar');
+    $c->render(text => "Our :bar placeholder matched $bar");
+  };
+
+  app->start;
+
+To separate them from the surrounding text, you can surround your placeholders
+with C<E<lt>> and C<E<gt>>, which also makes the colon prefix optional.
+
+=head2 Relaxed Placeholders
+
+Relaxed placeholders allow matching of everything until a C</> occurs, similar
+to the regular expression C<([^/]+)>.
+
+  use Mojolicious::Lite;
+
+  # /hello/test
+  # /hello/test.html
+  get '/hello/#you' => 'groovy';
+
+  app->start;
+  __DATA__
+
+  @@ groovy.html.ep
+  Your name is <%= $you %>.
+
+=head2 Wildcard placeholders
+
+Wildcard placeholders allow matching absolutely everything, including C</> and
+C<.>, similar to the regular expression C<(.+)>.
+
+  use Mojolicious::Lite;
+
+  # /hello/test
+  # /hello/test123
+  # /hello/test.123/test/123
+  get '/hello/*you' => 'groovy';
+
+  app->start;
+  __DATA__
+
+  @@ groovy.html.ep
+  Your name is <%= $you %>.
+
+=head2 HTTP methods
+
+Routes can be restricted to specific request methods with different keywords
+like L<Mojolicious::Lite/"get"> and L<Mojolicious::Lite/"any">.
+
+  use Mojolicious::Lite;
+
+  # GET /hello
+  get '/hello' => sub {
+    my $c = shift;
+    $c->render(text => 'Hello World!');
+  };
+
+  # PUT /hello
+  put '/hello' => sub {
+    my $c    = shift;
+    my $size = length $c->req->body;
+    $c->render(text => "You uploaded $size bytes to /hello.");
+  };
+
+  # GET|POST|PATCH /bye
+  any ['GET', 'POST', 'PATCH'] => '/bye' => sub {
+    my $c = shift;
+    $c->render(text => 'Bye World!');
+  };
+
+  # * /whatever
+  any '/whatever' => sub {
+    my $c      = shift;
+    my $method = $c->req->method;
+    $c->render(text => "You called /whatever with $method.");
+  };
+
+  app->start;
+
+=head2 Optional placeholders
+
+All placeholders require a value, but by assigning them default values you can
+make capturing optional.
+
+  use Mojolicious::Lite;
+
+  # /hello
+  # /hello/Sara
+  get '/hello/:name' => {name => 'Sebastian', day => 'Monday'} => sub {
+    my $c = shift;
+    $c->render(template => 'groovy', format => 'txt');
+  };
+
+  app->start;
+  __DATA__
+
+  @@ groovy.txt.ep
+  My name is <%= $name %> and it is <%= $day %>.
+
+Default values that don't belong to a placeholder simply get merged into the
+stash all the time.
+
+=head2 Restrictive placeholders
+
+A very easy way to make placeholders more restrictive are alternatives, you just
+make a list of possible values.
+
+  use Mojolicious::Lite;
+
+  # /test
+  # /123
+  any '/:foo' => [foo => ['test', '123']] => sub {
+    my $c   = shift;
+    my $foo = $c->param('foo');
+    $c->render(text => "Our :foo placeholder matched $foo");
+  };
+
+  app->start;
+
+All placeholders get compiled to a regular expression internally, this process
+can also be customized. Just make sure not to use C<^> and C<$>, or capturing
+groups C<(...)>, non-capturing groups C<(?:...)> are fine though.
+
+  use Mojolicious::Lite;
+
+  # /1
+  # /123
+  any '/:bar' => [bar => qr/\d+/] => sub {
+    my $c   = shift;
+    my $bar = $c->param('bar');
+    $c->render(text => "Our :bar placeholder matched $bar");
+  };
+
+  app->start;
+
+You can take a closer look at all the generated regular expressions with the
+command L<Mojolicious::Command::routes>.
+
+  $ ./myapp.pl routes -v
+
+=head2 Under
+
+Authentication and code shared between multiple routes can be realized easily
+with routes generated by L<Mojolicious::Lite/"under">. All following routes are
+only evaluated if the callback returned a true value.
+
+  use Mojolicious::Lite;
+
+  # Authenticate based on name parameter
+  under sub {
+    my $c = shift;
+
+    # Authenticated
+    my $name = $c->param('name') || '';
+    return 1 if $name eq 'Bender';
+
+    # Not authenticated
+    $c->render(template => 'denied');
+    return undef;
+  };
+
+  # Only reached when authenticated
+  get '/' => 'index';
+
+  app->start;
+  __DATA__
+
+  @@ denied.html.ep
+  You are not Bender, permission denied.
+
+  @@ index.html.ep
+  Hi Bender.
+
+Prefixing multiple routes is another good use for it.
+
+  use Mojolicious::Lite;
+
+  # /foo
+  under '/foo';
+
+  # /foo/bar
+  get '/bar' => {text => 'foo bar'};
+
+  # /foo/baz
+  get '/baz' => {text => 'foo baz'};
+
+  # / (reset)
+  under '/' => {msg => 'whatever'};
+
+  # /bar
+  get '/bar' => {inline => '<%= $msg %> works'};
+
+  app->start;
+
+You can also group related routes with L<Mojolicious::Lite/"group">, which
+allows nesting of routes generated with L<Mojolicious::Lite/"under">.
+
+  use Mojolicious::Lite;
+
+  # Global logic shared by all routes
+  under sub {
+    my $c = shift;
+    return 1 if $c->req->headers->header('X-Bender');
+    $c->render(text => "You're not Bender.");
+    return undef;
+  };
+
+  # Admin section
+  group {
+
+    # Local logic shared only by routes in this group
+    under '/admin' => sub {
+      my $c = shift;
+      return 1 if $c->req->headers->header('X-Awesome');
+      $c->render(text => "You're not awesome enough.");
+      return undef;
+    };
+
+    # GET /admin/dashboard
+    get '/dashboard' => {text => 'Nothing to see here yet.'};
+  };
+
+  # GET /welcome
+  get '/welcome' => {text => 'Hi Bender.'};
+
+  app->start;
+
+=head2 Formats
+
+Formats can be automatically detected from file extensions like C<.html>, they
+are used to find the right template and generate the correct C<Content-Type>
+header.
+
+  use Mojolicious::Lite;
+
+  # /detection
+  # /detection.html
+  # /detection.txt
+  get '/detection' => sub {
+    my $c = shift;
+    $c->render(template => 'detected');
+  };
+
+  app->start;
+  __DATA__
+
+  @@ detected.html.ep
+  <!DOCTYPE html>
+  <html>
+    <head><title>Detected</title></head>
+    <body>HTML was detected.</body>
+  </html>
+
+  @@ detected.txt.ep
+  TXT was detected.
+
+The default format is C<html>, and restrictive placeholders can be used to
+limit possible values.
+
+  use Mojolicious::Lite;
+
+  # /hello.json
+  # /hello.txt
+  get '/hello' => [format => ['json', 'txt']] => sub {
+    my $c = shift;
+    return $c->render(json => {hello => 'world'})
+      if $c->stash('format') eq 'json';
+    $c->render(text => 'hello world');
+  };
+
+  app->start;
+
+Or you can just disable format detection with a special type of restrictive
+placeholder.
+
+  use Mojolicious::Lite;
+
+  # /hello
+  get '/hello' => [format => 0] => {text => 'No format detection.'};
+
+  # Disable detection and allow the following routes to re-enable it on demand
+  under [format => 0];
+
+  # /foo
+  get '/foo' => {text => 'No format detection again.'};
+
+  # /bar.txt
+  get '/bar' => [format => 'txt'] => {text => ' Just one format.'};
+
+  app->start;
+
+=head2 Content negotiation
+
+For resources with different representations and that require truly RESTful
+content negotiation you can also use L<Mojolicious::Controller/"respond_to">.
+
+  use Mojolicious::Lite;
+
+  # /hello (Accept: application/json)
+  # /hello (Accept: application/xml)
+  # /hello.json
+  # /hello.xml
+  # /hello?format=json
+  # /hello?format=xml
+  get '/hello' => sub {
+    my $c = shift;
+    $c->respond_to(
+      json => {json => {hello => 'world'}},
+      xml  => {text => '<hello>world</hello>'},
+      any  => {data => '', status => 204}
+    );
+  };
+
+  app->start;
+
+MIME type mappings can be extended or changed easily with
+L<Mojolicious/"types">.
+
+  app->types->type(rdf => 'application/rdf+xml');
+
+=head2 Static files
+
+Similar to templates, but with only a single file extension and optional Base64
+encoding, static files can be inlined in the C<DATA> section and are served
+automatically.
+
+  use Mojolicious::Lite;
+
+  app->start;
+  __DATA__
+
+  @@ something.js
+  alert('hello!');
+
+  @@ test.txt (base64)
+  dGVzdCAxMjMKbGFsYWxh
+
+External static files are not limited to a single file extension and will be
+served automatically from a C<public> directory if it exists.
+
+  $ mkdir public
+  $ mv something.js public/something.js
+  $ mv mojolicious.tar.gz public/mojolicious.tar.gz
+
+Both have a higher precedence than routes for C<GET> and C<HEAD> requests.
+Content negotiation with C<Range>, C<If-None-Match> and C<If-Modified-Since>
+headers is supported as well and can be tested very easily with
+L<Mojolicious::Command::get>.
+
+  $ ./myapp.pl get /something.js -v -H 'Range: bytes=2-4'
+
+=head2 External templates
+
+External templates will be searched by the renderer in a C<templates> directory
+if it exists.
+
+  $ mkdir -p templates/foo
+  $ echo 'Hello World!' > templates/foo/bar.html.ep
+
+They have a higher precedence than templates in the C<DATA> section.
+
+  use Mojolicious::Lite;
+
+  # Render template "templates/foo/bar.html.ep"
+  any '/external' => sub {
+    my $c = shift;
+    $c->render(template => 'foo/bar');
+  };
+
+  app->start;
+
+=head2 Home
+
+You can use L<Mojolicious/"home"> to interact with the directory your
+application considers its home. This is the directory it will search for
+C<public> and C<templates> directories, but you can use it to store all sorts of
+application specific data.
+
+  $ mkdir cache
+  $ echo 'Hello World!' > cache/hello.txt
+
+There are many useful methods L<Mojo::Home> inherits from L<Mojo::File>, like
+L<Mojo::File/"child"> and L<Mojo::File/"slurp">, that will help you keep your
+application portable across many different operating systems.
+
+  use Mojolicious::Lite;
+
+  # Load message into memory
+  my $hello = app->home->child('cache', 'hello.txt')->slurp;
+
+  # Display message
+  get '/' => sub {
+    my $c = shift;
+    $c->render(text => $hello);
+  };
+
+You can also introspect your application from the command line with
+L<Mojolicious::Command::eval>.
+
+  $ ./myapp.pl eval -v 'app->home'
+
+=head2 Conditions
+
+Conditions such as C<agent> and C<host> from
+L<Mojolicious::Plugin::HeaderCondition> allow even more powerful route
+constructs.
+
+  use Mojolicious::Lite;
+
+  # Firefox
+  get '/foo' => (agent => qr/Firefox/) => sub {
+    my $c = shift;
+    $c->render(text => 'Congratulations, you are using a cool browser.');
+  };
+
+  # Internet Explorer
+  get '/foo' => (agent => qr/Internet Explorer/) => sub {
+    my $c = shift;
+    $c->render(text => 'Dude, you really need to upgrade to Firefox.');
+  };
+
+  # http://mojolicious.org/bar
+  get '/bar' => (host => 'mojolicious.org') => sub {
+    my $c = shift;
+    $c->render(text => 'Hello Mojolicious.');
+  };
+
+  app->start;
+
+=head2 Sessions
+
+Cookie-based sessions just work out of the box, as soon as you start using them
+through the helper L<Mojolicious::Plugin::DefaultHelpers/"session">. Just be
+aware that all session data gets serialized with L<Mojo::JSON> and stored
+client-side, with a cryptographic signature to prevent tampering.
+
+  use Mojolicious::Lite;
+
+  # Access session data in action and template
+  get '/counter' => sub {
+    my $c = shift;
+    $c->session->{counter}++;
+  };
+
+  app->start;
+  __DATA__
+
+  @@ counter.html.ep
+  Counter: <%= session 'counter' %>
+
+Note that you should use custom L<Mojolicious/"secrets"> to make signed cookies
+really tamper resistant.
+
+  app->secrets(['My secret passphrase here']);
+
+=head2 File uploads
+
+All files uploaded via C<multipart/form-data> request are automatically
+available as L<Mojo::Upload> objects from L<Mojolicious::Controller/"param">.
+And you don't have to worry about memory usage, because all files above 250KiB
+will be automatically streamed into a temporary file. To build HTML forms more
+efficiently, you can also use tag helpers like
+L<Mojolicious::Plugin::TagHelpers/"form_for">.
+
+  use Mojolicious::Lite;
+
+  # Upload form in DATA section
+  get '/' => 'form';
+
+  # Multipart upload handler
+  post '/upload' => sub {
+    my $c = shift;
+
+    # Check file size
+    return $c->render(text => 'File is too big.', status => 200)
+      if $c->req->is_limit_exceeded;
+
+    # Process uploaded file
+    return $c->redirect_to('form') unless my $example = $c->param('example');
+    my $size = $example->size;
+    my $name = $example->filename;
+    $c->render(text => "Thanks for uploading $size byte file $name.");
+  };
+
+  app->start;
+  __DATA__
+
+  @@ form.html.ep
+  <!DOCTYPE html>
+  <html>
+    <head><title>Upload</title></head>
+    <body>
+      %= form_for upload => (enctype => 'multipart/form-data') => begin
+        %= file_field 'example'
+        %= submit_button 'Upload'
+      % end
+    </body>
+  </html>
+
+To protect you from excessively large files there is also a limit of 16MiB by
+default, which you can tweak with the attribute
+L<Mojolicious/"max_request_size">.
+
+  # Increase limit to 1GiB
+  app->max_request_size(1073741824);
+
+=head2 User agent
+
+With L<Mojo::UserAgent>, which is available through the helper
+L<Mojolicious::Plugin::DefaultHelpers/"ua">, there's a full featured HTTP and
+WebSocket user agent built right in. Especially in combination with
+L<Mojo::JSON> and L<Mojo::DOM> this can be a very powerful tool.
+
+  use Mojolicious::Lite;
+
+  # Blocking
+  get '/headers' => sub {
+    my $c   = shift;
+    my $url = $c->param('url') || 'https://mojolicious.org';
+    my $dom = $c->ua->get($url)->result->dom;
+    $c->render(json => $dom->find('h1, h2, h3')->map('text')->to_array);
+  };
+
+  # Non-blocking
+  get '/title' => sub {
+    my $c = shift;
+    $c->ua->get('mojolicious.org' => sub {
+      my ($ua, $tx) = @_;
+      $c->render(data => $tx->result->dom->at('title')->text);
+    });
+  };
+
+  # Concurrent non-blocking
+  get '/titles' => sub {
+    my $c  = shift;
+    my $mojo = $c->ua->get_p('https://mojolicious.org');
+    my $cpan = $c->ua->get_p('https://metacpan.org');
+    Mojo::Promise->all($mojo, $cpan)->then(sub {
+      my ($mojo, $cpan) = @_;
+      $c->render(json => {
+        mojo => $mojo->[0]->result->dom->at('title')->text,
+        cpan => $cpan->[0]->result->dom->at('title')->text
+      });
+    })->wait;
+  };
+
+  app->start;
+
+For more information about the user agent see also
+L<Mojolicious::Guides::Cookbook/"USER AGENT">.
+
+=head2 WebSockets
+
+WebSocket applications have never been this simple before. Just receive
+messages by subscribing to events such as
+L<Mojo::Transaction::WebSocket/"json"> with L<Mojolicious::Controller/"on"> and
+return them with L<Mojolicious::Controller/"send">.
+
+  use Mojolicious::Lite;
+
+  websocket '/echo' => sub {
+    my $c = shift;
+    $c->on(json => sub {
+      my ($c, $hash) = @_;
+      $hash->{msg} = "echo: $hash->{msg}";
+      $c->send({json => $hash});
+    });
+  };
+
+  get '/' => 'index';
+
+  app->start;
+  __DATA__
+
+  @@ index.html.ep
+  <!DOCTYPE html>
+  <html>
+    <head>
+      <title>Echo</title>
+      <script>
+        var ws = new WebSocket('<%= url_for('echo')->to_abs %>');
+        ws.onmessage = function (event) {
+          document.body.innerHTML += JSON.parse(event.data).msg;
+        };
+        ws.onopen = function (event) {
+          ws.send(JSON.stringify({msg: 'I â¥ Mojolicious!'}));
+        };
+      </script>
+    </head>
+  </html>
+
+For more information about real-time web features see also
+L<Mojolicious::Guides::Cookbook/"REAL-TIME WEB">.
+
+=head2 Mode
+
+You can use the L<Mojo::Log> object from L<Mojolicious/"log"> to portably
+collect debug messages and automatically disable them later in a production
+setup by changing the L<Mojolicious> operating mode, which can also be retrieved
+from the attribute L<Mojolicious/"mode">.
+
+  use Mojolicious::Lite;
+
+  # Prepare mode specific message during startup
+  my $msg = app->mode eq 'development' ? 'Development!' : 'Something else!';
+
+  get '/' => sub {
+    my $c = shift;
+    $c->app->log->debug('Rendering mode specific message');
+    $c->render(text => $msg);
+  };
+
+  app->log->debug('Starting application');
+  app->start;
+
+The default operating mode will usually be C<development> and can be changed
+with command line options or the C<MOJO_MODE> and C<PLACK_ENV> environment
+variables. A mode other than C<development> will raise the log level from
+C<debug> to C<info>.
+
+  $ ./myapp.pl daemon -m production
+
+All messages will be written to C<STDERR> or a C<log/$mode.log> file if a
+C<log> directory exists.
+
+  $ mkdir log
+
+Mode changes also affect a few other aspects of the framework, such as the
+built-in C<exception> and C<not_found> pages. Once you switch modes from
+C<development> to C<production>, no sensitive information will be revealed on
+those pages anymore.
+
+=head2 Testing
+
+Testing your application is as easy as creating a C<t> directory and filling it
+with normal Perl tests like C<t/basic.t>, which can be a lot of fun thanks to
+L<Test::Mojo>.
+
+  use Test::More;
+  use Test::Mojo;
+
+  use FindBin;
+  require "$FindBin::Bin/../myapp.pl";
+
+  my $t = Test::Mojo->new;
+  $t->get_ok('/')->status_is(200)->content_like(qr/Funky/);
+
+  done_testing();
+
+Just run your tests with the command L<Mojolicious::Command::test> or L<prove>.
+
+  $ ./myapp.pl test
+  $ ./myapp.pl test -v t/basic.t
+  $ prove -l -v t/basic.t
+
+=head1 MORE
+
+You can continue with L<Mojolicious::Guides> now or take a look at the
+L<Mojolicious wiki|http://github.com/kraih/mojo/wiki>, which contains a lot more
+documentation and examples by many different authors.
+
+=head1 SUPPORT
+
+If you have any questions the documentation might not yet answer, don't
+hesitate to ask on the
+L<mailing list|http://groups.google.com/group/mojolicious> or the official IRC
+channel C<#mojo> on C<irc.perl.org>
+(L<chat now!|https://chat.mibbit.com/?channel=%23mojo&server=irc.perl.org>).
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Lite.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Lite.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Lite.pm	(revision 32205)
@@ -0,0 +1,358 @@
+package Mojolicious::Lite;
+use Mojo::Base 'Mojolicious';
+
+# "Bender: Bite my shiny metal ass!"
+use Mojo::File 'path';
+use Mojo::UserAgent::Server;
+use Mojo::Util 'monkey_patch';
+
+sub import {
+
+  # Remember executable for later
+  $ENV{MOJO_EXE} ||= (caller)[1];
+
+  # Reuse home directory if possible
+  local $ENV{MOJO_HOME} = path($ENV{MOJO_EXE})->dirname->to_string
+    unless $ENV{MOJO_HOME};
+
+  # Initialize application class
+  my $caller = caller;
+  no strict 'refs';
+  push @{"${caller}::ISA"}, 'Mojolicious';
+
+  # Generate moniker based on filename
+  my $moniker = path($ENV{MOJO_EXE})->basename('.pl', '.pm', '.t');
+  my $app = shift->new(moniker => $moniker);
+
+  # Initialize routes without namespaces
+  my $routes = $app->routes->namespaces([]);
+  $app->static->classes->[0] = $app->renderer->classes->[0] = $caller;
+
+  # The Mojolicious::Lite DSL
+  my $root = $routes;
+  for my $name (qw(any get options patch post put websocket)) {
+    monkey_patch $caller, $name, sub { $routes->$name(@_) };
+  }
+  monkey_patch($caller, $_, sub {$app}) for qw(new app);
+  monkey_patch $caller, del => sub { $routes->delete(@_) };
+  monkey_patch $caller, group => sub (&) {
+    (my $old, $root) = ($root, $routes);
+    shift->();
+    ($routes, $root) = ($root, $old);
+  };
+  monkey_patch $caller,
+    helper => sub { $app->helper(@_) },
+    hook   => sub { $app->hook(@_) },
+    plugin => sub { $app->plugin(@_) },
+    under  => sub { $routes = $root->under(@_) };
+
+  # Make sure there's a default application for testing
+  Mojo::UserAgent::Server->app($app) unless Mojo::UserAgent::Server->app;
+
+  # Lite apps are strict!
+  unshift @_, 'Mojo::Base', -strict;
+  goto &Mojo::Base::import;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Lite - Micro real-time web framework
+
+=head1 SYNOPSIS
+
+  # Automatically enables "strict", "warnings", "utf8" and Perl 5.10 features
+  use Mojolicious::Lite;
+
+  # Route with placeholder
+  get '/:foo' => sub {
+    my $c   = shift;
+    my $foo = $c->param('foo');
+    $c->render(text => "Hello from $foo.");
+  };
+
+  # Start the Mojolicious command system
+  app->start;
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Lite> is a tiny domain specific language built around
+L<Mojolicious>, made up of only about a dozen Perl functions.
+
+On Perl 5.20+ you can also use a C<-signatures> flag to enable support for
+L<subroutine signatures|perlsub/"Signatures">.
+
+  use Mojolicious::Lite -signatures;
+
+  get '/:foo' => sub ($c) {
+    my $foo = $c->param('foo');
+    $c->render(text => "Hello from $foo.");
+  };
+
+  app->start;
+
+See L<Mojolicious::Guides::Tutorial> for more!
+
+=head1 GROWING
+
+While L<Mojolicious::Guides::Growing> will give you a detailed introduction to
+growing a L<Mojolicious::Lite> prototype into a well-structured L<Mojolicious>
+application, here we have collected a few snippets that illustrate very well
+just how similar both of them are.
+
+=head2 Routes
+
+The functions L</"get">, L</"post"> and friends all have equivalent methods.
+
+  # Mojolicious::Lite
+  get '/foo' => sub {
+    my $c = shift;
+    $c->render(text => 'Hello World!');
+  };
+
+  # Mojolicious
+  sub startup {
+    my $self = shift;
+
+    my $routes = $self->routes;
+    $routes->get('/foo' => sub {
+      my $c = shift;
+      $c->render(text => 'Hello World!');
+    });
+  }
+
+=head2 Application
+
+The application object you can access with the function L</"app"> is the first
+argument passed to the C<startup> method.
+
+  # Mojolicious::Lite
+  app->max_request_size(16777216);
+
+  # Mojolicious
+  sub startup {
+    my $self = shift;
+    $self->max_request_size(16777216);
+  }
+
+=head2 Plugins
+
+Instead of the L</"plugin"> function you just use the method
+L<Mojolicious/"plugin">.
+
+  # Mojolicious::Lite
+  plugin 'Config';
+
+  # Mojolicious
+  sub startup {
+    my $self = shift;
+    $self->plugin('Config');
+  }
+
+=head2 Helpers
+
+Similar to plugins, instead of the L</"helper"> function you just use the method
+L<Mojolicious/"helper">.
+
+  # Mojolicious::Lite
+  helper two => sub {
+    my $c = shift;
+    return 1 + 1;
+  };
+
+  # Mojolicious
+  sub startup {
+    my $self = shift;
+    $self->helper(two => sub {
+      my $c = shift;
+      return 1 + 1;
+    });
+  }
+
+=head2 Under
+
+Instead of sequential function calls, we can use methods to build a tree with
+nested routes, that much better illustrates how routes work internally.
+
+  # Mojolicious::Lite
+  under '/foo';
+  get '/bar' => sub {...};
+
+  # Mojolicious
+  sub startup {
+    my $self = shift;
+
+    my $routes = $self->routes;
+    my $foo = $routes->under('/foo');
+    $foo->get('/bar' => sub {...});
+  }
+
+=head1 FUNCTIONS
+
+L<Mojolicious::Lite> implements the following functions, which are
+automatically exported.
+
+=head2 any
+
+  my $route = any '/:foo' => sub {...};
+  my $route = any '/:foo' => sub {...} => 'name';
+  my $route = any '/:foo' => {foo => 'bar'} => sub {...};
+  my $route = any '/:foo' => [foo => qr/\w+/] => sub {...};
+  my $route = any ['GET', 'POST'] => '/:foo' => sub {...};
+  my $route = any ['GET', 'POST'] => '/:foo' => [foo => qr/\w+/] => sub {...};
+  my $route = any
+    ['GET', 'POST'] => '/:foo' => (agent => qr/Firefox/) => sub {...};
+
+Generate route with L<Mojolicious::Routes::Route/"any">, matching any of the
+listed HTTP request methods or all. See L<Mojolicious::Guides::Tutorial> and
+L<Mojolicious::Guides::Routing> for more information.
+
+=head2 app
+
+  my $app = app;
+
+Returns the L<Mojolicious::Lite> application object, which is a subclass of
+L<Mojolicious>.
+
+  # Use all the available attributes and methods
+  app->log->level('error');
+  app->defaults(foo => 'bar');
+
+=head2 del
+
+  my $route = del '/:foo' => sub {...};
+  my $route = del '/:foo' => sub {...} => 'name';
+  my $route = del '/:foo' => {foo => 'bar'} => sub {...};
+  my $route = del '/:foo' => [foo => qr/\w+/] => sub {...};
+  my $route = del '/:foo' => (agent => qr/Firefox/) => sub {...};
+
+Generate route with L<Mojolicious::Routes::Route/"delete">, matching only
+C<DELETE> requests. See L<Mojolicious::Guides::Tutorial> and
+L<Mojolicious::Guides::Routing> for more information.
+
+=head2 get
+
+  my $route = get '/:foo' => sub {...};
+  my $route = get '/:foo' => sub {...} => 'name';
+  my $route = get '/:foo' => {foo => 'bar'} => sub {...};
+  my $route = get '/:foo' => [foo => qr/\w+/] => sub {...};
+  my $route = get '/:foo' => (agent => qr/Firefox/) => sub {...};
+
+Generate route with L<Mojolicious::Routes::Route/"get">, matching only C<GET>
+requests. See L<Mojolicious::Guides::Tutorial> and
+L<Mojolicious::Guides::Routing> for more information.
+
+=head2 group
+
+  group {...};
+
+Start a new route group.
+
+=head2 helper
+
+  helper foo => sub {...};
+
+Add a new helper with L<Mojolicious/"helper">.
+
+=head2 hook
+
+  hook after_dispatch => sub {...};
+
+Share code with L<Mojolicious/"hook">.
+
+=head2 options
+
+  my $route = options '/:foo' => sub {...};
+  my $route = options '/:foo' => sub {...} => 'name';
+  my $route = options '/:foo' => {foo => 'bar'} => sub {...};
+  my $route = options '/:foo' => [foo => qr/\w+/] => sub {...};
+  my $route = options '/:foo' => (agent => qr/Firefox/) => sub {...};
+
+Generate route with L<Mojolicious::Routes::Route/"options">, matching only
+C<OPTIONS> requests. See L<Mojolicious::Guides::Tutorial> and
+L<Mojolicious::Guides::Routing> for more information.
+
+=head2 patch
+
+  my $route = patch '/:foo' => sub {...};
+  my $route = patch '/:foo' => sub {...} => 'name';
+  my $route = patch '/:foo' => {foo => 'bar'} => sub {...};
+  my $route = patch '/:foo' => [foo => qr/\w+/] => sub {...};
+  my $route = patch '/:foo' => (agent => qr/Firefox/) => sub {...};
+
+Generate route with L<Mojolicious::Routes::Route/"patch">, matching only
+C<PATCH> requests. See L<Mojolicious::Guides::Tutorial> and
+L<Mojolicious::Guides::Routing> for more information.
+
+=head2 plugin
+
+  plugin SomePlugin => {foo => 23};
+
+Load a plugin with L<Mojolicious/"plugin">.
+
+=head2 post
+
+  my $route = post '/:foo' => sub {...};
+  my $route = post '/:foo' => sub {...} => 'name';
+  my $route = post '/:foo' => {foo => 'bar'} => sub {...};
+  my $route = post '/:foo' => [foo => qr/\w+/] => sub {...};
+  my $route = post '/:foo' => (agent => qr/Firefox/) => sub {...};
+
+Generate route with L<Mojolicious::Routes::Route/"post">, matching only C<POST>
+requests. See L<Mojolicious::Guides::Tutorial> and
+L<Mojolicious::Guides::Routing> for more information.
+
+=head2 put
+
+  my $route = put '/:foo' => sub {...};
+  my $route = put '/:foo' => sub {...} => 'name';
+  my $route = put '/:foo' => {foo => 'bar'} => sub {...};
+  my $route = put '/:foo' => [foo => qr/\w+/] => sub {...};
+  my $route = put '/:foo' => (agent => qr/Firefox/) => sub {...};
+
+Generate route with L<Mojolicious::Routes::Route/"put">, matching only C<PUT>
+requests. See L<Mojolicious::Guides::Tutorial> and
+L<Mojolicious::Guides::Routing> for more information.
+
+=head2 under
+
+  my $route = under sub {...};
+  my $route = under '/:foo' => sub {...};
+  my $route = under '/:foo' => {foo => 'bar'};
+  my $route = under '/:foo' => [foo => qr/\w+/];
+  my $route = under '/:foo' => (agent => qr/Firefox/);
+  my $route = under [format => 0];
+
+Generate nested route with L<Mojolicious::Routes::Route/"under">, to which all
+following routes are automatically appended. See
+L<Mojolicious::Guides::Tutorial> and L<Mojolicious::Guides::Routing> for more
+information.
+
+=head2 websocket
+
+  my $route = websocket '/:foo' => sub {...};
+  my $route = websocket '/:foo' => sub {...} => 'name';
+  my $route = websocket '/:foo' => {foo => 'bar'} => sub {...};
+  my $route = websocket '/:foo' => [foo => qr/\w+/] => sub {...};
+  my $route = websocket '/:foo' => (agent => qr/Firefox/) => sub {...};
+
+Generate route with L<Mojolicious::Routes::Route/"websocket">, matching only
+WebSocket handshakes. See L<Mojolicious::Guides::Tutorial> and
+L<Mojolicious::Guides::Routing> for more information.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Lite> inherits all attributes from L<Mojolicious>.
+
+=head1 METHODS
+
+L<Mojolicious::Lite> inherits all methods from L<Mojolicious>.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Plugin.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Plugin.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Plugin.pm	(revision 32205)
@@ -0,0 +1,52 @@
+package Mojolicious::Plugin;
+use Mojo::Base -base;
+
+use Carp 'croak';
+
+sub register { croak 'Method "register" not implemented by subclass' }
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Plugin - Plugin base class
+
+=head1 SYNOPSIS
+
+  # CamelCase plugin name
+  package Mojolicious::Plugin::MyPlugin;
+  use Mojo::Base 'Mojolicious::Plugin';
+
+  sub register {
+    my ($self, $app, $conf) = @_;
+
+    # Magic here! :)
+  }
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Plugin> is an abstract base class for L<Mojolicious> plugins.
+
+See L<Mojolicious::Plugins/"PLUGINS"> for a list of plugins that are available
+by default.
+
+=head1 METHODS
+
+L<Mojolicious::Plugin> inherits all methods from L<Mojo::Base> and implements
+the following new ones.
+
+=head2 register
+
+  $plugin->register(Mojolicious->new);
+  $plugin->register(Mojolicious->new, {foo => 'bar'});
+
+This method will be called by L<Mojolicious::Plugins> at startup time. Meant to
+be overloaded in a subclass.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Plugin/Config.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Plugin/Config.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Plugin/Config.pm	(revision 32205)
@@ -0,0 +1,185 @@
+package Mojolicious::Plugin::Config;
+use Mojo::Base 'Mojolicious::Plugin';
+
+use Mojo::File 'path';
+use Mojo::Util 'decode';
+
+sub load { $_[0]->parse(decode('UTF-8', path($_[1])->slurp), @_[1, 2, 3]) }
+
+sub parse {
+  my ($self, $content, $file, $conf, $app) = @_;
+
+  # Run Perl code in sandbox
+  my $config = eval 'package Mojolicious::Plugin::Config::Sandbox; no warnings;'
+    . "sub app; local *app = sub { \$app }; use Mojo::Base -strict; $content";
+  die qq{Can't load configuration from file "$file": $@} if $@;
+  die qq{Configuration file "$file" did not return a hash reference.\n}
+    unless ref $config eq 'HASH';
+
+  return $config;
+}
+
+sub register {
+  my ($self, $app, $conf) = @_;
+
+  # Override
+  $app->defaults(config => $app->config);
+  return $app->config if $app->config->{config_override};
+
+  # Config file
+  my $file = $conf->{file} || $ENV{MOJO_CONFIG};
+  $file ||= $app->moniker . '.' . ($conf->{ext} || 'conf');
+
+  # Mode specific config file
+  my $mode = $file =~ /^(.*)\.([^.]+)$/ ? join('.', $1, $app->mode, $2) : '';
+
+  my $home = $app->home;
+  $file = $home->child($file) unless path($file)->is_abs;
+  $mode = $home->child($mode) if $mode && !path($mode)->is_abs;
+  $mode = undef unless $mode && -e $mode;
+
+  # Read config file
+  my $config = {};
+  if (-e $file) { $config = $self->load($file, $conf, $app) }
+
+  # Check for default and mode specific config file
+  elsif (!$conf->{default} && !$mode) {
+    die qq{Configuration file "$file" missing, maybe you need to create it?\n};
+  }
+
+  # Merge everything
+  $config = {%$config, %{$self->load($mode, $conf, $app)}} if $mode;
+  $config = {%{$conf->{default}}, %$config} if $conf->{default};
+  return $app->config($config)->config;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Plugin::Config - Perl-ish configuration plugin
+
+=head1 SYNOPSIS
+
+  # myapp.conf (it's just Perl returning a hash)
+  {
+    # Just a value
+    foo => "bar",
+
+    # Nested data structures are fine too
+    baz => ['â¥'],
+
+    # You have full access to the application
+    music_dir => app->home->child('music')
+  };
+
+  # Mojolicious
+  my $config = $app->plugin('Config');
+  say $config->{foo};
+
+  # Mojolicious::Lite
+  my $config = plugin 'Config';
+  say $config->{foo};
+
+  # foo.html.ep
+  %= $config->{foo}
+
+  # The configuration is available application-wide
+  my $config = app->config;
+  say $config->{foo};
+
+  # Everything can be customized with options
+  my $config = plugin Config => {file => '/etc/myapp.stuff'};
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Plugin::Config> is a Perl-ish configuration plugin.
+
+The application object can be accessed via C<$app> or the C<app> function,
+L<strict>, L<warnings>, L<utf8> and Perl 5.10 L<features|feature> are
+automatically enabled. A default configuration filename in the application home
+directory will be generated from the value of L<Mojolicious/"moniker">
+(C<$moniker.conf>). You can extend the normal configuration file
+C<$moniker.conf> with C<mode> specific ones like C<$moniker.$mode.conf>, which
+will be detected automatically.
+
+If the configuration value C<config_override> has been set in
+L<Mojolicious/"config"> when this plugin is loaded, it will not do anything.
+
+The code of this plugin is a good example for learning to build new plugins,
+you're welcome to fork it.
+
+See L<Mojolicious::Plugins/"PLUGINS"> for a list of plugins that are available
+by default.
+
+=head1 OPTIONS
+
+L<Mojolicious::Plugin::Config> supports the following options.
+
+=head2 default
+
+  # Mojolicious::Lite
+  plugin Config => {default => {foo => 'bar'}};
+
+Default configuration, making configuration files optional.
+
+=head2 ext
+
+  # Mojolicious::Lite
+  plugin Config => {ext => 'stuff'};
+
+File extension for generated configuration filenames, defaults to C<conf>.
+
+=head2 file
+
+  # Mojolicious::Lite
+  plugin Config => {file => 'myapp.conf'};
+  plugin Config => {file => '/etc/foo.stuff'};
+
+Path to configuration file, absolute or relative to the application home
+directory, defaults to the value of the C<MOJO_CONFIG> environment variable or
+C<$moniker.conf> in the application home directory.
+
+=head1 METHODS
+
+L<Mojolicious::Plugin::Config> inherits all methods from L<Mojolicious::Plugin>
+and implements the following new ones.
+
+=head2 load
+
+  $plugin->load($file, $conf, $app);
+
+Loads configuration file and passes the content to L</"parse">.
+
+  sub load {
+    my ($self, $file, $conf, $app) = @_;
+    ...
+    return $self->parse($content, $file, $conf, $app);
+  }
+
+=head2 parse
+
+  $plugin->parse($content, $file, $conf, $app);
+
+Parse configuration file.
+
+  sub parse {
+    my ($self, $content, $file, $conf, $app) = @_;
+    ...
+    return $hash;
+  }
+
+=head2 register
+
+  my $config = $plugin->register(Mojolicious->new);
+  my $config = $plugin->register(Mojolicious->new, {file => '/etc/app.conf'});
+
+Register plugin in L<Mojolicious> application and merge configuration.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Plugin/DefaultHelpers.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Plugin/DefaultHelpers.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Plugin/DefaultHelpers.pm	(revision 32205)
@@ -0,0 +1,598 @@
+package Mojolicious::Plugin::DefaultHelpers;
+use Mojo::Base 'Mojolicious::Plugin';
+
+use Mojo::Asset::File;
+use Mojo::ByteStream;
+use Mojo::Collection;
+use Mojo::Exception;
+use Mojo::IOLoop;
+use Mojo::Util qw(deprecated dumper hmac_sha1_sum steady_time);
+use Time::HiRes qw(gettimeofday tv_interval);
+use Scalar::Util 'blessed';
+
+sub register {
+  my ($self, $app) = @_;
+
+  # Controller alias helpers
+  for my $name (qw(app flash param stash session url_for validation)) {
+    $app->helper($name => sub { shift->$name(@_) });
+  }
+
+  # Stash key shortcuts (should not generate log messages)
+  for my $name (qw(extends layout title)) {
+    $app->helper($name => sub { shift->stash($name, @_) });
+  }
+
+  $app->helper(accepts => sub { $_[0]->app->renderer->accepts(@_) });
+  $app->helper(b       => sub { shift; Mojo::ByteStream->new(@_) });
+  $app->helper(c       => sub { shift; Mojo::Collection->new(@_) });
+  $app->helper(config  => sub { shift->app->config(@_) });
+
+  $app->helper(content      => sub { _content(0, 0, @_) });
+  $app->helper(content_for  => sub { _content(1, 0, @_) });
+  $app->helper(content_with => sub { _content(0, 1, @_) });
+
+  # DEPRECATED!
+  $app->helper(
+    delay => sub {
+      deprecated 'delay helper is DEPRECATED';
+      my $c  = shift;
+      my $tx = $c->render_later->tx;
+      Mojo::IOLoop->delay(@_)
+        ->catch(sub { $c->helpers->reply->exception(pop) and undef $tx })->wait;
+    }
+  );
+
+  $app->helper($_ => $self->can("_$_"))
+    for qw(csrf_token current_route inactivity_timeout is_fresh url_with);
+
+  $app->helper(dumper => sub { shift; dumper @_ });
+  $app->helper(include => sub { shift->render_to_string(@_) });
+
+  $app->helper("reply.$_" => $self->can("_$_")) for qw(asset file static);
+
+  $app->helper('reply.exception' => sub { _development('exception', @_) });
+  $app->helper('reply.not_found' => sub { _development('not_found', @_) });
+
+  $app->helper('timing.begin'         => \&_timing_begin);
+  $app->helper('timing.elapsed'       => \&_timing_elapsed);
+  $app->helper('timing.rps'           => \&_timing_rps);
+  $app->helper('timing.server_timing' => \&_timing_server_timing);
+
+  $app->helper(ua => sub { shift->app->ua });
+}
+
+sub _asset {
+  my $c = shift;
+  $c->app->static->serve_asset($c, @_);
+  $c->rendered;
+}
+
+sub _block { ref $_[0] eq 'CODE' ? $_[0]() : $_[0] }
+
+sub _content {
+  my ($append, $replace, $c, $name, $content) = @_;
+  $name ||= 'content';
+
+  my $hash = $c->stash->{'mojo.content'} ||= {};
+  if (defined $content) {
+    if ($append) { $hash->{$name} .= _block($content) }
+    if ($replace) { $hash->{$name} = _block($content) }
+    else          { $hash->{$name} //= _block($content) }
+  }
+
+  return Mojo::ByteStream->new($hash->{$name} // '');
+}
+
+sub _csrf_token {
+  my $c = shift;
+  return $c->session->{csrf_token}
+    ||= hmac_sha1_sum($$ . steady_time . rand, $c->app->secrets->[0]);
+}
+
+sub _current_route {
+  return '' unless my $route = shift->match->endpoint;
+  return @_ ? $route->name eq shift : $route->name;
+}
+
+sub _development {
+  my ($page, $c, $e) = @_;
+
+  my $app = $c->app;
+  $app->log->error($e = _exception($e) ? $e : Mojo::Exception->new($e)->inspect)
+    if $page eq 'exception';
+
+  # Filtered stash snapshot
+  my $stash = $c->stash;
+  %{$stash->{snapshot} = {}} = map { $_ => $stash->{$_} }
+    grep { !/^mojo\./ and defined $stash->{$_} } keys %$stash;
+  $stash->{exception} = $page eq 'exception' ? $e : undef;
+
+  # Render with fallbacks
+  my $mode    = $app->mode;
+  my $options = {
+    format   => $stash->{format} || $app->renderer->default_format,
+    handler  => undef,
+    status   => $page eq 'exception' ? 500 : 404,
+    template => "$page.$mode"
+  };
+  my $bundled = 'mojo/' . ($mode eq 'development' ? 'debug' : $page);
+  return $c if _fallbacks($c, $options, $page, $bundled);
+  _fallbacks($c, {%$options, format => 'html'}, $page, $bundled);
+  return $c;
+}
+
+sub _exception { blessed $_[0] && $_[0]->isa('Mojo::Exception') }
+
+sub _fallbacks {
+  my ($c, $options, $template, $bundled) = @_;
+
+  # Mode specific template
+  return 1 if $c->render_maybe(%$options);
+
+  # Normal template
+  return 1 if $c->render_maybe(%$options, template => $template);
+
+  # Inline template
+  my $stash = $c->stash;
+  return undef unless $options->{format} eq 'html';
+  delete @$stash{qw(extends layout)};
+  return $c->render_maybe($bundled, %$options, handler => 'ep');
+}
+
+sub _file { _asset(shift, Mojo::Asset::File->new(path => shift)) }
+
+sub _inactivity_timeout {
+  my ($c, $timeout) = @_;
+  my $stream = Mojo::IOLoop->stream($c->tx->connection // '');
+  $stream->timeout($timeout) if $stream;
+  return $c;
+}
+
+sub _is_fresh {
+  my ($c, %options) = @_;
+  return $c->app->static->is_fresh($c, \%options);
+}
+
+sub _static {
+  my ($c, $file) = @_;
+  return !!$c->rendered if $c->app->static->serve($c, $file);
+  $c->app->log->debug(qq{Static file "$file" not found});
+  return !$c->helpers->reply->not_found;
+}
+
+sub _timing_begin { shift->stash->{'mojo.timing'}{shift()} = [gettimeofday] }
+
+sub _timing_elapsed {
+  my ($c, $name) = @_;
+  return undef unless my $started = $c->stash->{'mojo.timing'}{$name};
+  return tv_interval($started, [gettimeofday()]);
+}
+
+sub _timing_rps { $_[1] == 0 ? undef : sprintf '%.3f', 1 / $_[1] }
+
+sub _timing_server_timing {
+  my ($c, $metric, $desc, $dur) = @_;
+  my $value = $metric;
+  $value .= qq{;desc="$desc"} if defined $desc;
+  $value .= ";dur=$dur"       if defined $dur;
+  $c->res->headers->append('Server-Timing' => $value);
+}
+
+sub _url_with {
+  my $c = shift;
+  return $c->url_for(@_)->query($c->req->url->query->clone);
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Plugin::DefaultHelpers - Default helpers plugin
+
+=head1 SYNOPSIS
+
+  # Mojolicious
+  $app->plugin('DefaultHelpers');
+
+  # Mojolicious::Lite
+  plugin 'DefaultHelpers';
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Plugin::DefaultHelpers> is a collection of helpers for
+L<Mojolicious>.
+
+This is a core plugin, that means it is always enabled and its code a good
+example for learning to build new plugins, you're welcome to fork it.
+
+See L<Mojolicious::Plugins/"PLUGINS"> for a list of plugins that are available
+by default.
+
+=head1 HELPERS
+
+L<Mojolicious::Plugin::DefaultHelpers> implements the following helpers.
+
+=head2 accepts
+
+  my $formats = $c->accepts;
+  my $format  = $c->accepts('html', 'json', 'txt');
+
+Select best possible representation for resource from C<format> C<GET>/C<POST>
+parameter, C<format> stash value or C<Accept> request header with
+L<Mojolicious::Renderer/"accepts">, defaults to returning the first extension if
+no preference could be detected.
+
+  # Check if JSON is acceptable
+  $c->render(json => {hello => 'world'}) if $c->accepts('json');
+
+  # Check if JSON was specifically requested
+  $c->render(json => {hello => 'world'}) if $c->accepts('', 'json');
+
+  # Unsupported representation
+  $c->render(data => '', status => 204)
+    unless my $format = $c->accepts('html', 'json');
+
+  # Detected representations to select from
+  my @formats = @{$c->accepts};
+
+=head2 app
+
+  %= app->secrets->[0]
+
+Alias for L<Mojolicious::Controller/"app">.
+
+=head2 b
+
+  %= b('Joel is a slug')->slugify
+
+Turn string into a L<Mojo::ByteStream> object.
+
+=head2 c
+
+  %= c('a', 'b', 'c')->shuffle->join
+
+Turn list into a L<Mojo::Collection> object.
+
+=head2 config
+
+  %= config 'something'
+
+Alias for L<Mojolicious/"config">.
+
+=head2 content
+
+  %= content foo => begin
+    test
+  % end
+  %= content bar => 'Hello World!'
+  %= content 'foo'
+  %= content 'bar'
+  %= content
+
+Store partial rendered content in a named buffer and retrieve it later,
+defaults to retrieving the named buffer C<content>, which is used by the
+renderer for the C<layout> and C<extends> features. New content will be ignored
+if the named buffer is already in use.
+
+=head2 content_for
+
+  % content_for foo => begin
+    test
+  % end
+  %= content_for 'foo'
+
+Same as L</"content">, but appends content to named buffers if they are already
+in use.
+
+  % content_for message => begin
+    Hello
+  % end
+  % content_for message => begin
+    world!
+  % end
+  %= content 'message'
+
+=head2 content_with
+
+  % content_with foo => begin
+    test
+  % end
+  %= content_with 'foo'
+
+Same as L</"content">, but replaces content of named buffers if they are
+already in use.
+
+  % content message => begin
+    world!
+  % end
+  % content_with message => begin
+    Hello <%= content 'message' %>
+  % end
+  %= content 'message'
+
+=head2 csrf_token
+
+  %= csrf_token
+
+Get CSRF token from L</"session">, and generate one if none exists.
+
+=head2 current_route
+
+  % if (current_route 'login') {
+    Welcome to Mojolicious!
+  % }
+  %= current_route
+
+Check or get name of current route.
+
+=head2 dumper
+
+  %= dumper {some => 'data'}
+
+Dump a Perl data structure with L<Mojo::Util/"dumper">, very useful for
+debugging.
+
+=head2 extends
+
+  % extends 'blue';
+  % extends 'blue', title => 'Blue!';
+
+Set C<extends> stash value, all additional key/value pairs get merged into the
+L</"stash">.
+
+=head2 flash
+
+  %= flash 'foo'
+
+Alias for L<Mojolicious::Controller/"flash">.
+
+=head2 inactivity_timeout
+
+  $c = $c->inactivity_timeout(3600);
+
+Use L<Mojo::IOLoop/"stream"> to find the current connection and increase
+timeout if possible.
+
+  # Longer version
+  Mojo::IOLoop->stream($c->tx->connection)->timeout(3600);
+
+=head2 include
+
+  %= include 'menubar'
+  %= include 'menubar', format => 'txt'
+
+Alias for L<Mojolicious::Controller/"render_to_string">.
+
+=head2 is_fresh
+
+  my $bool = $c->is_fresh;
+  my $bool = $c->is_fresh(etag => 'abc');
+  my $bool = $c->is_fresh(last_modified => $epoch);
+
+Check freshness of request by comparing the C<If-None-Match> and
+C<If-Modified-Since> request headers to the C<ETag> and C<Last-Modified>
+response headers with L<Mojolicious::Static/"is_fresh">.
+
+  # Add ETag/Last-Modified headers and check freshness before rendering
+  $c->is_fresh(etag => 'abc', last_modified => 1424985708)
+    ? $c->rendered(304)
+    : $c->render(text => 'I â¥ Mojolicious!');
+
+=head2 layout
+
+  % layout 'green';
+  % layout 'green', title => 'Green!';
+
+Set C<layout> stash value, all additional key/value pairs get merged into the
+L</"stash">.
+
+=head2 param
+
+  %= param 'foo'
+
+Alias for L<Mojolicious::Controller/"param">.
+
+=head2 reply->asset
+
+  $c->reply->asset(Mojo::Asset::File->new);
+
+Reply with a L<Mojo::Asset::File> or L<Mojo::Asset::Memory> object using
+L<Mojolicious::Static/"serve_asset">, and perform content negotiation with
+C<Range>, C<If-Modified-Since> and C<If-None-Match> headers.
+
+  # Serve asset with custom modification time
+  my $asset = Mojo::Asset::Memory->new;
+  $asset->add_chunk('Hello World!')->mtime(784111777);
+  $c->res->headers->content_type('text/plain');
+  $c->reply->asset($asset);
+
+  # Serve static file if it exists
+  if (my $asset = $c->app->static->file('images/logo.png')) {
+    $c->res->headers->content_type('image/png');
+    $c->reply->asset($asset);
+  }
+
+=head2 reply->exception
+
+  $c = $c->reply->exception('Oops!');
+  $c = $c->reply->exception(Mojo::Exception->new);
+
+Render the exception template C<exception.$mode.$format.*> or
+C<exception.$format.*> and set the response status code to C<500>. Also sets
+the stash values C<exception> to a L<Mojo::Exception> object and C<snapshot> to
+a copy of the L</"stash"> for use in the templates.
+
+=head2 reply->file
+
+  $c->reply->file('/etc/passwd');
+
+Reply with a static file from an absolute path anywhere on the file system using
+L<Mojolicious/"static">.
+
+  # Longer version
+  $c->reply->asset(Mojo::Asset::File->new(path => '/etc/passwd'));
+
+  # Serve file from an absolute path with a custom content type
+  $c->res->headers->content_type('application/myapp');
+  $c->reply->file('/home/sri/foo.txt');
+
+  # Serve file from a secret application directory
+  $c->reply->file($c->app->home->child('secret', 'file.txt'));
+
+=head2 reply->not_found
+
+  $c = $c->reply->not_found;
+
+Render the not found template C<not_found.$mode.$format.*> or
+C<not_found.$format.*> and set the response status code to C<404>. Also sets
+the stash value C<snapshot> to a copy of the L</"stash"> for use in the
+templates.
+
+=head2 reply->static
+
+  my $bool = $c->reply->static('images/logo.png');
+  my $bool = $c->reply->static('../lib/MyApp.pm');
+
+Reply with a static file using L<Mojolicious/"static">, usually from the
+C<public> directories or C<DATA> sections of your application. Note that this
+helper uses a relative path, but does not protect from traversing to parent
+directories.
+
+  # Serve file from a relative path with a custom content type
+  $c->res->headers->content_type('application/myapp');
+  $c->reply->static('foo.txt');
+
+=head2 session
+
+  %= session 'foo'
+
+Alias for L<Mojolicious::Controller/"session">.
+
+=head2 stash
+
+  %= stash 'foo'
+  % stash foo => 'bar';
+
+Alias for L<Mojolicious::Controller/"stash">.
+
+  %= stash('name') // 'Somebody'
+
+=head2 timing->begin
+
+  $c->timing->begin('foo');
+
+Create named timestamp for L<"timing-E<gt>elapsed">. Note that this helper is
+EXPERIMENTAL and might change without warning!
+
+=head2 timing->elapsed
+
+  my $elapsed = $c->timing->elapsed('foo');
+
+Return fractional amount of time in seconds since named timstamp has been
+created with L</"timing-E<gt>begin"> or C<undef> if no such timestamp exists.
+Note that this helper is EXPERIMENTAL and might change without warning!
+
+  # Log timing information
+  $c->timing->begin('database_stuff');
+  ...
+  my $elapsed = $c->timing->elapsed('database_stuff');
+  $c->app->log->debug("Database stuff took $elapsed seconds");
+
+=head2 timing->rps
+
+  my $rps = $c->timing->rps('0.001');
+
+Return fractional number of requests that could be performed in one second if
+every singe one took the given amount of time in seconds or C<undef> if the
+number is too low. Note that this helper is EXPERIMENTAL and might change
+without warning!
+
+  # Log more timing information
+  $c->timing->begin('web_stuff');
+  ...
+  my $elapsed = $c->timing->elapsed('web_stuff');
+  my $rps     = $c->timing->rps($elapsed);
+  $c->app->log->debug("Web stuff took $elapsed seconds ($rps per second)");
+
+=head2 timing->server_timing
+
+  $c->timing->server_timing('metric');
+  $c->timing->server_timing('metric', 'Some Description');
+  $c->timing->server_timing('metric', 'Some Description', '0.001');
+
+Create C<Server-Timing> header with optional description and duration. Note that
+this helper is EXPERIMENTAL and might change without warning!
+
+  # "Server-Timing: miss"
+  $c->timing->server_timing('miss');
+
+  # "Server-Timing: dc;desc=atl"
+  $c->timing->server_timing('dc', 'atl');
+
+  # "Server-Timing: db;desc=Database;dur=0.0001"
+  $c->timing->begin('database_stuff');
+  ...
+  my $elapsed = $c->timing->elapsed('database_stuff');
+  $c->timing->server_timing('db', 'Database', $elapsed);
+
+  # "Server-Timing: miss, dc;desc=atl"
+  $c->timing->server_timing('miss');
+  $c->timing->server_timing('dc', 'atl');
+
+=head2 title
+
+  %= title
+  % title 'Welcome!';
+  % title 'Welcome!', foo => 'bar';
+
+Get or set C<title> stash value, all additional key/value pairs get merged into
+the L</"stash">.
+
+=head2 ua
+
+  %= ua->get('mojolicious.org')->result->dom->at('title')->text
+
+Alias for L<Mojolicious/"ua">.
+
+=head2 url_for
+
+  %= url_for 'named', controller => 'bar', action => 'baz'
+
+Alias for L<Mojolicious::Controller/"url_for">.
+
+  %= url_for('/index.html')->query(foo => 'bar')
+
+=head2 url_with
+
+  %= url_with 'named', controller => 'bar', action => 'baz'
+
+Does the same as L</"url_for">, but inherits query parameters from the current
+request.
+
+  %= url_with->query([page => 2])
+
+=head2 validation
+
+  %= validation->param('foo')
+
+Alias for L<Mojolicious::Controller/"validation">.
+
+=head1 METHODS
+
+L<Mojolicious::Plugin::DefaultHelpers> inherits all methods from
+L<Mojolicious::Plugin> and implements the following new ones.
+
+=head2 register
+
+  $plugin->register(Mojolicious->new);
+
+Register helpers in L<Mojolicious> application.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Plugin/EPLRenderer.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Plugin/EPLRenderer.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Plugin/EPLRenderer.pm	(revision 32205)
@@ -0,0 +1,102 @@
+package Mojolicious::Plugin::EPLRenderer;
+use Mojo::Base 'Mojolicious::Plugin';
+
+use Mojo::Template;
+use Mojo::Util qw(encode md5_sum);
+
+sub register {
+  my ($self, $app) = @_;
+  $app->renderer->add_handler(
+    epl => sub { _render(@_, Mojo::Template->new, $_[1]) });
+}
+
+sub _render {
+  my ($renderer, $c, $output, $options, $mt, @args) = @_;
+
+  # Cached
+  if ($mt->compiled) {
+    $c->app->log->debug("Rendering cached @{[$mt->name]}");
+    $$output = $mt->process(@args);
+  }
+
+  # Not cached
+  else {
+    my $inline = $options->{inline};
+    my $name = defined $inline ? md5_sum encode('UTF-8', $inline) : undef;
+    return unless defined($name //= $renderer->template_name($options));
+
+    # Inline
+    if (defined $inline) {
+      $c->app->log->debug(qq{Rendering inline template "$name"});
+      $$output = $mt->name(qq{inline template "$name"})->render($inline, @args);
+    }
+
+    # File
+    else {
+      if (my $encoding = $renderer->encoding) { $mt->encoding($encoding) }
+
+      # Try template
+      if (defined(my $path = $renderer->template_path($options))) {
+        $c->app->log->debug(qq{Rendering template "$name"});
+        $$output = $mt->name(qq{template "$name"})->render_file($path, @args);
+      }
+
+      # Try DATA section
+      elsif (defined(my $d = $renderer->get_data_template($options))) {
+        $c->app->log->debug(qq{Rendering template "$name" from DATA section});
+        $$output = $mt->name(qq{template "$name" from DATA section})
+          ->render($d, @args);
+      }
+
+      # No template
+      else { $c->app->log->debug(qq{Template "$name" not found}) }
+    }
+  }
+
+  # Exception
+  die $$output if ref $$output;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Plugin::EPLRenderer - Embedded Perl Lite renderer plugin
+
+=head1 SYNOPSIS
+
+  # Mojolicious
+  $app->plugin('EPLRenderer');
+
+  # Mojolicious::Lite
+  plugin 'EPLRenderer';
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Plugin::EPLRenderer> is a renderer for C<epl> templates, which
+are pretty much just raw L<Mojo::Template>.
+
+This is a core plugin, that means it is always enabled and its code a good
+example for learning to build new plugins, you're welcome to fork it.
+
+See L<Mojolicious::Plugins/"PLUGINS"> for a list of plugins that are available
+by default.
+
+=head1 METHODS
+
+L<Mojolicious::Plugin::EPLRenderer> inherits all methods from
+L<Mojolicious::Plugin> and implements the following new ones.
+
+=head2 register
+
+  $plugin->register(Mojolicious->new);
+
+Register renderer in L<Mojolicious> application.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Plugin/EPRenderer.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Plugin/EPRenderer.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Plugin/EPRenderer.pm	(revision 32205)
@@ -0,0 +1,120 @@
+package Mojolicious::Plugin::EPRenderer;
+use Mojo::Base 'Mojolicious::Plugin::EPLRenderer';
+
+use Mojo::Template;
+use Mojo::Util qw(encode md5_sum monkey_patch);
+
+sub DESTROY { Mojo::Util::_teardown(shift->{namespace}) }
+
+sub register {
+  my ($self, $app, $conf) = @_;
+
+  # Auto escape by default to prevent XSS attacks
+  my $ep = {auto_escape => 1, %{$conf->{template} || {}}, vars => 1};
+  my $ns = $self->{namespace} = $ep->{namespace}
+    //= 'Mojo::Template::Sandbox::' . md5_sum "$self";
+
+  # Make "$self" and "$c" available in templates
+  $ep->{prepend} = 'my $self = my $c = _C;' . ($ep->{prepend} // '');
+
+  # Add "ep" handler and make it the default
+  $app->renderer->default_handler('ep')->add_handler(
+    $conf->{name} || 'ep' => sub {
+      my ($renderer, $c, $output, $options) = @_;
+
+      my $name = $options->{inline} // $renderer->template_name($options);
+      return unless defined $name;
+      my $key = md5_sum encode 'UTF-8', $name;
+
+      my $cache = $renderer->cache;
+      my $mt    = $cache->get($key);
+      $cache->set($key => $mt = Mojo::Template->new($ep)) unless $mt;
+
+      # Export helpers only once
+      ++$self->{helpers} and _helpers($ns, $renderer->helpers)
+        unless $self->{helpers};
+
+      # Make current controller available and render with "epl" handler
+      no strict 'refs';
+      no warnings 'redefine';
+      local *{"${ns}::_C"} = sub {$c};
+      Mojolicious::Plugin::EPLRenderer::_render($renderer, $c, $output,
+        $options, $mt, $c->stash);
+    }
+  );
+}
+
+sub _helpers {
+  my ($class, $helpers) = @_;
+  for my $method (grep {/^\w+$/} keys %$helpers) {
+    my $sub = $helpers->{$method};
+    monkey_patch $class, $method, sub { $class->_C->$sub(@_) };
+  }
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Plugin::EPRenderer - Embedded Perl renderer plugin
+
+=head1 SYNOPSIS
+
+  # Mojolicious
+  $app->plugin('EPRenderer');
+  $app->plugin(EPRenderer => {name => 'foo'});
+  $app->plugin(EPRenderer => {name => 'bar', template => {line_start => '.'}});
+
+  # Mojolicious::Lite
+  plugin 'EPRenderer';
+  plugin EPRenderer => {name => 'foo'};
+  plugin EPRenderer => {name => 'bar', template => {line_start => '.'}};
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Plugin::EPRenderer> is a renderer for Embedded Perl templates.
+For more information see L<Mojolicious::Guides::Rendering/"Embedded Perl">.
+
+This is a core plugin, that means it is always enabled and its code a good
+example for learning to build new plugins, you're welcome to fork it.
+
+See L<Mojolicious::Plugins/"PLUGINS"> for a list of plugins that are available
+by default.
+
+=head1 OPTIONS
+
+L<Mojolicious::Plugin::EPRenderer> supports the following options.
+
+=head2 name
+
+  # Mojolicious::Lite
+  plugin EPRenderer => {name => 'foo'};
+
+Handler name, defaults to C<ep>.
+
+=head2 template
+
+  # Mojolicious::Lite
+  plugin EPRenderer => {template => {line_start => '.'}};
+
+Attribute values passed to L<Mojo::Template> object used to render templates.
+
+=head1 METHODS
+
+L<Mojolicious::Plugin::EPRenderer> inherits all methods from
+L<Mojolicious::Plugin::EPLRenderer> and implements the following new ones.
+
+=head2 register
+
+  $plugin->register(Mojolicious->new);
+  $plugin->register(Mojolicious->new, {name => 'foo'});
+
+Register renderer in L<Mojolicious> application.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Plugin/HeaderCondition.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Plugin/HeaderCondition.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Plugin/HeaderCondition.pm	(revision 32205)
@@ -0,0 +1,89 @@
+package Mojolicious::Plugin::HeaderCondition;
+use Mojo::Base 'Mojolicious::Plugin';
+
+sub register {
+  my ($self, $app) = @_;
+
+  $app->routes->add_condition(headers => \&_headers);
+  $app->routes->add_condition(
+    agent => sub { _headers(@_[0 .. 2], {'User-Agent' => $_[3]}) });
+  $app->routes->add_condition(
+    host => sub { _check($_[1]->req->url->to_abs->host, $_[3]) });
+}
+
+sub _check {
+  my ($value, $pattern) = @_;
+  return 1
+    if $value && $pattern && ref $pattern eq 'Regexp' && $value =~ $pattern;
+  return $value && defined $pattern && $pattern eq $value;
+}
+
+sub _headers {
+  my ($route, $c, $captures, $patterns) = @_;
+  return undef unless $patterns && ref $patterns eq 'HASH' && keys %$patterns;
+
+  # All headers need to match
+  my $headers = $c->req->headers;
+  _check($headers->header($_), $patterns->{$_}) || return undef
+    for keys %$patterns;
+  return 1;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Plugin::HeaderCondition - Header condition plugin
+
+=head1 SYNOPSIS
+
+  # Mojolicious
+  $app->plugin('HeaderCondition');
+  $app->routes->get('/:controller/:action')
+    ->over(headers => {Referer => qr/example\.com/});
+
+  # Mojolicious::Lite
+  plugin 'HeaderCondition';
+  get '/' => (headers => {Referer => qr/example\.com/}) => sub {...};
+
+  # All headers need to match
+  $app->routes->get('/:controller/:action')->over(headers => {
+    'X-Secret-Header' => 'Foo',
+    Referer => qr/example\.com/
+  });
+
+  # The "agent" condition is a shortcut for the "User-Agent" header
+  get '/' => (agent => qr/Firefox/) => sub {...};
+
+  # The "host" condition is a shortcut for the detected host
+  get '/' => (host => qr/mojolicious\.org/) => sub {...};
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Plugin::HeaderCondition> is a route condition for header-based
+routes.
+
+This is a core plugin, that means it is always enabled and its code a good
+example for learning to build new plugins, you're welcome to fork it.
+
+See L<Mojolicious::Plugins/"PLUGINS"> for a list of plugins that are available
+by default.
+
+=head1 METHODS
+
+L<Mojolicious::Plugin::HeaderCondition> inherits all methods from
+L<Mojolicious::Plugin> and implements the following new ones.
+
+=head2 register
+
+  $plugin->register(Mojolicious->new);
+
+Register conditions in L<Mojolicious> application.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Plugin/JSONConfig.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Plugin/JSONConfig.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Plugin/JSONConfig.pm	(revision 32205)
@@ -0,0 +1,147 @@
+package Mojolicious::Plugin::JSONConfig;
+use Mojo::Base 'Mojolicious::Plugin::Config';
+
+use Mojo::JSON 'from_json';
+use Mojo::Template;
+
+sub parse {
+  my ($self, $content, $file, $conf, $app) = @_;
+
+  my $config = eval { from_json $self->render($content, $file, $conf, $app) };
+  die qq{Can't parse config "$file": $@} if $@;
+  die qq{Invalid config "$file"} unless ref $config eq 'HASH';
+
+  return $config;
+}
+
+sub register { shift->SUPER::register(shift, {ext => 'json', %{shift()}}) }
+
+sub render {
+  my ($self, $content, $file, $conf, $app) = @_;
+
+  # Application instance and helper
+  my $prepend = q[no strict 'refs'; no warnings 'redefine';];
+  $prepend .= q[my $app = shift; sub app; local *app = sub { $app };];
+  $prepend .= q[use Mojo::Base -strict; no warnings 'ambiguous';];
+
+  my $mt = Mojo::Template->new($conf->{template} || {})->name($file);
+  my $output = $mt->prepend($prepend . $mt->prepend)->render($content, $app);
+  return ref $output ? die $output : $output;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Plugin::JSONConfig - JSON configuration plugin
+
+=head1 SYNOPSIS
+
+  # myapp.json (it's just JSON with embedded Perl)
+  {
+    %# Just a value
+    "foo": "bar",
+
+    %# Nested data structures are fine too
+    "baz": ["â¥"],
+
+    %# You have full access to the application
+    "music_dir": "<%= app->home->child('music') %>"
+  }
+
+  # Mojolicious
+  my $config = $app->plugin('JSONConfig');
+  say $config->{foo};
+
+  # Mojolicious::Lite
+  my $config = plugin 'JSONConfig';
+  say $config->{foo};
+
+  # foo.html.ep
+  %= $config->{foo}
+
+  # The configuration is available application-wide
+  my $config = app->config;
+  say $config->{foo};
+
+  # Everything can be customized with options
+  my $config = plugin JSONConfig => {file => '/etc/myapp.conf'};
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Plugin::JSONConfig> is a JSON configuration plugin that
+preprocesses its input with L<Mojo::Template>.
+
+The application object can be accessed via C<$app> or the C<app> function. A
+default configuration filename in the application home directory will be
+generated from the value of L<Mojolicious/"moniker"> (C<$moniker.json>). You can
+extend the normal configuration file C<$moniker.json> with C<mode> specific ones
+like C<$moniker.$mode.json>, which will be detected automatically.
+
+If the configuration value C<config_override> has been set in
+L<Mojolicious/"config"> when this plugin is loaded, it will not do anything.
+
+The code of this plugin is a good example for learning to build new plugins,
+you're welcome to fork it.
+
+See L<Mojolicious::Plugins/"PLUGINS"> for a list of plugins that are available
+by default.
+
+=head1 OPTIONS
+
+L<Mojolicious::Plugin::JSONConfig> inherits all options from
+L<Mojolicious::Plugin::Config> and supports the following new ones.
+
+=head2 template
+
+  # Mojolicious::Lite
+  plugin JSONConfig => {template => {line_start => '.'}};
+
+Attribute values passed to L<Mojo::Template> object used to preprocess
+configuration files.
+
+=head1 METHODS
+
+L<Mojolicious::Plugin::JSONConfig> inherits all methods from
+L<Mojolicious::Plugin::Config> and implements the following new ones.
+
+=head2 parse
+
+  $plugin->parse($content, $file, $conf, $app);
+
+Process content with L</"render"> and parse it with L<Mojo::JSON>.
+
+  sub parse {
+    my ($self, $content, $file, $conf, $app) = @_;
+    ...
+    $content = $self->render($content, $file, $conf, $app);
+    ...
+    return $hash;
+  }
+
+=head2 register
+
+  my $config = $plugin->register(Mojolicious->new);
+  my $config = $plugin->register(Mojolicious->new, {file => '/etc/foo.conf'});
+
+Register plugin in L<Mojolicious> application and merge configuration.
+
+=head2 render
+
+  $plugin->render($content, $file, $conf, $app);
+
+Process configuration file with L<Mojo::Template>.
+
+  sub render {
+    my ($self, $content, $file, $conf, $app) = @_;
+    ...
+    return $content;
+  }
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Plugin/Mount.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Plugin/Mount.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Plugin/Mount.pm	(revision 32205)
@@ -0,0 +1,81 @@
+package Mojolicious::Plugin::Mount;
+use Mojo::Base 'Mojolicious::Plugin';
+
+use Mojo::Server;
+
+sub register {
+  my ($self, $app, $conf) = @_;
+
+  my $path  = (keys %$conf)[0];
+  my $embed = Mojo::Server->new->load_app($conf->{$path});
+  $embed->secrets($app->secrets);
+
+  # Extract host
+  my $host;
+  ($host, $path) = ($1 ? qr/^(?:.*\.)?\Q$2\E$/i : qr/^\Q$2\E$/i, $3)
+    if $path =~ m!^(\*\.)?([^/]+)(/.*)?$!;
+
+  my $route = $app->routes->route($path)->detour(app => $embed);
+  return $host ? $route->over(host => $host) : $route;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Plugin::Mount - Application mount plugin
+
+=head1 SYNOPSIS
+
+  # Mojolicious
+  my $route = $app->plugin(Mount => {'/prefix' => '/home/sri/myapp.pl'});
+
+  # Mojolicious::Lite
+  my $route = plugin Mount => {'/prefix' => '/home/sri/myapp.pl'};
+
+  # Adjust the generated route and mounted application
+  my $example = plugin Mount => {'/example' => '/home/sri/example.pl'};
+  $example->to(message => 'It works great!');
+  my $app = $example->pattern->defaults->{app};
+  $app->config(foo => 'bar');
+  $app->log(app->log);
+
+  # Mount application with host
+  plugin Mount => {'example.com' => '/home/sri/myapp.pl'};
+
+  # Host and path
+  plugin Mount => {'example.com/myapp' => '/home/sri/myapp.pl'};
+
+  # Or even hosts with wildcard subdomains
+  plugin Mount => {'*.example.com/myapp' => '/home/sri/myapp.pl'};
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Plugin::Mount> is a plugin that allows you to mount whole
+L<Mojolicious> applications.
+
+The code of this plugin is a good example for learning to build new plugins,
+you're welcome to fork it.
+
+See L<Mojolicious::Plugins/"PLUGINS"> for a list of plugins that are available
+by default.
+
+=head1 METHODS
+
+L<Mojolicious::Plugin::Mount> inherits all methods from L<Mojolicious::Plugin>
+and implements the following new ones.
+
+=head2 register
+
+  my $route = $plugin->register(Mojolicious->new, {'/foo' => '/some/app.pl'});
+
+Mount L<Mojolicious> application and return the generated route, which is
+usually a L<Mojolicious::Routes::Route> object.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Plugin/PODRenderer.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Plugin/PODRenderer.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Plugin/PODRenderer.pm	(revision 32205)
@@ -0,0 +1,197 @@
+package Mojolicious::Plugin::PODRenderer;
+use Mojo::Base 'Mojolicious::Plugin';
+
+use Mojo::Asset::File;
+use Mojo::ByteStream;
+use Mojo::DOM;
+use Mojo::File 'path';
+use Mojo::URL;
+use Pod::Simple::XHTML;
+use Pod::Simple::Search;
+
+sub register {
+  my ($self, $app, $conf) = @_;
+
+  my $preprocess = $conf->{preprocess} || 'ep';
+  $app->renderer->add_handler(
+    $conf->{name} || 'pod' => sub {
+      my ($renderer, $c, $output, $options) = @_;
+      $renderer->handlers->{$preprocess}($renderer, $c, $output, $options);
+      $$output = _pod_to_html($$output) if defined $$output;
+    }
+  );
+
+  $app->helper(
+    pod_to_html => sub { shift; Mojo::ByteStream->new(_pod_to_html(@_)) });
+
+  # Perldoc browser
+  return undef if $conf->{no_perldoc};
+  my $defaults = {module => 'Mojolicious/Guides'};
+  return $app->routes->any(
+    '/perldoc/:module' => $defaults => [module => qr/[^.]+/] => \&_perldoc);
+}
+
+sub _indentation {
+  (sort map {/^(\s+)/} @{shift()})[0];
+}
+
+sub _html {
+  my ($c, $src) = @_;
+
+  # Rewrite links
+  my $dom     = Mojo::DOM->new(_pod_to_html($src));
+  my $perldoc = $c->url_for('/perldoc/');
+  $_->{href} =~ s!^https://metacpan\.org/pod/!$perldoc!
+    and $_->{href} =~ s!::!/!gi
+    for $dom->find('a[href]')->map('attr')->each;
+
+  # Rewrite code blocks for syntax highlighting and correct indentation
+  for my $e ($dom->find('pre > code')->each) {
+    next if (my $str = $e->content) =~ /^\s*(?:\$|Usage:)\s+/m;
+    next unless $str =~ /[\$\@\%]\w|-&gt;\w|^use\s+\w/m;
+    my $attrs = $e->attr;
+    my $class = $attrs->{class};
+    $attrs->{class} = defined $class ? "$class prettyprint" : 'prettyprint';
+  }
+
+  # Rewrite headers
+  my $toc = Mojo::URL->new->fragment('toc');
+  my @parts;
+  for my $e ($dom->find('h1, h2, h3, h4')->each) {
+
+    push @parts, [] if $e->tag eq 'h1' || !@parts;
+    my $link = Mojo::URL->new->fragment($e->{id});
+    push @{$parts[-1]}, my $text = $e->all_text, $link;
+    my $permalink = $c->link_to('#' => $link, class => 'permalink');
+    $e->content($permalink . $c->link_to($text => $toc));
+  }
+
+  # Try to find a title
+  my $title = 'Perldoc';
+  $dom->find('h1 + p')->first(sub { $title = shift->text });
+
+  # Combine everything to a proper response
+  $c->content_for(perldoc => "$dom");
+  $c->render('mojo/perldoc', title => $title, parts => \@parts);
+}
+
+sub _perldoc {
+  my $c = shift;
+
+  # Find module or redirect to CPAN
+  my $module = join '::', split('/', $c->param('module'));
+  $c->stash(cpan => "https://metacpan.org/pod/$module");
+  my $path
+    = Pod::Simple::Search->new->find($module, map { $_, "$_/pods" } @INC);
+  return $c->redirect_to($c->stash('cpan')) unless $path && -r $path;
+
+  my $src = path($path)->slurp;
+  $c->respond_to(txt => {data => $src}, html => sub { _html($c, $src) });
+}
+
+sub _pod_to_html {
+  return '' unless defined(my $pod = ref $_[0] eq 'CODE' ? shift->() : shift);
+
+  my $parser = Pod::Simple::XHTML->new;
+  $parser->perldoc_url_prefix('https://metacpan.org/pod/');
+  $parser->$_('') for qw(html_header html_footer);
+  $parser->strip_verbatim_indent(\&_indentation);
+  $parser->output_string(\(my $output));
+  return $@ unless eval { $parser->parse_string_document("$pod"); 1 };
+
+  return $output;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Plugin::PODRenderer - POD renderer plugin
+
+=head1 SYNOPSIS
+
+  # Mojolicious (with documentation browser under "/perldoc")
+  my $route = $app->plugin('PODRenderer');
+  my $route = $app->plugin(PODRenderer => {name => 'foo'});
+  my $route = $app->plugin(PODRenderer => {preprocess => 'epl'});
+
+  # Mojolicious::Lite (with documentation browser under "/perldoc")
+  my $route = plugin 'PODRenderer';
+  my $route = plugin PODRenderer => {name => 'foo'};
+  my $route = plugin PODRenderer => {preprocess => 'epl'};
+
+  # Without documentation browser
+  plugin PODRenderer => {no_perldoc => 1};
+
+  # foo.html.ep
+  %= pod_to_html "=head1 TEST\n\nC<123>"
+
+  # foo.html.pod
+  =head1 <%= uc 'test' %>
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Plugin::PODRenderer> is a renderer for true Perl hackers, rawr!
+
+The code of this plugin is a good example for learning to build new plugins,
+you're welcome to fork it.
+
+See L<Mojolicious::Plugins/"PLUGINS"> for a list of plugins that are available
+by default.
+
+=head1 OPTIONS
+
+L<Mojolicious::Plugin::PODRenderer> supports the following options.
+
+=head2 name
+
+  # Mojolicious::Lite
+  plugin PODRenderer => {name => 'foo'};
+
+Handler name, defaults to C<pod>.
+
+=head2 no_perldoc
+
+  # Mojolicious::Lite
+  plugin PODRenderer => {no_perldoc => 1};
+
+Disable L<Mojolicious::Guides> documentation browser that will otherwise be
+available under C</perldoc>.
+
+=head2 preprocess
+
+  # Mojolicious::Lite
+  plugin PODRenderer => {preprocess => 'epl'};
+
+Name of handler used to preprocess POD, defaults to C<ep>.
+
+=head1 HELPERS
+
+L<Mojolicious::Plugin::PODRenderer> implements the following helpers.
+
+=head2 pod_to_html
+
+  %= pod_to_html '=head2 lalala'
+  <%= pod_to_html begin %>=head2 lalala<% end %>
+
+Render POD to HTML without preprocessing.
+
+=head1 METHODS
+
+L<Mojolicious::Plugin::PODRenderer> inherits all methods from
+L<Mojolicious::Plugin> and implements the following new ones.
+
+=head2 register
+
+  my $route = $plugin->register(Mojolicious->new);
+  my $route = $plugin->register(Mojolicious->new, {name => 'foo'});
+
+Register renderer and helper in L<Mojolicious> application.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Plugin/TagHelpers.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Plugin/TagHelpers.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Plugin/TagHelpers.pm	(revision 32205)
@@ -0,0 +1,797 @@
+package Mojolicious::Plugin::TagHelpers;
+use Mojo::Base 'Mojolicious::Plugin';
+
+use Mojo::ByteStream;
+use Mojo::DOM::HTML 'tag_to_html';
+use Scalar::Util 'blessed';
+
+sub register {
+  my ($self, $app) = @_;
+
+  # Text field variations
+  my @time = qw(date month time week);
+  for my $name (@time, qw(color email number range search tel text url)) {
+    $app->helper("${name}_field" => sub { _input(@_, type => $name) });
+  }
+  $app->helper(datetime_field => sub { _input(@_, type => 'datetime-local') });
+
+  my @helpers = (
+    qw(csrf_field form_for hidden_field javascript label_for link_to),
+    qw(select_field stylesheet submit_button tag_with_error text_area)
+  );
+  $app->helper($_ => __PACKAGE__->can("_$_")) for @helpers;
+
+  $app->helper(button_to => sub { _button_to(0, @_) });
+  $app->helper(check_box => sub { _input(@_, type => 'checkbox') });
+  $app->helper(csrf_button_to => sub { _button_to(1, @_) });
+  $app->helper(file_field => sub { _empty_field('file', @_) });
+  $app->helper(image => sub { _tag('img', src => shift->url_for(shift), @_) });
+  $app->helper(input_tag      => sub { _input(@_) });
+  $app->helper(password_field => sub { _empty_field('password', @_) });
+  $app->helper(radio_button   => sub { _input(@_, type => 'radio') });
+
+  # "t" is just a shortcut for the "tag" helper
+  $app->helper($_ => sub { shift; _tag(@_) }) for qw(t tag);
+}
+
+sub _button_to {
+  my ($csrf, $c, $text) = (shift, shift, shift);
+  my $prefix = $csrf ? _csrf_field($c) : '';
+  return _form_for($c, @_, sub { $prefix . _submit_button($c, $text) });
+}
+
+sub _csrf_field {
+  my $c = shift;
+  return _hidden_field($c, csrf_token => $c->helpers->csrf_token, @_);
+}
+
+sub _empty_field {
+  my ($type, $c, $name) = (shift, shift, shift);
+  return _validation($c, $name, 'input', name => $name, @_, type => $type);
+}
+
+sub _form_for {
+  my ($c, @url) = (shift, shift);
+  push @url, shift if ref $_[0] eq 'HASH';
+
+  # Method detection
+  my $r      = $c->app->routes->lookup($url[0]);
+  my $method = $r ? $r->suggested_method : 'GET';
+  my @post   = $method ne 'GET' ? (method => 'POST') : ();
+
+  my $url = $c->url_for(@url);
+  $url->query({_method => $method}) if @post && $method ne 'POST';
+  return _tag('form', action => $url, @post, @_);
+}
+
+sub _hidden_field {
+  my ($c, $name, $value) = (shift, shift, shift);
+  return _tag('input', name => $name, value => $value, @_, type => 'hidden');
+}
+
+sub _input {
+  my ($c, $name) = (shift, shift);
+  my %attrs = @_ % 2 ? (value => shift, @_) : @_;
+
+  if (my @values = @{$c->every_param($name)}) {
+
+    # Checkbox or radiobutton
+    my $type = $attrs{type} || '';
+    if ($type eq 'checkbox' || $type eq 'radio') {
+      my $value = $attrs{value} // 'on';
+      delete $attrs{checked};
+      $attrs{checked} = undef if grep { $_ eq $value } @values;
+    }
+
+    # Others
+    else { $attrs{value} = $values[-1] }
+  }
+
+  return _validation($c, $name, 'input', name => $name, %attrs);
+}
+
+sub _javascript {
+  my $c = shift;
+  my $content
+    = ref $_[-1] eq 'CODE' ? "//<![CDATA[\n" . pop->() . "\n//]]>" : '';
+  my @src = @_ % 2 ? (src => $c->url_for(shift)) : ();
+  return _tag('script', @src, @_, sub {$content});
+}
+
+sub _label_for {
+  my ($c, $name) = (shift, shift);
+  my $content = ref $_[-1] eq 'CODE' ? pop : shift;
+  return _validation($c, $name, 'label', for => $name, @_, $content);
+}
+
+sub _link_to {
+  my ($c, $content) = (shift, shift);
+  my @url = ($content);
+
+  # Content
+  unless (ref $_[-1] eq 'CODE') {
+    @url = (shift);
+    push @_, $content;
+  }
+
+  # Captures
+  push @url, shift if ref $_[0] eq 'HASH';
+
+  return _tag('a', href => $c->url_for(@url), @_);
+}
+
+sub _option {
+  my ($values, $pair) = @_;
+
+  $pair = [$pair => $pair] unless ref $pair eq 'ARRAY';
+  my %attrs = (value => $pair->[1], @$pair[2 .. $#$pair]);
+  delete $attrs{selected} if keys %$values;
+  $attrs{selected} = undef if $values->{$pair->[1]};
+
+  return _tag('option', %attrs, $pair->[0]);
+}
+
+sub _select_field {
+  my ($c, $name, $options, %attrs) = (shift, shift, shift, @_);
+
+  my %values = map { $_ => 1 } @{$c->every_param($name)};
+
+  my $groups = '';
+  for my $group (@$options) {
+
+    # "optgroup" tag
+    if (blessed $group && $group->isa('Mojo::Collection')) {
+      my ($label, $values, %attrs) = @$group;
+      my $content = join '', map { _option(\%values, $_) } @$values;
+      $groups .= _tag('optgroup', label => $label, %attrs, sub {$content});
+    }
+
+    # "option" tag
+    else { $groups .= _option(\%values, $group) }
+  }
+
+  return _validation($c, $name, 'select', name => $name, %attrs, sub {$groups});
+}
+
+sub _stylesheet {
+  my $c = shift;
+  my $content
+    = ref $_[-1] eq 'CODE' ? "/*<![CDATA[*/\n" . pop->() . "\n/*]]>*/" : '';
+  return _tag('style', @_, sub {$content}) unless @_ % 2;
+  return _tag('link', rel => 'stylesheet', href => $c->url_for(shift), @_);
+}
+
+sub _submit_button {
+  my ($c, $value) = (shift, shift // 'Ok');
+  return _tag('input', value => $value, @_, type => 'submit');
+}
+
+sub _tag { Mojo::ByteStream->new(tag_to_html(@_)) }
+
+sub _tag_with_error {
+  my ($c, $tag) = (shift, shift);
+  my ($content, %attrs) = (@_ % 2 ? pop : undef, @_);
+  $attrs{class} .= $attrs{class} ? ' field-with-error' : 'field-with-error';
+  return _tag($tag, %attrs, defined $content ? $content : ());
+}
+
+sub _text_area {
+  my ($c, $name) = (shift, shift);
+
+  my $cb = ref $_[-1] eq 'CODE' ? pop : undef;
+  my $content = @_ % 2 ? shift : undef;
+  $content = $c->param($name) // $content // $cb // '';
+
+  return _validation($c, $name, 'textarea', name => $name, @_, $content);
+}
+
+sub _validation {
+  my ($c, $name) = (shift, shift);
+  return _tag(@_) unless $c->validation->has_error($name);
+  return $c->helpers->tag_with_error(@_);
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Plugin::TagHelpers - Tag helpers plugin
+
+=head1 SYNOPSIS
+
+  # Mojolicious
+  $app->plugin('TagHelpers');
+
+  # Mojolicious::Lite
+  plugin 'TagHelpers';
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Plugin::TagHelpers> is a collection of HTML tag helpers for
+L<Mojolicious>, based on the
+L<HTML Living Standard|https://html.spec.whatwg.org>.
+
+Most form helpers can automatically pick up previous input values and will show
+them as default. You can also use
+L<Mojolicious::Plugin::DefaultHelpers/"param"> to set them manually and let
+necessary attributes always be generated automatically.
+
+  % param country => 'germany' unless param 'country';
+  <%= radio_button country => 'germany' %> Germany
+  <%= radio_button country => 'france'  %> France
+  <%= radio_button country => 'uk'      %> UK
+
+For fields that failed validation with L<Mojolicious::Controller/"validation">
+the C<field-with-error> class will be automatically added through
+L</"tag_with_error">, to make styling with CSS easier.
+
+  <input class="field-with-error" name="age" type="text" value="250">
+
+This is a core plugin, that means it is always enabled and its code a good
+example for learning how to build new plugins, you're welcome to fork it.
+
+See L<Mojolicious::Plugins/"PLUGINS"> for a list of plugins that are available
+by default.
+
+=head1 HELPERS
+
+L<Mojolicious::Plugin::TagHelpers> implements the following helpers.
+
+=head2 button_to
+
+  %= button_to Test => 'some_get_route'
+  %= button_to Test => some_get_route => {id => 23} => (class => 'menu')
+  %= button_to Test => 'http://example.com/test' => (class => 'menu')
+  %= button_to Remove => 'some_delete_route'
+
+Generate portable C<form> tag with L</"form_for">, containing a single button.
+
+  <form action="/path/to/get/route">
+    <input type="submit" value="Test">
+  </form>
+  <form action="/path/to/get/route/23" class="menu">
+    <input type="submit" value="Test">
+  </form>
+  <form action="http://example.com/test" class="menu">
+    <input type="submit" value="Test">
+  </form>
+  <form action="/path/to/delete/route?_method=DELETE" method="POST">
+    <input type="submit" value="Remove">
+  </form>
+
+=head2 check_box
+
+  %= check_box 'employed'
+  %= check_box employed => 1
+  %= check_box employed => 1, checked => undef, id => 'foo'
+
+Generate C<input> tag of type C<checkbox>. Previous input values will
+automatically get picked up and shown as default.
+
+  <input name="employed" type="checkbox">
+  <input name="employed" type="checkbox" value="1">
+  <input checked id="foo" name="employed" type="checkbox" value="1">
+
+=head2 color_field
+
+  %= color_field 'background'
+  %= color_field background => '#ffffff'
+  %= color_field background => '#ffffff', id => 'foo'
+
+Generate C<input> tag of type C<color>. Previous input values will
+automatically get picked up and shown as default.
+
+  <input name="background" type="color">
+  <input name="background" type="color" value="#ffffff">
+  <input id="foo" name="background" type="color" value="#ffffff">
+
+=head2 csrf_button_to
+
+  %= csrf_button_to Remove => 'some_delete_route'
+
+Same as L</"button_to">, but also includes a L</"csrf_field">.
+
+  <form action="/path/to/delete/route?_method=DELETE" method="POST">
+    <input name="csrf_token" type="hidden" value="fa6a08...">
+    <input type="submit" value="Remove">
+  </form>
+
+=head2 csrf_field
+
+  %= csrf_field
+
+Generate C<input> tag of type C<hidden> with
+L<Mojolicious::Plugin::DefaultHelpers/"csrf_token">.
+
+  <input name="csrf_token" type="hidden" value="fa6a08...">
+
+=head2 date_field
+
+  %= date_field 'end'
+  %= date_field end => '2012-12-21'
+  %= date_field end => '2012-12-21', id => 'foo'
+
+Generate C<input> tag of type C<date>. Previous input values will automatically
+get picked up and shown as default.
+
+  <input name="end" type="date">
+  <input name="end" type="date" value="2012-12-21">
+  <input id="foo" name="end" type="date" value="2012-12-21">
+
+=head2 datetime_field
+
+  %= datetime_field 'end'
+  %= datetime_field end => '2012-12-21T23:59:59'
+  %= datetime_field end => '2012-12-21T23:59:59', id => 'foo'
+
+Generate C<input> tag of type C<datetime-local>. Previous input values will
+automatically get picked up and shown as default.
+
+  <input name="end" type="datetime-local">
+  <input name="end" type="datetime-local" value="2012-12-21T23:59:59">
+  <input id="foo" name="end" type="datetime-local" value="2012-12-21T23:59:59">
+
+=head2 email_field
+
+  %= email_field 'notify'
+  %= email_field notify => 'nospam@example.com'
+  %= email_field notify => 'nospam@example.com', id => 'foo'
+
+Generate C<input> tag of type C<email>. Previous input values will
+automatically get picked up and shown as default.
+
+  <input name="notify" type="email">
+  <input name="notify" type="email" value="nospam@example.com">
+  <input id="foo" name="notify" type="email" value="nospam@example.com">
+
+=head2 file_field
+
+  %= file_field 'avatar'
+  %= file_field 'avatar', id => 'foo'
+
+Generate C<input> tag of type C<file>.
+
+  <input name="avatar" type="file">
+  <input id="foo" name="avatar" type="file">
+
+=head2 form_for
+
+  %= form_for login => begin
+    %= text_field 'first_name'
+    %= submit_button
+  % end
+  %= form_for login => {format => 'txt'} => (method => 'POST') => begin
+    %= text_field 'first_name'
+    %= submit_button
+  % end
+  %= form_for '/login' => (enctype => 'multipart/form-data') => begin
+    %= text_field 'first_name', disabled => 'disabled'
+    %= submit_button
+  % end
+  %= form_for 'http://example.com/login' => (method => 'POST') => begin
+    %= text_field 'first_name'
+    %= submit_button
+  % end
+  %= form_for some_delete_route => begin
+    %= submit_button 'Remove'
+  % end
+
+Generate portable C<form> tag with L<Mojolicious::Controller/"url_for">. For
+routes that do not allow C<GET>, a C<method> attribute with the value C<POST>
+will be automatically added. And for methods other than C<GET> or C<POST>, an
+C<_method> query parameter will be added as well.
+
+  <form action="/path/to/login">
+    <input name="first_name" type="text">
+    <input type="submit" value="Ok">
+  </form>
+  <form action="/path/to/login.txt" method="POST">
+    <input name="first_name" type="text">
+    <input type="submit" value="Ok">
+  </form>
+  <form action="/path/to/login" enctype="multipart/form-data">
+    <input disabled="disabled" name="first_name" type="text">
+    <input type="submit" value="Ok">
+  </form>
+  <form action="http://example.com/login" method="POST">
+    <input name="first_name" type="text">
+    <input type="submit" value="Ok">
+  </form>
+  <form action="/path/to/delete/route?_method=DELETE" method="POST">
+    <input type="submit" value="Remove">
+  </form>
+
+=head2 hidden_field
+
+  %= hidden_field foo => 'bar'
+  %= hidden_field foo => 'bar', id => 'bar'
+
+Generate C<input> tag of type C<hidden>.
+
+  <input name="foo" type="hidden" value="bar">
+  <input id="bar" name="foo" type="hidden" value="bar">
+
+=head2 image
+
+  %= image '/images/foo.png'
+  %= image '/images/foo.png', alt => 'Foo'
+
+Generate portable C<img> tag.
+
+  <img src="/path/to/images/foo.png">
+  <img alt="Foo" src="/path/to/images/foo.png">
+
+=head2 input_tag
+
+  %= input_tag 'first_name'
+  %= input_tag first_name => 'Default'
+  %= input_tag 'employed', type => 'checkbox'
+
+Generate C<input> tag. Previous input values will automatically get picked up
+and shown as default.
+
+  <input name="first_name">
+  <input name="first_name" value="Default">
+  <input name="employed" type="checkbox">
+
+=head2 javascript
+
+  %= javascript '/script.js'
+  %= javascript '/script.js', defer => undef
+  %= javascript begin
+    var a = 'b';
+  % end
+
+Generate portable C<script> tag for JavaScript asset.
+
+  <script src="/path/to/script.js"></script>
+  <script defer src="/path/to/script.js"></script>
+  <script><![CDATA[
+    var a = 'b';
+  ]]></script>
+
+=head2 label_for
+
+  %= label_for first_name => 'First name'
+  %= label_for first_name => 'First name', class => 'user'
+  %= label_for first_name => begin
+    First name
+  % end
+  %= label_for first_name => (class => 'user') => begin
+    First name
+  % end
+
+Generate C<label> tag.
+
+  <label for="first_name">First name</label>
+  <label class="user" for="first_name">First name</label>
+  <label for="first_name">
+    First name
+  </label>
+  <label class="user" for="first_name">
+    First name
+  </label>
+
+=head2 link_to
+
+  %= link_to Home => 'index'
+  %= link_to Home => 'index' => {format => 'txt'} => (class => 'menu')
+  %= link_to index => {format => 'txt'} => (class => 'menu') => begin
+    Home
+  % end
+  %= link_to Contact => 'mailto:sri@example.com'
+  <%= link_to index => begin %>Home<% end %>
+  <%= link_to '/file.txt' => begin %>File<% end %>
+  <%= link_to 'https://mojolicious.org' => begin %>Mojolicious<% end %>
+  <%= link_to url_for->query(foo => 'bar')->to_abs => begin %>Retry<% end %>
+
+Generate portable C<a> tag with L<Mojolicious::Controller/"url_for">, defaults
+to using the capitalized link target as content.
+
+  <a href="/path/to/index">Home</a>
+  <a class="menu" href="/path/to/index.txt">Home</a>
+  <a class="menu" href="/path/to/index.txt">
+    Home
+  </a>
+  <a href="mailto:sri@example.com">Contact</a>
+  <a href="/path/to/index">Home</a>
+  <a href="/path/to/file.txt">File</a>
+  <a href="https://mojolicious.org">Mojolicious</a>
+  <a href="http://127.0.0.1:3000/current/path?foo=bar">Retry</a>
+
+=head2 month_field
+
+  %= month_field 'vacation'
+  %= month_field vacation => '2012-12'
+  %= month_field vacation => '2012-12', id => 'foo'
+
+Generate C<input> tag of type C<month>. Previous input values will
+automatically get picked up and shown as default.
+
+  <input name="vacation" type="month">
+  <input name="vacation" type="month" value="2012-12">
+  <input id="foo" name="vacation" type="month" value="2012-12">
+
+=head2 number_field
+
+  %= number_field 'age'
+  %= number_field age => 25
+  %= number_field age => 25, id => 'foo', min => 0, max => 200
+
+Generate C<input> tag of type C<number>. Previous input values will
+automatically get picked up and shown as default.
+
+  <input name="age" type="number">
+  <input name="age" type="number" value="25">
+  <input id="foo" max="200" min="0" name="age" type="number" value="25">
+
+=head2 password_field
+
+  %= password_field 'pass'
+  %= password_field 'pass', id => 'foo'
+
+Generate C<input> tag of type C<password>.
+
+  <input name="pass" type="password">
+  <input id="foo" name="pass" type="password">
+
+=head2 radio_button
+
+  %= radio_button 'test'
+  %= radio_button country => 'germany'
+  %= radio_button country => 'germany', checked => undef, id => 'foo'
+
+Generate C<input> tag of type C<radio>. Previous input values will
+automatically get picked up and shown as default.
+
+  <input name="test" type="radio">
+  <input name="country" type="radio" value="germany">
+  <input checked id="foo" name="country" type="radio" value="germany">
+
+=head2 range_field
+
+  %= range_field 'age'
+  %= range_field age => 25
+  %= range_field age => 25, id => 'foo', min => 0, max => 200
+
+Generate C<input> tag of type C<range>. Previous input values will
+automatically get picked up and shown as default.
+
+  <input name="age" type="range">
+  <input name="age" type="range" value="25">
+  <input id="foo" max="200" min="200" name="age" type="range" value="25">
+
+=head2 search_field
+
+  %= search_field 'q'
+  %= search_field q => 'perl'
+  %= search_field q => 'perl', id => 'foo'
+
+Generate C<input> tag of type C<search>. Previous input values will
+automatically get picked up and shown as default.
+
+  <input name="q" type="search">
+  <input name="q" type="search" value="perl">
+  <input id="foo" name="q" type="search" value="perl">
+
+=head2 select_field
+
+  %= select_field country => ['de', 'en']
+  %= select_field country => [[Germany => 'de'], 'en'], id => 'eu'
+  %= select_field country => [[Germany => 'de', selected => 'selected'], 'en']
+  %= select_field country => [c(EU => [[Germany => 'de'], 'en'], id => 'eu')]
+  %= select_field country => [c(EU => ['de', 'en']), c(Asia => ['cn', 'jp'])]
+
+Generate C<select> and C<option> tags from array references and C<optgroup>
+tags from L<Mojo::Collection> objects. Previous input values will automatically
+get picked up and shown as default.
+
+  <select name="country">
+    <option value="de">de</option>
+    <option value="en">en</option>
+  </select>
+  <select id="eu" name="country">
+    <option value="de">Germany</option>
+    <option value="en">en</option>
+  </select>
+  <select name="country">
+    <option selected="selected" value="de">Germany</option>
+    <option value="en">en</option>
+  </select>
+  <select name="country">
+    <optgroup id="eu" label="EU">
+      <option value="de">Germany</option>
+      <option value="en">en</option>
+    </optgroup>
+  </select>
+  <select name="country">
+    <optgroup label="EU">
+      <option value="de">de</option>
+      <option value="en">en</option>
+    </optgroup>
+    <optgroup label="Asia">
+      <option value="cn">cn</option>
+      <option value="jp">jp</option>
+    </optgroup>
+  </select>
+
+=head2 stylesheet
+
+  %= stylesheet '/foo.css'
+  %= stylesheet '/foo.css', title => 'Foo style'
+  %= stylesheet begin
+    body {color: #000}
+  % end
+
+Generate portable C<style> or C<link> tag for CSS asset.
+
+  <link href="/path/to/foo.css" rel="stylesheet">
+  <link href="/path/to/foo.css" rel="stylesheet" title="Foo style">
+  <style><![CDATA[
+    body {color: #000}
+  ]]></style>
+
+=head2 submit_button
+
+  %= submit_button
+  %= submit_button 'Ok!', id => 'foo'
+
+Generate C<input> tag of type C<submit>.
+
+  <input type="submit" value="Ok">
+  <input id="foo" type="submit" value="Ok!">
+
+=head2 t
+
+  %= t div => 'test & 123'
+
+Alias for L</"tag">.
+
+  <div>test &amp; 123</div>
+
+=head2 tag
+
+  %= tag 'br'
+  %= tag 'div'
+  %= tag 'div', id => 'foo', hidden => undef
+  %= tag 'div', 'test & 123'
+  %= tag 'div', id => 'foo', 'test & 123'
+  %= tag 'div', data => {my_id => 1, Name => 'test'}, 'test & 123'
+  %= tag div => begin
+    test & 123
+  % end
+  <%= tag div => (id => 'foo') => begin %>test & 123<% end %>
+
+Alias for L<Mojo::DOM/"new_tag">.
+
+  <br>
+  <div></div>
+  <div id="foo" hidden></div>
+  <div>test &amp; 123</div>
+  <div id="foo">test &amp; 123</div>
+  <div data-my-id="1" data-name="test">test &amp; 123</div>
+  <div>
+    test & 123
+  </div>
+  <div id="foo">test & 123</div>
+
+Very useful for reuse in more specific tag helpers.
+
+  my $output = $c->tag('meta');
+  my $output = $c->tag('meta', charset => 'UTF-8');
+  my $output = $c->tag('div', '<p>This will be escaped</p>');
+  my $output = $c->tag('div', sub { '<p>This will not be escaped</p>' });
+
+Results are automatically wrapped in L<Mojo::ByteStream> objects to prevent
+accidental double escaping in C<ep> templates.
+
+=head2 tag_with_error
+
+  %= tag_with_error 'input', class => 'foo'
+
+Same as L</"tag">, but adds the class C<field-with-error>.
+
+  <input class="foo field-with-error">
+
+=head2 tel_field
+
+  %= tel_field 'work'
+  %= tel_field work => '123456789'
+  %= tel_field work => '123456789', id => 'foo'
+
+Generate C<input> tag of type C<tel>. Previous input values will automatically
+get picked up and shown as default.
+
+  <input name="work" type="tel">
+  <input name="work" type="tel" value="123456789">
+  <input id="foo" name="work" type="tel" value="123456789">
+
+=head2 text_area
+
+  %= text_area 'story'
+  %= text_area 'story', cols => 40
+  %= text_area story => 'Default', cols => 40
+  %= text_area story => (cols => 40) => begin
+    Default
+  % end
+
+Generate C<textarea> tag. Previous input values will automatically get picked
+up and shown as default.
+
+  <textarea name="story"></textarea>
+  <textarea cols="40" name="story"></textarea>
+  <textarea cols="40" name="story">Default</textarea>
+  <textarea cols="40" name="story">
+    Default
+  </textarea>
+
+=head2 text_field
+
+  %= text_field 'first_name'
+  %= text_field first_name => 'Default'
+  %= text_field first_name => 'Default', class => 'user'
+
+Generate C<input> tag of type C<text>. Previous input values will automatically
+get picked up and shown as default.
+
+  <input name="first_name" type="text">
+  <input name="first_name" type="text" value="Default">
+  <input class="user" name="first_name" type="text" value="Default">
+
+=head2 time_field
+
+  %= time_field 'start'
+  %= time_field start => '23:59:59'
+  %= time_field start => '23:59:59', id => 'foo'
+
+Generate C<input> tag of type C<time>. Previous input values will automatically
+get picked up and shown as default.
+
+  <input name="start" type="time">
+  <input name="start" type="time" value="23:59:59">
+  <input id="foo" name="start" type="time" value="23:59:59">
+
+=head2 url_field
+
+  %= url_field 'address'
+  %= url_field address => 'https://mojolicious.org'
+  %= url_field address => 'https://mojolicious.org', id => 'foo'
+
+Generate C<input> tag of type C<url>. Previous input values will automatically
+get picked up and shown as default.
+
+  <input name="address" type="url">
+  <input name="address" type="url" value="https://mojolicious.org">
+  <input id="foo" name="address" type="url" value="https://mojolicious.org">
+
+=head2 week_field
+
+  %= week_field 'vacation'
+  %= week_field vacation => '2012-W17'
+  %= week_field vacation => '2012-W17', id => 'foo'
+
+Generate C<input> tag of type C<week>. Previous input values will automatically
+get picked up and shown as default.
+
+  <input name="vacation" type="week">
+  <input name="vacation" type="week" value="2012-W17">
+  <input id="foo" name="vacation" type="week" value="2012-W17">
+
+=head1 METHODS
+
+L<Mojolicious::Plugin::TagHelpers> inherits all methods from
+L<Mojolicious::Plugin> and implements the following new ones.
+
+=head2 register
+
+  $plugin->register(Mojolicious->new);
+
+Register helpers in L<Mojolicious> application.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Plugins.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Plugins.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Plugins.pm	(revision 32205)
@@ -0,0 +1,193 @@
+package Mojolicious::Plugins;
+use Mojo::Base 'Mojo::EventEmitter';
+
+use Mojo::Loader 'load_class';
+use Mojo::Util 'camelize';
+
+has namespaces => sub { ['Mojolicious::Plugin'] };
+
+sub emit_chain {
+  my ($self, $name, @args) = @_;
+
+  my $wrapper;
+  for my $cb (reverse @{$self->subscribers($name)}) {
+    my $next = $wrapper;
+    $wrapper = sub { $cb->($next, @args) };
+  }
+
+  !$wrapper ? return : return $wrapper->();
+}
+
+sub emit_hook {
+  my $self = shift;
+  for my $cb (@{$self->subscribers(shift)}) { $cb->(@_) }
+  return $self;
+}
+
+sub emit_hook_reverse {
+  my $self = shift;
+  for my $cb (reverse @{$self->subscribers(shift)}) { $cb->(@_) }
+  return $self;
+}
+
+sub load_plugin {
+  my ($self, $name) = @_;
+
+  # Try all namespaces and full module name
+  my $suffix = $name =~ /^[a-z]/ ? camelize $name : $name;
+  my @classes = map {"${_}::$suffix"} @{$self->namespaces};
+  for my $class (@classes, $name) { return $class->new if _load($class) }
+
+  # Not found
+  die qq{Plugin "$name" missing, maybe you need to install it?\n};
+}
+
+sub register_plugin {
+  shift->load_plugin(shift)->register(shift, ref $_[0] ? $_[0] : {@_});
+}
+
+sub _load {
+  my $module = shift;
+  return $module->isa('Mojolicious::Plugin') unless my $e = load_class $module;
+  ref $e ? die $e : return undef;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Plugins - Plugin manager
+
+=head1 SYNOPSIS
+
+  use Mojolicious::Plugins;
+
+  my $plugins = Mojolicious::Plugins->new;
+  push @{$plugins->namespaces}, 'MyApp::Plugin';
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Plugins> is the plugin manager of L<Mojolicious>.
+
+=head1 PLUGINS
+
+The following plugins are included in the L<Mojolicious> distribution as
+examples.
+
+=over 2
+
+=item L<Mojolicious::Plugin::Config>
+
+Perl-ish configuration files.
+
+=item L<Mojolicious::Plugin::DefaultHelpers>
+
+General purpose helper collection, loaded automatically.
+
+=item L<Mojolicious::Plugin::EPLRenderer>
+
+Renderer for plain embedded Perl templates, loaded automatically.
+
+=item L<Mojolicious::Plugin::EPRenderer>
+
+Renderer for more sophisticated embedded Perl templates, loaded automatically.
+
+=item L<Mojolicious::Plugin::HeaderCondition>
+
+Route condition for all kinds of headers, loaded automatically.
+
+=item L<Mojolicious::Plugin::JSONConfig>
+
+JSON configuration files.
+
+=item L<Mojolicious::Plugin::Mount>
+
+Mount whole L<Mojolicious> applications.
+
+=item L<Mojolicious::Plugin::PODRenderer>
+
+Renderer for turning POD into HTML and documentation browser for
+L<Mojolicious::Guides>.
+
+=item L<Mojolicious::Plugin::TagHelpers>
+
+Template specific helper collection, loaded automatically.
+
+=back
+
+=head1 EVENTS
+
+L<Mojolicious::Plugins> inherits all events from L<Mojo::EventEmitter>.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Plugins> implements the following attributes.
+
+=head2 namespaces
+
+  my $namespaces = $plugins->namespaces;
+  $plugins       = $plugins->namespaces(['Mojolicious::Plugin']);
+
+Namespaces to load plugins from, defaults to L<Mojolicious::Plugin>.
+
+  # Add another namespace to load plugins from
+  push @{$plugins->namespaces}, 'MyApp::Plugin';
+
+=head1 METHODS
+
+L<Mojolicious::Plugins> inherits all methods from L<Mojo::EventEmitter> and
+implements the following new ones.
+
+=head2 emit_chain
+
+  $plugins->emit_chain('foo');
+  $plugins->emit_chain(foo => 123);
+
+Emit events as chained hooks.
+
+=head2 emit_hook
+
+  $plugins = $plugins->emit_hook('foo');
+  $plugins = $plugins->emit_hook(foo => 123);
+
+Emit events as hooks.
+
+=head2 emit_hook_reverse
+
+  $plugins = $plugins->emit_hook_reverse('foo');
+  $plugins = $plugins->emit_hook_reverse(foo => 123);
+
+Emit events as hooks in reverse order.
+
+=head2 load_plugin
+
+  my $plugin = $plugins->load_plugin('some_thing');
+  my $plugin = $plugins->load_plugin('SomeThing');
+  my $plugin = $plugins->load_plugin('MyApp::Plugin::SomeThing');
+
+Load a plugin from the configured namespaces or by full module name.
+
+=head2 register_plugin
+
+  $plugins->register_plugin('some_thing', Mojolicious->new);
+  $plugins->register_plugin('some_thing', Mojolicious->new, foo => 23);
+  $plugins->register_plugin('some_thing', Mojolicious->new, {foo => 23});
+  $plugins->register_plugin('SomeThing', Mojolicious->new);
+  $plugins->register_plugin('SomeThing', Mojolicious->new, foo => 23);
+  $plugins->register_plugin('SomeThing', Mojolicious->new, {foo => 23});
+  $plugins->register_plugin('MyApp::Plugin::SomeThing', Mojolicious->new);
+  $plugins->register_plugin(
+    'MyApp::Plugin::SomeThing', Mojolicious->new, foo => 23);
+  $plugins->register_plugin(
+    'MyApp::Plugin::SomeThing', Mojolicious->new, {foo => 23});
+
+Load a plugin from the configured namespaces or by full module name and run
+C<register>, optional arguments are passed through.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Renderer.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Renderer.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Renderer.pm	(revision 32205)
@@ -0,0 +1,431 @@
+package Mojolicious::Renderer;
+use Mojo::Base -base;
+
+use Mojo::Cache;
+use Mojo::File 'path';
+use Mojo::JSON 'encode_json';
+use Mojo::Home;
+use Mojo::Loader 'data_section';
+use Mojo::Util qw(decamelize encode md5_sum monkey_patch);
+
+has cache   => sub { Mojo::Cache->new };
+has classes => sub { ['main'] };
+has default_format => 'html';
+has 'default_handler';
+has encoding => 'UTF-8';
+has [qw(handlers helpers)] => sub { {} };
+has paths => sub { [] };
+
+# Bundled templates
+my $TEMPLATES = Mojo::Home->new->mojo_lib_dir->child('Mojolicious', 'resources',
+  'templates');
+
+sub DESTROY { Mojo::Util::_teardown($_) for @{shift->{namespaces}} }
+
+sub accepts {
+  my ($self, $c) = (shift, shift);
+
+  # List representations
+  my $req  = $c->req;
+  my $fmt  = $req->param('format') || $c->stash->{format};
+  my @exts = $fmt ? ($fmt) : ();
+  push @exts, @{$c->app->types->detect($req->headers->accept)};
+  return \@exts unless @_;
+
+  # Find best representation
+  for my $ext (@exts) { $ext eq $_ and return $ext for @_ }
+  return @exts ? undef : shift;
+}
+
+sub add_handler { $_[0]->handlers->{$_[1]} = $_[2] and return $_[0] }
+
+sub add_helper {
+  my ($self, $name, $cb) = @_;
+  $self->helpers->{$name} = $cb;
+  delete $self->{proxy};
+  return $self;
+}
+
+sub get_data_template {
+  my ($self, $options) = @_;
+  return undef unless my $template = $self->template_name($options);
+  return data_section $self->{index}{$template}, $template;
+}
+
+sub get_helper {
+  my ($self, $name) = @_;
+
+  if (my $h = $self->{proxy}{$name} || $self->helpers->{$name}) { return $h }
+
+  my $found;
+  my $class = 'Mojolicious::Renderer::Helpers::' . md5_sum "$name:$self";
+  my $re = length $name ? qr/^(\Q$name\E\.([^.]+))/ : qr/^(([^.]+))/;
+  for my $key (keys %{$self->helpers}) {
+    $key =~ $re ? ($found, my $method) = (1, $2) : next;
+    my $sub = $self->get_helper($1);
+    monkey_patch $class, $method => sub { ${shift()}->$sub(@_) };
+  }
+
+  $found ? push @{$self->{namespaces}}, $class : return undef;
+  return $self->{proxy}{$name} = sub { bless \(my $dummy = shift), $class };
+}
+
+sub render {
+  my ($self, $c, $args) = @_;
+
+  my $stash   = $c->stash;
+  my $options = {
+    encoding => $self->encoding,
+    handler  => $stash->{handler},
+    template => delete $stash->{template},
+    variant  => $stash->{variant}
+  };
+  my $inline = $options->{inline} = delete $stash->{inline};
+  $options->{handler} //= $self->default_handler if defined $inline;
+  $options->{format} = $stash->{format} || $self->default_format;
+
+  # Data
+  return delete $stash->{data}, $options->{format} if defined $stash->{data};
+
+  # Text
+  return _maybe($options->{encoding}, delete $stash->{text}), $options->{format}
+    if defined $stash->{text};
+
+  # JSON
+  return encode_json(delete $stash->{json}), 'json' if exists $stash->{json};
+
+  # Template or templateless handler
+  $options->{template} //= $self->template_for($c);
+  return () unless $self->_render_template($c, \my $output, $options);
+
+  # Inheritance
+  my $content = $stash->{'mojo.content'} ||= {};
+  local $content->{content} = $output =~ /\S/ ? $output : undef
+    if $stash->{extends} || $stash->{layout};
+  while ((my $next = _next($stash)) && !defined $inline) {
+    @$options{qw(handler template)} = ($stash->{handler}, $next);
+    $options->{format} = $stash->{format} || $self->default_format;
+    if ($self->_render_template($c, \my $tmp, $options)) { $output = $tmp }
+    $content->{content} //= $output if $output =~ /\S/;
+  }
+
+  return $output if $args->{'mojo.string'};
+  return _maybe($options->{encoding}, $output), $options->{format};
+}
+
+sub template_for {
+  my ($self, $c) = @_;
+
+  # Normal default template
+  my $stash = $c->stash;
+  my ($controller, $action) = @$stash{qw(controller action)};
+  return join '/', split('-', decamelize $controller), $action
+    if $controller && $action;
+
+  # Try the route name if we don't have controller and action
+  return undef unless my $route = $c->match->endpoint;
+  return $route->name;
+}
+
+sub template_handler {
+  my ($self, $options) = @_;
+  return undef unless my $file = $self->template_name($options);
+  return $self->default_handler unless my $handlers = $self->{templates}{$file};
+  return $handlers->[0];
+}
+
+sub template_name {
+  my ($self, $options) = @_;
+
+  return undef unless defined(my $template = $options->{template});
+  return undef unless my $format = $options->{format};
+  $template .= ".$format";
+
+  $self->warmup unless $self->{templates};
+
+  # Variants
+  my $handler = $options->{handler};
+  if (defined(my $variant = $options->{variant})) {
+    $variant = "$template+$variant";
+    my $handlers = $self->{templates}{$variant} // [];
+    $template = $variant
+      if @$handlers && !defined $handler || grep { $_ eq $handler } @$handlers;
+  }
+
+  return defined $handler ? "$template.$handler" : $template;
+}
+
+sub template_path {
+  my ($self, $options) = @_;
+  return undef unless my $name = $self->template_name($options);
+  my @parts = split '/', $name;
+  -r and return $_
+    for map { path($_, @parts)->to_string } @{$self->paths}, $TEMPLATES;
+  return undef;
+}
+
+sub warmup {
+  my $self = shift;
+
+  my ($index, $templates) = @$self{qw(index templates)} = ({}, {});
+
+  # Handlers for templates
+  for my $path (@{$self->paths}, $TEMPLATES) {
+    s/\.(\w+)$// and push @{$templates->{$_}}, $1
+      for path($path)->list_tree->map(sub { join '/', @{$_->to_rel($path)} })
+      ->each;
+  }
+
+  # Handlers and classes for DATA templates
+  for my $class (reverse @{$self->classes}) {
+    $index->{$_} = $class for my @keys = sort keys %{data_section $class};
+    s/\.(\w+)$// and unshift @{$templates->{$_}}, $1 for reverse @keys;
+  }
+}
+
+sub _maybe { $_[0] ? encode @_ : $_[1] }
+
+sub _next {
+  my $stash = shift;
+  return delete $stash->{extends} if $stash->{extends};
+  return undef unless my $layout = delete $stash->{layout};
+  return join '/', 'layouts', $layout;
+}
+
+sub _render_template {
+  my ($self, $c, $output, $options) = @_;
+
+  my $handler = $options->{handler} ||= $self->template_handler($options);
+  return undef unless $handler;
+  $c->app->log->error(qq{No handler for "$handler" available}) and return undef
+    unless my $renderer = $self->handlers->{$handler};
+
+  $renderer->($self, $c, $output, $options);
+  return 1 if defined $$output;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Renderer - Generate dynamic content
+
+=head1 SYNOPSIS
+
+  use Mojolicious::Renderer;
+
+  my $renderer = Mojolicious::Renderer->new;
+  push @{$renderer->classes}, 'MyApp::Controller::Foo';
+  push @{$renderer->paths}, '/home/sri/templates';
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Renderer> is the standard L<Mojolicious> renderer.
+
+See L<Mojolicious::Guides::Rendering> for more.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Renderer> implements the following attributes.
+
+=head2 cache
+
+  my $cache = $renderer->cache;
+  $renderer = $renderer->cache(Mojo::Cache->new);
+
+Renderer cache, defaults to a L<Mojo::Cache> object.
+
+=head2 classes
+
+  my $classes = $renderer->classes;
+  $renderer   = $renderer->classes(['main']);
+
+Classes to use for finding templates in C<DATA> sections with L<Mojo::Loader>,
+first one has the highest precedence, defaults to C<main>. Only files with
+exactly two extensions will be used, like C<index.html.ep>. Note that for
+templates to be detected, these classes need to have already been loaded and
+added before L</"warmup"> is called, which usually happens automatically during
+application startup.
+
+  # Add another class with templates in DATA section
+  push @{$renderer->classes}, 'Mojolicious::Plugin::Fun';
+
+  # Add another class with templates in DATA section and higher precedence
+  unshift @{$renderer->classes}, 'Mojolicious::Plugin::MoreFun';
+
+=head2 default_format
+
+  my $default = $renderer->default_format;
+  $renderer   = $renderer->default_format('html');
+
+The default format to render if C<format> is not set in the stash, defaults to
+C<html>. Note that changing the default away from C<html> is not recommended, as
+it has the potential to break, for example, plugins with bundled templates.
+
+=head2 default_handler
+
+  my $default = $renderer->default_handler;
+  $renderer   = $renderer->default_handler('ep');
+
+The default template handler to use for rendering in cases where auto-detection
+doesn't work, like for C<inline> templates.
+
+=head2 encoding
+
+  my $encoding = $renderer->encoding;
+  $renderer    = $renderer->encoding('koi8-r');
+
+Will encode generated content if set, defaults to C<UTF-8>. Note that many
+renderers such as L<Mojolicious::Plugin::EPRenderer> also use this value to
+determine if template files should be decoded before processing.
+
+=head2 handlers
+
+  my $handlers = $renderer->handlers;
+  $renderer    = $renderer->handlers({epl => sub {...}});
+
+Registered handlers.
+
+=head2 helpers
+
+  my $helpers = $renderer->helpers;
+  $renderer   = $renderer->helpers({url_for => sub {...}});
+
+Registered helpers.
+
+=head2 paths
+
+  my $paths = $renderer->paths;
+  $renderer = $renderer->paths(['/home/sri/templates']);
+
+Directories to look for templates in, first one has the highest precedence.
+
+  # Add another "templates" directory
+  push @{$renderer->paths}, '/home/sri/templates';
+
+  # Add another "templates" directory with higher precedence
+  unshift @{$renderer->paths}, '/home/sri/themes/blue/templates';
+
+=head1 METHODS
+
+L<Mojolicious::Renderer> inherits all methods from L<Mojo::Base> and implements
+the following new ones.
+
+=head2 accepts
+
+  my $all  = $renderer->accepts(Mojolicious::Controller->new);
+  my $best = $renderer->accepts(Mojolicious::Controller->new, 'html', 'json');
+
+Select best possible representation for L<Mojolicious::Controller> object from
+C<format> C<GET>/C<POST> parameter, C<format> stash value, or C<Accept> request
+header, defaults to returning the first extension if no preference could be
+detected.
+
+=head2 add_handler
+
+  $renderer = $renderer->add_handler(epl => sub {...});
+
+Register a handler.
+
+  $renderer->add_handler(foo => sub {
+    my ($renderer, $c, $output, $options) = @_;
+    ...
+    $$output = 'Hello World!';
+  });
+
+=head2 add_helper
+
+  $renderer = $renderer->add_helper(url_for => sub {...});
+
+Register a helper.
+
+  $renderer->add_helper(foo => sub {
+    my ($c, @args) = @_;
+    ...
+  });
+
+=head2 get_data_template
+
+  my $template = $renderer->get_data_template({
+    template       => 'foo/bar',
+    format         => 'html',
+    handler        => 'epl'
+  });
+
+Return a C<DATA> section template from L</"classes"> for an options hash
+reference with C<template>, C<format>, C<variant> and C<handler> values, or
+C<undef> if no template could be found, usually used by handlers.
+
+=head2 get_helper
+
+  my $helper = $renderer->get_helper('url_for');
+
+Get a helper by full name, generate a helper dynamically for a prefix, or return
+C<undef> if no helper or prefix could be found. Generated helpers return a
+proxy object containing the current controller object and on which nested
+helpers can be called.
+
+=head2 render
+
+  my ($output, $format) = $renderer->render(Mojolicious::Controller->new, {
+    template => 'foo/bar',
+    foo      => 'bar'
+  });
+
+Render output through one of the renderers. See
+L<Mojolicious::Controller/"render"> for a more user-friendly interface.
+
+=head2 template_for
+
+  my $name = $renderer->template_for(Mojolicious::Controller->new);
+
+Return default template name for L<Mojolicious::Controller> object, or C<undef>
+if no name could be generated.
+
+=head2 template_handler
+
+  my $handler = $renderer->template_handler({
+    template => 'foo/bar',
+    format   => 'html'
+  });
+
+Return handler for an options hash reference with C<template>, C<format> and
+C<variant> values, or C<undef> if no handler could be found.
+
+=head2 template_name
+
+  my $template = $renderer->template_name({
+    template => 'foo/bar',
+    format   => 'html',
+    handler  => 'epl'
+  });
+
+Return a template name for an options hash reference with C<template>,
+C<format>, C<variant> and C<handler> values, or C<undef> if no template could be
+found, usually used by handlers.
+
+=head2 template_path
+
+  my $path = $renderer->template_path({
+    template => 'foo/bar',
+    format   => 'html',
+    handler  => 'epl'
+  });
+
+Return the full template path for an options hash reference with C<template>,
+C<format>, C<variant> and C<handler> values, or C<undef> if the file does not
+exist in L</"paths">, usually used by handlers.
+
+=head2 warmup
+
+  $renderer->warmup;
+
+Prepare templates from L</"classes"> for future use.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Routes.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Routes.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Routes.pm	(revision 32205)
@@ -0,0 +1,375 @@
+package Mojolicious::Routes;
+use Mojo::Base 'Mojolicious::Routes::Route';
+
+use List::Util 'first';
+use Mojo::Cache;
+use Mojo::Loader 'load_class';
+use Mojo::Util 'camelize';
+use Mojolicious::Routes::Match;
+use Scalar::Util 'weaken';
+
+has base_classes => sub { [qw(Mojolicious::Controller Mojolicious)] };
+has cache        => sub { Mojo::Cache->new };
+has [qw(conditions shortcuts)] => sub { {} };
+has types      => sub { {num => qr/[0-9]+/} };
+has hidden     => sub { [qw(attr has new tap)] };
+has namespaces => sub { [] };
+
+sub add_condition { $_[0]->conditions->{$_[1]} = $_[2] and return $_[0] }
+sub add_shortcut  { $_[0]->shortcuts->{$_[1]}  = $_[2] and return $_[0] }
+sub add_type      { $_[0]->types->{$_[1]}      = $_[2] and return $_[0] }
+
+sub continue {
+  my ($self, $c) = @_;
+
+  my $match    = $c->match;
+  my $stack    = $match->stack;
+  my $position = $match->position;
+  return _render($c) unless my $field = $stack->[$position];
+
+  # Merge captures into stash
+  my $stash = $c->stash;
+  @{$stash->{'mojo.captures'} //= {}}{keys %$field} = values %$field;
+  @$stash{keys %$field} = values %$field;
+
+  my $continue;
+  my $last = !$stack->[++$position];
+  if (my $cb = $field->{cb}) { $continue = $self->_callback($c, $cb, $last) }
+  else { $continue = $self->_controller($c, $field, $last) }
+  $match->position($position);
+  $self->continue($c) if $last || $continue;
+}
+
+sub dispatch {
+  my ($self, $c) = @_;
+  $self->match($c);
+  @{$c->match->stack} ? $self->continue($c) : return undef;
+  return 1;
+}
+
+sub hide { push @{shift->hidden}, @_ }
+
+sub is_hidden {
+  my ($self, $method) = @_;
+  my $h = $self->{hiding} ||= {map { $_ => 1 } @{$self->hidden}};
+  return !!($h->{$method} || $method =~ /^_/ || $method =~ /^[A-Z_]+$/);
+}
+
+sub lookup { ($_[0]{reverse} //= $_[0]->_index)->{$_[1]} }
+
+sub match {
+  my ($self, $c) = @_;
+
+  # Path (partial path gets priority)
+  my $req  = $c->req;
+  my $path = $c->stash->{path};
+  if (defined $path) { $path = "/$path" if $path !~ m!^/! }
+  else               { $path = $req->url->path->to_route }
+
+  # Method (HEAD will be treated as GET)
+  my $method = uc($req->url->query->clone->param('_method') || $req->method);
+  $method = 'GET' if $method eq 'HEAD';
+
+  # Check cache
+  my $ws = $c->tx->is_websocket ? 1 : 0;
+  my $match = Mojolicious::Routes::Match->new(root => $self);
+  $c->match($match);
+  my $cache = $self->cache;
+  if (my $result = $cache->get("$method:$path:$ws")) {
+    return $match->endpoint($result->{endpoint})->stack($result->{stack});
+  }
+
+  # Check routes
+  $match->find($c => {method => $method, path => $path, websocket => $ws});
+  return unless my $route = $match->endpoint;
+  $cache->set(
+    "$method:$path:$ws" => {endpoint => $route, stack => $match->stack});
+}
+
+sub _action { shift->plugins->emit_chain(around_action => @_) }
+
+sub _callback {
+  my ($self, $c, $cb, $last) = @_;
+  $c->stash->{'mojo.routed'} = 1 if $last;
+  my $app = $c->app;
+  $app->log->debug('Routing to a callback');
+  return _action($app, $c, $cb, $last);
+}
+
+sub _class {
+  my ($self, $c, $field) = @_;
+
+  # Application instance
+  return $field->{app} if ref $field->{app};
+
+  # Application class
+  my @classes;
+  my $class = $field->{controller} ? camelize $field->{controller} : '';
+  if ($field->{app}) { push @classes, $field->{app} }
+
+  # Specific namespace
+  elsif (defined(my $ns = $field->{namespace})) {
+    if ($class) { push @classes, $ns ? "${ns}::$class" : $class }
+    elsif ($ns) { push @classes, $ns }
+  }
+
+  # All namespaces
+  elsif ($class) { push @classes, "${_}::$class" for @{$self->namespaces} }
+
+  # Try to load all classes
+  my $log = $c->app->log;
+  for my $class (@classes) {
+
+    # Failed
+    next unless defined(my $found = $self->_load($class));
+    return !$log->debug(qq{Class "$class" is not a controller}) unless $found;
+
+    # Success
+    my $new = $class->new(%$c);
+    weaken $new->{$_} for qw(app tx);
+    return $new;
+  }
+
+  # Nothing found
+  $log->debug(qq{Controller "$classes[-1]" does not exist}) if @classes;
+  return @classes ? undef : 0;
+}
+
+sub _controller {
+  my ($self, $old, $field, $last) = @_;
+
+  # Load and instantiate controller/application
+  my $new;
+  unless ($new = $self->_class($old, $field)) { return defined $new }
+
+  # Application
+  my $class = ref $new;
+  my $app   = $old->app;
+  my $log   = $app->log;
+  if ($new->isa('Mojolicious')) {
+    $log->debug(qq{Routing to application "$class"});
+
+    # Try to connect routes
+    if (my $sub = $new->can('routes')) {
+      my $r = $new->$sub;
+      weaken $r->parent($old->match->endpoint)->{parent} unless $r->parent;
+    }
+    $new->handler($old);
+    $old->stash->{'mojo.routed'} = 1;
+  }
+
+  # Action
+  elsif (my $method = $field->{action}) {
+    if (!$self->is_hidden($method)) {
+      $log->debug(qq{Routing to controller "$class" and action "$method"});
+
+      if (my $sub = $new->can($method)) {
+        $old->stash->{'mojo.routed'} = 1 if $last;
+        return 1 if _action($app, $new, $sub, $last);
+      }
+
+      else { $log->debug('Action not found in controller') }
+    }
+    else { $log->debug(qq{Action "$method" is not allowed}) }
+  }
+
+  return undef;
+}
+
+sub _load {
+  my ($self, $app) = @_;
+
+  # Load unless already loaded
+  return 1 if $self->{loaded}{$app};
+  if (my $e = load_class $app) { ref $e ? die $e : return undef }
+
+  # Check base classes
+  return 0 unless first { $app->isa($_) } @{$self->base_classes};
+  return $self->{loaded}{$app} = 1;
+}
+
+sub _render {
+  my $c     = shift;
+  my $stash = $c->stash;
+  return if $stash->{'mojo.rendered'};
+  $c->render_maybe or $stash->{'mojo.routed'} or $c->helpers->reply->not_found;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Routes - Always find your destination with routes
+
+=head1 SYNOPSIS
+
+  use Mojolicious::Routes;
+
+  # Simple route
+  my $r = Mojolicious::Routes->new;
+  $r->route('/')->to(controller => 'blog', action => 'welcome');
+
+  # More advanced routes
+  my $blog = $r->under('/blog');
+  $blog->get('/list')->to('blog#list');
+  $blog->get('/:id' => [id => qr/\d+/])->to('blog#show', id => 23);
+  $blog->patch(sub { shift->render(text => 'Go away!', status => 405) });
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Routes> is the core of the L<Mojolicious> web framework.
+
+See L<Mojolicious::Guides::Routing> for more.
+
+=head1 TYPES
+
+These placeholder types are available by default.
+
+=head2 num
+
+  $r->get('/article/<id:num>');
+
+Placeholder value needs to be a non-fractional number, similar to the regular
+expression C<([0-9]+)>.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Routes> inherits all attributes from
+L<Mojolicious::Routes::Route> and implements the following new ones.
+
+=head2 base_classes
+
+  my $classes = $r->base_classes;
+  $r          = $r->base_classes(['MyApp::Controller']);
+
+Base classes used to identify controllers, defaults to
+L<Mojolicious::Controller> and L<Mojolicious>.
+
+=head2 cache
+
+  my $cache = $r->cache;
+  $r        = $r->cache(Mojo::Cache->new);
+
+Routing cache, defaults to a L<Mojo::Cache> object.
+
+=head2 conditions
+
+  my $conditions = $r->conditions;
+  $r             = $r->conditions({foo => sub {...}});
+
+Contains all available conditions.
+
+=head2 hidden
+
+  my $hidden = $r->hidden;
+  $r         = $r->hidden(['attr', 'has', 'new']);
+
+Controller attributes and methods that are hidden from router, defaults to
+C<attr>, C<has>, C<new> and C<tap>.
+
+=head2 namespaces
+
+  my $namespaces = $r->namespaces;
+  $r             = $r->namespaces(['MyApp::Controller', 'MyApp']);
+
+Namespaces to load controllers from.
+
+  # Add another namespace to load controllers from
+  push @{$r->namespaces}, 'MyApp::MyController';
+
+=head2 shortcuts
+
+  my $shortcuts = $r->shortcuts;
+  $r            = $r->shortcuts({foo => sub {...}});
+
+Contains all available shortcuts.
+
+=head2 types
+
+  my $types = $r->types;
+  $r        = $r->types({lower => qr/[a-z]+/});
+
+Registered placeholder types, by default only L</"num"> is already defined.
+
+=head1 METHODS
+
+L<Mojolicious::Routes> inherits all methods from L<Mojolicious::Routes::Route>
+and implements the following new ones.
+
+=head2 add_condition
+
+  $r = $r->add_condition(foo => sub {...});
+
+Register a condition.
+
+  $r->add_condition(foo => sub {
+    my ($route, $c, $captures, $arg) = @_;
+    ...
+    return 1;
+  });
+
+=head2 add_shortcut
+
+  $r = $r->add_shortcut(foo => sub {...});
+
+Register a shortcut.
+
+  $r->add_shortcut(foo => sub {
+    my ($route, @args) = @_;
+    ...
+  });
+
+=head2 add_type
+
+  $r = $r->add_type(foo => qr/\w+/);
+  $r = $r->add_type(foo => ['bar', 'baz']);
+
+Register a placeholder type.
+
+  $r->add_type(lower => qr/[a-z]+/);
+
+=head2 continue
+
+  $r->continue(Mojolicious::Controller->new);
+
+Continue dispatch chain and emit the hook L<Mojolicious/"around_action"> for
+every action.
+
+=head2 dispatch
+
+  my $bool = $r->dispatch(Mojolicious::Controller->new);
+
+Match routes with L</"match"> and dispatch with L</"continue">.
+
+=head2 hide
+
+  $r = $r->hide('foo', 'bar');
+
+Hide controller attributes and methods from router.
+
+=head2 is_hidden
+
+  my $bool = $r->is_hidden('foo');
+
+Check if controller attribute or method is hidden from router.
+
+=head2 lookup
+
+  my $route = $r->lookup('foo');
+
+Find route by name with L<Mojolicious::Routes::Route/"find"> and cache all
+results for future lookups.
+
+=head2 match
+
+  $r->match(Mojolicious::Controller->new);
+
+Match routes with L<Mojolicious::Routes::Match>.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Routes/Match.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Routes/Match.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Routes/Match.pm	(revision 32205)
@@ -0,0 +1,189 @@
+package Mojolicious::Routes::Match;
+use Mojo::Base -base;
+
+use Mojo::Util;
+
+has [qw(endpoint root)];
+has position => 0;
+has stack => sub { [] };
+
+sub find { $_[0]->_match($_[0]->root, $_[1], $_[2]) }
+
+sub path_for {
+  my ($self, $name, %values) = (shift, Mojo::Util::_options(@_));
+
+  # Current route
+  my $route;
+  if (!$name || $name eq 'current') {
+    return {} unless $route = $self->endpoint;
+  }
+
+  # Find endpoint
+  else { return {path => $name} unless $route = $self->root->lookup($name) }
+
+  # Merge values (clear format)
+  my $captures = $self->stack->[-1] || {};
+  %values = (%$captures, format => undef, %values);
+  my $pattern = $route->pattern;
+  $values{format}
+    //= defined $captures->{format}
+    ? $captures->{format}
+    : $pattern->defaults->{format}
+    if $pattern->constraints->{format};
+
+  my $path = $route->render(\%values);
+  return {path => $path, websocket => $route->has_websocket};
+}
+
+sub _match {
+  my ($self, $r, $c, $options) = @_;
+
+  # Pattern
+  my $path    = $options->{path};
+  my $partial = $r->partial;
+  my $detect  = (my $endpoint = $r->is_endpoint) && !$partial;
+  return undef
+    unless my $captures = $r->pattern->match_partial(\$path, $detect);
+  local $options->{path} = $path;
+  local @{$self->{captures} ||= {}}{keys %$captures} = values %$captures;
+  $captures = $self->{captures};
+
+  # Method
+  my $methods = $r->via;
+  return undef if $methods && !grep { $_ eq $options->{method} } @$methods;
+
+  # Conditions
+  if (my $over = $r->over) {
+    my $conditions = $self->{conditions} ||= $self->root->conditions;
+    for (my $i = 0; $i < @$over; $i += 2) {
+      return undef unless my $condition = $conditions->{$over->[$i]};
+      return undef if !$condition->($r, $c, $captures, $over->[$i + 1]);
+    }
+  }
+
+  # WebSocket
+  return undef if $r->is_websocket && !$options->{websocket};
+
+  # Partial
+  my $empty = !length $path || $path eq '/';
+  if ($partial) {
+    $captures->{path} = $path;
+    $self->endpoint($r);
+    $empty = 1;
+  }
+
+  # Endpoint (or intermediate destination)
+  if (($endpoint && $empty) || $r->inline) {
+    push @{$self->stack}, {%$captures};
+    if ($endpoint && $empty) {
+      my $format = $captures->{format};
+      if ($format) { $_->{format} = $format for @{$self->stack} }
+      return !!$self->endpoint($r);
+    }
+    delete @$captures{qw(app cb)};
+  }
+
+  # Match children
+  my $snapshot = $r->parent ? [@{$self->stack}] : [];
+  for my $child (@{$r->children}) {
+    return 1 if $self->_match($child, $c, $options);
+    $self->stack([@$snapshot]);
+  }
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Routes::Match - Find routes
+
+=head1 SYNOPSIS
+
+  use Mojolicious::Controller;
+  use Mojolicious::Routes;
+  use Mojolicious::Routes::Match;
+
+  # Routes
+  my $r = Mojolicious::Routes->new;
+  $r->get('/:controller/:action');
+  $r->put('/:controller/:action');
+
+  # Match
+  my $c = Mojolicious::Controller->new;
+  my $match = Mojolicious::Routes::Match->new(root => $r);
+  $match->find($c => {method => 'PUT', path => '/foo/bar'});
+  say $match->stack->[0]{controller};
+  say $match->stack->[0]{action};
+
+  # Render
+  say $match->path_for->{path};
+  say $match->path_for(action => 'baz')->{path};
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Routes::Match> finds routes in L<Mojolicious::Routes>
+structures.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Routes::Match> implements the following attributes.
+
+=head2 endpoint
+
+  my $route = $match->endpoint;
+  $match    = $match->endpoint(Mojolicious::Routes::Route->new);
+
+The route endpoint that matched, usually a L<Mojolicious::Routes::Route>
+object.
+
+=head2 position
+
+  my $position = $match->position;
+  $match       = $match->position(2);
+
+Current position on the L</"stack">, defaults to C<0>.
+
+=head2 root
+
+  my $root = $match->root;
+  $match   = $match->root(Mojolicious::Routes->new);
+
+The root of the route structure, usually a L<Mojolicious::Routes> object.
+
+=head2 stack
+
+  my $stack = $match->stack;
+  $match    = $match->stack([{action => 'foo'}, {action => 'bar'}]);
+
+Captured parameters with nesting history.
+
+=head1 METHODS
+
+L<Mojolicious::Routes::Match> inherits all methods from L<Mojo::Base> and
+implements the following new ones.
+
+=head2 find
+
+  $match->find(Mojolicious::Controller->new, {method => 'GET', path => '/'});
+
+Match controller and options against L</"root"> to find an appropriate
+L</"endpoint">.
+
+=head2 path_for
+
+  my $info = $match->path_for;
+  my $info = $match->path_for(foo => 'bar');
+  my $info = $match->path_for({foo => 'bar'});
+  my $info = $match->path_for('named');
+  my $info = $match->path_for('named', foo => 'bar');
+  my $info = $match->path_for('named', {foo => 'bar'});
+
+Render matching route with parameters into path.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Routes/Pattern.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Routes/Pattern.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Routes/Pattern.pm	(revision 32205)
@@ -0,0 +1,389 @@
+package Mojolicious::Routes::Pattern;
+use Mojo::Base -base;
+
+use Carp 'croak';
+use Mojo::Util 'deprecated';
+
+has [qw(constraints defaults types)] => sub { {} };
+has [qw(placeholder_start type_start)] => ':';
+has [qw(placeholders tree)] => sub { [] };
+has quote_end   => '>';
+has quote_start => '<';
+has [qw(regex unparsed)];
+has relaxed_start  => '#';
+has wildcard_start => '*';
+
+sub match {
+  my ($self, $path, $detect) = @_;
+  my $captures = $self->match_partial(\$path, $detect);
+  return !$path || $path eq '/' ? $captures : undef;
+}
+
+sub match_partial {
+  my ($self, $pathref, $detect) = @_;
+
+  # Compile on demand
+  $self->_compile($detect) unless $self->{regex};
+
+  return undef unless my @captures = $$pathref =~ $self->regex;
+  $$pathref = ${^POSTMATCH};
+  @captures = () if $#+ == 0;
+  my $captures = {%{$self->defaults}};
+  for my $placeholder (@{$self->placeholders}, 'format') {
+    last unless @captures;
+    my $capture = shift @captures;
+    $captures->{$placeholder} = $capture if defined $capture;
+  }
+
+  return $captures;
+}
+
+sub new { @_ > 1 ? shift->SUPER::new->parse(@_) : shift->SUPER::new }
+
+sub parse {
+  my $self = shift;
+
+  my $pattern = @_ % 2 ? (shift // '/') : '/';
+  $pattern =~ s!^/*|/+!/!g;
+  return $self->constraints({@_}) if $pattern eq '/';
+
+  $pattern =~ s!/$!!;
+  return $self->constraints({@_})->_tokenize($pattern);
+}
+
+sub render {
+  my ($self, $values, $endpoint) = @_;
+
+  my $start = $self->type_start;
+
+  # Placeholders can only be optional without a format
+  my $optional = !(my $format = $values->{format});
+
+  my $str = '';
+  for my $token (reverse @{$self->tree}) {
+    my ($op, $value) = @$token;
+    my $part = '';
+
+    # Text
+    if ($op eq 'text') { ($part, $optional) = ($value, 0) }
+
+    # Slash
+    elsif ($op eq 'slash') { $part = '/' unless $optional }
+
+    # Placeholder
+    else {
+      my $name = (split $start, $value)[0] // '';
+      my $default = $self->defaults->{$name};
+      $part = $values->{$name} // $default // '';
+      if (!defined $default || ($default ne $part)) { $optional = 0 }
+      elsif ($optional) { $part = '' }
+    }
+
+    $str = $part . $str;
+  }
+
+  # Format can be optional
+  return $endpoint && $format ? "$str.$format" : $str;
+}
+
+sub _compile {
+  my ($self, $detect) = @_;
+
+  my $placeholders = $self->placeholders;
+  my $constraints  = $self->constraints;
+  my $defaults     = $self->defaults;
+  my $start        = $self->type_start;
+  my $types        = $self->types;
+
+  my $block = my $regex = '';
+  my $optional = 1;
+  for my $token (reverse @{$self->tree}) {
+    my ($op, $value, $type) = @$token;
+    my $part = '';
+
+    # Text
+    if ($op eq 'text') { ($part, $optional) = (quotemeta $value, 0) }
+
+    # Slash
+    elsif ($op eq 'slash') {
+      $regex = ($optional ? "(?:/$block)?" : "/$block") . $regex;
+      ($block, $optional) = ('', 1);
+      next;
+    }
+
+    # Placeholder
+    else {
+      if ($value =~ /^(.+)\Q$start\E(.+)$/) {
+        ($value, $part) = ($1, _compile_req($types->{$2} // '?!'));
+      }
+      else {
+        $part = $type ? $type eq 'relaxed' ? '([^/]+)' : '(.+)' : '([^/.]+)';
+      }
+      unshift @$placeholders, $value;
+
+      # Custom regex
+      if (my $c = $constraints->{$value}) { $part = _compile_req($c) }
+
+      # Optional placeholder
+      exists $defaults->{$value} ? ($part .= '?') : ($optional = 0);
+    }
+
+    $block = $part . $block;
+  }
+
+  # Not rooted with a slash
+  $regex = $block . $regex if $block;
+
+  # Format
+  $regex .= _compile_format($constraints->{format}, $defaults->{format})
+    if $detect;
+
+  $self->regex(qr/^$regex/ps);
+}
+
+sub _compile_format {
+  my ($format, $default) = @_;
+
+  # Default regex
+  return '/?(?:\.([^/]+))?$' unless defined $format;
+
+  # No regex
+  return '' unless $format;
+
+  # Compile custom regex
+  my $regex = '\.' . _compile_req($format);
+  return $default ? "/?(?:$regex)?\$" : "/?$regex\$";
+}
+
+sub _compile_req {
+  my $req = shift;
+  return "($req)" if ref $req ne 'ARRAY';
+  return '(' . join('|', map {quotemeta} reverse sort @$req) . ')';
+}
+
+sub _tokenize {
+  my ($self, $pattern) = @_;
+
+  # DEPRECATED!
+  deprecated 'Placeholder quoting with "(placeholder)" is DEPRECATED'
+    . ' in favor of "<placeholder>"'
+    if $pattern =~ tr/()/<>/;
+
+  my $quote_end   = $self->quote_end;
+  my $quote_start = $self->quote_start;
+  my $start       = $self->placeholder_start;
+  my $relaxed     = $self->relaxed_start;
+  my $wildcard    = $self->wildcard_start;
+
+  my (@tree, $spec, $more);
+  for my $char (split '', $pattern) {
+
+    # Quoted
+    if ($char eq $quote_start) { push @tree, ['placeholder', ''] if ++$spec }
+    elsif ($char eq $quote_end) { $spec = $more = 0 }
+
+    # Placeholder
+    elsif (!$more && $char eq $start) {
+      push @tree, ['placeholder', ''] unless $spec++;
+    }
+
+    # Relaxed or wildcard (upgrade when quoted)
+    elsif (!$more && ($char eq $relaxed || $char eq $wildcard)) {
+      push @tree, ['placeholder', ''] unless $spec++;
+      $tree[-1][2] = $char eq $relaxed ? 'relaxed' : 'wildcard';
+    }
+
+    # Slash
+    elsif ($char eq '/') {
+      push @tree, ['slash'];
+      $spec = $more = 0;
+    }
+
+    # Placeholder
+    elsif ($spec && ++$more) { $tree[-1][1] .= $char }
+
+    # Text (optimize slash+text and *+text+slash+text)
+    elsif ($tree[-1][0] eq 'text') { $tree[-1][-1] .= $char }
+    elsif (!$tree[-2] && $tree[-1][0] eq 'slash') {
+      @tree = (['text', "/$char"]);
+    }
+    elsif ($tree[-2] && $tree[-2][0] eq 'text' && $tree[-1][0] eq 'slash') {
+      pop @tree && ($tree[-1][-1] .= "/$char");
+    }
+    else { push @tree, ['text', $char] }
+  }
+
+  return $self->unparsed($pattern)->tree(\@tree);
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Routes::Pattern - Route pattern
+
+=head1 SYNOPSIS
+
+  use Mojolicious::Routes::Pattern;
+
+  # Create pattern
+  my $pattern = Mojolicious::Routes::Pattern->new('/test/:name');
+
+  # Match routes
+  my $captures = $pattern->match('/test/sebastian');
+  say $captures->{name};
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Routes::Pattern> is the core of L<Mojolicious::Routes>.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Routes::Pattern> implements the following attributes.
+
+=head2 constraints
+
+  my $constraints = $pattern->constraints;
+  $pattern        = $pattern->constraints({foo => qr/\w+/});
+
+Regular expression constraints.
+
+=head2 defaults
+
+  my $defaults = $pattern->defaults;
+  $pattern     = $pattern->defaults({foo => 'bar'});
+
+Default parameters.
+
+=head2 placeholder_start
+
+  my $start = $pattern->placeholder_start;
+  $pattern  = $pattern->placeholder_start(':');
+
+Character indicating a placeholder, defaults to C<:>.
+
+=head2 placeholders
+
+  my $placeholders = $pattern->placeholders;
+  $pattern         = $pattern->placeholders(['foo', 'bar']);
+
+Placeholder names.
+
+=head2 quote_end
+
+  my $end  = $pattern->quote_end;
+  $pattern = $pattern->quote_end('}');
+
+Character indicating the end of a quoted placeholder, defaults to C<E<gt>>.
+
+=head2 quote_start
+
+  my $start = $pattern->quote_start;
+  $pattern  = $pattern->quote_start('{');
+
+Character indicating the start of a quoted placeholder, defaults to C<E<lt>>.
+
+=head2 regex
+
+  my $regex = $pattern->regex;
+  $pattern  = $pattern->regex($regex);
+
+Pattern in compiled regular expression form.
+
+=head2 relaxed_start
+
+  my $start = $pattern->relaxed_start;
+  $pattern  = $pattern->relaxed_start('*');
+
+Character indicating a relaxed placeholder, defaults to C<#>.
+
+=head2 tree
+
+  my $tree = $pattern->tree;
+  $pattern = $pattern->tree([['text', '/foo']]);
+
+Pattern in parsed form. Note that this structure should only be used very
+carefully since it is very dynamic.
+
+=head2 type_start
+
+  my $start = $pattern->type_start;
+  $pattern  = $pattern->type_start('|');
+
+Character indicating the start of a placeholder type, defaults to C<:>.
+
+=head2 types
+
+  my $types = $pattern->types;
+  $pattern  = $pattern->types({int => qr/[0-9]+/});
+
+Placeholder types.
+
+=head2 unparsed
+
+  my $unparsed = $pattern->unparsed;
+  $pattern     = $pattern->unparsed('/:foo/:bar');
+
+Raw unparsed pattern.
+
+=head2 wildcard_start
+
+  my $start = $pattern->wildcard_start;
+  $pattern  = $pattern->wildcard_start('*');
+
+Character indicating the start of a wildcard placeholder, defaults to C<*>.
+
+=head1 METHODS
+
+L<Mojolicious::Routes::Pattern> inherits all methods from L<Mojo::Base> and
+implements the following new ones.
+
+=head2 match
+
+  my $captures = $pattern->match('/foo/bar');
+  my $captures = $pattern->match('/foo/bar', 1);
+
+Match pattern against entire path, format detection is disabled by default.
+
+=head2 match_partial
+
+  my $captures = $pattern->match_partial(\$path);
+  my $captures = $pattern->match_partial(\$path, 1);
+
+Match pattern against path and remove matching parts, format detection is
+disabled by default.
+
+=head2 new
+
+  my $pattern = Mojolicious::Routes::Pattern->new;
+  my $pattern = Mojolicious::Routes::Pattern->new('/:action');
+  my $pattern
+    = Mojolicious::Routes::Pattern->new('/:action', action => qr/\w+/);
+  my $pattern = Mojolicious::Routes::Pattern->new(format => 0);
+
+Construct a new L<Mojolicious::Routes::Pattern> object and L</"parse"> pattern
+if necessary.
+
+=head2 parse
+
+  $pattern = $pattern->parse('/:action');
+  $pattern = $pattern->parse('/:action', action => qr/\w+/);
+  $pattern = $pattern->parse(format => 0);
+
+Parse pattern.
+
+=head2 render
+
+  my $path = $pattern->render({action => 'foo'});
+  my $path = $pattern->render({action => 'foo'}, 1);
+
+Render pattern into a path with parameters, format rendering is disabled by
+default.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Routes/Route.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Routes/Route.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Routes/Route.pm	(revision 32205)
@@ -0,0 +1,673 @@
+package Mojolicious::Routes::Route;
+use Mojo::Base -base;
+
+use Carp ();
+use Mojo::Util;
+use Mojolicious::Routes::Pattern;
+use Scalar::Util ();
+
+has [qw(inline parent partial)];
+has 'children' => sub { [] };
+has pattern    => sub { Mojolicious::Routes::Pattern->new };
+
+sub AUTOLOAD {
+  my $self = shift;
+
+  my ($package, $method) = our $AUTOLOAD =~ /^(.+)::(.+)$/;
+  Carp::croak "Undefined subroutine &${package}::$method called"
+    unless Scalar::Util::blessed $self && $self->isa(__PACKAGE__);
+
+  # Call shortcut with current route
+  Carp::croak qq{Can't locate object method "$method" via package "$package"}
+    unless my $shortcut = $self->root->shortcuts->{$method};
+  return $self->$shortcut(@_);
+}
+
+sub add_child {
+  my ($self, $route) = @_;
+  Scalar::Util::weaken $route->remove->parent($self)->{parent};
+  push @{$self->children}, $route;
+  $route->pattern->types($self->root->types);
+  return $self;
+}
+
+sub any { shift->_generate_route(ref $_[0] eq 'ARRAY' ? shift : [], @_) }
+
+sub delete { shift->_generate_route(DELETE => @_) }
+
+sub detour { shift->partial(1)->to(@_) }
+
+sub find { shift->_index->{shift()} }
+
+sub get { shift->_generate_route(GET => @_) }
+
+sub has_custom_name { !!shift->{custom} }
+
+sub has_websocket {
+  my $self = shift;
+  return $self->{has_websocket} if exists $self->{has_websocket};
+  return $self->{has_websocket} = grep { $_->is_websocket } @{$self->_chain};
+}
+
+sub is_endpoint { $_[0]->inline ? undef : !@{$_[0]->children} }
+
+sub is_websocket { !!shift->{websocket} }
+
+sub name {
+  my $self = shift;
+  return $self->{name} unless @_;
+  @$self{qw(name custom)} = (shift, 1);
+  return $self;
+}
+
+sub options { shift->_generate_route(OPTIONS => @_) }
+
+sub over {
+  my $self = shift;
+
+  # Routes with conditions can't be cached
+  return $self->{over} unless @_;
+  my $conditions = ref $_[0] eq 'ARRAY' ? $_[0] : [@_];
+  return $self unless @$conditions;
+  $self->{over} = $conditions;
+  $self->root->cache->max_keys(0);
+
+  return $self;
+}
+
+sub parse {
+  my $self = shift;
+  $self->{name} = $self->pattern->parse(@_)->unparsed // '';
+  $self->{name} =~ s/\W+//g;
+  return $self;
+}
+
+sub patch { shift->_generate_route(PATCH => @_) }
+sub post  { shift->_generate_route(POST  => @_) }
+sub put   { shift->_generate_route(PUT   => @_) }
+
+sub remove {
+  my $self = shift;
+  return $self unless my $parent = $self->parent;
+  @{$parent->children} = grep { $_ ne $self } @{$parent->children};
+  return $self->parent(undef);
+}
+
+sub render {
+  my ($self, $values) = @_;
+  my $path = join '',
+    map { $_->pattern->render($values, !@{$_->children} && !$_->partial) }
+    @{$self->_chain};
+  return $path || '/';
+}
+
+sub root { shift->_chain->[0] }
+
+sub route {
+  my $self   = shift;
+  my $route  = $self->add_child(__PACKAGE__->new->parse(@_))->children->[-1];
+  my $format = $self->pattern->constraints->{format};
+  $route->pattern->constraints->{format} //= 0 if defined $format && !$format;
+  return $route;
+}
+
+sub suggested_method {
+  my $self = shift;
+
+  my %via;
+  for my $route (@{$self->_chain}) {
+    next unless my @via = @{$route->via || []};
+    %via = map { $_ => 1 } keys %via ? grep { $via{$_} } @via : @via;
+  }
+
+  return 'POST' if $via{POST} && !$via{GET};
+  return $via{GET} ? 'GET' : (sort keys %via)[0] || 'GET';
+}
+
+sub to {
+  my $self = shift;
+
+  my $pattern = $self->pattern;
+  return $pattern->defaults unless @_;
+  my ($shortcut, %defaults) = Mojo::Util::_options(@_);
+
+  if ($shortcut) {
+
+    # Application
+    if (ref $shortcut || $shortcut =~ /^[\w:]+$/) {
+      $defaults{app} = $shortcut;
+    }
+
+    # Controller and action
+    elsif ($shortcut =~ /^([\w\-:]+)?\#(\w+)?$/) {
+      $defaults{controller} = $1 if defined $1;
+      $defaults{action}     = $2 if defined $2;
+    }
+  }
+
+  @{$pattern->defaults}{keys %defaults} = values %defaults;
+
+  return $self;
+}
+
+sub to_string {
+  join '', map { $_->pattern->unparsed // '' } @{shift->_chain};
+}
+
+sub under { shift->_generate_route(under => @_) }
+
+sub via {
+  my $self = shift;
+  return $self->{via} unless @_;
+  my $methods = [map uc($_), @{ref $_[0] ? $_[0] : [@_]}];
+  $self->{via} = $methods if @$methods;
+  return $self;
+}
+
+sub websocket {
+  my $route = shift->get(@_);
+  $route->{websocket} = 1;
+  return $route;
+}
+
+sub _chain {
+  my @chain = (my $parent = shift);
+  unshift @chain, $parent while $parent = $parent->parent;
+  return \@chain;
+}
+
+sub _generate_route {
+  my ($self, $methods, @args) = @_;
+
+  my (@conditions, @constraints, %defaults, $name, $pattern);
+  while (defined(my $arg = shift @args)) {
+
+    # First scalar is the pattern
+    if (!ref $arg && !$pattern) { $pattern = $arg }
+
+    # Scalar
+    elsif (!ref $arg && @args) { push @conditions, $arg, shift @args }
+
+    # Last scalar is the route name
+    elsif (!ref $arg) { $name = $arg }
+
+    # Callback
+    elsif (ref $arg eq 'CODE') { $defaults{cb} = $arg }
+
+    # Constraints
+    elsif (ref $arg eq 'ARRAY') { push @constraints, @$arg }
+
+    # Defaults
+    elsif (ref $arg eq 'HASH') { %defaults = (%defaults, %$arg) }
+  }
+
+  my $route
+    = $self->route($pattern, @constraints)->over(\@conditions)->to(\%defaults);
+  $methods eq 'under' ? $route->inline(1) : $route->via($methods);
+
+  return defined $name ? $route->name($name) : $route;
+}
+
+sub _index {
+  my $self = shift;
+
+  my (%auto, %custom);
+  my @children = (@{$self->children});
+  while (my $child = shift @children) {
+    if   ($child->has_custom_name) { $custom{$child->name} ||= $child }
+    else                           { $auto{$child->name}   ||= $child }
+    push @children, @{$child->children};
+  }
+
+  return {%auto, %custom};
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Routes::Route - Route
+
+=head1 SYNOPSIS
+
+  use Mojolicious::Routes::Route;
+
+  my $r = Mojolicious::Routes::Route->new;
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Routes::Route> is the route container used by
+L<Mojolicious::Routes>.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Routes::Route> implements the following attributes.
+
+=head2 children
+
+  my $children = $r->children;
+  $r           = $r->children([Mojolicious::Routes::Route->new]);
+
+The children of this route, used for nesting routes.
+
+=head2 inline
+
+  my $bool = $r->inline;
+  $r       = $r->inline($bool);
+
+Allow L</"under"> semantics for this route.
+
+=head2 parent
+
+  my $parent = $r->parent;
+  $r         = $r->parent(Mojolicious::Routes::Route->new);
+
+The parent of this route, usually a L<Mojolicious::Routes::Route> object.
+
+=head2 partial
+
+  my $bool = $r->partial;
+  $r       = $r->partial($bool);
+
+Route has no specific end, remaining characters will be captured in C<path>.
+
+=head2 pattern
+
+  my $pattern = $r->pattern;
+  $r          = $r->pattern(Mojolicious::Routes::Pattern->new);
+
+Pattern for this route, defaults to a L<Mojolicious::Routes::Pattern> object.
+
+=head1 METHODS
+
+L<Mojolicious::Routes::Route> inherits all methods from L<Mojo::Base> and
+implements the following new ones.
+
+=head2 add_child
+
+  $r = $r->add_child(Mojolicious::Routes::Route->new);
+
+Add a child to this route, it will be automatically removed from its current
+parent if necessary.
+
+  # Reattach route
+  $r->add_child($r->find('foo'));
+
+=head2 any
+
+  my $route = $r->any;
+  my $route = $r->any('/:foo');
+  my $route = $r->any('/:foo' => sub {...});
+  my $route = $r->any('/:foo' => sub {...} => 'name');
+  my $route = $r->any('/:foo' => {foo => 'bar'} => sub {...});
+  my $route = $r->any('/:foo' => [foo => qr/\w+/] => sub {...});
+  my $route = $r->any('/:foo' => (agent => qr/Firefox/) => sub {...});
+  my $route = $r->any(['GET', 'POST'] => '/:foo' => sub {...});
+  my $route = $r->any(['GET', 'POST'] => '/:foo' => [foo => qr/\w+/]);
+
+Generate L<Mojolicious::Routes::Route> object matching any of the listed HTTP
+request methods or all.
+
+  # Route with pattern and destination
+  $r->any('/user')->to('user#whatever');
+
+All arguments are optional, but some have to appear in a certain order, like the
+two supported array reference values, which contain the HTTP methods to match
+and restrictive placeholders.
+
+  # Route with HTTP methods, pattern, restrictive placeholders and destination
+  $r->any(['DELETE', 'PUT'] => '/:foo' => [foo => qr/\w+/])->to('foo#bar');
+
+There are also two supported string values, containing the route pattern and the
+route name, defaulting to the pattern C</> and a name based on the pattern.
+
+  # Route with pattern, name and destination
+  $r->any('/:foo' => 'foo_route')->to('foo#bar');
+
+An arbitrary number of key/value pairs in between the route pattern and name can
+be used to specify route conditions.
+
+  # Route with pattern, condition and destination
+  $r->any('/' => (agent => qr/Firefox/))->to('foo#bar');
+
+A hash reference is used to specify optional placeholders and default values for
+the stash.
+
+  # Route with pattern, optional placeholder and destination
+  $r->any('/:foo' => {foo => 'bar'})->to('foo#bar');
+
+And a code reference can be used to specify a C<cb> value to be merged into the
+default values for the stash.
+
+  # Route with pattern and a closure as destination
+  $r->any('/:foo' => sub {
+    my $c = shift;
+    $c->render(text => 'Hello World!');
+  });
+
+See L<Mojolicious::Guides::Tutorial> and L<Mojolicious::Guides::Routing> for
+more information.
+
+=head2 delete
+
+  my $route = $r->delete;
+  my $route = $r->delete('/:foo');
+  my $route = $r->delete('/:foo' => sub {...});
+  my $route = $r->delete('/:foo' => sub {...} => 'name');
+  my $route = $r->delete('/:foo' => {foo => 'bar'} => sub {...});
+  my $route = $r->delete('/:foo' => [foo => qr/\w+/] => sub {...});
+  my $route = $r->delete('/:foo' => (agent => qr/Firefox/) => sub {...});
+
+Generate L<Mojolicious::Routes::Route> object matching only C<DELETE> requests,
+takes the same arguments as L</"any"> (except for the HTTP methods to match,
+which are implied). See L<Mojolicious::Guides::Tutorial> and
+L<Mojolicious::Guides::Routing> for more information.
+
+  # Route with destination
+  $r->delete('/user')->to('user#remove');
+
+=head2 detour
+
+  $r = $r->detour(action => 'foo');
+  $r = $r->detour('controller#action');
+  $r = $r->detour(Mojolicious->new, foo => 'bar');
+  $r = $r->detour('MyApp', {foo => 'bar'});
+
+Set default parameters for this route and allow partial matching to simplify
+application embedding, takes the same arguments as L</"to">.
+
+=head2 find
+
+  my $route = $r->find('foo');
+
+Find child route by name, custom names have precedence over automatically
+generated ones.
+
+  # Change default parameters of a named route
+  $r->find('show_user')->to(foo => 'bar');
+
+=head2 get
+
+  my $route = $r->get;
+  my $route = $r->get('/:foo');
+  my $route = $r->get('/:foo' => sub {...});
+  my $route = $r->get('/:foo' => sub {...} => 'name');
+  my $route = $r->get('/:foo' => {foo => 'bar'} => sub {...});
+  my $route = $r->get('/:foo' => [foo => qr/\w+/] => sub {...});
+  my $route = $r->get('/:foo' => (agent => qr/Firefox/) => sub {...});
+
+Generate L<Mojolicious::Routes::Route> object matching only C<GET> requests,
+takes the same arguments as L</"any"> (except for the HTTP methods to match,
+which are implied). See L<Mojolicious::Guides::Tutorial> and
+L<Mojolicious::Guides::Routing> for more information.
+
+  # Route with destination
+  $r->get('/user')->to('user#show');
+
+=head2 has_custom_name
+
+  my $bool = $r->has_custom_name;
+
+Check if this route has a custom name.
+
+=head2 has_websocket
+
+  my $bool = $r->has_websocket;
+
+Check if this route has a WebSocket ancestor and cache the result for future
+checks.
+
+=head2 is_endpoint
+
+  my $bool = $r->is_endpoint;
+
+Check if this route qualifies as an endpoint.
+
+=head2 is_websocket
+
+  my $bool = $r->is_websocket;
+
+Check if this route is a WebSocket.
+
+=head2 name
+
+  my $name = $r->name;
+  $r       = $r->name('foo');
+
+The name of this route, defaults to an automatically generated name based on
+the route pattern. Note that the name C<current> is reserved for referring to
+the current route.
+
+  # Route with destination and custom name
+  $r->get('/user')->to('user#show')->name('show_user');
+
+=head2 options
+
+  my $route = $r->options;
+  my $route = $r->options('/:foo');
+  my $route = $r->options('/:foo' => sub {...});
+  my $route = $r->options('/:foo' => sub {...} => 'name');
+  my $route = $r->options('/:foo' => {foo => 'bar'} => sub {...});
+  my $route = $r->options('/:foo' => [foo => qr/\w+/] => sub {...});
+  my $route = $r->options('/:foo' => (agent => qr/Firefox/) => sub {...});
+
+Generate L<Mojolicious::Routes::Route> object matching only C<OPTIONS>
+requests, takes the same arguments as L</"any"> (except for the HTTP methods to
+match, which are implied). See L<Mojolicious::Guides::Tutorial> and
+L<Mojolicious::Guides::Routing> for more information.
+
+  # Route with destination
+  $r->options('/user')->to('user#overview');
+
+=head2 over
+
+  my $over = $r->over;
+  $r       = $r->over(foo => 1);
+  $r       = $r->over(foo => 1, bar => {baz => 'yada'});
+  $r       = $r->over([foo => 1, bar => {baz => 'yada'}]);
+
+Activate conditions for this route. Note that this automatically disables the
+routing cache, since conditions are too complex for caching.
+
+  # Route with condition and destination
+  $r->get('/foo')->over(host => qr/mojolicious\.org/)->to('foo#bar');
+
+=head2 parse
+
+  $r = $r->parse('/:action');
+  $r = $r->parse('/:action', action => qr/\w+/);
+  $r = $r->parse(format => 0);
+
+Parse pattern.
+
+=head2 patch
+
+  my $route = $r->patch;
+  my $route = $r->patch('/:foo');
+  my $route = $r->patch('/:foo' => sub {...});
+  my $route = $r->patch('/:foo' => sub {...} => 'name');
+  my $route = $r->patch('/:foo' => {foo => 'bar'} => sub {...});
+  my $route = $r->patch('/:foo' => [foo => qr/\w+/] => sub {...});
+  my $route = $r->patch('/:foo' => (agent => qr/Firefox/) => sub {...});
+
+Generate L<Mojolicious::Routes::Route> object matching only C<PATCH> requests,
+takes the same arguments as L</"any"> (except for the HTTP methods to match,
+which are implied). See L<Mojolicious::Guides::Tutorial> and
+L<Mojolicious::Guides::Routing> for more information.
+
+  # Route with destination
+  $r->patch('/user')->to('user#update');
+
+=head2 post
+
+  my $route = $r->post;
+  my $route = $r->post('/:foo');
+  my $route = $r->post('/:foo' => sub {...});
+  my $route = $r->post('/:foo' => sub {...} => 'name');
+  my $route = $r->post('/:foo' => {foo => 'bar'} => sub {...});
+  my $route = $r->post('/:foo' => [foo => qr/\w+/] => sub {...});
+  my $route = $r->post('/:foo' => (agent => qr/Firefox/) => sub {...});
+
+Generate L<Mojolicious::Routes::Route> object matching only C<POST> requests,
+takes the same arguments as L</"any"> (except for the HTTP methods to match,
+which are implied). See L<Mojolicious::Guides::Tutorial> and
+L<Mojolicious::Guides::Routing> for more information.
+
+  # Route with destination
+  $r->post('/user')->to('user#create');
+
+=head2 put
+
+  my $route = $r->put;
+  my $route = $r->put('/:foo');
+  my $route = $r->put('/:foo' => sub {...});
+  my $route = $r->put('/:foo' => sub {...} => 'name');
+  my $route = $r->put('/:foo' => {foo => 'bar'} => sub {...});
+  my $route = $r->put('/:foo' => [foo => qr/\w+/] => sub {...});
+  my $route = $r->put('/:foo' => (agent => qr/Firefox/) => sub {...});
+
+Generate L<Mojolicious::Routes::Route> object matching only C<PUT> requests,
+takes the same arguments as L</"any"> (except for the HTTP methods to match,
+which are implied). See L<Mojolicious::Guides::Tutorial> and
+L<Mojolicious::Guides::Routing> for more information.
+
+  # Route with destination
+  $r->put('/user')->to('user#replace');
+
+=head2 remove
+
+  $r = $r->remove;
+
+Remove route from parent.
+
+  # Remove route completely
+  $r->find('foo')->remove;
+
+  # Reattach route to new parent
+  $r->route('/foo')->add_child($r->find('bar')->remove);
+
+=head2 render
+
+  my $path = $r->render({foo => 'bar'});
+
+Render route with parameters into a path.
+
+=head2 root
+
+  my $root = $r->root;
+
+The L<Mojolicious::Routes> object this route is a descendant of.
+
+=head2 route
+
+  my $route = $r->route;
+  my $route = $r->route('/:action');
+  my $route = $r->route('/:action', action => qr/\w+/);
+  my $route = $r->route(format => 0);
+
+Low-level generator for routes matching all HTTP request methods, returns a
+L<Mojolicious::Routes::Route> object.
+
+=head2 suggested_method
+
+  my $method = $r->suggested_method;
+
+Suggested HTTP method for reaching this route, C<GET> and C<POST> are
+preferred.
+
+=head2 to
+
+  my $defaults = $r->to;
+  $r           = $r->to(action => 'foo');
+  $r           = $r->to({action => 'foo'});
+  $r           = $r->to('controller#action');
+  $r           = $r->to('controller#action', foo => 'bar');
+  $r           = $r->to('controller#action', {foo => 'bar'});
+  $r           = $r->to(Mojolicious->new);
+  $r           = $r->to(Mojolicious->new, foo => 'bar');
+  $r           = $r->to(Mojolicious->new, {foo => 'bar'});
+  $r           = $r->to('MyApp');
+  $r           = $r->to('MyApp', foo => 'bar');
+  $r           = $r->to('MyApp', {foo => 'bar'});
+
+Set default parameters for this route.
+
+=head2 to_string
+
+  my $str = $r->to_string;
+
+Stringify the whole route.
+
+=head2 under
+
+  my $route = $r->under(sub {...});
+  my $route = $r->under('/:foo' => sub {...});
+  my $route = $r->under('/:foo' => {foo => 'bar'});
+  my $route = $r->under('/:foo' => [foo => qr/\w+/]);
+  my $route = $r->under('/:foo' => (agent => qr/Firefox/));
+  my $route = $r->under([format => 0]);
+
+Generate L<Mojolicious::Routes::Route> object for a nested route with its own
+intermediate destination, takes the same arguments as L</"any"> (except for the
+HTTP methods to match, which are not available). See
+L<Mojolicious::Guides::Tutorial> and L<Mojolicious::Guides::Routing> for more
+information.
+
+  # Intermediate destination and prefix shared between two routes
+  my $auth = $r->under('/user')->to('user#auth');
+  $auth->get('/show')->to('#show');
+  $auth->post('/create')->to('#create');
+
+=head2 via
+
+  my $methods = $r->via;
+  $r          = $r->via('GET');
+  $r          = $r->via('GET', 'POST');
+  $r          = $r->via(['GET', 'POST']);
+
+Restrict HTTP methods this route is allowed to handle, defaults to no
+restrictions.
+
+  # Route with two methods and destination
+  $r->route('/foo')->via('GET', 'POST')->to('foo#bar');
+
+=head2 websocket
+
+  my $route = $r->websocket;
+  my $route = $r->websocket('/:foo');
+  my $route = $r->websocket('/:foo' => sub {...});
+  my $route = $r->websocket('/:foo' => sub {...} => 'name');
+  my $route = $r->websocket('/:foo' => {foo => 'bar'} => sub {...});
+  my $route = $r->websocket('/:foo' => [foo => qr/\w+/] => sub {...});
+  my $route = $r->websocket('/:foo' => (agent => qr/Firefox/) => sub {...});
+
+Generate L<Mojolicious::Routes::Route> object matching only WebSocket
+handshakes, takes the same arguments as L</"any"> (except for the HTTP methods
+to match, which are implied). See L<Mojolicious::Guides::Tutorial> and
+L<Mojolicious::Guides::Routing> for more information.
+
+  # Route with destination
+  $r->websocket('/echo')->to('example#echo');
+
+=head1 AUTOLOAD
+
+In addition to the L</"ATTRIBUTES"> and L</"METHODS"> above you can also call
+shortcuts provided by L</"root"> on L<Mojolicious::Routes::Route> objects.
+
+  # Add a "firefox" shortcut
+  $r->root->add_shortcut(firefox => sub {
+    my ($r, $path) = @_;
+    $r->get($path, agent => qr/Firefox/);
+  });
+
+  # Use "firefox" shortcut to generate routes
+  $r->firefox('/welcome')->to('firefox#welcome');
+  $r->firefox('/bye')->to('firefox#bye');
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Sessions.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Sessions.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Sessions.pm	(revision 32205)
@@ -0,0 +1,184 @@
+package Mojolicious::Sessions;
+use Mojo::Base -base;
+
+use Mojo::JSON;
+use Mojo::Util qw(b64_decode b64_encode);
+
+has [qw(cookie_domain secure)];
+has cookie_name        => 'mojolicious';
+has cookie_path        => '/';
+has default_expiration => 3600;
+has deserialize        => sub { \&Mojo::JSON::j };
+has serialize          => sub { \&Mojo::JSON::encode_json };
+
+sub load {
+  my ($self, $c) = @_;
+
+  return unless my $value = $c->signed_cookie($self->cookie_name);
+  $value =~ y/-/=/;
+  return unless my $session = $self->deserialize->(b64_decode $value);
+
+  # "expiration" value is inherited
+  my $expiration = $session->{expiration} // $self->default_expiration;
+  return if !(my $expires = delete $session->{expires}) && $expiration;
+  return if defined $expires && $expires <= time;
+
+  my $stash = $c->stash;
+  return unless $stash->{'mojo.active_session'} = keys %$session;
+  $stash->{'mojo.session'} = $session;
+  $session->{flash} = delete $session->{new_flash} if $session->{new_flash};
+}
+
+sub store {
+  my ($self, $c) = @_;
+
+  # Make sure session was active
+  my $stash = $c->stash;
+  return unless my $session = $stash->{'mojo.session'};
+  return unless keys %$session || $stash->{'mojo.active_session'};
+
+  # Don't reset flash for static files
+  my $old = delete $session->{flash};
+  $session->{new_flash} = $old if $stash->{'mojo.static'};
+  delete $session->{new_flash} unless keys %{$session->{new_flash}};
+
+  # Generate "expires" value from "expiration" if necessary
+  my $expiration = $session->{expiration} // $self->default_expiration;
+  my $default    = delete $session->{expires};
+  $session->{expires} = $default || time + $expiration
+    if $expiration || $default;
+
+  my $value = b64_encode $self->serialize->($session), '';
+  $value =~ y/=/-/;
+  my $options = {
+    domain   => $self->cookie_domain,
+    expires  => $session->{expires},
+    httponly => 1,
+    path     => $self->cookie_path,
+    secure   => $self->secure
+  };
+  $c->signed_cookie($self->cookie_name, $value, $options);
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Sessions - Session manager based on signed cookies
+
+=head1 SYNOPSIS
+
+  use Mojolicious::Sessions;
+
+  my $sessions = Mojolicious::Sessions->new;
+  $sessions->cookie_name('myapp');
+  $sessions->default_expiration(86400);
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Sessions> manages sessions based on signed cookies for
+L<Mojolicious>. All data gets serialized with L<Mojo::JSON> and stored Base64
+encoded on the client-side, but is protected from unwanted changes with a
+HMAC-SHA1 signature.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Sessions> implements the following attributes.
+
+=head2 cookie_domain
+
+  my $domain = $sessions->cookie_domain;
+  $sessions  = $sessions->cookie_domain('.example.com');
+
+Domain for session cookies, not defined by default.
+
+=head2 cookie_name
+
+  my $name  = $sessions->cookie_name;
+  $sessions = $sessions->cookie_name('session');
+
+Name for session cookies, defaults to C<mojolicious>.
+
+=head2 cookie_path
+
+  my $path  = $sessions->cookie_path;
+  $sessions = $sessions->cookie_path('/foo');
+
+Path for session cookies, defaults to C</>.
+
+=head2 default_expiration
+
+  my $time  = $sessions->default_expiration;
+  $sessions = $sessions->default_expiration(3600);
+
+Default time for sessions to expire in seconds from now, defaults to C<3600>.
+The expiration timeout gets refreshed for every request. Setting the value to
+C<0> will allow sessions to persist until the browser window is closed, this
+can have security implications though. For more control you can also use the
+C<expiration> and C<expires> session values.
+
+  # Expiration date in seconds from now (persists between requests)
+  $c->session(expiration => 604800);
+
+  # Expiration date as absolute epoch time (only valid for one request)
+  $c->session(expires => time + 604800);
+
+  # Delete whole session by setting an expiration date in the past
+  $c->session(expires => 1);
+
+=head2 deserialize
+
+  my $cb    = $sessions->deserialize;
+  $sessions = $sessions->deserialize(sub {...});
+
+A callback used to deserialize sessions, defaults to L<Mojo::JSON/"j">.
+
+  $sessions->deserialize(sub {
+    my $bytes = shift;
+    return {};
+  });
+
+=head2 secure
+
+  my $bool  = $sessions->secure;
+  $sessions = $sessions->secure($bool);
+
+Set the secure flag on all session cookies, so that browsers send them only
+over HTTPS connections.
+
+=head2 serialize
+
+  my $cb    = $sessions->serialize;
+  $sessions = $sessions->serialize(sub {...});
+
+A callback used to serialize sessions, defaults to L<Mojo::JSON/"encode_json">.
+
+  $sessions->serialize(sub {
+    my $hash = shift;
+    return '';
+  });
+
+=head1 METHODS
+
+L<Mojolicious::Sessions> inherits all methods from L<Mojo::Base> and implements
+the following new ones.
+
+=head2 load
+
+  $sessions->load(Mojolicious::Controller->new);
+
+Load session data from signed cookie.
+
+=head2 store
+
+  $sessions->store(Mojolicious::Controller->new);
+
+Store session data in signed cookie.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Static.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Static.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Static.pm	(revision 32205)
@@ -0,0 +1,299 @@
+package Mojolicious::Static;
+use Mojo::Base -base;
+
+use Mojo::Asset::File;
+use Mojo::Asset::Memory;
+use Mojo::Date;
+use Mojo::File 'path';
+use Mojo::Home;
+use Mojo::Loader qw(data_section file_is_binary);
+use Mojo::Util qw(encode md5_sum);
+
+# Bundled files
+my $PUBLIC = Mojo::Home->new(Mojo::Home->new->mojo_lib_dir)
+  ->child('Mojolicious', 'resources', 'public');
+my %EXTRA = $PUBLIC->list_tree->map(
+  sub { join('/', @{$_->to_rel($PUBLIC)}), $_->realpath->to_string })->each;
+
+has classes => sub { ['main'] };
+has extra   => sub { +{%EXTRA} };
+has paths   => sub { [] };
+
+sub dispatch {
+  my ($self, $c) = @_;
+
+  # Method (GET or HEAD)
+  my $req    = $c->req;
+  my $method = $req->method;
+  return undef unless $method eq 'GET' || $method eq 'HEAD';
+
+  # Canonical path
+  my $stash = $c->stash;
+  my $path  = $req->url->path;
+  $path = $stash->{path} ? $path->new($stash->{path}) : $path->clone;
+  return undef unless my @parts = @{$path->canonicalize->parts};
+
+  # Serve static file and prevent path traversal
+  my $canon_path = join '/', @parts;
+  return undef if $canon_path =~ /^\.\.\/|\\/ || !$self->serve($c, $canon_path);
+  $stash->{'mojo.static'} = 1;
+  return !!$c->rendered;
+}
+
+sub file {
+  my ($self, $rel) = @_;
+
+  # Search all paths
+  my @parts = split '/', $rel;
+  for my $path (@{$self->paths}) {
+    next unless my $asset = _get_file(path($path, @parts)->to_string);
+    return $asset;
+  }
+
+  # Search DATA
+  if (my $asset = $self->_get_data_file($rel)) { return $asset }
+
+  # Search extra files
+  my $extra = $self->extra;
+  return exists $extra->{$rel} ? _get_file($extra->{$rel}) : undef;
+}
+
+sub is_fresh {
+  my ($self, $c, $options) = @_;
+
+  my $res_headers = $c->res->headers;
+  my ($last, $etag) = @$options{qw(last_modified etag)};
+  $res_headers->last_modified(Mojo::Date->new($last)->to_string) if $last;
+  $res_headers->etag($etag = qq{"$etag"}) if $etag;
+
+  # Unconditional
+  my $req_headers = $c->req->headers;
+  my $match       = $req_headers->if_none_match;
+  return undef unless (my $since = $req_headers->if_modified_since) || $match;
+
+  # If-None-Match
+  return undef if $match && ($etag // $res_headers->etag // '') ne $match;
+
+  # If-Modified-Since
+  return !!$match unless ($last //= $res_headers->last_modified) && $since;
+  return _epoch($last) <= (_epoch($since) // 0);
+}
+
+sub serve {
+  my ($self, $c, $rel) = @_;
+
+  return undef unless my $asset = $self->file($rel);
+  my $headers = $c->res->headers;
+  return !!$self->serve_asset($c, $asset) if $headers->content_type;
+
+  # Content-Type
+  my $types = $c->app->types;
+  my $type = $rel =~ /\.(\w+)$/ ? $types->type($1) : undef;
+  $headers->content_type($type || $types->type('txt'));
+  return !!$self->serve_asset($c, $asset);
+}
+
+sub serve_asset {
+  my ($self, $c, $asset) = @_;
+
+  # Last-Modified and ETag
+  my $res = $c->res;
+  $res->code(200)->headers->accept_ranges('bytes');
+  my $mtime = $asset->mtime;
+  my $options = {etag => md5_sum($mtime), last_modified => $mtime};
+  return $res->code(304) if $self->is_fresh($c, $options);
+
+  # Range
+  return $res->content->asset($asset)
+    unless my $range = $c->req->headers->range;
+
+  # Not satisfiable
+  return $res->code(416) unless my $size = $asset->size;
+  return $res->code(416) unless $range =~ /^bytes=(\d+)?-(\d+)?/;
+  my ($start, $end) = ($1 // 0, defined $2 && $2 < $size ? $2 : $size - 1);
+  return $res->code(416) if $start > $end;
+
+  # Satisfiable
+  $res->code(206)->headers->content_length($end - $start + 1)
+    ->content_range("bytes $start-$end/$size");
+  return $res->content->asset($asset->start_range($start)->end_range($end));
+}
+
+sub warmup {
+  my $self = shift;
+  my $index = $self->{index} = {};
+  for my $class (reverse @{$self->classes}) {
+    $index->{$_} = $class for keys %{data_section $class};
+  }
+}
+
+sub _epoch { Mojo::Date->new(shift)->epoch }
+
+sub _get_data_file {
+  my ($self, $rel) = @_;
+
+  # Protect files without extensions and templates with two extensions
+  return undef if $rel !~ /\.\w+$/ || $rel =~ /\.\w+\.\w+$/;
+
+  $self->warmup unless $self->{index};
+
+  # Find file
+  my @args = ($self->{index}{$rel}, $rel);
+  return undef unless defined(my $data = data_section(@args));
+  return Mojo::Asset::Memory->new->add_chunk(
+    file_is_binary(@args) ? $data : encode 'UTF-8', $data);
+}
+
+sub _get_file {
+  my $path = shift;
+  no warnings 'newline';
+  return -f $path && -r _ ? Mojo::Asset::File->new(path => $path) : undef;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Static - Serve static files
+
+=head1 SYNOPSIS
+
+  use Mojolicious::Static;
+
+  my $static = Mojolicious::Static->new;
+  push @{$static->classes}, 'MyApp::Controller::Foo';
+  push @{$static->paths}, '/home/sri/public';
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Static> is a static file server with C<Range>,
+C<If-Modified-Since> and C<If-None-Match> support, based on
+L<RFC 7232|http://tools.ietf.org/html/rfc7232> and
+L<RFC 7233|http://tools.ietf.org/html/rfc7233>.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Static> implements the following attributes.
+
+=head2 classes
+
+  my $classes = $static->classes;
+  $static     = $static->classes(['main']);
+
+Classes to use for finding files in C<DATA> sections with L<Mojo::Loader>,
+first one has the highest precedence, defaults to C<main>. Only files with
+exactly one extension will be used, like C<index.html>. Note that for files to
+be detected, these classes need to have already been loaded and added before
+L</"warmup"> is called, which usually happens automatically during application
+startup.
+
+  # Add another class with static files in DATA section
+  push @{$static->classes}, 'Mojolicious::Plugin::Fun';
+
+  # Add another class with static files in DATA section and higher precedence
+  unshift @{$static->classes}, 'Mojolicious::Plugin::MoreFun';
+
+=head2 extra
+
+  my $extra = $static->extra;
+  $static   = $static->extra({'foo/bar.txt' => '/home/sri/myapp/bar.txt'});
+
+Paths for extra files to be served from locations other than L</"paths">, such
+as the images used by the built-in exception and not found pages. Note that
+extra files are only served if no better alternative could be found in
+L</"paths"> and L</"classes">.
+
+  # Remove built-in favicon
+  delete $static->extra->{'favicon.ico'};
+
+=head2 paths
+
+  my $paths = $static->paths;
+  $static   = $static->paths(['/home/sri/public']);
+
+Directories to serve static files from, first one has the highest precedence.
+
+  # Add another "public" directory
+  push @{$static->paths}, '/home/sri/public';
+
+  # Add another "public" directory with higher precedence
+  unshift @{$static->paths}, '/home/sri/themes/blue/public';
+
+=head1 METHODS
+
+L<Mojolicious::Static> inherits all methods from L<Mojo::Base> and implements
+the following new ones.
+
+=head2 dispatch
+
+  my $bool = $static->dispatch(Mojolicious::Controller->new);
+
+Serve static file for L<Mojolicious::Controller> object.
+
+=head2 file
+
+  my $asset = $static->file('images/logo.png');
+  my $asset = $static->file('../lib/MyApp.pm');
+
+Build L<Mojo::Asset::File> or L<Mojo::Asset::Memory> object for a file,
+relative to L</"paths"> or from L</"classes">, or return C<undef> if it doesn't
+exist. Note that this method uses a relative path, but does not protect from
+traversing to parent directories.
+
+  my $content = $static->file('foo/bar.html')->slurp;
+
+=head2 is_fresh
+
+  my $bool = $static->is_fresh(Mojolicious::Controller->new, {etag => 'abc'});
+
+Check freshness of request by comparing the C<If-None-Match> and
+C<If-Modified-Since> request headers to the C<ETag> and C<Last-Modified>
+response headers.
+
+These options are currently available:
+
+=over 2
+
+=item etag
+
+  etag => 'abc'
+
+Add C<ETag> header before comparing.
+
+=item last_modified
+
+  last_modified => $epoch
+
+Add C<Last-Modified> header before comparing.
+
+=back
+
+=head2 serve
+
+  my $bool = $static->serve(Mojolicious::Controller->new, 'images/logo.png');
+  my $bool = $static->serve(Mojolicious::Controller->new, '../lib/MyApp.pm');
+
+Serve a specific file, relative to L</"paths"> or from L</"classes">. Note that
+this method uses a relative path, but does not protect from traversing to parent
+directories.
+
+=head2 serve_asset
+
+  $static->serve_asset(Mojolicious::Controller->new, Mojo::Asset::File->new);
+
+Serve a L<Mojo::Asset::File> or L<Mojo::Asset::Memory> object with C<Range>,
+C<If-Modified-Since> and C<If-None-Match> support.
+
+=head2 warmup
+
+  $static->warmup;
+
+Prepare static files from L</"classes"> for future use.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Types.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Types.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Types.pm	(revision 32205)
@@ -0,0 +1,150 @@
+package Mojolicious::Types;
+use Mojo::Base -base;
+
+has mapping => sub {
+  {
+    appcache => ['text/cache-manifest'],
+    atom     => ['application/atom+xml'],
+    bin      => ['application/octet-stream'],
+    css      => ['text/css'],
+    gif      => ['image/gif'],
+    gz       => ['application/x-gzip'],
+    htm      => ['text/html'],
+    html     => ['text/html;charset=UTF-8'],
+    ico      => ['image/x-icon'],
+    jpeg     => ['image/jpeg'],
+    jpg      => ['image/jpeg'],
+    js       => ['application/javascript'],
+    json     => ['application/json;charset=UTF-8'],
+    mp3      => ['audio/mpeg'],
+    mp4      => ['video/mp4'],
+    ogg      => ['audio/ogg'],
+    ogv      => ['video/ogg'],
+    pdf      => ['application/pdf'],
+    png      => ['image/png'],
+    rss      => ['application/rss+xml'],
+    svg      => ['image/svg+xml'],
+    txt      => ['text/plain;charset=UTF-8'],
+    webm     => ['video/webm'],
+    woff     => ['application/font-woff'],
+    xml      => ['application/xml', 'text/xml'],
+    zip      => ['application/zip']
+  };
+};
+
+sub detect {
+  my ($self, $accept) = @_;
+
+  # Extract and prioritize MIME types
+  my %types;
+  /^\s*([^,; ]+)(?:\s*\;\s*q\s*=\s*(\d+(?:\.\d+)?))?\s*$/i
+    and $types{lc $1} = $2 // 1
+    for split ',', $accept // '';
+  my @detected = sort { $types{$b} <=> $types{$a} } sort keys %types;
+
+  # Detect extensions from MIME types
+  my %reverse;
+  my $mapping = $self->mapping;
+  for my $ext (sort keys %$mapping) {
+    my @types = @{$mapping->{$ext}};
+    push @{$reverse{$_}}, $ext for map { s/\;.*$//; lc $_ } @types;
+  }
+
+  return [map { @{$reverse{$_} // []} } @detected];
+}
+
+sub type {
+  my ($self, $ext, $type) = @_;
+  return $self->mapping->{lc $ext}[0] unless $type;
+  $self->mapping->{lc $ext} = ref $type ? $type : [$type];
+  return $self;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Types - MIME types
+
+=head1 SYNOPSIS
+
+  use Mojolicious::Types;
+
+  my $types = Mojolicious::Types->new;
+  $types->type(foo => 'text/foo');
+  say $types->type('foo');
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Types> manages MIME types for L<Mojolicious>.
+
+  appcache -> text/cache-manifest
+  atom     -> application/atom+xml
+  bin      -> application/octet-stream
+  css      -> text/css
+  gif      -> image/gif
+  gz       -> application/x-gzip
+  htm      -> text/html
+  html     -> text/html;charset=UTF-8
+  ico      -> image/x-icon
+  jpeg     -> image/jpeg
+  jpg      -> image/jpeg
+  js       -> application/javascript
+  json     -> application/json;charset=UTF-8
+  mp3      -> audio/mpeg
+  mp4      -> video/mp4
+  ogg      -> audio/ogg
+  ogv      -> video/ogg
+  pdf      -> application/pdf
+  png      -> image/png
+  rss      -> application/rss+xml
+  svg      -> image/svg+xml
+  txt      -> text/plain;charset=UTF-8
+  webm     -> video/webm
+  woff     -> application/font-woff
+  xml      -> application/xml,text/xml
+  zip      -> application/zip
+
+The most common ones are already defined.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Types> implements the following attributes.
+
+=head2 mapping
+
+  my $mapping = $types->mapping;
+  $types      = $types->mapping({png => ['image/png']});
+
+MIME type mapping.
+
+=head1 METHODS
+
+L<Mojolicious::Types> inherits all methods from L<Mojo::Base> and implements
+the following new ones.
+
+=head2 detect
+
+  my $exts = $types->detect('text/html, application/json;q=9');
+
+Detect file extensions from C<Accept> header value.
+
+  # List detected extensions prioritized
+  say for @{$types->detect('application/json, text/xml;q=0.1', 1)};
+
+=head2 type
+
+  my $type = $types->type('png');
+  $types   = $types->type(png => 'image/png');
+  $types   = $types->type(json => ['application/json', 'text/x-json']);
+
+Get or set MIME types for file extension, alternatives are only used for
+detection.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Validator.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Validator.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Validator.pm	(revision 32205)
@@ -0,0 +1,186 @@
+package Mojolicious::Validator;
+use Mojo::Base -base;
+
+use Mojo::Util 'trim';
+use Mojolicious::Validator::Validation;
+
+has checks => sub {
+  {
+    equal_to => \&_equal_to,
+    in       => \&_in,
+    like     => sub { $_[2] !~ $_[3] },
+    num      => \&_num,
+    size     => \&_size,
+    upload   => sub { !ref $_[2] || !$_[2]->isa('Mojo::Upload') }
+  };
+};
+has filters => sub { {trim => \&_trim} };
+
+sub add_check  { $_[0]->checks->{$_[1]}  = $_[2] and return $_[0] }
+sub add_filter { $_[0]->filters->{$_[1]} = $_[2] and return $_[0] }
+
+sub validation {
+  Mojolicious::Validator::Validation->new(validator => shift);
+}
+
+sub _equal_to {
+  my ($v, $name, $value, $to) = @_;
+  return 1 unless defined(my $other = $v->input->{$to});
+  return $value ne $other;
+}
+
+sub _in {
+  my ($v, $name, $value) = (shift, shift, shift);
+  $value eq $_ && return undef for @_;
+  return 1;
+}
+
+sub _num {
+  my ($v, $name, $value, $min, $max) = @_;
+  return 1 if $value !~ /^[0-9]+$/;
+  return defined $min && $min > $value || defined $max && $max < $value;
+}
+
+sub _size {
+  my ($v, $name, $value, $min, $max) = @_;
+  my $len = ref $value ? $value->size : length $value;
+  return $len < $min || $len > $max;
+}
+
+sub _trim { trim $_[2] // '' }
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Validator - Validate values
+
+=head1 SYNOPSIS
+
+  use Mojolicious::Validator;
+
+  my $validator = Mojolicious::Validator->new;
+  my $v = $validator->validation;
+  $v->input({foo => 'bar'});
+  $v->required('foo')->like(qr/ar$/);
+  say $v->param('foo');
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Validator> validates values for L<Mojolicious>.
+
+=head1 CHECKS
+
+These validation checks are available by default.
+
+=head2 equal_to
+
+  $v = $v->equal_to('foo');
+
+String value needs to be equal to the value of another field.
+
+=head2 in
+
+  $v = $v->in('foo', 'bar', 'baz');
+
+String value needs to match one of the values in the list.
+
+=head2 like
+
+  $v = $v->like(qr/^[A-Z]/);
+
+String value needs to match the regular expression.
+
+=head2 num
+
+  $v = $v->num;
+  $v = $v->num(2, 5);
+  $v = $v->num(2, undef);
+  $v = $v->num(undef, 5);
+
+String value needs to be a non-fractional number and if provided in the given
+range.
+
+=head2 size
+
+  $v = $v->size(2, 5);
+
+String value length or size of L<Mojo::Upload> object in bytes needs to be
+between these two values.
+
+=head2 upload
+
+  $v = $v->upload;
+
+Value needs to be a L<Mojo::Upload> object, representing a file upload.
+
+=head1 FILTERS
+
+These filters are available by default.
+
+=head2 trim
+
+  $v = $v->optional('foo', 'trim');
+
+Trim whitespace characters from both ends of string value with
+L<Mojo::Util/"trim">.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Validator> implements the following attributes.
+
+=head2 checks
+
+  my $checks = $validator->checks;
+  $validator = $validator->checks({size => sub {...}});
+
+Registered validation checks, by default only L</"equal_to">, L</"in">,
+L</"like">, L</"num">, L</"size"> and L</"upload"> are already defined.
+
+=head1 METHODS
+
+L<Mojolicious::Validator> inherits all methods from L<Mojo::Base> and
+implements the following new ones.
+
+=head2 add_check
+
+  $validator = $validator->add_check(size => sub {...});
+
+Register a validation check.
+
+  $validator->add_check(foo => sub {
+    my ($v, $name, $value, @args) = @_;
+    ...
+    return undef;
+  });
+
+=head2 add_filter
+
+  $validator = $validator->add_filter(trim => sub {...});
+
+Register a new filter.
+
+  $validator->add_filter(foo => sub {
+    my ($v, $name, $value) = @_;
+    ...
+    return $value;
+  });
+
+=head2 validation
+
+  my $v = $validator->validation;
+
+Build L<Mojolicious::Validator::Validation> object to perform validations.
+
+  my $v = $validator->validation;
+  $v->input({foo => 'bar'});
+  $v->required('foo')->size(1, 5);
+  say $v->param('foo');
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/Validator/Validation.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/Validator/Validation.pm	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/Validator/Validation.pm	(revision 32205)
@@ -0,0 +1,291 @@
+package Mojolicious::Validator::Validation;
+use Mojo::Base -base;
+
+use Carp         ();
+use Scalar::Util ();
+
+has [qw(csrf_token topic validator)];
+has [qw(input output)] => sub { {} };
+
+sub AUTOLOAD {
+  my $self = shift;
+
+  my ($package, $method) = our $AUTOLOAD =~ /^(.+)::(.+)$/;
+  Carp::croak "Undefined subroutine &${package}::$method called"
+    unless Scalar::Util::blessed $self && $self->isa(__PACKAGE__);
+
+  return $self->check($method => @_) if $self->validator->checks->{$method};
+  Carp::croak qq{Can't locate object method "$method" via package "$package"};
+}
+
+sub check {
+  my ($self, $check) = (shift, shift);
+
+  return $self unless $self->is_valid;
+
+  my $cb     = $self->validator->checks->{$check};
+  my $name   = $self->topic;
+  my $values = $self->output->{$name};
+  for my $value (ref $values eq 'ARRAY' ? @$values : $values) {
+    next unless my $result = $self->$cb($name, $value, @_);
+    return $self->error($name => [$check, $result, @_]);
+  }
+
+  return $self;
+}
+
+sub csrf_protect {
+  my $self  = shift;
+  my $token = $self->input->{csrf_token};
+  $self->error(csrf_token => ['csrf_protect'])
+    unless $token && $token eq ($self->csrf_token // '');
+  return $self;
+}
+
+sub error {
+  my ($self, $name) = (shift, shift);
+  return $self->{error}{$name} unless @_;
+  $self->{error}{$name} = shift;
+  delete $self->output->{$name};
+  return $self;
+}
+
+sub every_param {
+  return [] unless defined(my $value = $_[0]->output->{$_[1] // $_[0]->topic});
+  return [ref $value eq 'ARRAY' ? @$value : $value];
+}
+
+sub failed { [sort keys %{shift->{error}}] }
+
+sub has_data { !!keys %{shift->input} }
+
+sub has_error { $_[1] ? exists $_[0]{error}{$_[1]} : !!keys %{$_[0]{error}} }
+
+sub is_valid { exists $_[0]->output->{$_[1] // $_[0]->topic} }
+
+sub optional {
+  my ($self, $name, @filters) = @_;
+
+  return $self->topic($name) unless defined(my $input = $self->input->{$name});
+
+  my @input = ref $input eq 'ARRAY' ? @$input : ($input);
+  for my $cb (map { $self->validator->filters->{$_} } @filters) {
+    @input = map { $self->$cb($name, $_) } @input;
+  }
+  $self->output->{$name} = ref $input eq 'ARRAY' ? \@input : $input[0]
+    if @input && !grep { !length } @input;
+
+  return $self->topic($name);
+}
+
+sub param { shift->every_param(shift)->[-1] }
+
+sub passed { [sort keys %{shift->output}] }
+
+sub required {
+  my ($self, $name) = (shift, shift);
+  return $self if $self->optional($name, @_)->is_valid;
+  return $self->error($name => ['required']);
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Validator::Validation - Perform validations
+
+=head1 SYNOPSIS
+
+  use Mojolicious::Validator;
+  use Mojolicious::Validator::Validation;
+
+  my $validator = Mojolicious::Validator->new;
+  my $v = Mojolicious::Validator::Validation->new(validator => $validator);
+  $v->input({foo => 'bar'});
+  $v->required('foo')->in('bar', 'baz');
+  say $v->param('foo');
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Validator::Validation> performs L<Mojolicious::Validator>
+validation checks.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Validator::Validation> implements the following attributes.
+
+=head2 csrf_token
+
+  my $token = $v->csrf_token;
+  $v        = $v->csrf_token('fa6a08...');
+
+CSRF token.
+
+=head2 input
+
+  my $input = $v->input;
+  $v        = $v->input({foo => 'bar', baz => [123, 'yada']});
+
+Data to be validated.
+
+=head2 output
+
+  my $output = $v->output;
+  $v         = $v->output({foo => 'bar', baz => [123, 'yada']});
+
+Validated data.
+
+=head2 topic
+
+  my $topic = $v->topic;
+  $v        = $v->topic('foo');
+
+Name of field currently being validated.
+
+=head2 validator
+
+  my $v = $v->validator;
+  $v    = $v->validator(Mojolicious::Validator->new);
+
+L<Mojolicious::Validator> object this validation belongs to.
+
+=head1 METHODS
+
+L<Mojolicious::Validator::Validation> inherits all methods from L<Mojo::Base>
+and implements the following new ones.
+
+=head2 check
+
+  $v = $v->check('size', 2, 7);
+
+Perform validation check on all values of the current L</"topic">, no more
+checks will be performed on them after the first one failed. All checks from
+L<Mojolicious::Validator/"CHECKS"> are supported.
+
+=head2 csrf_protect
+
+  $v = $v->csrf_protect;
+
+Validate C<csrf_token> and protect from cross-site request forgery.
+
+=head2 error
+
+  my $err = $v->error('foo');
+  $v      = $v->error(foo => ['custom_check']);
+  $v      = $v->error(foo => [$check, $result, @args]);
+
+Get or set details for failed validation check, at any given time there can
+only be one per field.
+
+  # Details about failed validation
+  my ($check, $result, @args) = @{$v->error('foo')};
+
+  # Force validation to fail for a field without performing a check
+  $v->error(foo => ['some_made_up_check_name']);
+
+=head2 every_param
+
+  my $values = $v->every_param;
+  my $values = $v->every_param('foo');
+
+Similar to L</"param">, but returns all values sharing the same name as an
+array reference.
+
+  # Get first value
+  my $first = $v->every_param('foo')->[0];
+
+=head2 failed
+
+  my $names = $v->failed;
+
+Return an array reference with all names for values that failed validation.
+
+  # Names of all values that failed
+  say for @{$v->failed};
+
+=head2 has_data
+
+  my $bool = $v->has_data;
+
+Check if L</"input"> is available for validation.
+
+=head2 has_error
+
+  my $bool = $v->has_error;
+  my $bool = $v->has_error('foo');
+
+Check if validation resulted in errors, defaults to checking all fields.
+
+=head2 is_valid
+
+  my $bool = $v->is_valid;
+  my $bool = $v->is_valid('foo');
+
+Check if validation was successful and field has a value, defaults to checking
+the current L</"topic">.
+
+=head2 optional
+
+  $v = $v->optional('foo');
+  $v = $v->optional('foo', 'filter1', 'filter2');
+
+Change validation L</"topic"> and apply filters. All filters from
+L<Mojolicious::Validator/"FILTERS"> are supported.
+
+  # Trim value and check size
+  $v->optional('user', 'trim')->size(1, 15);
+
+=head2 param
+
+  my $value = $v->param;
+  my $value = $v->param('foo');
+
+Access validated values, defaults to the current L</"topic">. If there are
+multiple values sharing the same name, and you want to access more than just the
+last one, you can use L</"every_param">.
+
+  # Get value right away
+  my $user = $v->optional('user')->size(1, 15)->param;
+
+=head2 passed
+
+  my $names = $v->passed;
+
+Return an array reference with all names for values that passed validation.
+
+  # Names of all values that passed
+  say for @{$v->passed};
+
+=head2 required
+
+  $v = $v->required('foo');
+  $v = $v->required('foo', 'filter1', 'filter2');
+
+Change validation L</"topic">, apply filters, and make sure a value is present
+and not an empty string. All filters from L<Mojolicious::Validator/"FILTERS">
+are supported.
+
+  # Trim value and check size
+  $v->required('user', 'trim')->size(1, 15);
+
+=head1 AUTOLOAD
+
+In addition to the L</"ATTRIBUTES"> and L</"METHODS"> above, you can also call
+validation checks provided by L</"validator"> on
+L<Mojolicious::Validator::Validation> objects, similar to L</"check">.
+
+  # Call validation checks
+  $v->required('foo')->size(2, 5)->like(qr/^[A-Z]/);
+  $v->optional('bar')->equal_to('foo');
+  $v->optional('baz')->in('test', '123');
+
+  # Longer version
+  $v->required('foo')->check('size', 2, 5)->check('like', qr/^[A-Z]/);
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
+
+=cut
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/resources/public/mojo/jquery/jquery.js
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/resources/public/mojo/jquery/jquery.js	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/resources/public/mojo/jquery/jquery.js	(revision 32205)
@@ -0,0 +1,4 @@
+/*! jQuery v3.2.1 | (c) JS Foundation and other contributors | jquery.org/license */
+!function(a,b){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){"use strict";var c=[],d=a.document,e=Object.getPrototypeOf,f=c.slice,g=c.concat,h=c.push,i=c.indexOf,j={},k=j.toString,l=j.hasOwnProperty,m=l.toString,n=m.call(Object),o={};function p(a,b){b=b||d;var c=b.createElement("script");c.text=a,b.head.appendChild(c).parentNode.removeChild(c)}var q="3.2.1",r=function(a,b){return new r.fn.init(a,b)},s=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,t=/^-ms-/,u=/-([a-z])/g,v=function(a,b){return b.toUpperCase()};r.fn=r.prototype={jquery:q,constructor:r,length:0,toArray:function(){return f.call(this)},get:function(a){return null==a?f.call(this):a<0?this[a+this.length]:this[a]},pushStack:function(a){var b=r.merge(this.constructor(),a);return b.prevObject=this,b},each:function(a){return r.each(this,a)},map:function(a){return this.pushStack(r.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(f.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(a<0?b:0);return this.pushStack(c>=0&&c<b?[this[c]]:[])},end:function(){return this.prevObject||this.constructor()},push:h,sort:c.sort,splice:c.splice},r.extend=r.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||r.isFunction(g)||(g={}),h===i&&(g=this,h--);h<i;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(r.isPlainObject(d)||(e=Array.isArray(d)))?(e?(e=!1,f=c&&Array.isArray(c)?c:[]):f=c&&r.isPlainObject(c)?c:{},g[b]=r.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},r.extend({expando:"jQuery"+(q+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===r.type(a)},isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){var b=r.type(a);return("number"===b||"string"===b)&&!isNaN(a-parseFloat(a))},isPlainObject:function(a){var b,c;return!(!a||"[object Object]"!==k.call(a))&&(!(b=e(a))||(c=l.call(b,"constructor")&&b.constructor,"function"==typeof c&&m.call(c)===n))},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?j[k.call(a)]||"object":typeof a},globalEval:function(a){p(a)},camelCase:function(a){return a.replace(t,"ms-").replace(u,v)},each:function(a,b){var c,d=0;if(w(a)){for(c=a.length;d<c;d++)if(b.call(a[d],d,a[d])===!1)break}else for(d in a)if(b.call(a[d],d,a[d])===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(s,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(w(Object(a))?r.merge(c,"string"==typeof a?[a]:a):h.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:i.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;d<c;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;f<g;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,e,f=0,h=[];if(w(a))for(d=a.length;f<d;f++)e=b(a[f],f,c),null!=e&&h.push(e);else for(f in a)e=b(a[f],f,c),null!=e&&h.push(e);return g.apply([],h)},guid:1,proxy:function(a,b){var c,d,e;if("string"==typeof b&&(c=a[b],b=a,a=c),r.isFunction(a))return d=f.call(arguments,2),e=function(){return a.apply(b||this,d.concat(f.call(arguments)))},e.guid=a.guid=a.guid||r.guid++,e},now:Date.now,support:o}),"function"==typeof Symbol&&(r.fn[Symbol.iterator]=c[Symbol.iterator]),r.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(a,b){j["[object "+b+"]"]=b.toLowerCase()});function w(a){var b=!!a&&"length"in a&&a.length,c=r.type(a);return"function"!==c&&!r.isWindow(a)&&("array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a)}var x=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=function(a,b){for(var c=0,d=a.length;c<d;c++)if(a[c]===b)return c;return-1},J="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",K="[\\x20\\t\\r\\n\\f]",L="(?:\\\\.|[\\w-]|[^\0-\\xa0])+",M="\\["+K+"*("+L+")(?:"+K+"*([*^$|!~]?=)"+K+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+L+"))|)"+K+"*\\]",N=":("+L+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+M+")*)|.*)\\)|)",O=new RegExp(K+"+","g"),P=new RegExp("^"+K+"+|((?:^|[^\\\\])(?:\\\\.)*)"+K+"+$","g"),Q=new RegExp("^"+K+"*,"+K+"*"),R=new RegExp("^"+K+"*([>+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(N),U=new RegExp("^"+L+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L+"|[*])"),ATTR:new RegExp("^"+M),PSEUDO:new RegExp("^"+N),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),aa=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:d<0?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ba=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ca=function(a,b){return b?"\0"===a?"\ufffd":a.slice(0,-1)+"\\"+a.charCodeAt(a.length-1).toString(16)+" ":"\\"+a},da=function(){m()},ea=ta(function(a){return a.disabled===!0&&("form"in a||"label"in a)},{dir:"parentNode",next:"legend"});try{G.apply(D=H.call(v.childNodes),v.childNodes),D[v.childNodes.length].nodeType}catch(fa){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s=b&&b.ownerDocument,w=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==w&&9!==w&&11!==w)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==w&&(l=Z.exec(a)))if(f=l[1]){if(9===w){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(s&&(j=s.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(l[2])return G.apply(d,b.getElementsByTagName(a)),d;if((f=l[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==w)s=b,r=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(ba,ca):b.setAttribute("id",k=u),o=g(a),h=o.length;while(h--)o[h]="#"+k+" "+sa(o[h]);r=o.join(","),s=$.test(a)&&qa(b.parentNode)||b}if(r)try{return G.apply(d,s.querySelectorAll(r)),d}catch(x){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(P,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("fieldset");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&a.sourceIndex-b.sourceIndex;if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return function(b){return"form"in b?b.parentNode&&b.disabled===!1?"label"in b?"label"in b.parentNode?b.parentNode.disabled===a:b.disabled===a:b.isDisabled===a||b.isDisabled!==!a&&ea(b)===a:b.disabled===a:"label"in b&&b.disabled===a}}function pa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function qa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return!!b&&"HTML"!==b.nodeName},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),v!==n&&(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(n.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){return a.getAttribute("id")===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}}):(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c,d,e,f=b.getElementById(a);if(f){if(c=f.getAttributeNode("id"),c&&c.value===a)return[f];e=b.getElementsByName(a),d=0;while(f=e[d++])if(c=f.getAttributeNode("id"),c&&c.value===a)return[f]}return[]}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){if("undefined"!=typeof b.getElementsByClassName&&p)return b.getElementsByClassName(a)},r=[],q=[],(c.qsa=Y.test(n.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="<a id='"+u+"'></a><select id='"+u+"-\r\\' msallowcapture=''><option selected=''></option></select>",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){a.innerHTML="<a href='' disabled='disabled'></a><select disabled='disabled'><option/></select>";var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+K+"*[*^$|!~]?="),2!==a.querySelectorAll(":enabled").length&&q.push(":enabled",":disabled"),o.appendChild(a).disabled=!0,2!==a.querySelectorAll(":disabled").length&&q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Y.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"*"),s.call(a,"[s!='']:x"),r.push("!=",N)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Y.test(o.compareDocumentPosition),t=b||Y.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?I(k,a)-I(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?I(k,a)-I(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?la(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(S,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.escape=function(a){return(a+"").replace(ba,ca)},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(_,aa),a[3]=(a[3]||a[4]||a[5]||"").replace(_,aa),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return V.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&T.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(_,aa).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:!b||(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(O," ")+" ").indexOf(c)>-1:"|="===b&&(e===c||e.slice(0,c.length+1)===c+"-"))}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(P,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(_,aa),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return U.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(_,aa).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:oa(!1),disabled:oa(!0),checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:pa(function(){return[0]}),last:pa(function(a,b){return[b-1]}),eq:pa(function(a,b,c){return[c<0?c+b:c]}),even:pa(function(a,b){for(var c=0;c<b;c+=2)a.push(c);return a}),odd:pa(function(a,b){for(var c=1;c<b;c+=2)a.push(c);return a}),lt:pa(function(a,b,c){for(var d=c<0?c+b:c;--d>=0;)a.push(d);return a}),gt:pa(function(a,b,c){for(var d=c<0?c+b:c;++d<b;)a.push(d);return a})}},d.pseudos.nth=d.pseudos.eq;for(b in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})d.pseudos[b]=ma(b);for(b in{submit:!0,reset:!0})d.pseudos[b]=na(b);function ra(){}ra.prototype=d.filters=d.pseudos,d.setFilters=new ra,g=ga.tokenize=function(a,b){var c,e,f,g,h,i,j,k=z[a+" "];if(k)return b?0:k.slice(0);h=a,i=[],j=d.preFilter;while(h){c&&!(e=Q.exec(h))||(e&&(h=h.slice(e[0].length)||h),i.push(f=[])),c=!1,(e=R.exec(h))&&(c=e.shift(),f.push({value:c,type:e[0].replace(P," ")}),h=h.slice(c.length));for(g in d.filter)!(e=V[g].exec(h))||j[g]&&!(e=j[g](e))||(c=e.shift(),f.push({value:c,type:g,matches:e}),h=h.slice(c.length));if(!c)break}return b?h.length:h?ga.error(a):z(a,i).slice(0)};function sa(a){for(var b=0,c=a.length,d="";b<c;b++)d+=a[b].value;return d}function ta(a,b,c){var d=b.dir,e=b.next,f=e||d,g=c&&"parentNode"===f,h=x++;return b.first?function(b,c,e){while(b=b[d])if(1===b.nodeType||g)return a(b,c,e);return!1}:function(b,c,i){var j,k,l,m=[w,h];if(i){while(b=b[d])if((1===b.nodeType||g)&&a(b,c,i))return!0}else while(b=b[d])if(1===b.nodeType||g)if(l=b[u]||(b[u]={}),k=l[b.uniqueID]||(l[b.uniqueID]={}),e&&e===b.nodeName.toLowerCase())b=b[d]||b;else{if((j=k[f])&&j[0]===w&&j[1]===h)return m[2]=j[2];if(k[f]=m,m[2]=a(b,c,i))return!0}return!1}}function ua(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function va(a,b,c){for(var d=0,e=b.length;d<e;d++)ga(a,b[d],c);return c}function wa(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;h<i;h++)(f=a[h])&&(c&&!c(f,d,e)||(g.push(f),j&&b.push(h)));return g}function xa(a,b,c,d,e,f){return d&&!d[u]&&(d=xa(d)),e&&!e[u]&&(e=xa(e,f)),ia(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||va(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:wa(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=wa(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?I(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=wa(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ya(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ta(function(a){return a===b},h,!0),l=ta(function(a){return I(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];i<f;i++)if(c=d.relative[a[i].type])m=[ta(ua(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;e<f;e++)if(d.relative[a[e].type])break;return xa(i>1&&ua(m),i>1&&sa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(P,"$1"),c,i<e&&ya(a.slice(i,e)),e<f&&ya(a=a.slice(e)),e<f&&sa(a))}m.push(c)}return ua(m)}function za(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=E.call(i));u=wa(u)}G.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&ga.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=ya(b[c]),f[u]?d.push(f):e.push(f);f=A(a,za(e,d)),f.selector=a}return f},i=ga.select=function(a,b,c,e){var f,i,j,k,l,m="function"==typeof a&&a,n=!e&&g(a=m.selector||a);if(c=c||[],1===n.length){if(i=n[0]=n[0].slice(0),i.length>2&&"ID"===(j=i[0]).type&&9===b.nodeType&&p&&d.relative[i[1].type]){if(b=(d.find.ID(j.matches[0].replace(_,aa),b)||[])[0],!b)return c;m&&(b=b.parentNode),a=a.slice(i.shift().value.length)}f=V.needsContext.test(a)?0:i.length;while(f--){if(j=i[f],d.relative[k=j.type])break;if((l=d.find[k])&&(e=l(j.matches[0].replace(_,aa),$.test(i[0].type)&&qa(b.parentNode)||b))){if(i.splice(f,1),a=e.length&&sa(i),!a)return G.apply(c,e),c;break}}}return(m||h(a,n))(e,b,!p,c,!b||$.test(a)&&qa(b.parentNode)||b),c},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("fieldset"))}),ja(function(a){return a.innerHTML="<a href='#'></a>","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){if(!c)return a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="<input/>",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){if(!c&&"input"===a.nodeName.toLowerCase())return a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(J,function(a,b,c){var d;if(!c)return a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);r.find=x,r.expr=x.selectors,r.expr[":"]=r.expr.pseudos,r.uniqueSort=r.unique=x.uniqueSort,r.text=x.getText,r.isXMLDoc=x.isXML,r.contains=x.contains,r.escapeSelector=x.escape;var y=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&r(a).is(c))break;d.push(a)}return d},z=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},A=r.expr.match.needsContext;function B(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()}var C=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i,D=/^.[^:#\[\.,]*$/;function E(a,b,c){return r.isFunction(b)?r.grep(a,function(a,d){return!!b.call(a,d,a)!==c}):b.nodeType?r.grep(a,function(a){return a===b!==c}):"string"!=typeof b?r.grep(a,function(a){return i.call(b,a)>-1!==c}):D.test(b)?r.filter(b,a,c):(b=r.filter(b,a),r.grep(a,function(a){return i.call(b,a)>-1!==c&&1===a.nodeType}))}r.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?r.find.matchesSelector(d,a)?[d]:[]:r.find.matches(a,r.grep(b,function(a){return 1===a.nodeType}))},r.fn.extend({find:function(a){var b,c,d=this.length,e=this;if("string"!=typeof a)return this.pushStack(r(a).filter(function(){for(b=0;b<d;b++)if(r.contains(e[b],this))return!0}));for(c=this.pushStack([]),b=0;b<d;b++)r.find(a,e[b],c);return d>1?r.uniqueSort(c):c},filter:function(a){return this.pushStack(E(this,a||[],!1))},not:function(a){return this.pushStack(E(this,a||[],!0))},is:function(a){return!!E(this,"string"==typeof a&&A.test(a)?r(a):a||[],!1).length}});var F,G=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,H=r.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||F,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:G.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof r?b[0]:b,r.merge(this,r.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),C.test(e[1])&&r.isPlainObject(b))for(e in b)r.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&(this[0]=f,this.length=1),this}return a.nodeType?(this[0]=a,this.length=1,this):r.isFunction(a)?void 0!==c.ready?c.ready(a):a(r):r.makeArray(a,this)};H.prototype=r.fn,F=r(d);var I=/^(?:parents|prev(?:Until|All))/,J={children:!0,contents:!0,next:!0,prev:!0};r.fn.extend({has:function(a){var b=r(a,this),c=b.length;return this.filter(function(){for(var a=0;a<c;a++)if(r.contains(this,b[a]))return!0})},closest:function(a,b){var c,d=0,e=this.length,f=[],g="string"!=typeof a&&r(a);if(!A.test(a))for(;d<e;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&r.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?r.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?i.call(r(a),this[0]):i.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(r.uniqueSort(r.merge(this.get(),r(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function K(a,b){while((a=a[b])&&1!==a.nodeType);return a}r.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return y(a,"parentNode")},parentsUntil:function(a,b,c){return y(a,"parentNode",c)},next:function(a){return K(a,"nextSibling")},prev:function(a){return K(a,"previousSibling")},nextAll:function(a){return y(a,"nextSibling")},prevAll:function(a){return y(a,"previousSibling")},nextUntil:function(a,b,c){return y(a,"nextSibling",c)},prevUntil:function(a,b,c){return y(a,"previousSibling",c)},siblings:function(a){return z((a.parentNode||{}).firstChild,a)},children:function(a){return z(a.firstChild)},contents:function(a){return B(a,"iframe")?a.contentDocument:(B(a,"template")&&(a=a.content||a),r.merge([],a.childNodes))}},function(a,b){r.fn[a]=function(c,d){var e=r.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=r.filter(d,e)),this.length>1&&(J[a]||r.uniqueSort(e),I.test(a)&&e.reverse()),this.pushStack(e)}});var L=/[^\x20\t\r\n\f]+/g;function M(a){var b={};return r.each(a.match(L)||[],function(a,c){b[c]=!0}),b}r.Callbacks=function(a){a="string"==typeof a?M(a):r.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=e||a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h<f.length)f[h].apply(c[0],c[1])===!1&&a.stopOnFalse&&(h=f.length,c=!1)}a.memory||(c=!1),b=!1,e&&(f=c?[]:"")},j={add:function(){return f&&(c&&!b&&(h=f.length-1,g.push(c)),function d(b){r.each(b,function(b,c){r.isFunction(c)?a.unique&&j.has(c)||f.push(c):c&&c.length&&"string"!==r.type(c)&&d(c)})}(arguments),c&&!b&&i()),this},remove:function(){return r.each(arguments,function(a,b){var c;while((c=r.inArray(b,f,c))>-1)f.splice(c,1),c<=h&&h--}),this},has:function(a){return a?r.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||b||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j};function N(a){return a}function O(a){throw a}function P(a,b,c,d){var e;try{a&&r.isFunction(e=a.promise)?e.call(a).done(b).fail(c):a&&r.isFunction(e=a.then)?e.call(a,b,c):b.apply(void 0,[a].slice(d))}catch(a){c.apply(void 0,[a])}}r.extend({Deferred:function(b){var c=[["notify","progress",r.Callbacks("memory"),r.Callbacks("memory"),2],["resolve","done",r.Callbacks("once memory"),r.Callbacks("once memory"),0,"resolved"],["reject","fail",r.Callbacks("once memory"),r.Callbacks("once memory"),1,"rejected"]],d="pending",e={state:function(){return d},always:function(){return f.done(arguments).fail(arguments),this},"catch":function(a){return e.then(null,a)},pipe:function(){var a=arguments;return r.Deferred(function(b){r.each(c,function(c,d){var e=r.isFunction(a[d[4]])&&a[d[4]];f[d[1]](function(){var a=e&&e.apply(this,arguments);a&&r.isFunction(a.promise)?a.promise().progress(b.notify).done(b.resolve).fail(b.reject):b[d[0]+"With"](this,e?[a]:arguments)})}),a=null}).promise()},then:function(b,d,e){var f=0;function g(b,c,d,e){return function(){var h=this,i=arguments,j=function(){var a,j;if(!(b<f)){if(a=d.apply(h,i),a===c.promise())throw new TypeError("Thenable self-resolution");j=a&&("object"==typeof a||"function"==typeof a)&&a.then,r.isFunction(j)?e?j.call(a,g(f,c,N,e),g(f,c,O,e)):(f++,j.call(a,g(f,c,N,e),g(f,c,O,e),g(f,c,N,c.notifyWith))):(d!==N&&(h=void 0,i=[a]),(e||c.resolveWith)(h,i))}},k=e?j:function(){try{j()}catch(a){r.Deferred.exceptionHook&&r.Deferred.exceptionHook(a,k.stackTrace),b+1>=f&&(d!==O&&(h=void 0,i=[a]),c.rejectWith(h,i))}};b?k():(r.Deferred.getStackHook&&(k.stackTrace=r.Deferred.getStackHook()),a.setTimeout(k))}}return r.Deferred(function(a){c[0][3].add(g(0,a,r.isFunction(e)?e:N,a.notifyWith)),c[1][3].add(g(0,a,r.isFunction(b)?b:N)),c[2][3].add(g(0,a,r.isFunction(d)?d:O))}).promise()},promise:function(a){return null!=a?r.extend(a,e):e}},f={};return r.each(c,function(a,b){var g=b[2],h=b[5];e[b[1]]=g.add,h&&g.add(function(){d=h},c[3-a][2].disable,c[0][2].lock),g.add(b[3].fire),f[b[0]]=function(){return f[b[0]+"With"](this===f?void 0:this,arguments),this},f[b[0]+"With"]=g.fireWith}),e.promise(f),b&&b.call(f,f),f},when:function(a){var b=arguments.length,c=b,d=Array(c),e=f.call(arguments),g=r.Deferred(),h=function(a){return function(c){d[a]=this,e[a]=arguments.length>1?f.call(arguments):c,--b||g.resolveWith(d,e)}};if(b<=1&&(P(a,g.done(h(c)).resolve,g.reject,!b),"pending"===g.state()||r.isFunction(e[c]&&e[c].then)))return g.then();while(c--)P(e[c],h(c),g.reject);return g.promise()}});var Q=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;r.Deferred.exceptionHook=function(b,c){a.console&&a.console.warn&&b&&Q.test(b.name)&&a.console.warn("jQuery.Deferred exception: "+b.message,b.stack,c)},r.readyException=function(b){a.setTimeout(function(){throw b})};var R=r.Deferred();r.fn.ready=function(a){return R.then(a)["catch"](function(a){r.readyException(a)}),this},r.extend({isReady:!1,readyWait:1,ready:function(a){(a===!0?--r.readyWait:r.isReady)||(r.isReady=!0,a!==!0&&--r.readyWait>0||R.resolveWith(d,[r]))}}),r.ready.then=R.then;function S(){d.removeEventListener("DOMContentLoaded",S),
+a.removeEventListener("load",S),r.ready()}"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(r.ready):(d.addEventListener("DOMContentLoaded",S),a.addEventListener("load",S));var T=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===r.type(c)){e=!0;for(h in c)T(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,r.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(r(a),c)})),b))for(;h<i;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},U=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function V(){this.expando=r.expando+V.uid++}V.uid=1,V.prototype={cache:function(a){var b=a[this.expando];return b||(b={},U(a)&&(a.nodeType?a[this.expando]=b:Object.defineProperty(a,this.expando,{value:b,configurable:!0}))),b},set:function(a,b,c){var d,e=this.cache(a);if("string"==typeof b)e[r.camelCase(b)]=c;else for(d in b)e[r.camelCase(d)]=b[d];return e},get:function(a,b){return void 0===b?this.cache(a):a[this.expando]&&a[this.expando][r.camelCase(b)]},access:function(a,b,c){return void 0===b||b&&"string"==typeof b&&void 0===c?this.get(a,b):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d=a[this.expando];if(void 0!==d){if(void 0!==b){Array.isArray(b)?b=b.map(r.camelCase):(b=r.camelCase(b),b=b in d?[b]:b.match(L)||[]),c=b.length;while(c--)delete d[b[c]]}(void 0===b||r.isEmptyObject(d))&&(a.nodeType?a[this.expando]=void 0:delete a[this.expando])}},hasData:function(a){var b=a[this.expando];return void 0!==b&&!r.isEmptyObject(b)}};var W=new V,X=new V,Y=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,Z=/[A-Z]/g;function $(a){return"true"===a||"false"!==a&&("null"===a?null:a===+a+""?+a:Y.test(a)?JSON.parse(a):a)}function _(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(Z,"-$&").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c=$(c)}catch(e){}X.set(a,b,c)}else c=void 0;return c}r.extend({hasData:function(a){return X.hasData(a)||W.hasData(a)},data:function(a,b,c){return X.access(a,b,c)},removeData:function(a,b){X.remove(a,b)},_data:function(a,b,c){return W.access(a,b,c)},_removeData:function(a,b){W.remove(a,b)}}),r.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=X.get(f),1===f.nodeType&&!W.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=r.camelCase(d.slice(5)),_(f,d,e[d])));W.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){X.set(this,a)}):T(this,function(b){var c;if(f&&void 0===b){if(c=X.get(f,a),void 0!==c)return c;if(c=_(f,a),void 0!==c)return c}else this.each(function(){X.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){X.remove(this,a)})}}),r.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=W.get(a,b),c&&(!d||Array.isArray(c)?d=W.access(a,b,r.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=r.queue(a,b),d=c.length,e=c.shift(),f=r._queueHooks(a,b),g=function(){r.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return W.get(a,c)||W.access(a,c,{empty:r.Callbacks("once memory").add(function(){W.remove(a,[b+"queue",c])})})}}),r.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?r.queue(this[0],a):void 0===b?this:this.each(function(){var c=r.queue(this,a,b);r._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&r.dequeue(this,a)})},dequeue:function(a){return this.each(function(){r.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=r.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=W.get(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}});var aa=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,ba=new RegExp("^(?:([+-])=|)("+aa+")([a-z%]*)$","i"),ca=["Top","Right","Bottom","Left"],da=function(a,b){return a=b||a,"none"===a.style.display||""===a.style.display&&r.contains(a.ownerDocument,a)&&"none"===r.css(a,"display")},ea=function(a,b,c,d){var e,f,g={};for(f in b)g[f]=a.style[f],a.style[f]=b[f];e=c.apply(a,d||[]);for(f in b)a.style[f]=g[f];return e};function fa(a,b,c,d){var e,f=1,g=20,h=d?function(){return d.cur()}:function(){return r.css(a,b,"")},i=h(),j=c&&c[3]||(r.cssNumber[b]?"":"px"),k=(r.cssNumber[b]||"px"!==j&&+i)&&ba.exec(r.css(a,b));if(k&&k[3]!==j){j=j||k[3],c=c||[],k=+i||1;do f=f||".5",k/=f,r.style(a,b,k+j);while(f!==(f=h()/i)&&1!==f&&--g)}return c&&(k=+k||+i||0,e=c[1]?k+(c[1]+1)*c[2]:+c[2],d&&(d.unit=j,d.start=k,d.end=e)),e}var ga={};function ha(a){var b,c=a.ownerDocument,d=a.nodeName,e=ga[d];return e?e:(b=c.body.appendChild(c.createElement(d)),e=r.css(b,"display"),b.parentNode.removeChild(b),"none"===e&&(e="block"),ga[d]=e,e)}function ia(a,b){for(var c,d,e=[],f=0,g=a.length;f<g;f++)d=a[f],d.style&&(c=d.style.display,b?("none"===c&&(e[f]=W.get(d,"display")||null,e[f]||(d.style.display="")),""===d.style.display&&da(d)&&(e[f]=ha(d))):"none"!==c&&(e[f]="none",W.set(d,"display",c)));for(f=0;f<g;f++)null!=e[f]&&(a[f].style.display=e[f]);return a}r.fn.extend({show:function(){return ia(this,!0)},hide:function(){return ia(this)},toggle:function(a){return"boolean"==typeof a?a?this.show():this.hide():this.each(function(){da(this)?r(this).show():r(this).hide()})}});var ja=/^(?:checkbox|radio)$/i,ka=/<([a-z][^\/\0>\x20\t\r\n\f]+)/i,la=/^$|\/(?:java|ecma)script/i,ma={option:[1,"<select multiple='multiple'>","</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};ma.optgroup=ma.option,ma.tbody=ma.tfoot=ma.colgroup=ma.caption=ma.thead,ma.th=ma.td;function na(a,b){var c;return c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[],void 0===b||b&&B(a,b)?r.merge([a],c):c}function oa(a,b){for(var c=0,d=a.length;c<d;c++)W.set(a[c],"globalEval",!b||W.get(b[c],"globalEval"))}var pa=/<|&#?\w+;/;function qa(a,b,c,d,e){for(var f,g,h,i,j,k,l=b.createDocumentFragment(),m=[],n=0,o=a.length;n<o;n++)if(f=a[n],f||0===f)if("object"===r.type(f))r.merge(m,f.nodeType?[f]:f);else if(pa.test(f)){g=g||l.appendChild(b.createElement("div")),h=(ka.exec(f)||["",""])[1].toLowerCase(),i=ma[h]||ma._default,g.innerHTML=i[1]+r.htmlPrefilter(f)+i[2],k=i[0];while(k--)g=g.lastChild;r.merge(m,g.childNodes),g=l.firstChild,g.textContent=""}else m.push(b.createTextNode(f));l.textContent="",n=0;while(f=m[n++])if(d&&r.inArray(f,d)>-1)e&&e.push(f);else if(j=r.contains(f.ownerDocument,f),g=na(l.appendChild(f),"script"),j&&oa(g),c){k=0;while(f=g[k++])la.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),o.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="<textarea>x</textarea>",o.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var ra=d.documentElement,sa=/^key/,ta=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,ua=/^([^.]*)(?:\.(.+)|)/;function va(){return!0}function wa(){return!1}function xa(){try{return d.activeElement}catch(a){}}function ya(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)ya(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=wa;else if(!e)return a;return 1===f&&(g=e,e=function(a){return r().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=r.guid++)),a.each(function(){r.event.add(this,b,e,d,c)})}r.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=W.get(a);if(q){c.handler&&(f=c,c=f.handler,e=f.selector),e&&r.find.matchesSelector(ra,e),c.guid||(c.guid=r.guid++),(i=q.events)||(i=q.events={}),(g=q.handle)||(g=q.handle=function(b){return"undefined"!=typeof r&&r.event.triggered!==b.type?r.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(L)||[""],j=b.length;while(j--)h=ua.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n&&(l=r.event.special[n]||{},n=(e?l.delegateType:l.bindType)||n,l=r.event.special[n]||{},k=r.extend({type:n,origType:p,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&r.expr.match.needsContext.test(e),namespace:o.join(".")},f),(m=i[n])||(m=i[n]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,o,g)!==!1||a.addEventListener&&a.addEventListener(n,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),r.event.global[n]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=W.hasData(a)&&W.get(a);if(q&&(i=q.events)){b=(b||"").match(L)||[""],j=b.length;while(j--)if(h=ua.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n){l=r.event.special[n]||{},n=(d?l.delegateType:l.bindType)||n,m=i[n]||[],h=h[2]&&new RegExp("(^|\\.)"+o.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&p!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,o,q.handle)!==!1||r.removeEvent(a,n,q.handle),delete i[n])}else for(n in i)r.event.remove(a,n+b[j],c,d,!0);r.isEmptyObject(i)&&W.remove(a,"handle events")}},dispatch:function(a){var b=r.event.fix(a),c,d,e,f,g,h,i=new Array(arguments.length),j=(W.get(this,"events")||{})[b.type]||[],k=r.event.special[b.type]||{};for(i[0]=b,c=1;c<arguments.length;c++)i[c]=arguments[c];if(b.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,b)!==!1){h=r.event.handlers.call(this,b,j),c=0;while((f=h[c++])&&!b.isPropagationStopped()){b.currentTarget=f.elem,d=0;while((g=f.handlers[d++])&&!b.isImmediatePropagationStopped())b.rnamespace&&!b.rnamespace.test(g.namespace)||(b.handleObj=g,b.data=g.data,e=((r.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==e&&(b.result=e)===!1&&(b.preventDefault(),b.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,b),b.result}},handlers:function(a,b){var c,d,e,f,g,h=[],i=b.delegateCount,j=a.target;if(i&&j.nodeType&&!("click"===a.type&&a.button>=1))for(;j!==this;j=j.parentNode||this)if(1===j.nodeType&&("click"!==a.type||j.disabled!==!0)){for(f=[],g={},c=0;c<i;c++)d=b[c],e=d.selector+" ",void 0===g[e]&&(g[e]=d.needsContext?r(e,this).index(j)>-1:r.find(e,this,null,[j]).length),g[e]&&f.push(d);f.length&&h.push({elem:j,handlers:f})}return j=this,i<b.length&&h.push({elem:j,handlers:b.slice(i)}),h},addProp:function(a,b){Object.defineProperty(r.Event.prototype,a,{enumerable:!0,configurable:!0,get:r.isFunction(b)?function(){if(this.originalEvent)return b(this.originalEvent)}:function(){if(this.originalEvent)return this.originalEvent[a]},set:function(b){Object.defineProperty(this,a,{enumerable:!0,configurable:!0,writable:!0,value:b})}})},fix:function(a){return a[r.expando]?a:new r.Event(a)},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==xa()&&this.focus)return this.focus(),!1},delegateType:"focusin"},blur:{trigger:function(){if(this===xa()&&this.blur)return this.blur(),!1},delegateType:"focusout"},click:{trigger:function(){if("checkbox"===this.type&&this.click&&B(this,"input"))return this.click(),!1},_default:function(a){return B(a.target,"a")}},beforeunload:{postDispatch:function(a){void 0!==a.result&&a.originalEvent&&(a.originalEvent.returnValue=a.result)}}}},r.removeEvent=function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c)},r.Event=function(a,b){return this instanceof r.Event?(a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||void 0===a.defaultPrevented&&a.returnValue===!1?va:wa,this.target=a.target&&3===a.target.nodeType?a.target.parentNode:a.target,this.currentTarget=a.currentTarget,this.relatedTarget=a.relatedTarget):this.type=a,b&&r.extend(this,b),this.timeStamp=a&&a.timeStamp||r.now(),void(this[r.expando]=!0)):new r.Event(a,b)},r.Event.prototype={constructor:r.Event,isDefaultPrevented:wa,isPropagationStopped:wa,isImmediatePropagationStopped:wa,isSimulated:!1,preventDefault:function(){var a=this.originalEvent;this.isDefaultPrevented=va,a&&!this.isSimulated&&a.preventDefault()},stopPropagation:function(){var a=this.originalEvent;this.isPropagationStopped=va,a&&!this.isSimulated&&a.stopPropagation()},stopImmediatePropagation:function(){var a=this.originalEvent;this.isImmediatePropagationStopped=va,a&&!this.isSimulated&&a.stopImmediatePropagation(),this.stopPropagation()}},r.each({altKey:!0,bubbles:!0,cancelable:!0,changedTouches:!0,ctrlKey:!0,detail:!0,eventPhase:!0,metaKey:!0,pageX:!0,pageY:!0,shiftKey:!0,view:!0,"char":!0,charCode:!0,key:!0,keyCode:!0,button:!0,buttons:!0,clientX:!0,clientY:!0,offsetX:!0,offsetY:!0,pointerId:!0,pointerType:!0,screenX:!0,screenY:!0,targetTouches:!0,toElement:!0,touches:!0,which:function(a){var b=a.button;return null==a.which&&sa.test(a.type)?null!=a.charCode?a.charCode:a.keyCode:!a.which&&void 0!==b&&ta.test(a.type)?1&b?1:2&b?3:4&b?2:0:a.which}},r.event.addProp),r.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(a,b){r.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c,d=this,e=a.relatedTarget,f=a.handleObj;return e&&(e===d||r.contains(d,e))||(a.type=f.origType,c=f.handler.apply(this,arguments),a.type=b),c}}}),r.fn.extend({on:function(a,b,c,d){return ya(this,a,b,c,d)},one:function(a,b,c,d){return ya(this,a,b,c,d,1)},off:function(a,b,c){var d,e;if(a&&a.preventDefault&&a.handleObj)return d=a.handleObj,r(a.delegateTarget).off(d.namespace?d.origType+"."+d.namespace:d.origType,d.selector,d.handler),this;if("object"==typeof a){for(e in a)this.off(e,b,a[e]);return this}return b!==!1&&"function"!=typeof b||(c=b,b=void 0),c===!1&&(c=wa),this.each(function(){r.event.remove(this,a,c,b)})}});var za=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi,Aa=/<script|<style|<link/i,Ba=/checked\s*(?:[^=]|=\s*.checked.)/i,Ca=/^true\/(.*)/,Da=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;function Ea(a,b){return B(a,"table")&&B(11!==b.nodeType?b:b.firstChild,"tr")?r(">tbody",a)[0]||a:a}function Fa(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function Ga(a){var b=Ca.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Ha(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(W.hasData(a)&&(f=W.access(a),g=W.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;c<d;c++)r.event.add(b,e,j[e][c])}X.hasData(a)&&(h=X.access(a),i=r.extend({},h),X.set(b,i))}}function Ia(a,b){var c=b.nodeName.toLowerCase();"input"===c&&ja.test(a.type)?b.checked=a.checked:"input"!==c&&"textarea"!==c||(b.defaultValue=a.defaultValue)}function Ja(a,b,c,d){b=g.apply([],b);var e,f,h,i,j,k,l=0,m=a.length,n=m-1,q=b[0],s=r.isFunction(q);if(s||m>1&&"string"==typeof q&&!o.checkClone&&Ba.test(q))return a.each(function(e){var f=a.eq(e);s&&(b[0]=q.call(this,e,f.html())),Ja(f,b,c,d)});if(m&&(e=qa(b,a[0].ownerDocument,!1,a,d),f=e.firstChild,1===e.childNodes.length&&(e=f),f||d)){for(h=r.map(na(e,"script"),Fa),i=h.length;l<m;l++)j=e,l!==n&&(j=r.clone(j,!0,!0),i&&r.merge(h,na(j,"script"))),c.call(a[l],j,l);if(i)for(k=h[h.length-1].ownerDocument,r.map(h,Ga),l=0;l<i;l++)j=h[l],la.test(j.type||"")&&!W.access(j,"globalEval")&&r.contains(k,j)&&(j.src?r._evalUrl&&r._evalUrl(j.src):p(j.textContent.replace(Da,""),k))}return a}function Ka(a,b,c){for(var d,e=b?r.filter(b,a):a,f=0;null!=(d=e[f]);f++)c||1!==d.nodeType||r.cleanData(na(d)),d.parentNode&&(c&&r.contains(d.ownerDocument,d)&&oa(na(d,"script")),d.parentNode.removeChild(d));return a}r.extend({htmlPrefilter:function(a){return a.replace(za,"<$1></$2>")},clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=r.contains(a.ownerDocument,a);if(!(o.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||r.isXMLDoc(a)))for(g=na(h),f=na(a),d=0,e=f.length;d<e;d++)Ia(f[d],g[d]);if(b)if(c)for(f=f||na(a),g=g||na(h),d=0,e=f.length;d<e;d++)Ha(f[d],g[d]);else Ha(a,h);return g=na(h,"script"),g.length>0&&oa(g,!i&&na(a,"script")),h},cleanData:function(a){for(var b,c,d,e=r.event.special,f=0;void 0!==(c=a[f]);f++)if(U(c)){if(b=c[W.expando]){if(b.events)for(d in b.events)e[d]?r.event.remove(c,d):r.removeEvent(c,d,b.handle);c[W.expando]=void 0}c[X.expando]&&(c[X.expando]=void 0)}}}),r.fn.extend({detach:function(a){return Ka(this,a,!0)},remove:function(a){return Ka(this,a)},text:function(a){return T(this,function(a){return void 0===a?r.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=a)})},null,a,arguments.length)},append:function(){return Ja(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ea(this,a);b.appendChild(a)}})},prepend:function(){return Ja(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ea(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return Ja(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return Ja(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(r.cleanData(na(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null!=a&&a,b=null==b?a:b,this.map(function(){return r.clone(this,a,b)})},html:function(a){return T(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!Aa.test(a)&&!ma[(ka.exec(a)||["",""])[1].toLowerCase()]){a=r.htmlPrefilter(a);try{for(;c<d;c++)b=this[c]||{},1===b.nodeType&&(r.cleanData(na(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=[];return Ja(this,arguments,function(b){var c=this.parentNode;r.inArray(this,a)<0&&(r.cleanData(na(this)),c&&c.replaceChild(b,this))},a)}}),r.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){r.fn[a]=function(a){for(var c,d=[],e=r(a),f=e.length-1,g=0;g<=f;g++)c=g===f?this:this.clone(!0),r(e[g])[b](c),h.apply(d,c.get());return this.pushStack(d)}});var La=/^margin/,Ma=new RegExp("^("+aa+")(?!px)[a-z%]+$","i"),Na=function(b){var c=b.ownerDocument.defaultView;return c&&c.opener||(c=a),c.getComputedStyle(b)};!function(){function b(){if(i){i.style.cssText="box-sizing:border-box;position:relative;display:block;margin:auto;border:1px;padding:1px;top:1%;width:50%",i.innerHTML="",ra.appendChild(h);var b=a.getComputedStyle(i);c="1%"!==b.top,g="2px"===b.marginLeft,e="4px"===b.width,i.style.marginRight="50%",f="4px"===b.marginRight,ra.removeChild(h),i=null}}var c,e,f,g,h=d.createElement("div"),i=d.createElement("div");i.style&&(i.style.backgroundClip="content-box",i.cloneNode(!0).style.backgroundClip="",o.clearCloneStyle="content-box"===i.style.backgroundClip,h.style.cssText="border:0;width:8px;height:0;top:0;left:-9999px;padding:0;margin-top:1px;position:absolute",h.appendChild(i),r.extend(o,{pixelPosition:function(){return b(),c},boxSizingReliable:function(){return b(),e},pixelMarginRight:function(){return b(),f},reliableMarginLeft:function(){return b(),g}}))}();function Oa(a,b,c){var d,e,f,g,h=a.style;return c=c||Na(a),c&&(g=c.getPropertyValue(b)||c[b],""!==g||r.contains(a.ownerDocument,a)||(g=r.style(a,b)),!o.pixelMarginRight()&&Ma.test(g)&&La.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=g,g=c.width,h.width=d,h.minWidth=e,h.maxWidth=f)),void 0!==g?g+"":g}function Pa(a,b){return{get:function(){return a()?void delete this.get:(this.get=b).apply(this,arguments)}}}var Qa=/^(none|table(?!-c[ea]).+)/,Ra=/^--/,Sa={position:"absolute",visibility:"hidden",display:"block"},Ta={letterSpacing:"0",fontWeight:"400"},Ua=["Webkit","Moz","ms"],Va=d.createElement("div").style;function Wa(a){if(a in Va)return a;var b=a[0].toUpperCase()+a.slice(1),c=Ua.length;while(c--)if(a=Ua[c]+b,a in Va)return a}function Xa(a){var b=r.cssProps[a];return b||(b=r.cssProps[a]=Wa(a)||a),b}function Ya(a,b,c){var d=ba.exec(b);return d?Math.max(0,d[2]-(c||0))+(d[3]||"px"):b}function Za(a,b,c,d,e){var f,g=0;for(f=c===(d?"border":"content")?4:"width"===b?1:0;f<4;f+=2)"margin"===c&&(g+=r.css(a,c+ca[f],!0,e)),d?("content"===c&&(g-=r.css(a,"padding"+ca[f],!0,e)),"margin"!==c&&(g-=r.css(a,"border"+ca[f]+"Width",!0,e))):(g+=r.css(a,"padding"+ca[f],!0,e),"padding"!==c&&(g+=r.css(a,"border"+ca[f]+"Width",!0,e)));return g}function $a(a,b,c){var d,e=Na(a),f=Oa(a,b,e),g="border-box"===r.css(a,"boxSizing",!1,e);return Ma.test(f)?f:(d=g&&(o.boxSizingReliable()||f===a.style[b]),"auto"===f&&(f=a["offset"+b[0].toUpperCase()+b.slice(1)]),f=parseFloat(f)||0,f+Za(a,b,c||(g?"border":"content"),d,e)+"px")}r.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=Oa(a,"opacity");return""===c?"1":c}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":"cssFloat"},style:function(a,b,c,d){if(a&&3!==a.nodeType&&8!==a.nodeType&&a.style){var e,f,g,h=r.camelCase(b),i=Ra.test(b),j=a.style;return i||(b=Xa(h)),g=r.cssHooks[b]||r.cssHooks[h],void 0===c?g&&"get"in g&&void 0!==(e=g.get(a,!1,d))?e:j[b]:(f=typeof c,"string"===f&&(e=ba.exec(c))&&e[1]&&(c=fa(a,b,e),f="number"),null!=c&&c===c&&("number"===f&&(c+=e&&e[3]||(r.cssNumber[h]?"":"px")),o.clearCloneStyle||""!==c||0!==b.indexOf("background")||(j[b]="inherit"),g&&"set"in g&&void 0===(c=g.set(a,c,d))||(i?j.setProperty(b,c):j[b]=c)),void 0)}},css:function(a,b,c,d){var e,f,g,h=r.camelCase(b),i=Ra.test(b);return i||(b=Xa(h)),g=r.cssHooks[b]||r.cssHooks[h],g&&"get"in g&&(e=g.get(a,!0,c)),void 0===e&&(e=Oa(a,b,d)),"normal"===e&&b in Ta&&(e=Ta[b]),""===c||c?(f=parseFloat(e),c===!0||isFinite(f)?f||0:e):e}}),r.each(["height","width"],function(a,b){r.cssHooks[b]={get:function(a,c,d){if(c)return!Qa.test(r.css(a,"display"))||a.getClientRects().length&&a.getBoundingClientRect().width?$a(a,b,d):ea(a,Sa,function(){return $a(a,b,d)})},set:function(a,c,d){var e,f=d&&Na(a),g=d&&Za(a,b,d,"border-box"===r.css(a,"boxSizing",!1,f),f);return g&&(e=ba.exec(c))&&"px"!==(e[3]||"px")&&(a.style[b]=c,c=r.css(a,b)),Ya(a,c,g)}}}),r.cssHooks.marginLeft=Pa(o.reliableMarginLeft,function(a,b){if(b)return(parseFloat(Oa(a,"marginLeft"))||a.getBoundingClientRect().left-ea(a,{marginLeft:0},function(){return a.getBoundingClientRect().left}))+"px"}),r.each({margin:"",padding:"",border:"Width"},function(a,b){r.cssHooks[a+b]={expand:function(c){for(var d=0,e={},f="string"==typeof c?c.split(" "):[c];d<4;d++)e[a+ca[d]+b]=f[d]||f[d-2]||f[0];return e}},La.test(a)||(r.cssHooks[a+b].set=Ya)}),r.fn.extend({css:function(a,b){return T(this,function(a,b,c){var d,e,f={},g=0;if(Array.isArray(b)){for(d=Na(a),e=b.length;g<e;g++)f[b[g]]=r.css(a,b[g],!1,d);return f}return void 0!==c?r.style(a,b,c):r.css(a,b)},a,b,arguments.length>1)}});function _a(a,b,c,d,e){return new _a.prototype.init(a,b,c,d,e)}r.Tween=_a,_a.prototype={constructor:_a,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||r.easing._default,this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(r.cssNumber[c]?"":"px")},cur:function(){var a=_a.propHooks[this.prop];return a&&a.get?a.get(this):_a.propHooks._default.get(this)},run:function(a){var b,c=_a.propHooks[this.prop];return this.options.duration?this.pos=b=r.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):_a.propHooks._default.set(this),this}},_a.prototype.init.prototype=_a.prototype,_a.propHooks={_default:{get:function(a){var b;return 1!==a.elem.nodeType||null!=a.elem[a.prop]&&null==a.elem.style[a.prop]?a.elem[a.prop]:(b=r.css(a.elem,a.prop,""),b&&"auto"!==b?b:0)},set:function(a){r.fx.step[a.prop]?r.fx.step[a.prop](a):1!==a.elem.nodeType||null==a.elem.style[r.cssProps[a.prop]]&&!r.cssHooks[a.prop]?a.elem[a.prop]=a.now:r.style(a.elem,a.prop,a.now+a.unit)}}},_a.propHooks.scrollTop=_a.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},r.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2},_default:"swing"},r.fx=_a.prototype.init,r.fx.step={};var ab,bb,cb=/^(?:toggle|show|hide)$/,db=/queueHooks$/;function eb(){bb&&(d.hidden===!1&&a.requestAnimationFrame?a.requestAnimationFrame(eb):a.setTimeout(eb,r.fx.interval),r.fx.tick())}function fb(){return a.setTimeout(function(){ab=void 0}),ab=r.now()}function gb(a,b){var c,d=0,e={height:a};for(b=b?1:0;d<4;d+=2-b)c=ca[d],e["margin"+c]=e["padding"+c]=a;return b&&(e.opacity=e.width=a),e}function hb(a,b,c){for(var d,e=(kb.tweeners[b]||[]).concat(kb.tweeners["*"]),f=0,g=e.length;f<g;f++)if(d=e[f].call(c,b,a))return d}function ib(a,b,c){var d,e,f,g,h,i,j,k,l="width"in b||"height"in b,m=this,n={},o=a.style,p=a.nodeType&&da(a),q=W.get(a,"fxshow");c.queue||(g=r._queueHooks(a,"fx"),null==g.unqueued&&(g.unqueued=0,h=g.empty.fire,g.empty.fire=function(){g.unqueued||h()}),g.unqueued++,m.always(function(){m.always(function(){g.unqueued--,r.queue(a,"fx").length||g.empty.fire()})}));for(d in b)if(e=b[d],cb.test(e)){if(delete b[d],f=f||"toggle"===e,e===(p?"hide":"show")){if("show"!==e||!q||void 0===q[d])continue;p=!0}n[d]=q&&q[d]||r.style(a,d)}if(i=!r.isEmptyObject(b),i||!r.isEmptyObject(n)){l&&1===a.nodeType&&(c.overflow=[o.overflow,o.overflowX,o.overflowY],j=q&&q.display,null==j&&(j=W.get(a,"display")),k=r.css(a,"display"),"none"===k&&(j?k=j:(ia([a],!0),j=a.style.display||j,k=r.css(a,"display"),ia([a]))),("inline"===k||"inline-block"===k&&null!=j)&&"none"===r.css(a,"float")&&(i||(m.done(function(){o.display=j}),null==j&&(k=o.display,j="none"===k?"":k)),o.display="inline-block")),c.overflow&&(o.overflow="hidden",m.always(function(){o.overflow=c.overflow[0],o.overflowX=c.overflow[1],o.overflowY=c.overflow[2]})),i=!1;for(d in n)i||(q?"hidden"in q&&(p=q.hidden):q=W.access(a,"fxshow",{display:j}),f&&(q.hidden=!p),p&&ia([a],!0),m.done(function(){p||ia([a]),W.remove(a,"fxshow");for(d in n)r.style(a,d,n[d])})),i=hb(p?q[d]:0,d,m),d in q||(q[d]=i.start,p&&(i.end=i.start,i.start=0))}}function jb(a,b){var c,d,e,f,g;for(c in a)if(d=r.camelCase(c),e=b[d],f=a[c],Array.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=r.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function kb(a,b,c){var d,e,f=0,g=kb.prefilters.length,h=r.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=ab||fb(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;g<i;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),f<1&&i?c:(i||h.notifyWith(a,[j,1,0]),h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:r.extend({},b),opts:r.extend(!0,{specialEasing:{},easing:r.easing._default},c),originalProperties:b,originalOptions:c,startTime:ab||fb(),duration:c.duration,tweens:[],createTween:function(b,c){var d=r.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;c<d;c++)j.tweens[c].run(1);return b?(h.notifyWith(a,[j,1,0]),h.resolveWith(a,[j,b])):h.rejectWith(a,[j,b]),this}}),k=j.props;for(jb(k,j.opts.specialEasing);f<g;f++)if(d=kb.prefilters[f].call(j,a,k,j.opts))return r.isFunction(d.stop)&&(r._queueHooks(j.elem,j.opts.queue).stop=r.proxy(d.stop,d)),d;return r.map(k,hb,j),r.isFunction(j.opts.start)&&j.opts.start.call(a,j),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always),r.fx.timer(r.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j}r.Animation=r.extend(kb,{tweeners:{"*":[function(a,b){var c=this.createTween(a,b);return fa(c.elem,a,ba.exec(b),c),c}]},tweener:function(a,b){r.isFunction(a)?(b=a,a=["*"]):a=a.match(L);for(var c,d=0,e=a.length;d<e;d++)c=a[d],kb.tweeners[c]=kb.tweeners[c]||[],kb.tweeners[c].unshift(b)},prefilters:[ib],prefilter:function(a,b){b?kb.prefilters.unshift(a):kb.prefilters.push(a)}}),r.speed=function(a,b,c){var d=a&&"object"==typeof a?r.extend({},a):{complete:c||!c&&b||r.isFunction(a)&&a,duration:a,easing:c&&b||b&&!r.isFunction(b)&&b};return r.fx.off?d.duration=0:"number"!=typeof d.duration&&(d.duration in r.fx.speeds?d.duration=r.fx.speeds[d.duration]:d.duration=r.fx.speeds._default),null!=d.queue&&d.queue!==!0||(d.queue="fx"),d.old=d.complete,d.complete=function(){r.isFunction(d.old)&&d.old.call(this),d.queue&&r.dequeue(this,d.queue)},d},r.fn.extend({fadeTo:function(a,b,c,d){return this.filter(da).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=r.isEmptyObject(a),f=r.speed(b,c,d),g=function(){var b=kb(this,r.extend({},a),f);(e||W.get(this,"finish"))&&b.stop(!0)};return g.finish=g,e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,b,c){var d=function(a){var b=a.stop;delete a.stop,b(c)};return"string"!=typeof a&&(c=b,b=a,a=void 0),b&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,e=null!=a&&a+"queueHooks",f=r.timers,g=W.get(this);if(e)g[e]&&g[e].stop&&d(g[e]);else for(e in g)g[e]&&g[e].stop&&db.test(e)&&d(g[e]);for(e=f.length;e--;)f[e].elem!==this||null!=a&&f[e].queue!==a||(f[e].anim.stop(c),b=!1,f.splice(e,1));!b&&c||r.dequeue(this,a)})},finish:function(a){return a!==!1&&(a=a||"fx"),this.each(function(){var b,c=W.get(this),d=c[a+"queue"],e=c[a+"queueHooks"],f=r.timers,g=d?d.length:0;for(c.finish=!0,r.queue(this,a,[]),e&&e.stop&&e.stop.call(this,!0),b=f.length;b--;)f[b].elem===this&&f[b].queue===a&&(f[b].anim.stop(!0),f.splice(b,1));for(b=0;b<g;b++)d[b]&&d[b].finish&&d[b].finish.call(this);delete c.finish})}}),r.each(["toggle","show","hide"],function(a,b){var c=r.fn[b];r.fn[b]=function(a,d,e){return null==a||"boolean"==typeof a?c.apply(this,arguments):this.animate(gb(b,!0),a,d,e)}}),r.each({slideDown:gb("show"),slideUp:gb("hide"),slideToggle:gb("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){r.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),r.timers=[],r.fx.tick=function(){var a,b=0,c=r.timers;for(ab=r.now();b<c.length;b++)a=c[b],a()||c[b]!==a||c.splice(b--,1);c.length||r.fx.stop(),ab=void 0},r.fx.timer=function(a){r.timers.push(a),r.fx.start()},r.fx.interval=13,r.fx.start=function(){bb||(bb=!0,eb())},r.fx.stop=function(){bb=null},r.fx.speeds={slow:600,fast:200,_default:400},r.fn.delay=function(b,c){return b=r.fx?r.fx.speeds[b]||b:b,c=c||"fx",this.queue(c,function(c,d){var e=a.setTimeout(c,b);d.stop=function(){a.clearTimeout(e)}})},function(){var a=d.createElement("input"),b=d.createElement("select"),c=b.appendChild(d.createElement("option"));a.type="checkbox",o.checkOn=""!==a.value,o.optSelected=c.selected,a=d.createElement("input"),a.value="t",a.type="radio",o.radioValue="t"===a.value}();var lb,mb=r.expr.attrHandle;r.fn.extend({attr:function(a,b){return T(this,r.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){r.removeAttr(this,a)})}}),r.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return"undefined"==typeof a.getAttribute?r.prop(a,b,c):(1===f&&r.isXMLDoc(a)||(e=r.attrHooks[b.toLowerCase()]||(r.expr.match.bool.test(b)?lb:void 0)),void 0!==c?null===c?void r.removeAttr(a,b):e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:(a.setAttribute(b,c+""),c):e&&"get"in e&&null!==(d=e.get(a,b))?d:(d=r.find.attr(a,b),
+null==d?void 0:d))},attrHooks:{type:{set:function(a,b){if(!o.radioValue&&"radio"===b&&B(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}},removeAttr:function(a,b){var c,d=0,e=b&&b.match(L);if(e&&1===a.nodeType)while(c=e[d++])a.removeAttribute(c)}}),lb={set:function(a,b,c){return b===!1?r.removeAttr(a,c):a.setAttribute(c,c),c}},r.each(r.expr.match.bool.source.match(/\w+/g),function(a,b){var c=mb[b]||r.find.attr;mb[b]=function(a,b,d){var e,f,g=b.toLowerCase();return d||(f=mb[g],mb[g]=e,e=null!=c(a,b,d)?g:null,mb[g]=f),e}});var nb=/^(?:input|select|textarea|button)$/i,ob=/^(?:a|area)$/i;r.fn.extend({prop:function(a,b){return T(this,r.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[r.propFix[a]||a]})}}),r.extend({prop:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return 1===f&&r.isXMLDoc(a)||(b=r.propFix[b]||b,e=r.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=r.find.attr(a,"tabindex");return b?parseInt(b,10):nb.test(a.nodeName)||ob.test(a.nodeName)&&a.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),o.optSelected||(r.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null},set:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}}),r.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){r.propFix[this.toLowerCase()]=this});function pb(a){var b=a.match(L)||[];return b.join(" ")}function qb(a){return a.getAttribute&&a.getAttribute("class")||""}r.fn.extend({addClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).addClass(a.call(this,b,qb(this)))});if("string"==typeof a&&a){b=a.match(L)||[];while(c=this[i++])if(e=qb(c),d=1===c.nodeType&&" "+pb(e)+" "){g=0;while(f=b[g++])d.indexOf(" "+f+" ")<0&&(d+=f+" ");h=pb(d),e!==h&&c.setAttribute("class",h)}}return this},removeClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).removeClass(a.call(this,b,qb(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof a&&a){b=a.match(L)||[];while(c=this[i++])if(e=qb(c),d=1===c.nodeType&&" "+pb(e)+" "){g=0;while(f=b[g++])while(d.indexOf(" "+f+" ")>-1)d=d.replace(" "+f+" "," ");h=pb(d),e!==h&&c.setAttribute("class",h)}}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):r.isFunction(a)?this.each(function(c){r(this).toggleClass(a.call(this,c,qb(this),b),b)}):this.each(function(){var b,d,e,f;if("string"===c){d=0,e=r(this),f=a.match(L)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else void 0!==a&&"boolean"!==c||(b=qb(this),b&&W.set(this,"__className__",b),this.setAttribute&&this.setAttribute("class",b||a===!1?"":W.get(this,"__className__")||""))})},hasClass:function(a){var b,c,d=0;b=" "+a+" ";while(c=this[d++])if(1===c.nodeType&&(" "+pb(qb(c))+" ").indexOf(b)>-1)return!0;return!1}});var rb=/\r/g;r.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=r.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,r(this).val()):a,null==e?e="":"number"==typeof e?e+="":Array.isArray(e)&&(e=r.map(e,function(a){return null==a?"":a+""})),b=r.valHooks[this.type]||r.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=r.valHooks[e.type]||r.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(rb,""):null==c?"":c)}}}),r.extend({valHooks:{option:{get:function(a){var b=r.find.attr(a,"value");return null!=b?b:pb(r.text(a))}},select:{get:function(a){var b,c,d,e=a.options,f=a.selectedIndex,g="select-one"===a.type,h=g?null:[],i=g?f+1:e.length;for(d=f<0?i:g?f:0;d<i;d++)if(c=e[d],(c.selected||d===f)&&!c.disabled&&(!c.parentNode.disabled||!B(c.parentNode,"optgroup"))){if(b=r(c).val(),g)return b;h.push(b)}return h},set:function(a,b){var c,d,e=a.options,f=r.makeArray(b),g=e.length;while(g--)d=e[g],(d.selected=r.inArray(r.valHooks.option.get(d),f)>-1)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),r.each(["radio","checkbox"],function(){r.valHooks[this]={set:function(a,b){if(Array.isArray(b))return a.checked=r.inArray(r(a).val(),b)>-1}},o.checkOn||(r.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var sb=/^(?:focusinfocus|focusoutblur)$/;r.extend(r.event,{trigger:function(b,c,e,f){var g,h,i,j,k,m,n,o=[e||d],p=l.call(b,"type")?b.type:b,q=l.call(b,"namespace")?b.namespace.split("."):[];if(h=i=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!sb.test(p+r.event.triggered)&&(p.indexOf(".")>-1&&(q=p.split("."),p=q.shift(),q.sort()),k=p.indexOf(":")<0&&"on"+p,b=b[r.expando]?b:new r.Event(p,"object"==typeof b&&b),b.isTrigger=f?2:3,b.namespace=q.join("."),b.rnamespace=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=e),c=null==c?[b]:r.makeArray(c,[b]),n=r.event.special[p]||{},f||!n.trigger||n.trigger.apply(e,c)!==!1)){if(!f&&!n.noBubble&&!r.isWindow(e)){for(j=n.delegateType||p,sb.test(j+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),i=h;i===(e.ownerDocument||d)&&o.push(i.defaultView||i.parentWindow||a)}g=0;while((h=o[g++])&&!b.isPropagationStopped())b.type=g>1?j:n.bindType||p,m=(W.get(h,"events")||{})[b.type]&&W.get(h,"handle"),m&&m.apply(h,c),m=k&&h[k],m&&m.apply&&U(h)&&(b.result=m.apply(h,c),b.result===!1&&b.preventDefault());return b.type=p,f||b.isDefaultPrevented()||n._default&&n._default.apply(o.pop(),c)!==!1||!U(e)||k&&r.isFunction(e[p])&&!r.isWindow(e)&&(i=e[k],i&&(e[k]=null),r.event.triggered=p,e[p](),r.event.triggered=void 0,i&&(e[k]=i)),b.result}},simulate:function(a,b,c){var d=r.extend(new r.Event,c,{type:a,isSimulated:!0});r.event.trigger(d,null,b)}}),r.fn.extend({trigger:function(a,b){return this.each(function(){r.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];if(c)return r.event.trigger(a,b,c,!0)}}),r.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(a,b){r.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),r.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),o.focusin="onfocusin"in a,o.focusin||r.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){r.event.simulate(b,a.target,r.event.fix(a))};r.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=W.access(d,b);e||d.addEventListener(a,c,!0),W.access(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=W.access(d,b)-1;e?W.access(d,b,e):(d.removeEventListener(a,c,!0),W.remove(d,b))}}});var tb=a.location,ub=r.now(),vb=/\?/;r.parseXML=function(b){var c;if(!b||"string"!=typeof b)return null;try{c=(new a.DOMParser).parseFromString(b,"text/xml")}catch(d){c=void 0}return c&&!c.getElementsByTagName("parsererror").length||r.error("Invalid XML: "+b),c};var wb=/\[\]$/,xb=/\r?\n/g,yb=/^(?:submit|button|image|reset|file)$/i,zb=/^(?:input|select|textarea|keygen)/i;function Ab(a,b,c,d){var e;if(Array.isArray(b))r.each(b,function(b,e){c||wb.test(a)?d(a,e):Ab(a+"["+("object"==typeof e&&null!=e?b:"")+"]",e,c,d)});else if(c||"object"!==r.type(b))d(a,b);else for(e in b)Ab(a+"["+e+"]",b[e],c,d)}r.param=function(a,b){var c,d=[],e=function(a,b){var c=r.isFunction(b)?b():b;d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(null==c?"":c)};if(Array.isArray(a)||a.jquery&&!r.isPlainObject(a))r.each(a,function(){e(this.name,this.value)});else for(c in a)Ab(c,a[c],b,e);return d.join("&")},r.fn.extend({serialize:function(){return r.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=r.prop(this,"elements");return a?r.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!r(this).is(":disabled")&&zb.test(this.nodeName)&&!yb.test(a)&&(this.checked||!ja.test(a))}).map(function(a,b){var c=r(this).val();return null==c?null:Array.isArray(c)?r.map(c,function(a){return{name:b.name,value:a.replace(xb,"\r\n")}}):{name:b.name,value:c.replace(xb,"\r\n")}}).get()}});var Bb=/%20/g,Cb=/#.*$/,Db=/([?&])_=[^&]*/,Eb=/^(.*?):[ \t]*([^\r\n]*)$/gm,Fb=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Gb=/^(?:GET|HEAD)$/,Hb=/^\/\//,Ib={},Jb={},Kb="*/".concat("*"),Lb=d.createElement("a");Lb.href=tb.href;function Mb(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(L)||[];if(r.isFunction(c))while(d=f[e++])"+"===d[0]?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Nb(a,b,c,d){var e={},f=a===Jb;function g(h){var i;return e[h]=!0,r.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Ob(a,b){var c,d,e=r.ajaxSettings.flatOptions||{};for(c in b)void 0!==b[c]&&((e[c]?a:d||(d={}))[c]=b[c]);return d&&r.extend(!0,a,d),a}function Pb(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===d&&(d=a.mimeType||b.getResponseHeader("Content-Type"));if(d)for(e in h)if(h[e]&&h[e].test(d)){i.unshift(e);break}if(i[0]in c)f=i[0];else{for(e in c){if(!i[0]||a.converters[e+" "+i[0]]){f=e;break}g||(g=e)}f=f||g}if(f)return f!==i[0]&&i.unshift(f),c[f]}function Qb(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}r.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:tb.href,type:"GET",isLocal:Fb.test(tb.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Kb,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":r.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Ob(Ob(a,r.ajaxSettings),b):Ob(r.ajaxSettings,a)},ajaxPrefilter:Mb(Ib),ajaxTransport:Mb(Jb),ajax:function(b,c){"object"==typeof b&&(c=b,b=void 0),c=c||{};var e,f,g,h,i,j,k,l,m,n,o=r.ajaxSetup({},c),p=o.context||o,q=o.context&&(p.nodeType||p.jquery)?r(p):r.event,s=r.Deferred(),t=r.Callbacks("once memory"),u=o.statusCode||{},v={},w={},x="canceled",y={readyState:0,getResponseHeader:function(a){var b;if(k){if(!h){h={};while(b=Eb.exec(g))h[b[1].toLowerCase()]=b[2]}b=h[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return k?g:null},setRequestHeader:function(a,b){return null==k&&(a=w[a.toLowerCase()]=w[a.toLowerCase()]||a,v[a]=b),this},overrideMimeType:function(a){return null==k&&(o.mimeType=a),this},statusCode:function(a){var b;if(a)if(k)y.always(a[y.status]);else for(b in a)u[b]=[u[b],a[b]];return this},abort:function(a){var b=a||x;return e&&e.abort(b),A(0,b),this}};if(s.promise(y),o.url=((b||o.url||tb.href)+"").replace(Hb,tb.protocol+"//"),o.type=c.method||c.type||o.method||o.type,o.dataTypes=(o.dataType||"*").toLowerCase().match(L)||[""],null==o.crossDomain){j=d.createElement("a");try{j.href=o.url,j.href=j.href,o.crossDomain=Lb.protocol+"//"+Lb.host!=j.protocol+"//"+j.host}catch(z){o.crossDomain=!0}}if(o.data&&o.processData&&"string"!=typeof o.data&&(o.data=r.param(o.data,o.traditional)),Nb(Ib,o,c,y),k)return y;l=r.event&&o.global,l&&0===r.active++&&r.event.trigger("ajaxStart"),o.type=o.type.toUpperCase(),o.hasContent=!Gb.test(o.type),f=o.url.replace(Cb,""),o.hasContent?o.data&&o.processData&&0===(o.contentType||"").indexOf("application/x-www-form-urlencoded")&&(o.data=o.data.replace(Bb,"+")):(n=o.url.slice(f.length),o.data&&(f+=(vb.test(f)?"&":"?")+o.data,delete o.data),o.cache===!1&&(f=f.replace(Db,"$1"),n=(vb.test(f)?"&":"?")+"_="+ub++ +n),o.url=f+n),o.ifModified&&(r.lastModified[f]&&y.setRequestHeader("If-Modified-Since",r.lastModified[f]),r.etag[f]&&y.setRequestHeader("If-None-Match",r.etag[f])),(o.data&&o.hasContent&&o.contentType!==!1||c.contentType)&&y.setRequestHeader("Content-Type",o.contentType),y.setRequestHeader("Accept",o.dataTypes[0]&&o.accepts[o.dataTypes[0]]?o.accepts[o.dataTypes[0]]+("*"!==o.dataTypes[0]?", "+Kb+"; q=0.01":""):o.accepts["*"]);for(m in o.headers)y.setRequestHeader(m,o.headers[m]);if(o.beforeSend&&(o.beforeSend.call(p,y,o)===!1||k))return y.abort();if(x="abort",t.add(o.complete),y.done(o.success),y.fail(o.error),e=Nb(Jb,o,c,y)){if(y.readyState=1,l&&q.trigger("ajaxSend",[y,o]),k)return y;o.async&&o.timeout>0&&(i=a.setTimeout(function(){y.abort("timeout")},o.timeout));try{k=!1,e.send(v,A)}catch(z){if(k)throw z;A(-1,z)}}else A(-1,"No Transport");function A(b,c,d,h){var j,m,n,v,w,x=c;k||(k=!0,i&&a.clearTimeout(i),e=void 0,g=h||"",y.readyState=b>0?4:0,j=b>=200&&b<300||304===b,d&&(v=Pb(o,y,d)),v=Qb(o,v,y,j),j?(o.ifModified&&(w=y.getResponseHeader("Last-Modified"),w&&(r.lastModified[f]=w),w=y.getResponseHeader("etag"),w&&(r.etag[f]=w)),204===b||"HEAD"===o.type?x="nocontent":304===b?x="notmodified":(x=v.state,m=v.data,n=v.error,j=!n)):(n=x,!b&&x||(x="error",b<0&&(b=0))),y.status=b,y.statusText=(c||x)+"",j?s.resolveWith(p,[m,x,y]):s.rejectWith(p,[y,x,n]),y.statusCode(u),u=void 0,l&&q.trigger(j?"ajaxSuccess":"ajaxError",[y,o,j?m:n]),t.fireWith(p,[y,x]),l&&(q.trigger("ajaxComplete",[y,o]),--r.active||r.event.trigger("ajaxStop")))}return y},getJSON:function(a,b,c){return r.get(a,b,c,"json")},getScript:function(a,b){return r.get(a,void 0,b,"script")}}),r.each(["get","post"],function(a,b){r[b]=function(a,c,d,e){return r.isFunction(c)&&(e=e||d,d=c,c=void 0),r.ajax(r.extend({url:a,type:b,dataType:e,data:c,success:d},r.isPlainObject(a)&&a))}}),r._evalUrl=function(a){return r.ajax({url:a,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},r.fn.extend({wrapAll:function(a){var b;return this[0]&&(r.isFunction(a)&&(a=a.call(this[0])),b=r(a,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstElementChild)a=a.firstElementChild;return a}).append(this)),this},wrapInner:function(a){return r.isFunction(a)?this.each(function(b){r(this).wrapInner(a.call(this,b))}):this.each(function(){var b=r(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=r.isFunction(a);return this.each(function(c){r(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(a){return this.parent(a).not("body").each(function(){r(this).replaceWith(this.childNodes)}),this}}),r.expr.pseudos.hidden=function(a){return!r.expr.pseudos.visible(a)},r.expr.pseudos.visible=function(a){return!!(a.offsetWidth||a.offsetHeight||a.getClientRects().length)},r.ajaxSettings.xhr=function(){try{return new a.XMLHttpRequest}catch(b){}};var Rb={0:200,1223:204},Sb=r.ajaxSettings.xhr();o.cors=!!Sb&&"withCredentials"in Sb,o.ajax=Sb=!!Sb,r.ajaxTransport(function(b){var c,d;if(o.cors||Sb&&!b.crossDomain)return{send:function(e,f){var g,h=b.xhr();if(h.open(b.type,b.url,b.async,b.username,b.password),b.xhrFields)for(g in b.xhrFields)h[g]=b.xhrFields[g];b.mimeType&&h.overrideMimeType&&h.overrideMimeType(b.mimeType),b.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest");for(g in e)h.setRequestHeader(g,e[g]);c=function(a){return function(){c&&(c=d=h.onload=h.onerror=h.onabort=h.onreadystatechange=null,"abort"===a?h.abort():"error"===a?"number"!=typeof h.status?f(0,"error"):f(h.status,h.statusText):f(Rb[h.status]||h.status,h.statusText,"text"!==(h.responseType||"text")||"string"!=typeof h.responseText?{binary:h.response}:{text:h.responseText},h.getAllResponseHeaders()))}},h.onload=c(),d=h.onerror=c("error"),void 0!==h.onabort?h.onabort=d:h.onreadystatechange=function(){4===h.readyState&&a.setTimeout(function(){c&&d()})},c=c("abort");try{h.send(b.hasContent&&b.data||null)}catch(i){if(c)throw i}},abort:function(){c&&c()}}}),r.ajaxPrefilter(function(a){a.crossDomain&&(a.contents.script=!1)}),r.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(a){return r.globalEval(a),a}}}),r.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET")}),r.ajaxTransport("script",function(a){if(a.crossDomain){var b,c;return{send:function(e,f){b=r("<script>").prop({charset:a.scriptCharset,src:a.url}).on("load error",c=function(a){b.remove(),c=null,a&&f("error"===a.type?404:200,a.type)}),d.head.appendChild(b[0])},abort:function(){c&&c()}}}});var Tb=[],Ub=/(=)\?(?=&|$)|\?\?/;r.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=Tb.pop()||r.expando+"_"+ub++;return this[a]=!0,a}}),r.ajaxPrefilter("json jsonp",function(b,c,d){var e,f,g,h=b.jsonp!==!1&&(Ub.test(b.url)?"url":"string"==typeof b.data&&0===(b.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ub.test(b.data)&&"data");if(h||"jsonp"===b.dataTypes[0])return e=b.jsonpCallback=r.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,h?b[h]=b[h].replace(Ub,"$1"+e):b.jsonp!==!1&&(b.url+=(vb.test(b.url)?"&":"?")+b.jsonp+"="+e),b.converters["script json"]=function(){return g||r.error(e+" was not called"),g[0]},b.dataTypes[0]="json",f=a[e],a[e]=function(){g=arguments},d.always(function(){void 0===f?r(a).removeProp(e):a[e]=f,b[e]&&(b.jsonpCallback=c.jsonpCallback,Tb.push(e)),g&&r.isFunction(f)&&f(g[0]),g=f=void 0}),"script"}),o.createHTMLDocument=function(){var a=d.implementation.createHTMLDocument("").body;return a.innerHTML="<form></form><form></form>",2===a.childNodes.length}(),r.parseHTML=function(a,b,c){if("string"!=typeof a)return[];"boolean"==typeof b&&(c=b,b=!1);var e,f,g;return b||(o.createHTMLDocument?(b=d.implementation.createHTMLDocument(""),e=b.createElement("base"),e.href=d.location.href,b.head.appendChild(e)):b=d),f=C.exec(a),g=!c&&[],f?[b.createElement(f[1])]:(f=qa([a],b,g),g&&g.length&&r(g).remove(),r.merge([],f.childNodes))},r.fn.load=function(a,b,c){var d,e,f,g=this,h=a.indexOf(" ");return h>-1&&(d=pb(a.slice(h)),a=a.slice(0,h)),r.isFunction(b)?(c=b,b=void 0):b&&"object"==typeof b&&(e="POST"),g.length>0&&r.ajax({url:a,type:e||"GET",dataType:"html",data:b}).done(function(a){f=arguments,g.html(d?r("<div>").append(r.parseHTML(a)).find(d):a)}).always(c&&function(a,b){g.each(function(){c.apply(this,f||[a.responseText,b,a])})}),this},r.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(a,b){r.fn[b]=function(a){return this.on(b,a)}}),r.expr.pseudos.animated=function(a){return r.grep(r.timers,function(b){return a===b.elem}).length},r.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=r.css(a,"position"),l=r(a),m={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=r.css(a,"top"),i=r.css(a,"left"),j=("absolute"===k||"fixed"===k)&&(f+i).indexOf("auto")>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),r.isFunction(b)&&(b=b.call(a,c,r.extend({},h))),null!=b.top&&(m.top=b.top-h.top+g),null!=b.left&&(m.left=b.left-h.left+e),"using"in b?b.using.call(a,m):l.css(m)}},r.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){r.offset.setOffset(this,a,b)});var b,c,d,e,f=this[0];if(f)return f.getClientRects().length?(d=f.getBoundingClientRect(),b=f.ownerDocument,c=b.documentElement,e=b.defaultView,{top:d.top+e.pageYOffset-c.clientTop,left:d.left+e.pageXOffset-c.clientLeft}):{top:0,left:0}},position:function(){if(this[0]){var a,b,c=this[0],d={top:0,left:0};return"fixed"===r.css(c,"position")?b=c.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),B(a[0],"html")||(d=a.offset()),d={top:d.top+r.css(a[0],"borderTopWidth",!0),left:d.left+r.css(a[0],"borderLeftWidth",!0)}),{top:b.top-d.top-r.css(c,"marginTop",!0),left:b.left-d.left-r.css(c,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent;while(a&&"static"===r.css(a,"position"))a=a.offsetParent;return a||ra})}}),r.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,b){var c="pageYOffset"===b;r.fn[a]=function(d){return T(this,function(a,d,e){var f;return r.isWindow(a)?f=a:9===a.nodeType&&(f=a.defaultView),void 0===e?f?f[b]:a[d]:void(f?f.scrollTo(c?f.pageXOffset:e,c?e:f.pageYOffset):a[d]=e)},a,d,arguments.length)}}),r.each(["top","left"],function(a,b){r.cssHooks[b]=Pa(o.pixelPosition,function(a,c){if(c)return c=Oa(a,b),Ma.test(c)?r(a).position()[b]+"px":c})}),r.each({Height:"height",Width:"width"},function(a,b){r.each({padding:"inner"+a,content:b,"":"outer"+a},function(c,d){r.fn[d]=function(e,f){var g=arguments.length&&(c||"boolean"!=typeof e),h=c||(e===!0||f===!0?"margin":"border");return T(this,function(b,c,e){var f;return r.isWindow(b)?0===d.indexOf("outer")?b["inner"+a]:b.document.documentElement["client"+a]:9===b.nodeType?(f=b.documentElement,Math.max(b.body["scroll"+a],f["scroll"+a],b.body["offset"+a],f["offset"+a],f["client"+a])):void 0===e?r.css(b,c,h):r.style(b,c,e,h)},b,g?e:void 0,g)}})}),r.fn.extend({bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return 1===arguments.length?this.off(a,"**"):this.off(b,a||"**",c)}}),r.holdReady=function(a){a?r.readyWait++:r.ready(!0)},r.isArray=Array.isArray,r.parseJSON=JSON.parse,r.nodeName=B,"function"==typeof define&&define.amd&&define("jquery",[],function(){return r});var Vb=a.jQuery,Wb=a.$;return r.noConflict=function(b){return a.$===r&&(a.$=Wb),b&&a.jQuery===r&&(a.jQuery=Vb),r},b||(a.jQuery=a.$=r),r});
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/resources/public/mojo/prettify/prettify-mojo-dark.css
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/resources/public/mojo/prettify/prettify-mojo-dark.css	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/resources/public/mojo/prettify/prettify-mojo-dark.css	(revision 32205)
@@ -0,0 +1,1 @@
+.str,.atv{color:#9daa7e}.kwd,.tag{color:#d5b57c}.com{color:#726d73}.typ,.atn,.dec{color:#dd7e5e}.lit{color:#fcf0a4}.pun,.opn,.clo{color:#a78353}.pln{color:#889dbc}pre.prettyprint{border:0;padding-bottom:0.75em;padding-top:0.75em}
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/resources/public/mojo/prettify/prettify-mojo-light.css
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/resources/public/mojo/prettify/prettify-mojo-light.css	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/resources/public/mojo/prettify/prettify-mojo-light.css	(revision 32205)
@@ -0,0 +1,1 @@
+.str{color:#50a14f}.kwd{color:#a626a4}.com{color:#a0a1a7}.typ,.dec{color:#c18401} .lit,.atn{color:#0184bc} .pun,.opn,.clo,.pln{color:#383a42} .tag,.var,.fun{color:#e45649} .atv{color:#4078f2} pre.prettyprint{border:1px solid #c1c1c1;padding:1em}
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/resources/public/mojo/prettify/run_prettify.js
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/resources/public/mojo/prettify/run_prettify.js	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/resources/public/mojo/prettify/run_prettify.js	(revision 32205)
@@ -0,0 +1,63 @@
+!function(){/*
+
+ Copyright (C) 2013 Google 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.
+
+ Copyright (C) 2006 Google 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(){function ba(g){function k(){try{M.doScroll("left")}catch(g){t.setTimeout(k,50);return}z("poll")}function z(k){if("readystatechange"!=k.type||"complete"==A.readyState)("load"==k.type?t:A)[B](p+k.type,z,!1),!q&&(q=!0)&&g.call(t,k.type||k)}var Y=A.addEventListener,q=!1,C=!0,x=Y?"addEventListener":"attachEvent",B=Y?"removeEventListener":"detachEvent",p=Y?"":"on";if("complete"==A.readyState)g.call(t,"lazy");else{if(A.createEventObject&&M.doScroll){try{C=!t.frameElement}catch(da){}C&&k()}A[x](p+
+"DOMContentLoaded",z,!1);A[x](p+"readystatechange",z,!1);t[x](p+"load",z,!1)}}function U(){V&&ba(function(){var g=N.length;ca(g?function(){for(var k=0;k<g;++k)(function(g){t.setTimeout(function(){t.exports[N[g]].apply(t,arguments)},0)})(k)}:void 0)})}for(var t=window,A=document,M=A.documentElement,O=A.head||A.getElementsByTagName("head")[0]||M,B="",F=A.getElementsByTagName("script"),q=F.length;0<=--q;){var P=F[q],Z=P.src.match(/^[^?#]*\/run_prettify\.js(\?[^#]*)?(?:#.*)?$/);if(Z){B=Z[1]||"";P.parentNode.removeChild(P);
+break}}var V=!0,H=[],Q=[],N=[];B.replace(/[?&]([^&=]+)=([^&]+)/g,function(g,k,z){z=decodeURIComponent(z);k=decodeURIComponent(k);"autorun"==k?V=!/^[0fn]/i.test(z):"lang"==k?H.push(z):"skin"==k?Q.push(z):"callback"==k&&N.push(z)});q=0;for(B=H.length;q<B;++q)(function(){var g=A.createElement("script");g.onload=g.onerror=g.onreadystatechange=function(){!g||g.readyState&&!/loaded|complete/.test(g.readyState)||(g.onerror=g.onload=g.onreadystatechange=null,--T,T||t.setTimeout(U,0),g.parentNode&&g.parentNode.removeChild(g),
+g=null)};g.type="text/javascript";g.src="https://cdn.rawgit.com/google/code-prettify/master/loader/lang-"+encodeURIComponent(H[q])+".js";O.insertBefore(g,O.firstChild)})(H[q]);for(var T=H.length,F=[],q=0,B=Q.length;q<B;++q)F.push("https://cdn.rawgit.com/google/code-prettify/master/loader/skins/"+encodeURIComponent(Q[q])+".css");F.push("https://cdn.rawgit.com/google/code-prettify/master/loader/prettify.css");(function(g){function k(q){if(q!==z){var t=A.createElement("link");t.rel="stylesheet";t.type=
+"text/css";q+1<z&&(t.error=t.onerror=function(){k(q+1)});t.href=g[q];O.appendChild(t)}}var z=g.length;k(0)})(F);var ca=function(){window.PR_SHOULD_USE_CONTINUATION=!0;var g;(function(){function k(a){function d(e){var b=e.charCodeAt(0);if(92!==b)return b;var a=e.charAt(1);return(b=W[a])?b:"0"<=a&&"7">=a?parseInt(e.substring(1),8):"u"===a||"x"===a?parseInt(e.substring(2),16):e.charCodeAt(1)}function f(e){if(32>e)return(16>e?"\\x0":"\\x")+e.toString(16);e=String.fromCharCode(e);return"\\"===e||"-"===
+e||"]"===e||"^"===e?"\\"+e:e}function b(e){var b=e.substring(1,e.length-1).match(/\\u[0-9A-Fa-f]{4}|\\x[0-9A-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\s\S]|-|[^-\\]/g);e=[];var a="^"===b[0],c=["["];a&&c.push("^");for(var a=a?1:0,h=b.length;a<h;++a){var l=b[a];if(/\\[bdsw]/i.test(l))c.push(l);else{var l=d(l),n;a+2<h&&"-"===b[a+1]?(n=d(b[a+2]),a+=2):n=l;e.push([l,n]);65>n||122<l||(65>n||90<l||e.push([Math.max(65,l)|32,Math.min(n,90)|32]),97>n||122<l||e.push([Math.max(97,l)&-33,Math.min(n,122)&-33]))}}e.sort(function(e,
+a){return e[0]-a[0]||a[1]-e[1]});b=[];h=[];for(a=0;a<e.length;++a)l=e[a],l[0]<=h[1]+1?h[1]=Math.max(h[1],l[1]):b.push(h=l);for(a=0;a<b.length;++a)l=b[a],c.push(f(l[0])),l[1]>l[0]&&(l[1]+1>l[0]&&c.push("-"),c.push(f(l[1])));c.push("]");return c.join("")}function g(e){for(var a=e.source.match(/(?:\[(?:[^\x5C\x5D]|\\[\s\S])*\]|\\u[A-Fa-f0-9]{4}|\\x[A-Fa-f0-9]{2}|\\[0-9]+|\\[^ux0-9]|\(\?[:!=]|[\(\)\^]|[^\x5B\x5C\(\)\^]+)/g),c=a.length,d=[],h=0,l=0;h<c;++h){var n=a[h];"("===n?++l:"\\"===n.charAt(0)&&(n=
++n.substring(1))&&(n<=l?d[n]=-1:a[h]=f(n))}for(h=1;h<d.length;++h)-1===d[h]&&(d[h]=++k);for(l=h=0;h<c;++h)n=a[h],"("===n?(++l,d[l]||(a[h]="(?:")):"\\"===n.charAt(0)&&(n=+n.substring(1))&&n<=l&&(a[h]="\\"+d[n]);for(h=0;h<c;++h)"^"===a[h]&&"^"!==a[h+1]&&(a[h]="");if(e.ignoreCase&&I)for(h=0;h<c;++h)n=a[h],e=n.charAt(0),2<=n.length&&"["===e?a[h]=b(n):"\\"!==e&&(a[h]=n.replace(/[a-zA-Z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return a.join("")}for(var k=0,I=!1,
+m=!1,J=0,c=a.length;J<c;++J){var r=a[J];if(r.ignoreCase)m=!0;else if(/[a-z]/i.test(r.source.replace(/\\u[0-9a-f]{4}|\\x[0-9a-f]{2}|\\[^ux]/gi,""))){I=!0;m=!1;break}}for(var W={b:8,t:9,n:10,v:11,f:12,r:13},u=[],J=0,c=a.length;J<c;++J){r=a[J];if(r.global||r.multiline)throw Error(""+r);u.push("(?:"+g(r)+")")}return new RegExp(u.join("|"),m?"gi":"g")}function q(a,d){function f(a){var c=a.nodeType;if(1==c){if(!b.test(a.className)){for(c=a.firstChild;c;c=c.nextSibling)f(c);c=a.nodeName.toLowerCase();if("br"===
+c||"li"===c)g[m]="\n",I[m<<1]=k++,I[m++<<1|1]=a}}else if(3==c||4==c)c=a.nodeValue,c.length&&(c=d?c.replace(/\r\n?/g,"\n"):c.replace(/[ \t\r\n]+/g," "),g[m]=c,I[m<<1]=k,k+=c.length,I[m++<<1|1]=a)}var b=/(?:^|\s)nocode(?:\s|$)/,g=[],k=0,I=[],m=0;f(a);return{a:g.join("").replace(/\n$/,""),c:I}}function t(a,d,f,b,g){f&&(a={h:a,l:1,j:null,m:null,a:f,c:null,i:d,g:null},b(a),g.push.apply(g,a.g))}function A(a){for(var d=void 0,f=a.firstChild;f;f=f.nextSibling)var b=f.nodeType,d=1===b?d?a:f:3===b?T.test(f.nodeValue)?
+a:d:d;return d===a?void 0:d}function C(a,d){function f(a){for(var m=a.i,k=a.h,c=[m,"pln"],r=0,W=a.a.match(g)||[],u={},e=0,q=W.length;e<q;++e){var D=W[e],w=u[D],h=void 0,l;if("string"===typeof w)l=!1;else{var n=b[D.charAt(0)];if(n)h=D.match(n[1]),w=n[0];else{for(l=0;l<p;++l)if(n=d[l],h=D.match(n[1])){w=n[0];break}h||(w="pln")}!(l=5<=w.length&&"lang-"===w.substring(0,5))||h&&"string"===typeof h[1]||(l=!1,w="src");l||(u[D]=w)}n=r;r+=D.length;if(l){l=h[1];var E=D.indexOf(l),G=E+l.length;h[2]&&(G=D.length-
+h[2].length,E=G-l.length);w=w.substring(5);t(k,m+n,D.substring(0,E),f,c);t(k,m+n+E,l,F(w,l),c);t(k,m+n+G,D.substring(G),f,c)}else c.push(m+n,w)}a.g=c}var b={},g;(function(){for(var f=a.concat(d),m=[],p={},c=0,r=f.length;c<r;++c){var q=f[c],u=q[3];if(u)for(var e=u.length;0<=--e;)b[u.charAt(e)]=q;q=q[1];u=""+q;p.hasOwnProperty(u)||(m.push(q),p[u]=null)}m.push(/[\0-\uffff]/);g=k(m)})();var p=d.length;return f}function x(a){var d=[],f=[];a.tripleQuotedStrings?d.push(["str",/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,
+null,"'\""]):a.multiLineStrings?d.push(["str",/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,"'\"`"]):d.push(["str",/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,"\"'"]);a.verbatimStrings&&f.push(["str",/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null]);var b=a.hashComments;b&&(a.cStyleComments?(1<b?d.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,null,"#"]):d.push(["com",/^#(?:(?:define|e(?:l|nd)if|else|error|ifn?def|include|line|pragma|undef|warning)\b|[^\r\n]*)/,
+null,"#"]),f.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h(?:h|pp|\+\+)?|[a-z]\w*)>/,null])):d.push(["com",/^#[^\r\n]*/,null,"#"]));a.cStyleComments&&(f.push(["com",/^\/\/[^\r\n]*/,null]),f.push(["com",/^\/\*[\s\S]*?(?:\*\/|$)/,null]));if(b=a.regexLiterals){var g=(b=1<b?"":"\n\r")?".":"[\\S\\s]";f.push(["lang-regex",RegExp("^(?:^^\\.?|[+-]|[!=]=?=?|\\#|%=?|&&?=?|\\(|\\*=?|[+\\-]=|->|\\/=?|::?|<<?=?|>>?>?=?|,|;|\\?|@|\\[|~|{|\\^\\^?=?|\\|\\|?=?|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*("+
+("/(?=[^/*"+b+"])(?:[^/\\x5B\\x5C"+b+"]|\\x5C"+g+"|\\x5B(?:[^\\x5C\\x5D"+b+"]|\\x5C"+g+")*(?:\\x5D|$))+/")+")")])}(b=a.types)&&f.push(["typ",b]);b=(""+a.keywords).replace(/^ | $/g,"");b.length&&f.push(["kwd",new RegExp("^(?:"+b.replace(/[\s,]+/g,"|")+")\\b"),null]);d.push(["pln",/^\s+/,null," \r\n\t\u00a0"]);b="^.[^\\s\\w.$@'\"`/\\\\]*";a.regexLiterals&&(b+="(?!s*/)");f.push(["lit",/^@[a-z_$][a-z_$@0-9]*/i,null],["typ",/^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null],["pln",/^[a-z_$][a-z_$@0-9]*/i,
+null],["lit",/^(?:0x[a-f0-9]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+\-]?\d+)?)[a-z]*/i,null,"0123456789"],["pln",/^\\[\s\S]?/,null],["pun",new RegExp(b),null]);return C(d,f)}function B(a,d,f){function b(a){var c=a.nodeType;if(1==c&&!k.test(a.className))if("br"===a.nodeName)g(a),a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)b(a);else if((3==c||4==c)&&f){var d=a.nodeValue,p=d.match(q);p&&(c=d.substring(0,p.index),a.nodeValue=c,(d=d.substring(p.index+p[0].length))&&
+a.parentNode.insertBefore(m.createTextNode(d),a.nextSibling),g(a),c||a.parentNode.removeChild(a))}}function g(a){function b(a,c){var d=c?a.cloneNode(!1):a,n=a.parentNode;if(n){var n=b(n,1),e=a.nextSibling;n.appendChild(d);for(var f=e;f;f=e)e=f.nextSibling,n.appendChild(f)}return d}for(;!a.nextSibling;)if(a=a.parentNode,!a)return;a=b(a.nextSibling,0);for(var d;(d=a.parentNode)&&1===d.nodeType;)a=d;c.push(a)}for(var k=/(?:^|\s)nocode(?:\s|$)/,q=/\r\n?|\n/,m=a.ownerDocument,p=m.createElement("li");a.firstChild;)p.appendChild(a.firstChild);
+for(var c=[p],r=0;r<c.length;++r)b(c[r]);d===(d|0)&&c[0].setAttribute("value",d);var t=m.createElement("ol");t.className="linenums";d=Math.max(0,d-1|0)||0;for(var r=0,u=c.length;r<u;++r)p=c[r],p.className="L"+(r+d)%10,p.firstChild||p.appendChild(m.createTextNode("\u00a0")),t.appendChild(p);a.appendChild(t)}function p(a,d){for(var f=d.length;0<=--f;){var b=d[f];X.hasOwnProperty(b)?R.console&&console.warn("cannot override language handler %s",b):X[b]=a}}function F(a,d){a&&X.hasOwnProperty(a)||(a=/^\s*</.test(d)?
+"default-markup":"default-code");return X[a]}function H(a){var d=a.j;try{var f=q(a.h,a.l),b=f.a;a.a=b;a.c=f.c;a.i=0;F(d,b)(a);var g=/\bMSIE\s(\d+)/.exec(navigator.userAgent),g=g&&8>=+g[1],d=/\n/g,p=a.a,k=p.length,f=0,m=a.c,t=m.length,b=0,c=a.g,r=c.length,x=0;c[r]=k;var u,e;for(e=u=0;e<r;)c[e]!==c[e+2]?(c[u++]=c[e++],c[u++]=c[e++]):e+=2;r=u;for(e=u=0;e<r;){for(var A=c[e],D=c[e+1],w=e+2;w+2<=r&&c[w+1]===D;)w+=2;c[u++]=A;c[u++]=D;e=w}c.length=u;var h=a.h;a="";h&&(a=h.style.display,h.style.display="none");
+try{for(;b<t;){var l=m[b+2]||k,n=c[x+2]||k,w=Math.min(l,n),E=m[b+1],G;if(1!==E.nodeType&&(G=p.substring(f,w))){g&&(G=G.replace(d,"\r"));E.nodeValue=G;var aa=E.ownerDocument,v=aa.createElement("span");v.className=c[x+1];var B=E.parentNode;B.replaceChild(v,E);v.appendChild(E);f<l&&(m[b+1]=E=aa.createTextNode(p.substring(w,l)),B.insertBefore(E,v.nextSibling))}f=w;f>=l&&(b+=2);f>=n&&(x+=2)}}finally{h&&(h.style.display=a)}}catch(y){R.console&&console.log(y&&y.stack||y)}}var R=window,K=["break,continue,do,else,for,if,return,while"],
+L=[[K,"auto,case,char,const,default,double,enum,extern,float,goto,inline,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"],"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],S=[L,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,delegate,dynamic_cast,explicit,export,friend,generic,late_check,mutable,namespace,nullptr,property,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],
+M=[L,"abstract,assert,boolean,byte,extends,finally,final,implements,import,instanceof,interface,null,native,package,strictfp,super,synchronized,throws,transient"],N=[L,"abstract,as,base,bool,by,byte,checked,decimal,delegate,descending,dynamic,event,finally,fixed,foreach,from,group,implicit,in,interface,internal,into,is,let,lock,null,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var,virtual,where"],L=[L,"debugger,eval,export,function,get,instanceof,null,set,undefined,var,with,Infinity,NaN"],
+O=[K,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"],P=[K,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],K=[K,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],Q=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)\b/,
+T=/\S/,U=x({keywords:[S,N,M,L,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",O,P,K],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),X={};p(U,["default-code"]);p(C([],[["pln",/^[^<?]+/],["dec",/^<!\w[^>]*(?:>|$)/],["com",/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",
+/^<xmp\b[^>]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^<script\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^<style\b[^>]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),"default-markup htm html mxml xhtml xml xsl".split(" "));p(C([["pln",/^[\s]+/,null," \t\r\n"],["atv",/^(?:\"[^\"]*\"?|\'[^\']*\'?)/,null,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],
+["pun",/^[=<>\/]+/],["lang-js",/^on\w+\s*=\s*\"([^\"]+)\"/i],["lang-js",/^on\w+\s*=\s*\'([^\']+)\'/i],["lang-js",/^on\w+\s*=\s*([^\"\'>\s]+)/i],["lang-css",/^style\s*=\s*\"([^\"]+)\"/i],["lang-css",/^style\s*=\s*\'([^\']+)\'/i],["lang-css",/^style\s*=\s*([^\"\'>\s]+)/i]]),["in.tag"]);p(C([],[["atv",/^[\s\S]+/]]),["uq.val"]);p(x({keywords:S,hashComments:!0,cStyleComments:!0,types:Q}),"c cc cpp cxx cyc m".split(" "));p(x({keywords:"null,true,false"}),["json"]);p(x({keywords:N,hashComments:!0,cStyleComments:!0,
+verbatimStrings:!0,types:Q}),["cs"]);p(x({keywords:M,cStyleComments:!0}),["java"]);p(x({keywords:K,hashComments:!0,multiLineStrings:!0}),["bash","bsh","csh","sh"]);p(x({keywords:O,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}),["cv","py","python"]);p(x({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:2}),
+["perl","pl","pm"]);p(x({keywords:P,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb","ruby"]);p(x({keywords:L,cStyleComments:!0,regexLiterals:!0}),["javascript","js"]);p(x({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,throw,true,try,unless,until,when,while,yes",hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);p(C([],[["str",/^[\s\S]+/]]),["regex"]);
+var V=R.PR={createSimpleLexer:C,registerLangHandler:p,sourceDecorator:x,PR_ATTRIB_NAME:"atn",PR_ATTRIB_VALUE:"atv",PR_COMMENT:"com",PR_DECLARATION:"dec",PR_KEYWORD:"kwd",PR_LITERAL:"lit",PR_NOCODE:"nocode",PR_PLAIN:"pln",PR_PUNCTUATION:"pun",PR_SOURCE:"src",PR_STRING:"str",PR_TAG:"tag",PR_TYPE:"typ",prettyPrintOne:function(a,d,f){f=f||!1;d=d||null;var b=document.createElement("div");b.innerHTML="<pre>"+a+"</pre>";b=b.firstChild;f&&B(b,f,!0);H({j:d,m:f,h:b,l:1,a:null,i:null,c:null,g:null});return b.innerHTML},
+prettyPrint:g=g=function(a,d){function f(){for(var b=R.PR_SHOULD_USE_CONTINUATION?c.now()+250:Infinity;r<p.length&&c.now()<b;r++){for(var d=p[r],k=h,q=d;q=q.previousSibling;){var m=q.nodeType,v=(7===m||8===m)&&q.nodeValue;if(v?!/^\??prettify\b/.test(v):3!==m||/\S/.test(q.nodeValue))break;if(v){k={};v.replace(/\b(\w+)=([\w:.%+-]+)/g,function(a,b,c){k[b]=c});break}}q=d.className;if((k!==h||u.test(q))&&!e.test(q)){m=!1;for(v=d.parentNode;v;v=v.parentNode)if(w.test(v.tagName)&&v.className&&u.test(v.className)){m=
+!0;break}if(!m){d.className+=" prettyprinted";m=k.lang;if(!m){var m=q.match(t),C;!m&&(C=A(d))&&z.test(C.tagName)&&(m=C.className.match(t));m&&(m=m[1])}if(x.test(d.tagName))v=1;else var v=d.currentStyle,y=g.defaultView,v=(v=v?v.whiteSpace:y&&y.getComputedStyle?y.getComputedStyle(d,null).getPropertyValue("white-space"):0)&&"pre"===v.substring(0,3);y=k.linenums;(y="true"===y||+y)||(y=(y=q.match(/\blinenums\b(?::(\d+))?/))?y[1]&&y[1].length?+y[1]:!0:!1);y&&B(d,y,v);H({j:m,h:d,m:y,l:v,a:null,i:null,c:null,
+g:null})}}}r<p.length?R.setTimeout(f,250):"function"===typeof a&&a()}for(var b=d||document.body,g=b.ownerDocument||document,b=[b.getElementsByTagName("pre"),b.getElementsByTagName("code"),b.getElementsByTagName("xmp")],p=[],k=0;k<b.length;++k)for(var m=0,q=b[k].length;m<q;++m)p.push(b[k][m]);var b=null,c=Date;c.now||(c={now:function(){return+new Date}});var r=0,t=/\blang(?:uage)?-([\w.]+)(?!\S)/,u=/\bprettyprint\b/,e=/\bprettyprinted\b/,x=/pre|xmp/i,z=/^code$/i,w=/^(?:pre|code|xmp)$/i,h={};f()}},
+S=R.define;"function"===typeof S&&S.amd&&S("google-code-prettify",[],function(){return V})})();return g}();T||t.setTimeout(U,0)})();}()
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/resources/templates/mojo/debug.html.ep
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/resources/templates/mojo/debug.html.ep	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/resources/templates/mojo/debug.html.ep	(revision 32205)
@@ -0,0 +1,337 @@
+<!DOCTYPE html>
+<!-- Request ID: <%= $c->req->request_id %> -->
+<html>
+  <head>
+    % my $title = stash('exception') ? 'Server error' : 'Page not found';
+    <title><%= $title %> (<%= app->mode %> mode)</title>
+    <meta http-equiv="Pragma" content="no-cache">
+    <meta http-equiv="Expires" content="-1">
+    %= javascript '/mojo/jquery/jquery.js'
+    %= javascript '/mojo/prettify/run_prettify.js'
+    %= stylesheet '/mojo/prettify/prettify-mojo-dark.css'
+    <style>
+      a img { border: 0 }
+      body {
+        background: url(<%= url_for '/mojo/pinstripe-light.png' %>);
+        color: #445555;
+        font: 0.9em 'Helvetica Neue', Helvetica, sans-serif;
+        font-weight: normal;
+        line-height: 1.5em;
+        margin: 0;
+      }
+      :not(pre) > code {
+        background-color: #eef9ff;
+        border: solid #cce4ff 1px;
+        border-radius: 5px;
+        color: #333;
+        font: 0.9em Consolas, Menlo, Monaco, Courier, monospace;
+        padding: 0.4em;
+      }
+      h1 {
+        color: #2a2a2a;
+        font-size: 1.5em;
+        margin: 0;
+      }
+      pre {
+        font: 0.9em Consolas, Menlo, Monaco, Courier, monospace;
+        margin: 0;
+        white-space: pre-wrap;
+      }
+      pre > code {
+        display: block;
+        font: 1.0em Consolas, Menlo, Monaco, Courier, monospace;
+        margin-bottom: 0.7em;
+        margin-top: 0.7em;
+      }
+      table {
+        border-collapse: collapse;
+        width: 100%;
+      }
+      td { padding: 0.5em }
+      .box {
+        background-color: #fff;
+        border: 1px solid #c1c1c1;
+        overflow: hidden;
+        padding: 1em;
+      }
+      .code {
+        background-color: #1a1a1a;
+        background: url(<%= url_for '/mojo/pinstripe-dark.png' %>);
+        color: #eee;
+        text-shadow: #333 0 1px 0;
+      }
+      .important { background-color: rgba(47, 48, 50, .75) }
+      .infobox { color: #333 }
+      .infobox tr:nth-child(odd) .value { background-color: #ddeeff }
+      .infobox tr:nth-child(even) .value { background-color: #eef9ff }
+      .key { text-align: right }
+      .more table { margin-bottom: 1em }
+      .spaced {
+        margin-left: 5em;
+        margin-right: 5em;
+      }
+      .striped { border-top: solid #cce4ff 1px }
+      .tap {
+        font: 0.5em Verdana, sans-serif;
+        text-align: center;
+      }
+      .value { padding-left: 1em }
+      .wide { width: 100% }
+      #error {
+        font: 1.5em 'Helvetica Neue', Helvetica, sans-serif;
+        font-weight: 300;
+        margin: 0;
+        text-shadow: #333 0 1px 0;
+      }
+      #footer {
+        padding-top: 0.5em;
+        text-align: center;
+      }
+      #nothing {
+        border-bottom: 0;
+        padding-top: 60px;
+      }
+      #showcase {
+        border-bottom: 0;
+        border-top: 0;
+      }
+      #showcase table { margin-top: 1em }
+      #showcase td {
+        padding-top: 0;
+        padding-bottom: 0;
+      }
+      #showcase .key { padding-right: 0 }
+      #log {
+        border-radius: 5px;
+        margin-top: 1em;
+      }
+      #log table { margin: 0 }
+      #more, #trace {
+        border-bottom-left-radius: 5px;
+        border-bottom-right-radius: 5px;
+      }
+      #more .tap, #trace .tap { text-shadow: #ddd 0 1px 0 }
+      #request {
+        border-bottom: 0;
+        border-top-left-radius: 5px;
+        border-top-right-radius: 5px;
+        margin-top: 1em;
+      }
+      #routes {
+        border-bottom-left-radius: 5px;
+        border-bottom-right-radius: 5px;
+        padding-top: 70px;
+      }
+      #trace { border-top: 0 }
+      #wrapperlicious {
+        max-width: 1000px;
+        margin: 0 auto;
+      }
+    </style>
+  </head>
+  <body>
+    %= include 'mojo/menubar'
+    <script>
+      function mojoDrawer(handle, drawer) {
+        $(handle).on('click', function () {
+          $(drawer).slideToggle('slow');
+          var text = $(handle + ' div.tap').text();
+          text = text == "tap for more" ? "tap for less" : "tap for more";
+          $(handle + ' div.tap').text(text);
+        });
+        $(drawer).toggle();
+      }
+      $(function () {
+        mojoDrawer('#trace', '#frames');
+        mojoDrawer('#more', '#infos');
+      });
+    </script>
+    <div id="wrapperlicious">
+      % my $kv = begin
+        % my ($key, $value) = @_;
+        <tr>
+          <td class="key"><%= $key %>:</td>
+          <td class="striped value wide"><pre><%= $value %></pre></td>
+        </tr>
+      % end
+      % if (my $exception = stash 'exception') {
+        <div id="nothing" class="box spaced"></div>
+        % my $cv = begin
+          % my ($key, $value, $i) = @_;
+          %= tag 'tr', $i ? (class => 'important') : (), begin
+            <td class="key"><%= $key %></td>
+            <td class="value wide">
+              <pre><code class="prettyprint"><%= $value %></code></pre>
+            </td>
+          % end
+        % end
+        <div id="showcase" class="box code spaced">
+          <pre id="error"><%= $exception->message %></pre>
+          <div id="context" class="more">
+            <table>
+              % for my $line (@{$exception->lines_before}) {
+                %= $cv->($line->[0], $line->[1])
+              % }
+              % if (defined $exception->line->[1]) {
+                %= $cv->($exception->line->[0], $exception->line->[1], 1)
+              % }
+              % for my $line (@{$exception->lines_after}) {
+                %= $cv->($line->[0], $line->[1])
+              % }
+            </table>
+          </div>
+          % if (defined $exception->line->[2]) {
+            <div id="insight" class="more">
+              <table>
+                % for my $line (@{$exception->lines_before}) {
+                  %= $cv->($line->[0], $line->[2])
+                % }
+                %= $cv->($exception->line->[0], $exception->line->[2], 1)
+                % for my $line (@{$exception->lines_after}) {
+                  %= $cv->($line->[0], $line->[2])
+                % }
+              </table>
+            </div>
+            <div class="tap">tap for more</div>
+            <script>
+              var current = '#context';
+              function mojoShowcase() {
+                $('#showcase').on('click', function () {
+                  $(this).unbind('click');
+                  $(current).slideToggle('slow', function () {
+                    current = current == '#context' ? '#insight' : '#context';
+                    $(current).slideToggle('slow', function () {
+                      mojoShowcase();
+                    });
+                  });
+                });
+              }
+              mojoShowcase();
+              $('#insight').toggle();
+            </script>
+          % }
+        </div>
+        <div id="trace" class="box spaced">
+          % if (@{$exception->frames}) {
+            <div id="frames" class="infobox more">
+              <table>
+                % for my $frame (@{$exception->frames}) {
+                  <tr>
+                    <td class="striped value wide">
+                      <pre><%= $frame->[1] . ':' . $frame->[2] %></pre>
+                    </td>
+                  </tr>
+                % }
+              </table>
+            </div>
+            <div class="tap">tap for more</div>
+          % }
+        </div>
+      % }
+      % else {
+        <div id="routes" class="box infobox spaced">
+          <h1>Page not found... yet!</h1>
+          <p>
+            None of these routes could generate a response for your
+            <code><%= $c->req->method %></code> request for
+            <code><%= $c->req->url->path->to_route %></code>, maybe you need
+            to add a new one?
+          </p>
+          % my $walk = begin
+            % my ($walk, $route, $depth) = @_;
+            <tr>
+              <td class="striped value">
+                % my $pattern = $route->pattern->unparsed || '/';
+                % $pattern = "+$pattern" if $depth;
+                <pre><%= '  ' x $depth %><%= $pattern %></pre>
+              </td>
+              <td class="striped value">
+                <pre><%= uc(join ',', @{$route->via || []}) || '*' %></pre>
+              </td>
+              <td class="striped value">
+                % my $name = $route->name;
+                <pre><%= $route->has_custom_name ? qq{"$name"} : $name %></pre>
+              </td>
+            </tr>
+            % $depth++;
+            %= $walk->($walk, $_, $depth) for @{$route->children};
+            % $depth--;
+          % end
+          <table>
+            <thead>
+              <tr>
+                <th>Pattern</th>
+                <th>Methods</th>
+                <th>Name</th>
+              </tr>
+            </thead>
+            %= $walk->($walk, $_, 0) for @{app->routes->children};
+          </table>
+        </div>
+      % }
+      <div id="request" class="box infobox spaced">
+        <table>
+          % my $req = $c->req;
+          %= $kv->('Request ID' => $req->request_id)
+          %= $kv->(Method => $req->method)
+          % my $url = $req->url;
+          %= $kv->(URL => $url->to_string)
+          %= $kv->('Base URL' => $url->base->to_string)
+          %= $kv->(Parameters => dumper $req->params->to_hash)
+          %= $kv->(Stash => dumper $snapshot)
+          %= $kv->(Session => dumper session)
+          %= $kv->(Version => $req->version)
+          % for my $name (sort @{$c->req->headers->names}) {
+            % my $value = $c->req->headers->header($name);
+            %= $kv->($name, $value)
+          % }
+        </table>
+      </div>
+      <div id="more" class="box infobox more spaced">
+        <div id="infos">
+          <table>
+            %= $kv->(Perl => "$^V ($^O)")
+            % my $version  = $Mojolicious::VERSION;
+            % my $codename = $Mojolicious::CODENAME;
+            %= $kv->(Mojolicious => "$version ($codename)")
+            %= $kv->(Home => app->home)
+            %= $kv->('Template paths' => dumper app->renderer->paths)
+            %= $kv->('Template classes' => dumper app->renderer->classes)
+            %= $kv->('Static paths' => dumper app->static->paths)
+            %= $kv->('Static classes' => dumper app->static->classes)
+            %= $kv->(Include => dumper \@INC)
+            %= $kv->(Config => dumper app->config)
+            %= $kv->(Moniker => app->moniker)
+            %= $kv->(Name => $0)
+            %= $kv->(Executable => $^X)
+            %= $kv->(PID => $$)
+            %= $kv->(Time => scalar localtime(time))
+          </table>
+        </div>
+        <div class="tap">tap for more</div>
+      </div>
+      % if (@{app->log->history}) {
+        <div id="log" class="box infobox spaced">
+          <table>
+            % for my $msg (@{app->log->history}) {
+              <tr>
+                <td class="striped value wide">
+                  <pre><%= app->log->format->(@$msg) %></pre>
+                </td>
+              </tr>
+            % }
+          </table>
+        </div>
+      % }
+    </div>
+    <div id="footer">
+      %= link_to 'https://mojolicious.org' => begin
+        <picture>
+          <img src="<%= url_for '/mojo/logo-black.png' %>"
+            srcset="<%= url_for '/mojo/logo-black-2x.png' %> 2x"
+            alt="Mojolicious logo">
+        </picture>
+      % end
+    </div>
+  </body>
+</html>
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/resources/templates/mojo/exception.html.ep
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/resources/templates/mojo/exception.html.ep	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/resources/templates/mojo/exception.html.ep	(revision 32205)
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- Request ID: <%= $c->req->request_id %> -->
+<html>
+  <head>
+    <title>Server error</title>
+    <style>
+      body { background-color: #caecf6 }
+      #raptor {
+        background: url(<%= url_for '/mojo/failraptor.png' %>);
+        height: 488px;
+        left: 50%;
+        margin-left: -371px;
+        margin-top: -244px;
+        position:absolute;
+        top: 50%;
+        width: 743px;
+      }
+    </style>
+  </head>
+  <body><div id="raptor"></div></body>
+</html>
+<!-- a padding to disable MSIE and Chrome friendly error page -->
+<!-- a padding to disable MSIE and Chrome friendly error page -->
+<!-- a padding to disable MSIE and Chrome friendly error page -->
+<!-- a padding to disable MSIE and Chrome friendly error page -->
+<!-- a padding to disable MSIE and Chrome friendly error page -->
+<!-- a padding to disable MSIE and Chrome friendly error page -->
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/resources/templates/mojo/menubar.html.ep
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/resources/templates/mojo/menubar.html.ep	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/resources/templates/mojo/menubar.html.ep	(revision 32205)
@@ -0,0 +1,136 @@
+%= javascript '/mojo/jquery/jquery.js'
+<div id="mojobar">
+  <style scoped="scoped">
+    #mojobar {
+      background-color: #1a1a1a;
+      background: -webkit-linear-gradient(top, #2a2a2a 0%, #000 100%);
+      background: -moz-linear-gradient(top, #2a2a2a 0%, #000 100%);
+      background: linear-gradient(top, #2a2a2a 0%, #000 100%);
+      box-shadow: 0px 1px 0px rgba(0, 0, 0, 0.5);
+      height: 46px;
+      overflow: hidden;
+      position: absolute;
+      width: 100%;
+      z-index: 1000;
+    }
+    #mojobar-brand {
+      display: inline-block;
+      padding-left: 50px;
+      padding-top: 4px;
+    }
+    #mojobar-links {
+      display: inline;
+      float: right;
+      height: 60px;
+      margin-top: 0.8em;
+      padding-right: 50px;
+    }
+    #mojobar-links a {
+      color: #bbb;
+      font: 0.9em 'Helvetica Neue', Helvetica, sans-serif;
+      margin-left: 0.5em;
+      text-decoration: none;
+      transition: all 200ms ease-in-out;
+    }
+    #mojobar-links a:hover { color: #fff }
+    #mojobar-links input {
+      background-color: #3a3a3a;
+      border: 1px solid rgba(0, 0, 0, 0.1);
+      border-radius: 3px;
+      color: #eee;
+      display: inline;
+      margin-left: 1em;
+      padding: 4px;
+      transition: all 0.15s;
+    }
+    #mojobar-links input:focus {
+      background-color: #fff;
+      color: #111;
+      outline: 0;
+    }
+    #mojobar-links form { display: inline }
+    .animated { transition: all 0.25s ease }
+  </style>
+  %= link_to 'https://mojolicious.org' => (id => 'mojobar-brand') => begin
+    <picture>
+      <img src="<%= url_for '/mojo/logo-white.png' %>"
+        srcset="<%= url_for '/mojo/logo-white-2x.png' %> 2x">
+    </picture>
+  % end
+  <div id="mojobar-links">
+    %= link_to Documentation => 'https://mojolicious.org/perldoc'
+    %= link_to Chat => 'https://chat.mibbit.com/?channel=%23mojo&server=irc.perl.org'
+    %= link_to Wiki => 'https://github.com/kraih/mojo/wiki'
+    %= link_to GitHub => 'https://github.com/kraih/mojo'
+    %= link_to CPAN => 'https://metacpan.org/release/Mojolicious/'
+    %= link_to MailingList => 'https://groups.google.com/group/mojolicious'
+    %= link_to Twitter => 'https://twitter.com/kraih'
+    %= form_for 'https://www.google.com/cse' => (target => '_blank') => begin
+      %= hidden_field cx => '014527573091551588235:pwfplkjpgbi'
+      %= hidden_field ie => 'UTF-8'
+      %= search_field 'q', placeholder => 'Search'
+    %= end
+  </div>
+</div>
+<script>
+  var mojobar = $('#mojobar');
+  var mojobarHeight = mojobar.outerHeight();
+  function getElementByHash(hash) {
+    return $(hash.replace(/(:|\.|\[|\]|,)/g, '\\$1'));
+  }
+  function fixOffset() {
+    var offset = getElementByHash(window.location.hash).offset();
+    if (offset) {
+      $('html, body').animate({scrollTop: offset.top - mojobarHeight}, 1);
+    }
+  }
+  $(window).on('load', function () {
+    if (window.location.hash) {
+      fixOffset();
+    }
+    var start = mojobar.offset().top;
+    var fixed;
+    var lastScrollTop = $(window).scrollTop();
+    var hidden = mojobarHeight + 1;
+    $(window).on('scroll', function () {
+      var st = $(window).scrollTop();
+      if (fixed) {
+        if (st <= start) {
+          fixed = false;
+          mojobar.removeClass('animated');
+          mojobar.css({'position': 'absolute', 'top': start + 'px'});
+        }
+        else if (Math.abs(lastScrollTop - st) > 100) {
+          if (!mojobar.hasClass('animated')) {
+            mojobar.addClass('animated');
+          }
+          if (st > lastScrollTop && st > (mojobarHeight + start + 250)) {
+            mojobar.css('transform', 'translateY(-' + hidden + 'px)');
+          }
+          else if (st < lastScrollTop) {
+            mojobar.css('transform', 'translateY(0px)');
+          }
+          lastScrollTop = st;
+        }
+      }
+      else if (st > start) {
+        fixed = true;
+        mojobar.css({'position': 'fixed', 'top': 0});
+        lastScrollTop = $(window).scrollTop();
+      }
+    });
+  });
+  $(function () {
+    $('a[href^="#"]').addClass('mojoscroll').on('click', function (e) {
+      e.preventDefault();
+      e.stopPropagation();
+      var hash   = '#' + this.href.split('#')[1];
+      var target = getElementByHash(hash);
+      var old    = target.attr('id');
+      target.attr('id', '');
+      location.hash = hash;
+      target.attr('id', old);
+      fixOffset();
+    });
+  });
+</script>
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/resources/templates/mojo/not_found.html.ep
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/resources/templates/mojo/not_found.html.ep	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/resources/templates/mojo/not_found.html.ep	(revision 32205)
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- Request ID: <%= $c->req->request_id %> -->
+<html>
+  <head>
+    <title>Page not found</title>
+    <style>
+      a img { border: 0 }
+      body { background-color: #caecf6 }
+      #noraptor {
+        left: 0%;
+        position: fixed;
+        top: 60%;
+      }
+      #notfound {
+        background: url(<%= url_for '/mojo/notfound.png' %>);
+        height: 62px;
+        left: 50%;
+        margin-left: -153px;
+        margin-top: -31px;
+        position:absolute;
+        top: 50%;
+        width: 306px;
+      }
+    </style>
+  </head>
+  <body>
+    %= link_to url_for->base => begin
+      %= image '/mojo/noraptor.png', alt => 'Bye!', id => 'noraptor'
+    % end
+    <div id="notfound"></div>
+  </body>
+</html>
+<!-- a padding to disable MSIE and Chrome friendly error page -->
+<!-- a padding to disable MSIE and Chrome friendly error page -->
+<!-- a padding to disable MSIE and Chrome friendly error page -->
+<!-- a padding to disable MSIE and Chrome friendly error page -->
+<!-- a padding to disable MSIE and Chrome friendly error page -->
+<!-- a padding to disable MSIE and Chrome friendly error page -->
Index: main/trunk/greenstone2/perllib/cpan/Mojolicious/resources/templates/mojo/perldoc.html.ep
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Mojolicious/resources/templates/mojo/perldoc.html.ep	(revision 32205)
+++ main/trunk/greenstone2/perllib/cpan/Mojolicious/resources/templates/mojo/perldoc.html.ep	(revision 32205)
@@ -0,0 +1,144 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title><%= $title %></title>
+    %= javascript '/mojo/prettify/run_prettify.js'
+    %= stylesheet '/mojo/prettify/prettify-mojo-light.css'
+    <style>
+      a { color: inherit }
+      a:hover { color: #2a2a2a }
+      a img { border: 0 }
+      body {
+        background: url(<%= url_for '/mojo/pinstripe-light.png' %>);
+        color: #445555;
+        font: 0.9em 'Helvetica Neue', Helvetica, sans-serif;
+        font-weight: normal;
+        line-height: 1.5em;
+        margin: 0;
+      }
+      dd {
+        margin: 0;
+        margin-left: 2em;
+      }
+      dt {
+        color: #2a2a2a;
+        font-weight: bold;
+        margin-left: 0.9em;
+      }
+      :not(pre) > code {
+        background-color: rgba(0, 0, 0, 0.04);
+        border-radius: 3px;
+        font: 0.9em Consolas, Menlo, Monaco, Courier, monospace;
+        padding: 0.3em;
+      }
+      h1 { font-size: 1.5em }
+      h2 { font-size: 1.3em }
+      h3 { font-size: 1.1em }
+      h4 { font-size: 0.9em }
+      h1, h2, h3, h4 {
+        color: #2a2a2a;
+        margin: 0;
+        position: relative;
+      }
+      h1 a, h2 a, h3 a, h4 a { text-decoration: none }
+      li > p {
+        margin-bottom: 0;
+        margin-top: 0;
+      }
+      pre {
+        background-color: #fafafa;
+        border: 1px solid #c1c1c1;
+        border-radius: 3px;
+        font: 100% Consolas, Menlo, Monaco, Courier, monospace;
+        padding: 1em;
+      }
+      pre > code {
+        color: #4d4d4c;
+        font: 0.9em Consolas, Menlo, Monaco, Courier, monospace;
+        line-height: 1.5em;
+        text-align: left;
+        text-shadow: #eee 0 1px 0;
+        white-space: pre-wrap;
+      }
+      ul { list-style-type: square }
+      #footer {
+        padding-top: 0.5em;
+        text-align: center;
+      }
+      #more {
+        display: inline;
+        font-size: 0.8em;
+      }
+      #perldoc {
+        background-color: #fff;
+        border: 1px solid #c1c1c1;
+        border-bottom-left-radius: 5px;
+        border-bottom-right-radius: 5px;
+        margin-left: 5em;
+        margin-right: 5em;
+        padding: 3em;
+        padding-top: 70px;
+      }
+      #perldoc > ul:first-of-type a { text-decoration: none }
+      #links { padding-bottom: 1em }
+      #wrapperlicious {
+        max-width: 1000px;
+        margin: 0 auto;
+      }
+      .permalink {
+        display: none;
+        left: -0.75em;
+        position: absolute;
+        padding-right: 0.25em;
+      }
+      h1:hover .permalink, h2:hover .permalink, h3:hover .permalink,
+      h4:hover .permalink {
+        display: block;
+      }
+    </style>
+  </head>
+  <body>
+    %= include 'mojo/menubar'
+    <div id="wrapperlicious">
+      <div id="perldoc">
+        <div id="links">
+          % my $path;
+          % for my $part (split '/', $module) {
+            %= '::' if $path
+            % $path .= "/$part";
+            %= link_to $part => url_for("/perldoc$path")
+          % }
+          <div id="more">
+            (<%= link_to 'source' => url_for("/perldoc$path.txt") %>,
+            <%= link_to 'CPAN' => $cpan %>)
+          </div>
+        </div>
+        <h1><a id="toc">CONTENTS</a></h1>
+        <ul>
+          % for my $part (@$parts) {
+            <li>
+              %= link_to splice(@$part, 0, 2)
+              % if (@$part) {
+                <ul>
+                  % while (@$part) {
+                    <li><%= link_to splice(@$part, 0, 2) %></li>
+                  % }
+                </ul>
+              % }
+            </li>
+          % }
+        </ul>
+        %= content 'perldoc'
+      </div>
+    </div>
+    <div id="footer">
+      %= link_to 'https://mojolicious.org' => begin
+        <picture>
+          <img src="<%= url_for '/mojo/logo-black.png' %>"
+            srcset="<%= url_for '/mojo/logo-black-2x.png' %> 2x"
+            alt="Mojolicious logo">
+        </picture>
+      % end
+    </div>
+  </body>
+</html>
Index: main/trunk/greenstone2/perllib/plugins/ConvertBinaryFile.pm
===================================================================
--- main/trunk/greenstone2/perllib/plugins/ConvertBinaryFile.pm	(revision 32204)
+++ main/trunk/greenstone2/perllib/plugins/ConvertBinaryFile.pm	(revision 32205)
@@ -161,5 +161,5 @@
     }
 
-    if ($convert_to =~ /^html/) { # may be html or html_multi
+    if ($convert_to =~ /^html/ || $convert_to eq "paged_html") { # may be html or html_multi, or paged_html with the new Xpdf's own pdftohtml
 	$self->{'convert_to_plugin'} = "HTMLPlugin";
 	$self->{'convert_to_ext'} = "html";
@@ -349,4 +349,6 @@
 	    $output_filename = $tmp_dirname . "\/$utf8_tailname\/" . $utf8_tailname . ".$output_type";
 	}
+    } elsif ($output_type eq "paged_html") {
+	$output_filename =~ s/$lc_suffix$/.html/;
     } else {
 	$output_filename =~ s/$lc_suffix$/.$output_type/;
@@ -371,7 +373,13 @@
         
     if ("$conv_filename" eq "") {return -1;} # had an error, will be passed down pipeline 
-    if (! -e "$conv_filename") {return -1;} 
+
+    # We used to return -1 here if $conv_filename didn't exist at this stage
+    # However, for "paged_html" convert_to mode, the converted HTML file $conv_filename 
+    # will only be created from conversion products *after* convert_post_process() returns
+    my $output_type=$self->{'convert_to'};
+    if ($output_type ne "paged_html" && ! -e "$conv_filename") {return -1;}  
     $self->{'conv_filename'} = $conv_filename;
     $self->convert_post_process($conv_filename);
+    if ($output_type eq "paged_html" && ! -e "$conv_filename") {return -1;}  
 
     # Run the "fribidi" (http://fribidi.org) Unicode Bidirectional Algorithm program over the converted file
Index: main/trunk/greenstone2/perllib/plugins/PDFPlugin.pm
===================================================================
--- main/trunk/greenstone2/perllib/plugins/PDFPlugin.pm	(revision 32204)
+++ main/trunk/greenstone2/perllib/plugins/PDFPlugin.pm	(revision 32205)
@@ -27,7 +27,9 @@
 use strict;
 no strict 'refs'; # so we can use a var for filehandles (e.g. STDERR)
+no strict 'subs'; # allow filehandles to be variables and viceversa
 
 use ReadTextFile;
 use unicode;
+use Mojo::DOM; # for HTML parsing
 
 use AutoLoadConverters;
@@ -44,4 +46,6 @@
       {	'name' => "text",
 	'desc' => "{ConvertBinaryFile.convert_to.text}" },
+      { 'name' => "paged_html",
+	'desc' => "{PDFPlugin.convert_to.paged_html}"},
       { 'name' => "pagedimg_jpg",
 	'desc' => "{ConvertBinaryFile.convert_to.pagedimg_jpg}"},
@@ -145,4 +149,5 @@
 
     # check convert_to
+    # TODO: Start supporting PDF to txt on Windows if we're going to be using XPDF Tools (incl pdftotext) on Windows/Linux/Mac
     if ($self->{'convert_to'} eq "text" && $ENV{'GSDLOS'} =~ /^windows$/i) {
 	print STDERR "Windows does not support pdf to text. PDFs will be converted to HTML instead\n";
@@ -281,4 +286,244 @@
     my ($conv_filename) = @_;
 
+    my $outhandle=$self->{'outhandle'};
+#    print STDERR "@@@ convert_to: ".$self->{'convert_to'}."\n";
+
+    if($self->{'convert_to'} eq "paged_html") { 
+	# special post-processing for paged_html mode, as HTML pages generated
+	# by xpdf's pdftohtml need to be massaged into the form we want 
+	$self->xpdftohtml_convert_post_process($conv_filename);
+    }
+    else { # use PDFPlugin's usual post processing
+	$self->default_convert_post_process($conv_filename);
+    }
+}
+
+# Called after gsConvert.pl has been run to convert a PDF to paged_html
+# using Xpdftools' pdftohtml
+# This method will do some cleanup of the HTML files produced after XPDF has produced
+# an HTML doc for each PDF page: it first gets rid of the default index.html.
+# Instead, it constructs a single html page containing each original HTML page
+# <body> nested as divs instead, with simple section information inserted at the top
+# of each 'page' <div> and some further styling customisation. This HTML manipulation
+# is to be done with the Mojo::DOM perl package.
+# Note that since xpdf's pdftohtml would have failed if the output dir already
+# existed and for simpler naming, the output files are created in a new "pages"
+# subdirectory of the tmp location parent of $conv_filename instead
+sub xpdftohtml_convert_post_process
+{
+    my $self = shift (@_);
+    my ($output_filename) = @_; # output_filename = tmp location + filename 
+    # if a single html were generated.
+    # We just want the tmp location, append "pages", and read all the html files
+    # in except for index.html. Then we create a new html file by name
+    # $output_filename, which will consist of a slightly modified version of
+    # each of the other html files concatenated together.
+
+    my $outhandle=$self->{'outhandle'};
+
+    my ($tailname, $tmp_dir, $suffix)
+	= &File::Basename::fileparse($output_filename, "\\.[^\\.]+\$");
+    my $pages_subdir = &FileUtils::filenameConcatenate($tmp_dir, "pages");
+
+    # Code from util::create_itemfile()
+    # Read in all the files
+    opendir(DIR, $pages_subdir) || die "can't opendir $pages_subdir: $!";
+    my @page_files = grep {-f "$pages_subdir/$_"} readdir(DIR);
+    closedir DIR;
+    # Sort files in the directory by page_num
+    # files are named index.html, page1.html, page2.html, ..., pagen.html
+    sub page_number {
+	my ($dir) = @_;
+	my ($pagenum) =($dir =~ m/^page(\d+)\.html?$/i);
+	$pagenum = 0 unless defined $pagenum; # index.html will be given pagenum=0
+	return $pagenum;
+    }
+    # sort the files in the directory in the order of page_num rather than lexically.
+    @page_files = sort { page_number($a) <=> page_number($b) } @page_files;
+
+    #my $num_html_pages = (scalar(@page_files) - 1)/2; # skip index file.
+              # For every html file there's an img file, so halve the total num.
+              # What about other file types that may potentially be there too???
+    my $num_html_pages = 0;
+    foreach my $pagefile (@page_files) {
+	$num_html_pages++ if $pagefile =~ m/\.html?$/ && $pagefile !~ /^index\.html?/i;	
+    }
+
+    # Prepare to create our new html page that will contain all the individual
+    # htmls generated by xpdf's pdftohtml in sequence.
+    # First write the opening html tags out to the output file. These are the
+    # same tags and their contents, including <meta>, as is generated by 
+    # Xpdf's pdftohtml for each of its individual html pages.
+    my $start_text = "<html>\n<head>\n";
+    $start_text .= "<title>$tailname</title>\n";
+    $start_text .= "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n";
+    $start_text .= "</head>\n<body>\n\n";
+
+    #handle content encodings the same way that default_convert_post_process does
+    # $self->utf8_write_file ($start_text, $conv_filename); # will close file after write    
+    # Don't want to build a giant string in memory of all the pages concatenated
+    # and then write it out in one go. Instead, build up the final single page
+    # by writing each modified paged_html file out to it as this is processed.
+    # Copying file open/close code from CommonUtil::utf8_write_file()
+    if (!open (OUTFILE, ">:utf8", $output_filename)) {
+	gsprintf(STDERR, "PDFPlugin::xpdftohtml_convert_post_process {ConvertToPlug.could_not_open_for_writing} ($!)\n", $output_filename);
+	die "\n";
+    }
+    print OUTFILE $start_text;
+
+    # Get the contents of each individual HTML page generated by Xpdf, after first
+    # modifying each, and write each out into our single all-encompassing html
+    foreach my $pagefile (@page_files) {
+	if ($pagefile =~ m/\.html?$/ && $pagefile !~ /^index\.html?/i) {
+	    my $page_num = page_number($pagefile);    
+	    # get full path to pagefile
+	    $pagefile = &FileUtils::filenameConcatenate($pages_subdir, $pagefile);
+#	    print STDERR "@@@ About to process html file $pagefile (num $page_num)\n";
+	    my $modified_page_contents = $self->_process_paged_html_page($pagefile, $page_num, $num_html_pages);
+	    print OUTFILE "$modified_page_contents\n\n";
+	}
+    }
+
+    # we've now created a single HTML file by concatenating (a modified version)
+    # of each paged html file
+    print OUTFILE "</body>\n</html>\n"; # write out closing tags
+    close OUTFILE; # done
+
+    # Get rid of all the htm(l) files incl index.html in the associated "pages"
+    # subdir, since we've now processed them all into a single html file
+    # one folder level up and we don't want HTMLPlugin to process all of them next.
+#    my @fullpath_page_files = map { &FileUtils::filenameConcatenate($pages_subdir, $_) } @page_files;
+    &FileUtils::removeFilesFiltered($pages_subdir, "\.html?\$"); #  no specific whitelist, but blacklist htm(l)
+
+    # now the tmp area should contain a single html file contain all the html pages'
+    # contents in sequence, and a "pages" subdir containing the screenshot images
+    # of each page.    
+    # HTMLPlugin will process these further in the plugin pipeline
+}
+
+# For whatever reason, most html <tags> don't get printed out in GLI
+# So when debugging, use this function to print them out as [tags] instead.
+sub _debug_print_html
+{
+    my $self = shift (@_);
+    my ($string_or_dom) = @_;
+
+    # can't seem to determine type of string with ref/reftype
+    # https://stackoverflow.com/questions/1731333/how-do-i-tell-what-type-of-value-is-in-a-perl-variable
+
+    # $dom objects appear to get correctly stringified in string contexts
+    # $dom.to_string/$dom.stringify seem to get called, no need to call them
+    # https://stackoverflow.com/questions/5214543/what-is-stringification-in-perl
+    my $escapedTxt = $string_or_dom; 
+    $escapedTxt =~ s@\<@[@sg;
+    $escapedTxt =~ s@\>@]@sg;
+
+    print STDERR "#### $escapedTxt\n";
+}
+
+# Helper function to read in each paged_html generated by Xpdf's pdftohtml
+# then modify the html suitably using the HTML parsing functions offered by 
+# Mojo::DOM, then return the modified HTML content as a string
+# See https://mojolicious.org/perldoc/Mojo/DOM
+sub _process_paged_html_page
+{
+    my $self = shift (@_);
+    my ($pagefile, $page_num, $num_html_pages) = @_;
+
+    my $text = "";
+
+    # handling content encoding the same way default_convert_post_process does
+    $self->read_file ($pagefile, "utf8", "", \$text);
+
+    my $dom = Mojo::DOM->new($text);
+
+#    $self->_debug_print_html($dom);
+
+    # there's a <style> element on the <html>, we need to shift it into the <div>
+    # tag that we'll be creating. We'll first slightly modify the <style> element
+    # store the first style element, which is the only one and in the <body>
+    # we'll later insert it as child of an all-encompassing div that we'll create
+#    my $page_style_tag_str = $dom->find('style')->[0]->to_string;
+#    my $page_style_tag_str = $dom->find('html style')->[0]->to_string;
+    my $page_style_tag_str = $dom->at('html')->at('style')->to_string;
+    # In the style tag, convert id style references to class style references
+    my $css_class = ".p".$page_num."f";
+    $page_style_tag_str =~ s@\#f@$css_class@sg;
+    my $style_element = Mojo::DOM->new($page_style_tag_str)->at('style'); # modified    
+#$self->_debug_print_html($style_element);
+
+    # need to know the image's height to set the height of the surrounding
+    # div that's to replace this page's <body>:
+    my $img_height = $dom->find('img')->[0]{height};
+
+
+    # 1. Fix up the style attr on the image by additionally setting z-index=-1 for it
+    # 2. Adjust the img#background src attribute to point to the pages subdir for imgs
+    # 3. Set that img tag's class=background, and change its id to background+$page_num
+    my $bg_img_tag=$dom->find('img#background')->[0];
+
+    my $img_style_str = $bg_img_tag->{style}; # = $dom->find('img#background')->[0]{style}
+    $img_style_str = $img_style_str." z-index=-1;";
+#print STDERR "img_style_str: " . $img_style_str."\n";
+    my $img_src_str = $bg_img_tag->{src};
+    $img_src_str = "pages/$img_src_str";
+    $bg_img_tag->attr({style => $img_style_str, src => $img_src_str}); # reset
+#$self->_debug_print_html($bg_img_tag);
+    # set both class and modified id attributes in one step:
+    $bg_img_tag->attr({class => "background", id => "background".$page_num});
+#$self->_debug_print_html($bg_img_tag);
+
+    # get all the <span> nested inside <div class="txt"> elements and
+    # 1. set their class attr to be "p + page_num + id-of-the-span",
+    # 2. then delete the id, because the span ids have been reused when element
+    # ids ought to be unique. Which is why we set the modified ids to be the
+    # value of the class attribute instead
+    $dom->find('div.txt span')->each(sub { 
+	$_->attr(class => "p". $page_num. $_->{id});
+	delete $_->{id};
+				     }); # both changes done in one find() operation
+#$self->_debug_print_html($dom->find('div.txt span')->last);
+
+    # Finally can create our new dom, starting with a div tag for the current page
+    # Must be: <div id="$page_num" style="position:relative; height:$img_height;"/>
+    my $new_dom = Mojo::DOM->new_tag('div', id => "page".$page_num, style => "position: relative; height: ".$img_height."px;" );
+#$self->_debug_print_html($new_dom);
+    $new_dom->at('div')->append_content($style_element)->root;
+
+    # Append a page range bucket heading if applicable
+    # Dr Bainbridge thinks for now we need only consider PDFs where the
+    # total number of pages < 1000 and create buckets of size 10 (e.g. 1-10, ... 51-60, ...)
+    # If number of remaining pages >= 10, then create new bucket heading
+    # e.g. "Pages 30-40"
+    if(($num_html_pages - $page_num) > 10) {
+	# Double-digit page numbers that start with 2
+	# i.e. 21 to 29 (and 30) should be in 21 to 30 range
+	my $start_range = $page_num - ($page_num % 10) + 1;
+	my $end_range = $page_num + 10 - ($page_num % 10);
+	if($page_num % 10 == 0) { # page 20 however, should be in 11 to 20 range
+	    $start_range -= 10;
+	    $end_range -= 10;
+	}
+	$new_dom->at('div')->append_content($new_dom->new_tag('h1', "Pages ".$start_range . "-" . $end_range))->root;
+    }
+
+    # Add a simpler heading: just the pagenumber, "Page #"
+    $new_dom->at('div')->append_content($new_dom->new_tag('h2', "Page ".$page_num))->root;
+#$self->_debug_print_html($new_dom);
+    # Copy across all the old html's body tag's child nodes into the new dom's new div tag
+    $dom->at('body')->child_nodes->each(sub { $new_dom->at('div')->append_content($_)}); #$_->to_string
+#$self->_debug_print_html($new_dom);
+
+    # Finished processing a single html page of the paged_html output generated by
+    # Xpdf's pdftohtml: finished massaging that single html page into the right form
+    return $new_dom->to_string;
+}
+
+# This subroutine is called to do the PDFPlugin post-processing for all cases
+# except the "paged_html" conversion mode. This is what PDFPlugin always used to do:
+sub default_convert_post_process
+{
+    my $self = shift (@_);
+    my ($conv_filename) = @_;
     my $outhandle=$self->{'outhandle'};
 
Index: main/trunk/greenstone2/perllib/strings.properties
===================================================================
--- main/trunk/greenstone2/perllib/strings.properties	(revision 32204)
+++ main/trunk/greenstone2/perllib/strings.properties	(revision 32205)
@@ -1163,4 +1163,6 @@
 PDFPlugin.complex:Create more complex output. With this option set the output html will look much more like the original PDF file. For this to function properly you Ghostscript installed (for *nix gs should be on your path while for windows you must have gswin32c.exe on your path).
 
+PDFPlugin.convert_to.paged_html:A series of HTML pages, one for each page. Each HTML page contains selectable text positionally overlaid on top of a screenshot of the PDF page background comprising any images, tables and drawings. Generated with Xpdf tools.
+
 PDFPlugin.desc:Plugin that processes PDF documents.
 
@@ -1173,5 +1175,5 @@
 PDFPlugin.use_sections:Create a separate section for each page of the PDF file.
 
-PDFPlugin.zoom:The factor by which to zoom the PDF for output (this is only useful if -complex is set).
+PDFPlugin.zoom:The factor by which to zoom the PDF for output. When not outputting as paged_html, then zoom is only useful if -complex is set. If output is as paged_html, then a zoom factor of 1 means 100 percent.
 
 PostScriptPlugin.desc:This is a \"poor man's\" ps to text converter. If you are serious, consider using the PRESCRIPT package, which is available for download at http://www.nzdl.org/html/software.html
Index: main/trunk/greenstone2/setup.bash
===================================================================
--- main/trunk/greenstone2/setup.bash	(revision 32204)
+++ main/trunk/greenstone2/setup.bash	(revision 32205)
@@ -193,4 +193,8 @@
       ;;
   esac
+
+  # for xpdf tools, need to know whether we're using the bin32 or bin64 folder
+  BITNESS=$GSDLARCH
+  export BITNESS
 
   # Only want non-trival GSDLARCH value set if there is evidence of
