code:
NEWS ( CHANGELOG and HISTORY )                                     HTMLPurifier
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

= KEY ====================
    # Breaks back-compat
    ! Feature
    - Bugfix
      + Sub-comment
    . Internal change
==========================

4.10.0, released 2018-02-22
# PHP 5.3 is no longer officially supported by HTML Purifier
  (we did not specifically break support, but we are no longer
  testing on PHP 5.3)
! Relative CSS length units are now supported
- A few PHP 7.2 compatibility fixes, thanks John Flatness
  <john@zerocrates.org>
- Improve portability with old versions of libxml which don't
  support accessing the data of a node
- IDNA2008 is now used for converting domains to ASCII, fixing
  some rather strange bugs with international domains
- Fix race condition resulting in E_WARNING when creating
  directories with Serializer

4.9.3, released 2017-06-02
- Workaround PHP 7.1 infinite loop when opcode cache is enabled.
  Thanks @Xiphin (#134, #135)
- Don't use autoloader when testing for DOMDocument.  Hypothetically,
  this could cause your install to start using DirectLex if you had
  previously been monkeypatching in a custom, autoloaded implementation
  of DOMDocument.  Don't do that.  Thanks @Izumi-kun (#130)

4.9.2, released 2017-03-12
- Fixes PHP 5.3 compatibility
- Fix breakage when decoding decimal entities.  Thanks @rybakit (#129)

4.9.1, released 2017-03-08
! %URI.DefaultScheme can now be set to null, in which case
  all relative paths are removed.
! New CSS properties: min-width, max-width, min-height, max-height (#94)
! Transparency (rgba) and hsl/hsla supported where color CSS is present.
  Thanks @fxbt for contributing the patch. (#118)
- When idn_to_ascii is defined, we might accept malformed
  hostnames.  Apply validation to the result in such cases.
- Close directory when done in Serializer DefinitionCache (#100)
- Deleted some asserts to avoid linters from choking (#97)
- Rework Serializer cache behavior to avoid chmod'ing if possible (#32)
- Embedded semicolons in strings in CSS are now handled correctly!
- We accidentally dropped certain Unicode characters if there was
  one or more invalid characters.  This has been fixed, thanks
  to mpyw <ryosuke_i_628@yahoo.co.jp>
- Fix for "Don't truncate upon encountering
 when using DOMLex"
  caused a regression with HTML 4.01 Strict parsing with libxml 2.9.1
  (and maybe later versions, but known OK with libxml 2.9.4).  The
  fix is to go about handling truncation a bit more cleverly so that
  we can wrap with divs (sidestepping the bug) but slurping out the
  rest of the text in case it ran off the end.  (#78)
- Fix PREG_BACKTRACK_LIMIT_ERROR in HTMLPurifier_Filter_ExtractStyle.
  Thanks @breathbath for contributing the report and fix (#120)
- Fix entity decoding algorithm to be more conservative about
  decoding entities that are missing trailing semicolon.
  To get old behavior, set %Core.LegacyEntityDecoder to true.
  (#119)
- Workaround libxml bug when HTML tags are embedded inside
  script tags.  To disable workaround set %Core.AggressivelyRemoveScript
  to false. (#83)
# By default, when a link has a target attribute associated
  with it, we now also add rel="noopener" in order to
  prevent the new window from being able to overwrite
  the original frame.  To disable this protection,
  set %HTML.TargetNoopener to FALSE.

4.9.0 was cut on Git but never properly released; when we did the
real release we decided to skip this version number.

4.8.0, released 2016-07-16
# By default, when a link has a target attribute associated
  with it, we now also add rel="noreferrer" in order to
  prevent the new window from being able to overwrite
  the original frame.  To disable this protection,
  set %HTML.TargetNoreferrer to FALSE.
! Full PHP 7 compatibility, the test suite is ALL GO.
! %CSS.AllowDuplicates permits duplicate CSS properties.
! Support for 'tel' URIs.
! Partial support for 'border-radius' properties when %CSS.AllowProprietary is true.
  The slash syntax, i.e., 'border-radius: 2em 1em 4em / 0.5em 3em' is not
  yet supported.
! %Attr.ID.HTML5 turns on HTML5-style ID handling.
- alt truncation could result in malformed UTF-8 sequence. Don't
  truncate.  Thanks Brandon Farber for reporting.
- Linkify regex is smarter, based off of Gruber's regex.
- IDNA supported natively on PHP 5.3 and later.
- Non all-numeric top-level names (e.g., foo.1f, 1f) are now
  allowed.
- Minor bounds error fix to squash a PHP 7 notice.
- Support non-/tmp temporary directories for data:// validation
- Give a better error message when a user attempts to allow
  ul/ol without allowing li.
- On some versions of PHP, the Serializer DefinitionCache could
  infinite loop when the directory exists but is not listable. (#49)
- Don't match for  inside comments with
  %Core.ConvertDocumentToFragment. (#67)
- SafeObject is now less case sensitive. (#57)
- AutoFormat.RemoveEmpty.Predicate now correctly renders in
  web form. (#85)

4.7.0, released 2015-08-04
# opacity is now considered a "tricky" CSS property rather than a
  proprietary one.
! %AutoFormat.RemoveEmpty.Predicate for specifying exactly when
  an element should be considered "empty" (maybe preserve if it
  has attributes), and modify iframe support so that the iframe
  is removed if it is missing a src attribute.  Thanks meeva for
  reporting.
- Don't truncate upon encountering
 when using DOMLex.  Thanks
  Myrto Christina for finally convincing me to fix this.
- Update YouTube filter for new code.
- Fix parsing of rgb() values with spaces in them for 'border'
  attribute.
- Don't remove foo="" attributes if foo is a boolean attribute.  Thanks
  valME for reporting.

4.6.0, released 2013-11-30
# Secure URI munge hashing algorithm has changed to hash_hmac("sha256", $url, $secret).
  Please update any verification scripts you may have.
# URI parsing algorithm was made more strict, so only prefixes which
  looks like schemes will actually be schemes.  Thanks
  Michael Gusev <mgusev@sugarcrm.com> for fixing.
# %Core.EscapeInvalidChildren is no longer supported, and no longer does
  anything.
! New directive %Core.AllowHostnameUnderscore which allows underscores
  in hostnames.
- Eliminate quadratic behavior in DOMLex by using a proper queue.
  Thanks Ole Laursen for noticing this.
- Rewritten MakeWellFormed/FixNesting implementation eliminates quadratic
  behavior in the rest of the purificaiton pipeline.  Thanks Chedburn
  Networks for sponsoring this work.
- Made Linkify URL parser a bit less permissive, so that non-breaking
  spaces and commas are not included as part of URL.  Thanks nAS for fixing.
- Fix some bad interactions with %HTML.Allowed and injectors.  Thanks
  David Hirtz for reporting.
- Fix infinite loop in DirectLex. Thanks Ashar Javed (@soaj1664ashar)
  for reporting.

4.5.0, released 2013-02-17
# Fix bug where stacked attribute transforms clobber each other;
  this also means it's no longer possible to override attribute
  transforms in later modules.  No internal code was using this
  but this may break some clients.
# We now use SHA-1 to identify cached definitions, instead of MD5.
! Support display:inline-block
! Support for more white-space CSS values.
! Permit underscores in font families
! Support for page-break-* CSS3 properties when proprietary properties
  are enabled.
! New directive %Core.DisableExcludes; can be set to 'true' to turn off
  SGML excludes checking.  If HTML Purifier is removing too much text
  and you don't care about full standards compliance, try setting this to
  'true'.
- Use prepend for SPL autoloading on PHP 5.3 and later.
- Fix bug with nofollow transform when pre-existing rel exists.
- Fix bug where background:url() always gets lower-cased
  (but not background-image:url())
- Fix bug with non lower-case color names in HTML
- Fix bug where data URI validation doesn't remove temporary files.
  Thanks Javier Marín Ros <javiermarinros@gmail.com> for reporting.
- Don't remove certain empty tags on RemoveEmpty.

4.4.0, released 2012-01-18
# Removed PEARSax3 handler.
# URI.Munge now munges URIs inside the same host that go from https
  to http.  Reported by Neike Taika-Tessaro.
# Core.EscapeNonASCIICharacters now always transforms entities to
  entities, even if target encoding is UTF-8.
# Tighten up selector validation in ExtractStyleBlocks.
  Non-syntactically valid selectors are now rejected, along with
  some of the more obscure ones such as attribute selectors, the
  :lang pseudoselector, and anything not in CSS2.1.  Furthermore,
  ID and class selectors now work properly with the relevant
  configuration attributes.  Also, mute errors when parsing CSS
  with CSS Tidy.  Reported by Mario Heiderich and Norman Hippert.
! Added support for 'scope' attribute on tables.
! Added %HTML.TargetBlank, which adds target="blank" to all outgoing links.
! Properly handle sub-lists directly nested inside of lists in
  a standards compliant way, by moving them into the preceding

! Added %HTML.AllowedComments and %HTML.AllowedCommentsRegexp for
  limited allowed comments in untrusted situations.
! Implement iframes, and allow them to be used in untrusted mode with
  %HTML.SafeIframe and %URI.SafeIframeRegexp.  Thanks Bradley M. Froehle
  <brad.froehle@gmail.com> for submitting an initial version of the patch.
! The Forms module now works properly for transitional doctypes.
! Added support for internationalized domain names. You need the PEAR
  Net_IDNA2 module to be in your path; if it is installed, ensure the
  class can be loaded and then set %Core.EnableIDNA to true.
- Color keywords are now case insensitive.  Thanks Yzmir Ramirez
  <yramirez-htmlpurifier@adicio.com> for reporting.
- Explicitly initialize anonModule variable to null.
- Do not duplicate nofollow if already present.  Thanks 178
  for reporting.
- Do not add nofollow if hostname matches our current host.  Thanks 178
  for reporting, and Neike Taika-Tessaro for helping diagnose.
- Do not unset parser variable; this fixes intermittent serialization
  problems.  Thanks Neike Taika-Tessaro for reporting, bill
  <10010tiger@gmail.com> for diagnosing.
- Fix iconv truncation bug, where non-UTF-8 target encodings see
  output truncated after around 8000 characters.  Thanks Jörg Ludwig
  <joerg.ludwig@iserv.eu> for reporting.
- Fix broken table content model for XHTML1.1 (and also earlier
  versions, although the W3C validator doesn't catch those violations).
  Thanks GlitchMr <glitch.mr@gmail.com> for reporting.

4.3.0, released 2011-03-27
# Fixed broken caching of customized raw definitions, but requires an
  API change.  The old API still works but will emit a warning,
  see http://htmlpurifier.org/docs/enduser-customize.html#optimized
  for how to upgrade your code.
# Protect against Internet Explorer innerHTML behavior by specially
  treating attributes with backticks but no angled brackets, quotes or
  spaces.  This constitutes a slight semantic change, which can be
  reverted using %Output.FixInnerHTML.  Reported by Neike Taika-Tessaro
  and Mario Heiderich.
# Protect against cssText/innerHTML by restricting allowed characters
  used in fonts further than mandated by the specification and encoding
  some extra special characters in URLs.  Reported by Neike
  Taika-Tessaro and Mario Heiderich.
! Added %HTML.Nofollow to add rel="nofollow" to external links.
! More types of SPL autoloaders allowed on later versions of PHP.
! Implementations for position, top, left, right, bottom, z-index
  when %CSS.Trusted is on.
! Add %Cache.SerializerPermissions option for custom serializer
  directory/file permissions
! Fix longstanding bug in Flash support for non-IE browsers, and
  allow more wmode attributes.
! Add %CSS.AllowedFonts to restrict permissible font names.
- Switch to an iterative traversal of the DOM, which prevents us
  from running out of stack space for deeply nested documents.
  Thanks Maxim Krizhanovsky for contributing a patch.
- Make removal of conditional IE comments ungreedy; thanks Bernd
  for reporting.
- Escape CDATA before removing Internet Explorer comments.
- Fix removal of id attributes under certain conditions by ensuring
  armor attributes are preserved when recreating tags.
- Check if schema.ser was corrupted.
- Check if zend.ze1_compatibility_mode is on, and error out if it is.
  This safety check is only done for HTMLPurifier.auto.php; if you
  are using standalone or the specialized includes files, you're
  expected to know what you're doing.
- Stop repeatedly writing the cache file after I'm done customizing a
  raw definition.  Reported by ajh.
- Switch to using require_once in the Bootstrap to work around bad
  interaction with Zend Debugger and APC.  Reported by Antonio Parraga.
- Fix URI handling when hostname is missing but scheme is present.
  Reported by Neike Taika-Tessaro.
- Fix missing numeric entities on DirectLex; thanks Neike Taika-Tessaro
  for reporting.
- Fix harmless notice from indexing into empty string.  Thanks Matthijs
  Kooijman <matthijs@stdin.nl> for reporting.
- Don't autoclose no parent elements are able to support the element
  that triggered the autoclose.  In particular fixes strange behavior
  of stray
 tags.  Thanks pkuliga@gmail.com for reporting and
  Neike Taika-Tessaro <pinkgothic@gmail.com> for debugging assistance.

4.2.0, released 2010-09-15
! Added %Core.RemoveProcessingInstructions, which lets you remove
  <? ... ?> statements.
! Added %URI.DisableResources functionality; the directive originally
  did nothing.  Thanks David Rothstein for reporting.
! Add documentation about configuration directive types.
! Add %CSS.ForbiddenProperties configuration directive.
! Add %HTML.FlashAllowFullScreen to permit embedded Flash objects
  to utilize full-screen mode.
! Add optional support for the file URI scheme, enable
  by explicitly setting %URI.AllowedSchemes.
! Add %Core.NormalizeNewlines options to allow turning off newline
  normalization.
- Fix improper handling of Internet Explorer conditional comments
  by parser.  Thanks zmonteca for reporting.
- Fix missing attributes bug when running on Mac Snow Leopard and APC.
  Thanks sidepodcast for the fix.
- Warn if an element is allowed, but an attribute it requires is
  not allowed.

4.1.1, released 2010-05-31
- Fix undefined index warnings in maintenance scripts.
- Fix bug in DirectLex for parsing elements with a single attribute
  with entities.
- Rewrite CSS output logic for font-family and url().  Thanks Mario
  Heiderich <mario.heiderich@googlemail.com> for reporting and Takeshi
  Terada <t-terada@violet.plala.or.jp> for suggesting the fix.
- Emit an error for CollectErrors if a body is extracted
- Fix bug where in background-position for center keyword handling.
- Fix infinite loop when a wrapper element is inserted in a context
  where it's not allowed.  Thanks Lars <lars@renoz.dk> for reporting.
- Remove +x bit and shebang from index.php; only supported mode is to
  explicitly call it with php.
- Make test script less chatty when log_errors is on.

4.1.0, released 2010-04-26
! Support proprietary height attribute on table element
! Support YouTube slideshows that contain /cp/ in their URL.
! Support for data: URI scheme; not enabled by default, add it using
  %URI.AllowedSchemes
! Support flashvars when using %HTML.SafeObject and %HTML.SafeEmbed.
! Support for Internet Explorer compatibility with %HTML.SafeObject
  using %Output.FlashCompat.
! Handle

 properly, by inserting the necessary
 tag.
- Always quote the insides of url(...) in CSS.

4.0.0, released 2009-07-07
# APIs for ConfigSchema subsystem have substantially changed. See
  docs/dev-config-bcbreaks.txt for details; in essence, anything that
  had both namespace and directive now have a single unified key.
# Some configuration directives were renamed, specifically:
    %AutoFormatParam.PurifierLinkifyDocURL -> %AutoFormat.PurifierLinkify.DocURL
    %FilterParam.ExtractStyleBlocksEscaping -> %Filter.ExtractStyleBlocks.Escaping
    %FilterParam.ExtractStyleBlocksScope -> %Filter.ExtractStyleBlocks.Scope
    %FilterParam.ExtractStyleBlocksTidyImpl -> %Filter.ExtractStyleBlocks.TidyImpl
  As usual, the old directive names will still work, but will throw E_NOTICE
  errors.
# The allowed values for class have been relaxed to allow all of CDATA for
  doctypes that are not XHTML 1.1 or XHTML 2.0.  For old behavior, set
  %Attr.ClassUseCDATA to false.
# Instead of appending the content model to an old content model, a blank
  element will replace the old content model.  You can use #SUPER to get
  the old content model.
! More robust support for name="" and id=""
! HTMLPurifier_Config::inherit($config) allows you to inherit one
  configuration, and have changes to that configuration be propagated
  to all of its children.
! Implement %HTML.Attr.Name.UseCDATA, which relaxes validation rules on
  the name attribute when set. Use with care. Thanks Ian Cook for
  sponsoring.
! Implement %AutoFormat.RemoveEmpty.RemoveNbsp, which removes empty
  tags that contain non-breaking spaces as well other whitespace. You
  can also modify which tags should have   maintained with
  %AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions.
! Implement %Attr.AllowedClasses, which allows administrators to restrict
  classes users can use to a specified finite set of classes, and
  %Attr.ForbiddenClasses, which is the logical inverse.
! You can now maintain your own configuration schema directories by
  creating a config-schema.php file or passing an extra argument. Check
  docs/dev-config-schema.html for more details.
! Added HTMLPurifier_Config->serialize() method, which lets you save away
  your configuration in a compact serial file, which you can unserialize
  and use directly without having to go through the overhead of setup.
- Fix bug where URIDefinition would not get cleared if it's directives got
  changed.
- Fix fatal error in HTMLPurifier_Encoder on certain platforms (probably NetBSD 5.0)
- Fix bug in Linkify autoformatter involving http://foo
- Make %URI.Munge not apply to links that have the same host as your host.
- Prevent stray  tag from truncating output, if a second
  is present.
. Created script maintenance/rename-config.php for renaming a configuration
  directive while maintaining its alias.  This script does not change source code.
. Implement namespace locking for definition construction, to prevent
  bugs where a directive is used for definition construction but is not
  used to construct the cache hash.

3.3.0, released 2009-02-16
! Implement CSS property 'overflow' when %CSS.AllowTricky is true.
! Implement generic property list classess
- Fix bug with testEncodingSupportsASCII() algorithm when iconv() implementation
  does not do the "right thing" with characters not supported in the output
  set.
- Spellcheck UTF-8: The Secret To Character Encoding
- Fix improper removal of the contents of elements with only whitespace. Thanks
  Eric Wald for reporting.
- Fix broken test suite in versions of PHP without spl_autoload_register()
- Fix degenerate case with YouTube filter involving double hyphens.
  Thanks Pierre Attar for reporting.
- Fix YouTube rendering problem on certain versions of Firefox.
- Fix CSSDefinition Printer problems with decorators
- Add text parameter to unit tests, forces text output
. Add verbose mode to command line test runner, use (--verbose)
. Turn on unit tests for UnitConverter
. Fix missing version number in configuration %Attr.DefaultImageAlt (added 3.2.0)
. Fix newline errors that caused spurious failures when CRLF HTML Purifier was
  tested on Linux.
. Removed trailing whitespace from all text files, see
  remote-trailing-whitespace.php maintenance script.
. Convert configuration to use property list backend.

3.2.0, released 2008-10-31
# Using %Core.CollectErrors forces line number/column tracking on, whereas
  previously you could theoretically turn it off.
# HTMLPurifier_Injector->notifyEnd() is formally deprecated. Please
  use handleEnd() instead.
! %Output.AttrSort for when you need your attributes in alphabetical order to
  deal with a bug in FCKEditor. Requested by frank farmer.
! Enable HTML comments when %HTML.Trusted is on. Requested by Waldo Jaquith.
! Proper support for name attribute. It is now allowed and equivalent to the id
  attribute in a and img tags, and is only converted to id when %HTML.TidyLevel
  is heavy (for all doctypes).
! %AutoFormat.RemoveEmpty to remove some empty tags from documents. Please don't
  use on hand-written HTML.
! Add error-cases for unsupported elements in MakeWellFormed. This enables
  the strategy to be used, standalone, on untrusted input.
! %Core.AggressivelyFixLt is on by default. This causes more sensible
  processing of left angled brackets in smileys and other whatnot.
! Test scripts now have a 'type' parameter, which lets you say 'htmlpurifier',
  'phpt', 'vtest', etc. in order to only execute those tests. This supercedes
  the --only-phpt parameter, although for backwards-compatibility the flag
  will still work.
! AutoParagraph auto-formatter will now preserve double-newlines upon output.
  Users who are not performing inbound filtering, this may seem a little
  useless, but as a bonus, the test suite and handling of edge cases is also
  improved.
! Experimental implementation of forms for %HTML.Trusted
! Track column numbers when maintain line numbers is on
! Proprietary 'background' attribute on table-related elements converted into
  corresponding CSS.  Thanks Fusemail for sponsoring this feature!
! Add forward(), forwardUntilEndToken(), backward() and current() to Injector
  supertype.
! HTMLPurifier_Injector->handleEnd() permits modification to end tokens. The
  time of operation varies slightly from notifyEnd() as *all* end tokens are
  processed by the injector before they are subject to the well-formedness rules.
! %Attr.DefaultImageAlt allows overriding default behavior of setting alt to
  basename of image when not present.
! %AutoFormat.DisplayLinkURI neuters  tags into plain text URLs.
- Fix two bugs in %URI.MakeAbsolute; one involving empty paths in base URLs,
  the other involving an undefined $is_folder error.
- Throw error when %Core.Encoding is set to a spurious value. Previously,
  this errored silently and returned false.
- Redirected stderr to stdout for flush error output.
- %URI.DisableExternal will now use the host in %URI.Base if %URI.Host is not
  available.
- Do not re-munge URL if the output URL has the same host as the input URL.
  Requested by Chris.
- Fix error in documentation regarding %Filter.ExtractStyleBlocks
- Prevent <![CDATA[]]> from triggering %Core.ConvertDocumentToFragment
- Fix bug with inline elements in blockquotes conflicting with strict doctype
- Detect if HTML support is disabled for DOM by checking for loadHTML() method.
- Fix bug where dots and double-dots in absolute URLs without hostname were
  not collapsed by URIFilter_MakeAbsolute.
- Fix bug with anonymous modules operating on SafeEmbed or SafeObject elements
  by reordering their addition.
- Will now throw exception on many error conditions during lexer creation; also
  throw an exception when MaintainLineNumbers is true, but a non-tracksLineNumbers
  is being used.
- Detect if domxml extension is loaded, and use DirectLEx accordingly.
- Improve handling of big numbers with floating point arithmetic in UnitConverter.
  Reported by David Morton.
. Strategy_MakeWellFormed now operates in-place, saving memory and allowing
  for more interesting filter-backtracking
. New HTMLPurifier_Injector->rewind() functionality, allows injectors to rewind
  index to reprocess tokens.
. StringHashParser now allows for multiline sections with "empty" content;
  previously the section would remain undefined.
. Added --quick option to multitest.php, which tests only the most recent
  release for each series.
. Added --distro option to multitest.php, which accepts either 'normal' or
  'standalone'. This supercedes --exclude-normal and --exclude-standalone

3.1.1, released 2008-06-19
# %URI.Munge now, by default, does not munge resources (for example, )
  In order to enable this again, please set %URI.MungeResources to true.
! More robust imagecrash protection with height/width CSS with %CSS.MaxImgLength,
  and height/width HTML with %HTML.MaxImgLength.
! %URI.MungeSecretKey for secure URI munging. Thanks Chris
  for sponsoring this feature. Check out the corresponding documentation
  for details. (Att Nightly testers: The API for this feature changed before
  the general release. Namely, rename your directives %URI.SecureMungeSecretKey =>
  %URI.MungeSecretKey and and %URI.SecureMunge => %URI.Munge)
! Implemented post URI filtering. Set member variable $post to true to set
  a URIFilter as such.
! Allow modules to define injectors via $info_injector. Injectors are
  automatically disabled if injector's needed elements are not found.
! Support for "safe" objects added, use %HTML.SafeObject and %HTML.SafeEmbed.
  Thanks Chris for sponsoring. If you've been using ad hoc code from the
  forums, PLEASE use this instead.
! Added substitutions for %e, %n, %a and %p in %URI.Munge (in order,
  embedded, tag name, attribute name, CSS property name). See %URI.Munge
  for more details. Requested by Jochem Blok.
- Disable percent height/width attributes for img.
- AttrValidator operations are now atomic; updates to attributes are not
  manifest in token until end of operations. This prevents naughty internal
  code from directly modifying CurrentToken when they're not supposed to.
  This semantics change was requested by frank farmer.
- Percent encoding checks enabled for URI query and fragment
- Fix stray backslashes in font-family; CSS Unicode character escapes are
  now properly resolved (although *only* in font-family). Thanks Takeshi Terada
  for reporting.
- Improve parseCDATA algorithm to take into account newline normalization
- Account for browser confusion between Yen character and backslash in
  Shift_JIS encoding. This fix generalizes to any other encoding which is not
  a strict superset of printable ASCII. Thanks Takeshi Terada for reporting.
- Fix missing configuration parameter in Generator calls. Thanks vs for the
  partial patch.
- Improved adherence to Unicode by checking for non-character codepoints.
  Thanks Geoffrey Sneddon for reporting. This may result in degraded
  performance for extremely large inputs.
- Allow CSS property-value pair ''text-decoration: none''. Thanks Jochem Blok
  for reporting.
. Added HTMLPurifier_UnitConverter and HTMLPurifier_Length for convenient
  handling of CSS-style lengths. HTMLPurifier_AttrDef_CSS_Length now uses
  this class.
. API of HTMLPurifier_AttrDef_CSS_Length changed from __construct($disable_negative)
  to __construct($min, $max). __construct(true) is equivalent to
  __construct('0').
. Added HTMLPurifier_AttrDef_Switch class
. Rename HTMLPurifier_HTMLModule_Tidy->construct() to setup() and bubble method
  up inheritance hierarchy to HTMLPurifier_HTMLModule. All HTMLModules
  get this called with the configuration object.  All modules now
  use this rather than __construct(), although legacy code using constructors
  will still work--the new format, however, lets modules access the
  configuration object for HTML namespace dependant tweaks.
. AttrDef_HTML_Pixels now takes a single construction parameter, pixels.
. ConfigSchema data-structure heavily optimized; on average it uses a third
  the memory it did previously. The interface has changed accordingly,
  consult changes to HTMLPurifier_Config for details.
. Variable parsing types now are magic integers instead of strings
. Added benchmark for ConfigSchema
. HTMLPurifier_Generator requires $config and $context parameters. If you
  don't know what they should be, use HTMLPurifier_Config::createDefault()
  and new HTMLPurifier_Context().
. Printers now properly distinguish between output configuration, and
  target configuration. This is not applicable to scripts using
  the Printers for HTML Purifier related tasks.
. HTML/CSS Printers must be primed with prepareGenerator($gen_config), otherwise
  fatal errors will ensue.
. URIFilter->prepare can return false in order to abort loading of the filter
. Factory for AttrDef_URI implemented, URI#embedded to indicate URI that embeds
  an external resource.
. %URI.Munge functionality factored out into a post-filter class.
. Added CurrentCSSProperty context variable during CSS validation

3.1.0, released 2008-05-18
# Unnecessary references to objects (vestiges of PHP4) removed from method
  signatures.  The following methods do not need references when assigning from
  them and will result in E_STRICT errors if you try:
    + HTMLPurifier_Config->get*Definition() [* = HTML, CSS]
    + HTMLPurifier_ConfigSchema::instance()
    + HTMLPurifier_DefinitionCacheFactory::instance()
    + HTMLPurifier_DefinitionCacheFactory->create()
    + HTMLPurifier_DoctypeRegistry->register()
    + HTMLPurifier_DoctypeRegistry->get()
    + HTMLPurifier_HTMLModule->addElement()
    + HTMLPurifier_HTMLModule->addBlankElement()
    + HTMLPurifier_LanguageFactory::instance()
# Printer_ConfigForm's get*() functions were static-ified
# %HTML.ForbiddenAttributes requires attribute declarations to be in the
  form of tag@attr, NOT tag.attr (which will throw an error and won't do
  anything). This is for forwards compatibility with XML; you'd do best
  to migrate an %HTML.AllowedAttributes directives to this syntax too.
! Allow index to be false for config from form creation
! Added HTMLPurifier::VERSION constant
! Commas, not dashes, used for serializer IDs. This change is forwards-compatible
  and allows for version numbers like "3.1.0-dev".
! %HTML.Allowed deals gracefully with whitespace anywhere, anytime!
! HTML Purifier's URI handling is a lot more robust, with much stricter
  validation checks and better percent encoding handling. Thanks Gareth Heyes
  for indicating security vulnerabilities from lax percent encoding.
! Bootstrap autoloader deals more robustly with classes that don't exist,
  preventing class_exists($class, true) from barfing.
- InterchangeBuilder now alphabetizes its lists
- Validation error in configdoc output fixed
- Iconv and other encoding errors muted even with custom error handlers that
  do not honor error_reporting
- Add protection against imagecrash attack with CSS height/width
- HTMLPurifier::instance() created for consistency, is equivalent to getInstance()
- Fixed and revamped broken ConfigForm smoketest
- Bug with bool/null fields in Printer_ConfigForm fixed
- Bug with global forbidden attributes fixed
- Improved error messages for allowed and forbidden HTML elements and attributes
- Missing (or null) in configdoc documentation restored
- If DOM throws and exception during parsing with PH5P (occurs in newer versions
  of DOM), HTML Purifier punts to DirectLex
- Fatal error with unserialization of ScriptRequired
- Created directories are now chmod'ed properly
- Fixed bug with fallback languages in LanguageFactory
- Standalone testing setup properly with autoload
. Out-of-date documentation revised
. UTF-8 encoding check optimization as suggested by Diego
. HTMLPurifier_Error removed in favor of exceptions
. More copy() function removed; should use clone instead
. More extensive unit tests for HTMLDefinition
. assertPurification moved to central harness
. HTMLPurifier_Generator accepts $config and $context parameters during
  instantiation, not runtime
. Double-quotes outside of attribute values are now unescaped

3.1.0rc1, released 2008-04-22
# Autoload support added. Internal require_once's removed in favor of an
  explicit require list or autoloading. To use HTML Purifier,
  you must now either use HTMLPurifier.auto.php
  or HTMLPurifier.includes.php; setting the include path and including
  HTMLPurifier.php is insufficient--in such cases include HTMLPurifier.autoload.php
  as well to register our autoload handler (or modify your autoload function
  to check HTMLPurifier_Bootstrap::getPath($class)). You can also use
  HTMLPurifier.safe-includes.php for a less performance friendly but more
  user-friendly library load.
# HTMLPurifier_ConfigSchema static functions are officially deprecated. Schema
  information is stored in the ConfigSchema directory, and the
  maintenance/generate-schema-cache.php generates the schema.ser file, which
  is now instantiated. Support for userland schema changes coming soon!
# HTMLPurifier_Config will now throw E_USER_NOTICE when you use a directive
  alias; to get rid of these errors just modify your configuration to use
  the new directive name.
# HTMLPurifier->addFilter is deprecated; built-in filters can now be
  enabled using %Filter.$filter_name or by setting your own filters using
  %Filter.Custom
# Directive-level safety properties superceded in favor of module-level
  safety. Internal method HTMLModule->addElement() has changed, although
  the externally visible HTMLDefinition->addElement has *not* changed.
! Extra utility classes for testing and non-library operations can
  be found in extras/. Specifically, these are FSTools and ConfigDoc.
  You may find a use for these in your own project, but right now they
  are highly experimental and volatile.
! Integration with PHPT allows for automated smoketests
! Limited support for proprietary HTML elements, namely <marquee>, sponsored
  by Chris. You can enable them with %HTML.Proprietary if your client
  demands them.
! Support for !important CSS cascade modifier. By default, this will be stripped
  from CSS, but you can enable it using %CSS.AllowImportant
! Support for display and visibility CSS properties added, set %CSS.AllowTricky
  to true to use them.
! HTML Purifier now has its own Exception hierarchy under HTMLPurifier_Exception.
  Developer error (not enduser error) can cause these to be triggered.
! Experimental kses() wrapper introduced with HTMLPurifier.kses.php
! Finally %CSS.AllowedProperties for tweaking allowed CSS properties without
  mucking around with HTMLPurifier_CSSDefinition
! ConfigDoc output has been enhanced with version and deprecation info.
! %HTML.ForbiddenAttributes and %HTML.ForbiddenElements implemented.
- Autoclose now operates iteratively, i.e.
 now has
  both span tags closed.
- Various HTMLPurifier_Config convenience functions now accept another parameter
  $schema which defines what HTMLPurifier_ConfigSchema to use besides the
  global default.
- Fix bug with trusted script handling in libxml versions later than 2.6.28.
- Fix bug in ExtractStyleBlocks with comments in style tags
- Fix bug in comment parsing for DirectLex
- Flush output now displayed when in command line mode for unit tester
- Fix bug with rgb(0, 1, 2) color syntax with spaces inside shorthand syntax
- HTMLPurifier_HTMLDefinition->addAttribute can now be called multiple times
  on the same element without emitting errors.
- Fixed fatal error in PH5P lexer with invalid tag names
. Plugins now get their own changelogs according to project conventions.
. Convert tokens to use instanceof, reducing memory footprint and
  improving comparison speed.
. Dry runs now supported in SimpleTest; testing facilities improved
. Bootstrap class added for handling autoloading functionality
. Implemented recursive glob at FSTools->globr
. ConfigSchema now has instance methods for all corresponding define*
  static methods.
. A couple of new historical maintenance scripts were added.
. HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php split into two files
. tests/index.php can now be run from any directory.
. HTMLPurifier_Token subclasses split into seperate files
. HTMLPURIFIER_PREFIX now is defined in Bootstrap.php, NOT HTMLPurifier.php
. HTMLPURIFIER_PREFIX can now be defined outside of HTML Purifier
. New --php=php flag added, allows PHP executable to be specified (command
  line only!)
. htmlpurifier_add_test() preferred method to translate test files in to
  classes, because it handles PHPT files too.
. Debugger class is deprecated and will be removed soon.
. Command line argument parsing for testing scripts revamped, now --opt value
  format is supported.
. Smoketests now cleanup after magic quotes
. Generator now can output comments (however, comments are still stripped
  from HTML Purifier output)
. HTMLPurifier_ConfigSchema->validate() deprecated in favor of
  HTMLPurifier_VarParser->parse()
. Integers auto-cast into float type by VarParser.
. HTMLPURIFIER_STRICT removed; no validation is performed on runtime, only
  during cache generation
. Reordered script calls in maintenance/flush.php
. Command line scripts now honor exit codes
. When --flush fails in unit testers, abort tests and print message
. Improved documentation in docs/dev-flush.html about the maintenance scripts
. copy() methods removed in favor of clone keyword

3.0.0, released 2008-01-06
# HTML Purifier is PHP 5 only! The 2.1.x branch will be maintained
  until PHP 4 is completely deprecated, but no new features will be added
  to it.
  + Visibility declarations added
  + Constructor methods renamed to __construct()
  + PHP4 reference cruft removed (in progress)
! CSS properties are now case-insensitive
! DefinitionCacheFactory now can register new implementations
! New HTMLPurifier_Filter_ExtractStyleBlocks for extracting

path: vendor/ezyang/htmlpurifier/NEWS


code:
HTML Purifier [![Build Status](https://secure.travis-ci.org/ezyang/htmlpurifier.svg?branch=master)](http://travis-ci.org/ezyang/htmlpurifier)
=============

HTML Purifier is an HTML filtering solution that uses a unique combination
of robust whitelists and aggressive parsing to ensure that not only are
XSS attacks thwarted, but the resulting HTML is standards compliant.

HTML Purifier is oriented towards richly formatted documents from
untrusted sources that require CSS and a full tag-set.  This library can
be configured to accept a more restrictive set of tags, but it won't be
as efficient as more bare-bones parsers. It will, however, do the job
right, which may be more important.

Places to go:

* See INSTALL for a quick installation guide
* See docs/ for developer-oriented documentation, code examples and
  an in-depth installation guide.
* See WYSIWYG for information on editors like TinyMCE and FCKeditor

HTML Purifier can be found on the web at: [http://htmlpurifier.org/](http://htmlpurifier.org/)

## Installation

Package available on [Composer](https://packagist.org/packages/ezyang/htmlpurifier).

If you're using Composer to manage dependencies, you can use

    $ composer require "ezyang/htmlpurifier":"dev-master"

repo_name: andremichelle/gnu

license: mit


code:
TODO List

= KEY ====================
    # Flagship
    - Regular
    ? Maybe I'll Do It
==========================

If no interest is expressed for a feature that may require a considerable
amount of effort to implement, it may get endlessly delayed. Do not be
afraid to cast your vote for the next feature to be implemented!

Things to do as soon as possible:

 - http://htmlpurifier.org/phorum/read.php?3,5560,6307#msg-6307
 - Think about allowing explicit order of operations hooks for transforms
 - Fix "<.<" bug (trailing < is removed if not EOD)
 - Build in better internal state dumps and debugging tools for remote
   debugging
 - Allowed/Allowed* have strange interactions when both set
 ? Transform lone embeds into object tags
 - Deprecated config options that emit warnings when you set them (with'
   a way of muting the warning if you really want to)
 - Make HTML.Trusted work with Output.FlashCompat
 - HTML.Trusted and HTML.SafeObject have funny interaction; general
   problem is what to do when a module "supersedes" another
   (see also tables and basic tables.)  This is a little dicier
   because HTML.SafeObject has some extra functionality that
   trusted might find useful.  See http://htmlpurifier.org/phorum/read.php?3,5762,6100

FUTURE VERSIONS
---------------

4.9 release [OMG CONFIG PONIES]
 ! Fix Printer. It's from the old days when we didn't have decent XML classes
 ! Factor demo.php into a set of Printer classes, and then create a stub
   file for users here (inside the actual HTML Purifier library)
 - Fix error handling with form construction
 - Do encoding validation in Printers, or at least, where user data comes in
 - Config: Add examples to everything (make built-in which also automatically
   gives output)
 - Add "register" field to config schemas to eliminate dependence on
   naming conventions (try to remember why we ultimately decided on tihs)

5.0 release [HTML 5]
 # Swap out code to use html5lib tokenizer and tree-builder
 ! Allow turning off of FixNesting and required attribute insertion

5.1 release [It's All About Trust] (floating)
 # Implement untrusted, dangerous elements/attributes
 # Implement IDREF support (harder than it seems, since you cannot have
   IDREFs to non-existent IDs)
     - Implement <area> (client and server side image maps are blocking
       on IDREF support)
 # Frameset XHTML 1.0 and HTML 4.01 doctypes
 - Figure out how to simultaneously set %CSS.Trusted and %HTML.Trusted (?)

5.2 release [Error'ed]
 # Error logging for filtering/cleanup procedures
 # Additional support for poorly written HTML
    - Microsoft Word HTML cleaning (i.e. MsoNormal, but research essential!)
    - Friendly strict handling of <address> (block ->
)
 - XSS-attempt detection--certain errors are flagged XSS-like
 - Append something to duplicate IDs so they're still usable (impl. note: the
   dupe detector would also need to detect the suffix as well)

6.0 release [Beyond HTML]
 # Legit token based CSS parsing (will require revamping almost every
   AttrDef class). Probably will use CSSTidy
 # More control over allowed CSS properties using a modularization
 # IRI support (this includes IDN)
 - Standardize token armor for all areas of processing

7.0 release [To XML and Beyond]
 - Extended HTML capabilities based on namespacing and tag transforms (COMPLEX)
    - Hooks for adding custom processors to custom namespaced tags and
      attributes, offer default implementation
    - Lots of documentation and samples

Ongoing
 - More refactoring to take advantage of PHP5's facilities
 - Refactor unit tests into lots of test methods
 - Plugins for major CMSes (COMPLEX)
    - phpBB
    - Also, a FAQ for extension writers with HTML Purifier

AutoFormat
 - Smileys
 - Syntax highlighting (with GeSHi) with
 and possibly <?php
 - Look at http://drupal.org/project/Modules/category/63 for ideas

Neat feature related
 ! Support exporting configuration, so users can easily tweak settings
   in the demo, and then copy-paste into their own setup
 - Advanced URI filtering schemes (see docs/proposal-new-directives.txt)
 - Allow scoped="scoped" attribute in

path: vendor/ezyang/htmlpurifier/TODO


path: vendor/ezyang/htmlpurifier/VERSION


code:
HTML Purifier 4.9.x is a maintenance release, collecting a year
of accumulated bug fixes plus a few new features.  New features
include support for min/max-width/height CSS, and rgba/hsl/hsla
in color specifications.  Major bugfixes include improvements
in the Serializer cache to avoid chmod'ing directories, better
entity decoding (we won't accidentally encode entities that occur
in URLs) and rel="noopener" on links with target attributes,
to prevent them from overwriting the original frame.

4.9.3 works around an infinite loop bug in PHP 7.1 with the opcode
cache (and has one other, minor bugfix, avoiding using autoloading
when testing for DOMDocument presence).  If these bugs do not
affect you, you do not need to upgrade.

path: vendor/ezyang/htmlpurifier/WHATSNEW


code:
WYSIWYG - What You See Is What You Get
    HTML Purifier: A Pretty Good Fit for TinyMCE and FCKeditor

Javascript-based WYSIWYG editors, simply stated, are quite amazing.  But I've
always been wary about using them due to security issues: they handle the
client-side magic, but once you've been served a piping hot load of unfiltered
HTML, what should be done then?  In some situations, you can serve it uncleaned,
since you only offer these facilities to trusted(?) authors.

Unfortunantely, for blog comments and anonymous input, BBCode, Textile and
other markup languages still reign supreme.  Put simply: filtering HTML is
hard work, and these WYSIWYG authors don't offer anything to alleviate that
trouble.  Therein lies the solution:

HTML Purifier is perfect for filtering pure-HTML input from WYSIWYG editors.

Enough said.

    vim: et sw=4 sts=4

path: vendor/ezyang/htmlpurifier/WYSIWYG


ezyang/htmlpurifier

description: Standards compliant HTML filter written in PHP

type: library

license: LGPL

HTMLPurifier: library/


code:
<?php

/**
 * Decorator/extender XSLT processor specifically for HTML documents.
 */
class ConfigDoc_HTMLXSLTProcessor
{

    /**
     * Instance of XSLTProcessor
     */
    protected $xsltProcessor;

    public function __construct($proc = false)
    {
        if ($proc === false) $proc = new XSLTProcessor();
        $this->xsltProcessor = $proc;
    }

    /**
     * @note Allows a string $xsl filename to be passed
     */
    public function importStylesheet($xsl)
    {
        if (is_string($xsl)) {
            $xsl_file = $xsl;
            $xsl = new DOMDocument();
            $xsl->load($xsl_file);
        }
        return $this->xsltProcessor->importStylesheet($xsl);
    }

    /**
     * Transforms an XML file into compatible XHTML based on the stylesheet
     * @param $xml XML DOM tree, or string filename
     * @return string HTML output
     * @todo Rename to transformToXHTML, as transformToHTML is misleading
     */
    public function transformToHTML($xml)
    {
        if (is_string($xml)) {
            $dom = new DOMDocument();
            $dom->load($xml);
        } else {
            $dom = $xml;
        }
        $out = $this->xsltProcessor->transformToXML($dom);

        // fudges for HTML backwards compatibility
        // assumes that document is XHTML
        $out = str_replace('/>', ' />', $out); //
 not

        $out = str_replace(' xmlns=""', '', $out); // rm unnecessary xmlns

        if (class_exists('Tidy')) {
            // cleanup output
            $config = array(
                'indent'        => true,
                'output-xhtml'  => true,
                'wrap'          => 80
            );
            $tidy = new Tidy;
            $tidy->parseString($out, $config, 'utf8');
            $tidy->cleanRepair();
            $out = (string) $tidy;
        }

        return $out;
    }

    /**
     * Bulk sets parameters for the XSL stylesheet
     * @param array $options Associative array of options to set
     */
    public function setParameters($options)
    {
        foreach ($options as $name => $value) {
            $this->xsltProcessor->setParameter('', $name, $value);
        }
    }

    /**
     * Forward any other calls to the XSLT processor
     */
    public function __call($name, $arguments)
    {
        call_user_func_array(array($this->xsltProcessor, $name), $arguments);
    }

}

// vim: et sw=4 sts=4


code:
<?php

/**
 * Filesystem tools not provided by default; can recursively create, copy
 * and delete folders. Some template methods are provided for extensibility.
 *
 * @note This class must be instantiated to be used, although it does
 *       not maintain state.
 */
class FSTools
{

    private static $singleton;

    /**
     * Returns a global instance of FSTools
     */
    public static function singleton()
    {
        if (empty(FSTools::$singleton)) FSTools::$singleton = new FSTools();
        return FSTools::$singleton;
    }

    /**
     * Sets our global singleton to something else; useful for overloading
     * functions.
     */
    public static function setSingleton($singleton)
    {
        FSTools::$singleton = $singleton;
    }

    /**
     * Recursively creates a directory
     * @param string $folder Name of folder to create
     * @note Adapted from the PHP manual comment 76612
     */
    public function mkdirr($folder)
    {
        $folders = preg_split("#[\\\\/]#", $folder);
        $base = '';
        for($i = 0, $c = count($folders); $i < $c; $i++) {
            if(empty($folders[$i])) {
                if (!$i) {
                    // special case for root level
                    $base .= DIRECTORY_SEPARATOR;
                }
                continue;
            }
            $base .= $folders[$i];
            if(!is_dir($base)){
                $this->mkdir($base);
            }
            $base .= DIRECTORY_SEPARATOR;
        }
    }

    /**
     * Copy a file, or recursively copy a folder and its contents; modified
     * so that copied files, if PHP, have includes removed
     * @note Adapted from http://aidanlister.com/repos/v/function.copyr.php
     */
    public function copyr($source, $dest)
    {
        // Simple copy for a file
        if (is_file($source)) {
            return $this->copy($source, $dest);
        }
        // Make destination directory
        if (!is_dir($dest)) {
            $this->mkdir($dest);
        }
        // Loop through the folder
        $dir = $this->dir($source);
        while ( false !== ($entry = $dir->read()) ) {
            // Skip pointers
            if ($entry == '.' || $entry == '..') {
                continue;
            }
            if (!$this->copyable($entry)) {
                continue;
            }
            // Deep copy directories
            if ($dest !== "$source/$entry") {
                $this->copyr("$source/$entry", "$dest/$entry");
            }
        }
        // Clean up
        $dir->close();
        return true;
    }

    /**
     * Overloadable function that tests a filename for copyability. By
     * default, everything should be copied; you can restrict things to
     * ignore hidden files, unreadable files, etc. This function
     * applies to copyr().
     */
    public function copyable($file)
    {
        return true;
    }

    /**
     * Delete a file, or a folder and its contents
     * @note Adapted from http://aidanlister.com/repos/v/function.rmdirr.php
     */
    public function rmdirr($dirname)
    {
        // Sanity check
        if (!$this->file_exists($dirname)) {
            return false;
        }

        // Simple delete for a file
        if ($this->is_file($dirname) || $this->is_link($dirname)) {
            return $this->unlink($dirname);
        }

        // Loop through the folder
        $dir = $this->dir($dirname);
        while (false !== $entry = $dir->read()) {
            // Skip pointers
            if ($entry == '.' || $entry == '..') {
                continue;
            }
            // Recurse
            $this->rmdirr($dirname . DIRECTORY_SEPARATOR . $entry);
        }

        // Clean up
        $dir->close();
        return $this->rmdir($dirname);
    }

    /**
     * Recursively globs a directory.
     */
    public function globr($dir, $pattern, $flags = NULL)
    {
        $files = $this->glob("$dir/$pattern", $flags);
        if ($files === false) $files = array();
        $sub_dirs = $this->glob("$dir/*", GLOB_ONLYDIR);
        if ($sub_dirs === false) $sub_dirs = array();
        foreach ($sub_dirs as $sub_dir) {
            $sub_files = $this->globr($sub_dir, $pattern, $flags);
            $files = array_merge($files, $sub_files);
        }
        return $files;
    }

    /**
     * Allows for PHP functions to be called and be stubbed.
     * @warning This function will not work for functions that need
     *      to pass references; manually define a stub function for those.
     */
    public function __call($name, $args)
    {
        return call_user_func_array($name, $args);
    }

}

// vim: et sw=4 sts=4


code:
<?php

/**
 * Represents a file in the filesystem
 *
 * @warning Be sure to distinguish between get() and write() versus
 *      read() and put(), the former operates on the entire file, while
 *      the latter operates on a handle.
 */
class FSTools_File
{

    /** Filename of file this object represents */
    protected $name;

    /** Handle for the file */
    protected $handle = false;

    /** Instance of FSTools for interfacing with filesystem */
    protected $fs;

    /**
     * Filename of file you wish to instantiate.
     * @note This file need not exist
     */
    public function __construct($name, $fs = false)
    {
        $this->name = $name;
        $this->fs = $fs ? $fs : FSTools::singleton();
    }

    /** Returns the filename of the file. */
    public function getName() {return $this->name;}

    /** Returns directory of the file without trailing slash */
    public function getDirectory() {return $this->fs->dirname($this->name);}

    /**
     * Retrieves the contents of a file
     * @todo Throw an exception if file doesn't exist
     */
    public function get()
    {
        return $this->fs->file_get_contents($this->name);
    }

    /** Writes contents to a file, creates new file if necessary */
    public function write($contents)
    {
        return $this->fs->file_put_contents($this->name, $contents);
    }

    /** Deletes the file */
    public function delete()
    {
        return $this->fs->unlink($this->name);
    }

    /** Returns true if file exists and is a file. */
    public function exists()
    {
        return $this->fs->is_file($this->name);
    }

    /** Returns last file modification time */
    public function getMTime()
    {
        return $this->fs->filemtime($this->name);
    }

    /**
     * Chmod a file
     * @note We ignore errors because of some weird owner trickery due
     *       to SVN duality
     */
    public function chmod($octal_code)
    {
        return @$this->fs->chmod($this->name, $octal_code);
    }

    /** Opens file's handle */
    public function open($mode)
    {
        if ($this->handle) $this->close();
        $this->handle = $this->fs->fopen($this->name, $mode);
        return true;
    }

    /** Closes file's handle */
    public function close()
    {
        if (!$this->handle) return false;
        $status = $this->fs->fclose($this->handle);
        $this->handle = false;
        return $status;
    }

    /** Retrieves a line from an open file, with optional max length $length */
    public function getLine($length = null)
    {
        if (!$this->handle) $this->open('r');
        if ($length === null) return $this->fs->fgets($this->handle);
        else return $this->fs->fgets($this->handle, $length);
    }

    /** Retrieves a character from an open file */
    public function getChar()
    {
        if (!$this->handle) $this->open('r');
        return $this->fs->fgetc($this->handle);
    }

    /** Retrieves an $length bytes of data from an open data */
    public function read($length)
    {
        if (!$this->handle) $this->open('r');
        return $this->fs->fread($this->handle, $length);
    }

    /** Writes to an open file */
    public function put($string)
    {
        if (!$this->handle) $this->open('a');
        return $this->fs->fwrite($this->handle, $string);
    }

    /** Returns TRUE if the end of the file has been reached */
    public function eof()
    {
        if (!$this->handle) return true;
        return $this->fs->feof($this->handle);
    }

    public function __destruct()
    {
        if ($this->handle) $this->close();
    }

}

// vim: et sw=4 sts=4


code:
<?php

/**
 * This is a stub include that automatically configures the include path.
 */

set_include_path(dirname(__FILE__) . PATH_SEPARATOR . get_include_path() );
require_once 'HTMLPurifierExtras.php';
require_once 'HTMLPurifierExtras.autoload.php';

// vim: et sw=4 sts=4


code:
<?php

/**
 * @file
 * Legacy autoloader for systems lacking spl_autoload_register
 *
 * Must be separate to prevent deprecation warning on PHP 7.2
 */

function __autoload($class)
{
    return HTMLPurifierExtras::autoload($class);
}

// vim: et sw=4 sts=4


code:
<?php

/**
 * @file
 * Convenience file that registers autoload handler for HTML Purifier.
 *
 * @warning
 *      This autoloader does not contain the compatibility code seen in
 *      HTMLPurifier_Bootstrap; the user is expected to make any necessary
 *      changes to use this library.
 */

if (function_exists('spl_autoload_register')) {
    spl_autoload_register(array('HTMLPurifierExtras', 'autoload'));
    if (function_exists('__autoload')) {
        // Be polite and ensure that userland autoload gets retained
        spl_autoload_register('__autoload');
    }
} elseif (!function_exists('__autoload')) {
    require dirname(__FILE__) . '/HTMLPurifierExtras.autoload-legacy.php';
}

// vim: et sw=4 sts=4


code:
<?php

/**
 * Meta-class for HTML Purifier's extra class hierarchies, similar to
 * HTMLPurifier_Bootstrap.
 */
class HTMLPurifierExtras
{

    public static function autoload($class)
    {
        $path = HTMLPurifierExtras::getPath($class);
        if (!$path) return false;
        require $path;
        return true;
    }

    public static function getPath($class)
    {
        if (
            strncmp('FSTools', $class, 7) !== 0 &&
            strncmp('ConfigDoc', $class, 9) !== 0
        ) return false;
        // Custom implementations can go here
        // Standard implementation:
        return str_replace('_', '/', $class) . '.php';
    }

}

// vim: et sw=4 sts=4


code:
HTML Purifier Extras
    The Method Behind The Madness!

The extras/ folder in HTML Purifier contains--you guessed it--extra things
for HTML Purifier.  Specifically, these are two extra libraries called
FSTools and ConfigSchema.  They're extra for a reason: you don't need them
if you're using HTML Purifier for normal usage: filtering HTML.  However,
if you're a developer, and would like to test HTML Purifier, or need to
use one of HTML Purifier's maintenance scripts, chances are they'll need
these libraries. Who knows: maybe you'll find them useful too!

Here are the libraries:

FSTools
-------

Short for File System Tools, this is a poor-man's object-oriented wrapper for
the filesystem. It currently consists of two classes:

- FSTools: This is a singleton that contains a manner of useful functions
  such as recursive glob, directory removal, etc, as well as the ability
  to call arbitrary native PHP functions through it like $FS->fopen(...).
  This makes it a lot simpler to mock these filesystem calls for unit testing.

- FSTools_File: This object represents a single file, and has almost any
  method imaginable one would need.

Check the files themselves for more information.

    vim: et sw=4 sts=4


code:
<?php

/**
 * This is a stub include that automatically configures the include path.
 */

set_include_path(dirname(__FILE__) . PATH_SEPARATOR . get_include_path() );
require_once 'HTMLPurifier/Bootstrap.php';
require_once 'HTMLPurifier.autoload.php';

// vim: et sw=4 sts=4


code:
<?php

/**
 * @file
 * Legacy autoloader for systems lacking spl_autoload_register
 *
 * Must be separate to prevent deprecation warning on PHP 7.2
 */

function __autoload($class)
{
    return HTMLPurifier_Bootstrap::autoload($class);
}

// vim: et sw=4 sts=4


code:
<?php

/**
 * @file
 * Convenience file that registers autoload handler for HTML Purifier.
 * It also does some sanity checks.
 */

if (function_exists('spl_autoload_register') && function_exists('spl_autoload_unregister')) {
    // We need unregister for our pre-registering functionality
    HTMLPurifier_Bootstrap::registerAutoload();
    if (function_exists('__autoload')) {
        // Be polite and ensure that userland autoload gets retained
        spl_autoload_register('__autoload');
    }
} elseif (!function_exists('__autoload')) {
    require dirname(__FILE__) . '/HTMLPurifier.autoload-legacy.php';
}

if (ini_get('zend.ze1_compatibility_mode')) {
    trigger_error("HTML Purifier is not compatible with zend.ze1_compatibility_mode; please turn it off", E_USER_ERROR);
}

// vim: et sw=4 sts=4


code:
<?php
if (!defined('HTMLPURIFIER_PREFIX')) {
    define('HTMLPURIFIER_PREFIX', dirname(__FILE__));
}


code:
<?php

/**
 * @file
 * Defines a function wrapper for HTML Purifier for quick use.
 * @note ''HTMLPurifier()'' is NOT the same as ''new HTMLPurifier()''
 */

/**
 * Purify HTML.
 * @param string $html String HTML to purify
 * @param mixed $config Configuration to use, can be any value accepted by
 *        HTMLPurifier_Config::create()
 * @return string
 */
function HTMLPurifier($html, $config = null)
{
    static $purifier = false;
    if (!$purifier) {
        $purifier = new HTMLPurifier();
    }
    return $purifier->purify($html, $config);
}

// vim: et sw=4 sts=4


code:
<?php

/**
 * @file
 * This file was auto-generated by generate-includes.php and includes all of
 * the core files required by HTML Purifier. Use this if performance is a
 * primary concern and you are using an opcode cache. PLEASE DO NOT EDIT THIS
 * FILE, changes will be overwritten the next time the script is run.
 *
 * @version 4.10.0
 *
 * @warning
 *      You must *not* include any other HTML Purifier files before this file,
 *      because 'require' not 'require_once' is used.
 *
 * @warning
 *      This file requires that the include path contains the HTML Purifier
 *      library directory; this is not auto-set.
 */

require 'HTMLPurifier.php';
require 'HTMLPurifier/Arborize.php';
require 'HTMLPurifier/AttrCollections.php';
require 'HTMLPurifier/AttrDef.php';
require 'HTMLPurifier/AttrTransform.php';
require 'HTMLPurifier/AttrTypes.php';
require 'HTMLPurifier/AttrValidator.php';
require 'HTMLPurifier/Bootstrap.php';
require 'HTMLPurifier/Definition.php';
require 'HTMLPurifier/CSSDefinition.php';
require 'HTMLPurifier/ChildDef.php';
require 'HTMLPurifier/Config.php';
require 'HTMLPurifier/ConfigSchema.php';
require 'HTMLPurifier/ContentSets.php';
require 'HTMLPurifier/Context.php';
require 'HTMLPurifier/DefinitionCache.php';
require 'HTMLPurifier/DefinitionCacheFactory.php';
require 'HTMLPurifier/Doctype.php';
require 'HTMLPurifier/DoctypeRegistry.php';
require 'HTMLPurifier/ElementDef.php';
require 'HTMLPurifier/Encoder.php';
require 'HTMLPurifier/EntityLookup.php';
require 'HTMLPurifier/EntityParser.php';
require 'HTMLPurifier/ErrorCollector.php';
require 'HTMLPurifier/ErrorStruct.php';
require 'HTMLPurifier/Exception.php';
require 'HTMLPurifier/Filter.php';
require 'HTMLPurifier/Generator.php';
require 'HTMLPurifier/HTMLDefinition.php';
require 'HTMLPurifier/HTMLModule.php';
require 'HTMLPurifier/HTMLModuleManager.php';
require 'HTMLPurifier/IDAccumulator.php';
require 'HTMLPurifier/Injector.php';
require 'HTMLPurifier/Language.php';
require 'HTMLPurifier/LanguageFactory.php';
require 'HTMLPurifier/Length.php';
require 'HTMLPurifier/Lexer.php';
require 'HTMLPurifier/Node.php';
require 'HTMLPurifier/PercentEncoder.php';
require 'HTMLPurifier/PropertyList.php';
require 'HTMLPurifier/PropertyListIterator.php';
require 'HTMLPurifier/Queue.php';
require 'HTMLPurifier/Strategy.php';
require 'HTMLPurifier/StringHash.php';
require 'HTMLPurifier/StringHashParser.php';
require 'HTMLPurifier/TagTransform.php';
require 'HTMLPurifier/Token.php';
require 'HTMLPurifier/TokenFactory.php';
require 'HTMLPurifier/URI.php';
require 'HTMLPurifier/URIDefinition.php';
require 'HTMLPurifier/URIFilter.php';
require 'HTMLPurifier/URIParser.php';
require 'HTMLPurifier/URIScheme.php';
require 'HTMLPurifier/URISchemeRegistry.php';
require 'HTMLPurifier/UnitConverter.php';
require 'HTMLPurifier/VarParser.php';
require 'HTMLPurifier/VarParserException.php';
require 'HTMLPurifier/Zipper.php';
require 'HTMLPurifier/AttrDef/CSS.php';
require 'HTMLPurifier/AttrDef/Clone.php';
require 'HTMLPurifier/AttrDef/Enum.php';
require 'HTMLPurifier/AttrDef/Integer.php';
require 'HTMLPurifier/AttrDef/Lang.php';
require 'HTMLPurifier/AttrDef/Switch.php';
require 'HTMLPurifier/AttrDef/Text.php';
require 'HTMLPurifier/AttrDef/URI.php';
require 'HTMLPurifier/AttrDef/CSS/Number.php';
require 'HTMLPurifier/AttrDef/CSS/AlphaValue.php';
require 'HTMLPurifier/AttrDef/CSS/Background.php';
require 'HTMLPurifier/AttrDef/CSS/BackgroundPosition.php';
require 'HTMLPurifier/AttrDef/CSS/Border.php';
require 'HTMLPurifier/AttrDef/CSS/Color.php';
require 'HTMLPurifier/AttrDef/CSS/Composite.php';
require 'HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php';
require 'HTMLPurifier/AttrDef/CSS/Filter.php';
require 'HTMLPurifier/AttrDef/CSS/Font.php';
require 'HTMLPurifier/AttrDef/CSS/FontFamily.php';
require 'HTMLPurifier/AttrDef/CSS/Ident.php';
require 'HTMLPurifier/AttrDef/CSS/ImportantDecorator.php';
require 'HTMLPurifier/AttrDef/CSS/Length.php';
require 'HTMLPurifier/AttrDef/CSS/ListStyle.php';
require 'HTMLPurifier/AttrDef/CSS/Multiple.php';
require 'HTMLPurifier/AttrDef/CSS/Percentage.php';
require 'HTMLPurifier/AttrDef/CSS/TextDecoration.php';
require 'HTMLPurifier/AttrDef/CSS/URI.php';
require 'HTMLPurifier/AttrDef/HTML/Bool.php';
require 'HTMLPurifier/AttrDef/HTML/Nmtokens.php';
require 'HTMLPurifier/AttrDef/HTML/Class.php';
require 'HTMLPurifier/AttrDef/HTML/Color.php';
require 'HTMLPurifier/AttrDef/HTML/FrameTarget.php';
require 'HTMLPurifier/AttrDef/HTML/ID.php';
require 'HTMLPurifier/AttrDef/HTML/Pixels.php';
require 'HTMLPurifier/AttrDef/HTML/Length.php';
require 'HTMLPurifier/AttrDef/HTML/LinkTypes.php';
require 'HTMLPurifier/AttrDef/HTML/MultiLength.php';
require 'HTMLPurifier/AttrDef/URI/Email.php';
require 'HTMLPurifier/AttrDef/URI/Host.php';
require 'HTMLPurifier/AttrDef/URI/IPv4.php';
require 'HTMLPurifier/AttrDef/URI/IPv6.php';
require 'HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php';
require 'HTMLPurifier/AttrTransform/Background.php';
require 'HTMLPurifier/AttrTransform/BdoDir.php';
require 'HTMLPurifier/AttrTransform/BgColor.php';
require 'HTMLPurifier/AttrTransform/BoolToCSS.php';
require 'HTMLPurifier/AttrTransform/Border.php';
require 'HTMLPurifier/AttrTransform/EnumToCSS.php';
require 'HTMLPurifier/AttrTransform/ImgRequired.php';
require 'HTMLPurifier/AttrTransform/ImgSpace.php';
require 'HTMLPurifier/AttrTransform/Input.php';
require 'HTMLPurifier/AttrTransform/Lang.php';
require 'HTMLPurifier/AttrTransform/Length.php';
require 'HTMLPurifier/AttrTransform/Name.php';
require 'HTMLPurifier/AttrTransform/NameSync.php';
require 'HTMLPurifier/AttrTransform/Nofollow.php';
require 'HTMLPurifier/AttrTransform/SafeEmbed.php';
require 'HTMLPurifier/AttrTransform/SafeObject.php';
require 'HTMLPurifier/AttrTransform/SafeParam.php';
require 'HTMLPurifier/AttrTransform/ScriptRequired.php';
require 'HTMLPurifier/AttrTransform/TargetBlank.php';
require 'HTMLPurifier/AttrTransform/TargetNoopener.php';
require 'HTMLPurifier/AttrTransform/TargetNoreferrer.php';
require 'HTMLPurifier/AttrTransform/Textarea.php';
require 'HTMLPurifier/ChildDef/Chameleon.php';
require 'HTMLPurifier/ChildDef/Custom.php';
require 'HTMLPurifier/ChildDef/Empty.php';
require 'HTMLPurifier/ChildDef/List.php';
require 'HTMLPurifier/ChildDef/Required.php';
require 'HTMLPurifier/ChildDef/Optional.php';
require 'HTMLPurifier/ChildDef/StrictBlockquote.php';
require 'HTMLPurifier/ChildDef/Table.php';
require 'HTMLPurifier/DefinitionCache/Decorator.php';
require 'HTMLPurifier/DefinitionCache/Null.php';
require 'HTMLPurifier/DefinitionCache/Serializer.php';
require 'HTMLPurifier/DefinitionCache/Decorator/Cleanup.php';
require 'HTMLPurifier/DefinitionCache/Decorator/Memory.php';
require 'HTMLPurifier/HTMLModule/Bdo.php';
require 'HTMLPurifier/HTMLModule/CommonAttributes.php';
require 'HTMLPurifier/HTMLModule/Edit.php';
require 'HTMLPurifier/HTMLModule/Forms.php';
require 'HTMLPurifier/HTMLModule/Hypertext.php';
require 'HTMLPurifier/HTMLModule/Iframe.php';
require 'HTMLPurifier/HTMLModule/Image.php';
require 'HTMLPurifier/HTMLModule/Legacy.php';
require 'HTMLPurifier/HTMLModule/List.php';
require 'HTMLPurifier/HTMLModule/Name.php';
require 'HTMLPurifier/HTMLModule/Nofollow.php';
require 'HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php';
require 'HTMLPurifier/HTMLModule/Object.php';
require 'HTMLPurifier/HTMLModule/Presentation.php';
require 'HTMLPurifier/HTMLModule/Proprietary.php';
require 'HTMLPurifier/HTMLModule/Ruby.php';
require 'HTMLPurifier/HTMLModule/SafeEmbed.php';
require 'HTMLPurifier/HTMLModule/SafeObject.php';
require 'HTMLPurifier/HTMLModule/SafeScripting.php';
require 'HTMLPurifier/HTMLModule/Scripting.php';
require 'HTMLPurifier/HTMLModule/StyleAttribute.php';
require 'HTMLPurifier/HTMLModule/Tables.php';
require 'HTMLPurifier/HTMLModule/Target.php';
require 'HTMLPurifier/HTMLModule/TargetBlank.php';
require 'HTMLPurifier/HTMLModule/TargetNoopener.php';
require 'HTMLPurifier/HTMLModule/TargetNoreferrer.php';
require 'HTMLPurifier/HTMLModule/Text.php';
require 'HTMLPurifier/HTMLModule/Tidy.php';
require 'HTMLPurifier/HTMLModule/XMLCommonAttributes.php';
require 'HTMLPurifier/HTMLModule/Tidy/Name.php';
require 'HTMLPurifier/HTMLModule/Tidy/Proprietary.php';
require 'HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php';
require 'HTMLPurifier/HTMLModule/Tidy/Strict.php';
require 'HTMLPurifier/HTMLModule/Tidy/Transitional.php';
require 'HTMLPurifier/HTMLModule/Tidy/XHTML.php';
require 'HTMLPurifier/Injector/AutoParagraph.php';
require 'HTMLPurifier/Injector/DisplayLinkURI.php';
require 'HTMLPurifier/Injector/Linkify.php';
require 'HTMLPurifier/Injector/PurifierLinkify.php';
require 'HTMLPurifier/Injector/RemoveEmpty.php';
require 'HTMLPurifier/Injector/RemoveSpansWithoutAttributes.php';
require 'HTMLPurifier/Injector/SafeObject.php';
require 'HTMLPurifier/Lexer/DOMLex.php';
require 'HTMLPurifier/Lexer/DirectLex.php';
require 'HTMLPurifier/Node/Comment.php';
require 'HTMLPurifier/Node/Element.php';
require 'HTMLPurifier/Node/Text.php';
require 'HTMLPurifier/Strategy/Composite.php';
require 'HTMLPurifier/Strategy/Core.php';
require 'HTMLPurifier/Strategy/FixNesting.php';
require 'HTMLPurifier/Strategy/MakeWellFormed.php';
require 'HTMLPurifier/Strategy/RemoveForeignElements.php';
require 'HTMLPurifier/Strategy/ValidateAttributes.php';
require 'HTMLPurifier/TagTransform/Font.php';
require 'HTMLPurifier/TagTransform/Simple.php';
require 'HTMLPurifier/Token/Comment.php';
require 'HTMLPurifier/Token/Tag.php';
require 'HTMLPurifier/Token/Empty.php';
require 'HTMLPurifier/Token/End.php';
require 'HTMLPurifier/Token/Start.php';
require 'HTMLPurifier/Token/Text.php';
require 'HTMLPurifier/URIFilter/DisableExternal.php';
require 'HTMLPurifier/URIFilter/DisableExternalResources.php';
require 'HTMLPurifier/URIFilter/DisableResources.php';
require 'HTMLPurifier/URIFilter/HostBlacklist.php';
require 'HTMLPurifier/URIFilter/MakeAbsolute.php';
require 'HTMLPurifier/URIFilter/Munge.php';
require 'HTMLPurifier/URIFilter/SafeIframe.php';
require 'HTMLPurifier/URIScheme/data.php';
require 'HTMLPurifier/URIScheme/file.php';
require 'HTMLPurifier/URIScheme/ftp.php';
require 'HTMLPurifier/URIScheme/http.php';
require 'HTMLPurifier/URIScheme/https.php';
require 'HTMLPurifier/URIScheme/mailto.php';
require 'HTMLPurifier/URIScheme/news.php';
require 'HTMLPurifier/URIScheme/nntp.php';
require 'HTMLPurifier/URIScheme/tel.php';
require 'HTMLPurifier/VarParser/Flexible.php';
require 'HTMLPurifier/VarParser/Native.php';


code:
<?php

/**
 * @file
 * Emulation layer for code that used kses(), substituting in HTML Purifier.
 */

require_once dirname(__FILE__) . '/HTMLPurifier.auto.php';

function kses($string, $allowed_html, $allowed_protocols = null)
{
    $config = HTMLPurifier_Config::createDefault();
    $allowed_elements = array();
    $allowed_attributes = array();
    foreach ($allowed_html as $element => $attributes) {
        $allowed_elements[$element] = true;
        foreach ($attributes as $attribute => $x) {
            $allowed_attributes["$element.$attribute"] = true;
        }
    }
    $config->set('HTML.AllowedElements', $allowed_elements);
    $config->set('HTML.AllowedAttributes', $allowed_attributes);
    if ($allowed_protocols !== null) {
        $config->set('URI.AllowedSchemes', $allowed_protocols);
    }
    $purifier = new HTMLPurifier($config);
    return $purifier->purify($string);
}

// vim: et sw=4 sts=4


code:
<?php

/**
 * @file
 * Convenience stub file that adds HTML Purifier's library file to the path
 * without any other side-effects.
 */

set_include_path(dirname(__FILE__) . PATH_SEPARATOR . get_include_path() );

// vim: et sw=4 sts=4


code:
<?php

/*! @mainpage
 *
 * HTML Purifier is an HTML filter that will take an arbitrary snippet of
 * HTML and rigorously test, validate and filter it into a version that
 * is safe for output onto webpages. It achieves this by:
 *
 *  -# Lexing (parsing into tokens) the document,
 *  -# Executing various strategies on the tokens:
 *      -# Removing all elements not in the whitelist,
 *      -# Making the tokens well-formed,
 *      -# Fixing the nesting of the nodes, and
 *      -# Validating attributes of the nodes; and
 *  -# Generating HTML from the purified tokens.
 *
 * However, most users will only need to interface with the HTMLPurifier
 * and HTMLPurifier_Config.
 */

/*
    HTML Purifier 4.10.0 - Standards Compliant HTML Filtering
    Copyright (C) 2006-2008 Edward Z. Yang

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

/**
 * Facade that coordinates HTML Purifier's subsystems in order to purify HTML.
 *
 * @note There are several points in which configuration can be specified
 *       for HTML Purifier.  The precedence of these (from lowest to
 *       highest) is as follows:
 *          -# Instance: new HTMLPurifier($config)
 *          -# Invocation: purify($html, $config)
 *       These configurations are entirely independent of each other and
 *       are *not* merged (this behavior may change in the future).
 *
 * @todo We need an easier way to inject strategies using the configuration
 *       object.
 */
class HTMLPurifier
{

    /**
     * Version of HTML Purifier.
     * @type string
     */
    public $version = '4.10.0';

    /**
     * Constant with version of HTML Purifier.
     */
    const VERSION = '4.10.0';

    /**
     * Global configuration object.
     * @type HTMLPurifier_Config
     */
    public $config;

    /**
     * Array of extra filter objects to run on HTML,
     * for backwards compatibility.
     * @type HTMLPurifier_Filter[]
     */
    private $filters = array();

    /**
     * Single instance of HTML Purifier.
     * @type HTMLPurifier
     */
    private static $instance;

    /**
     * @type HTMLPurifier_Strategy_Core
     */
    protected $strategy;

    /**
     * @type HTMLPurifier_Generator
     */
    protected $generator;

    /**
     * Resultant context of last run purification.
     * Is an array of contexts if the last called method was purifyArray().
     * @type HTMLPurifier_Context
     */
    public $context;

    /**
     * Initializes the purifier.
     *
     * @param HTMLPurifier_Config|mixed $config Optional HTMLPurifier_Config object
     *                for all instances of the purifier, if omitted, a default
     *                configuration is supplied (which can be overridden on a
     *                per-use basis).
     *                The parameter can also be any type that
     *                HTMLPurifier_Config::create() supports.
     */
    public function __construct($config = null)
    {
        $this->config = HTMLPurifier_Config::create($config);
        $this->strategy = new HTMLPurifier_Strategy_Core();
    }

    /**
     * Adds a filter to process the output. First come first serve
     *
     * @param HTMLPurifier_Filter $filter HTMLPurifier_Filter object
     */
    public function addFilter($filter)
    {
        trigger_error(
            'HTMLPurifier->addFilter() is deprecated, use configuration directives' .
            ' in the Filter namespace or Filter.Custom',
            E_USER_WARNING
        );
        $this->filters[] = $filter;
    }

    /**
     * Filters an HTML snippet/document to be XSS-free and standards-compliant.
     *
     * @param string $html String of HTML to purify
     * @param HTMLPurifier_Config $config Config object for this operation,
     *                if omitted, defaults to the config object specified during this
     *                object's construction. The parameter can also be any type
     *                that HTMLPurifier_Config::create() supports.
     *
     * @return string Purified HTML
     */
    public function purify($html, $config = null)
    {
        // :TODO: make the config merge in, instead of replace
        $config = $config ? HTMLPurifier_Config::create($config) : $this->config;

        // implementation is partially environment dependant, partially
        // configuration dependant
        $lexer = HTMLPurifier_Lexer::create($config);

        $context = new HTMLPurifier_Context();

        // setup HTML generator
        $this->generator = new HTMLPurifier_Generator($config, $context);
        $context->register('Generator', $this->generator);

        // set up global context variables
        if ($config->get('Core.CollectErrors')) {
            // may get moved out if other facilities use it
            $language_factory = HTMLPurifier_LanguageFactory::instance();
            $language = $language_factory->create($config, $context);
            $context->register('Locale', $language);

            $error_collector = new HTMLPurifier_ErrorCollector($context);
            $context->register('ErrorCollector', $error_collector);
        }

        // setup id_accumulator context, necessary due to the fact that
        // AttrValidator can be called from many places
        $id_accumulator = HTMLPurifier_IDAccumulator::build($config, $context);
        $context->register('IDAccumulator', $id_accumulator);

        $html = HTMLPurifier_Encoder::convertToUTF8($html, $config, $context);

        // setup filters
        $filter_flags = $config->getBatch('Filter');
        $custom_filters = $filter_flags['Custom'];
        unset($filter_flags['Custom']);
        $filters = array();
        foreach ($filter_flags as $filter => $flag) {
            if (!$flag) {
                continue;
            }
            if (strpos($filter, '.') !== false) {
                continue;
            }
            $class = "HTMLPurifier_Filter_$filter";
            $filters[] = new $class;
        }
        foreach ($custom_filters as $filter) {
            // maybe "HTMLPurifier_Filter_$filter", but be consistent with AutoFormat
            $filters[] = $filter;
        }
        $filters = array_merge($filters, $this->filters);
        // maybe prepare(), but later

        for ($i = 0, $filter_size = count($filters); $i < $filter_size; $i++) {
            $html = $filters[$i]->preFilter($html, $config, $context);
        }

        // purified HTML
        $html =
            $this->generator->generateFromTokens(
                // list of tokens
                $this->strategy->execute(
                    // list of un-purified tokens
                    $lexer->tokenizeHTML(
                        // un-purified HTML
                        $html,
                        $config,
                        $context
                    ),
                    $config,
                    $context
                )
            );

        for ($i = $filter_size - 1; $i >= 0; $i--) {
            $html = $filters[$i]->postFilter($html, $config, $context);
        }

        $html = HTMLPurifier_Encoder::convertFromUTF8($html, $config, $context);
        $this->context =& $context;
        return $html;
    }

    /**
     * Filters an array of HTML snippets
     *
     * @param string[] $array_of_html Array of html snippets
     * @param HTMLPurifier_Config $config Optional config object for this operation.
     *                See HTMLPurifier::purify() for more details.
     *
     * @return string[] Array of purified HTML
     */
    public function purifyArray($array_of_html, $config = null)
    {
        $context_array = array();
        foreach ($array_of_html as $key => $html) {
            $array_of_html[$key] = $this->purify($html, $config);
            $context_array[$key] = $this->context;
        }
        $this->context = $context_array;
        return $array_of_html;
    }

    /**
     * Singleton for enforcing just one HTML Purifier in your system
     *
     * @param HTMLPurifier|HTMLPurifier_Config $prototype Optional prototype
     *                   HTMLPurifier instance to overload singleton with,
     *                   or HTMLPurifier_Config instance to configure the
     *                   generated version with.
     *
     * @return HTMLPurifier
     */
    public static function instance($prototype = null)
    {
        if (!self::$instance || $prototype) {
            if ($prototype instanceof HTMLPurifier) {
                self::$instance = $prototype;
            } elseif ($prototype) {
                self::$instance = new HTMLPurifier($prototype);
            } else {
                self::$instance = new HTMLPurifier();
            }
        }
        return self::$instance;
    }

    /**
     * Singleton for enforcing just one HTML Purifier in your system
     *
     * @param HTMLPurifier|HTMLPurifier_Config $prototype Optional prototype
     *                   HTMLPurifier instance to overload singleton with,
     *                   or HTMLPurifier_Config instance to configure the
     *                   generated version with.
     *
     * @return HTMLPurifier
     * @note Backwards compatibility, see instance()
     */
    public static function getInstance($prototype = null)
    {
        return HTMLPurifier::instance($prototype);
    }
}

// vim: et sw=4 sts=4


code:
<?php

/**
 * @file
 * This file was auto-generated by generate-includes.php and includes all of
 * the core files required by HTML Purifier. This is a convenience stub that
 * includes all files using dirname(__FILE__) and require_once. PLEASE DO NOT
 * EDIT THIS FILE, changes will be overwritten the next time the script is run.
 *
 * Changes to include_path are not necessary.
 */

$__dir = dirname(__FILE__);

require_once $__dir . '/HTMLPurifier.php';
require_once $__dir . '/HTMLPurifier/Arborize.php';
require_once $__dir . '/HTMLPurifier/AttrCollections.php';
require_once $__dir . '/HTMLPurifier/AttrDef.php';
require_once $__dir . '/HTMLPurifier/AttrTransform.php';
require_once $__dir . '/HTMLPurifier/AttrTypes.php';
require_once $__dir . '/HTMLPurifier/AttrValidator.php';
require_once $__dir . '/HTMLPurifier/Bootstrap.php';
require_once $__dir . '/HTMLPurifier/Definition.php';
require_once $__dir . '/HTMLPurifier/CSSDefinition.php';
require_once $__dir . '/HTMLPurifier/ChildDef.php';
require_once $__dir . '/HTMLPurifier/Config.php';
require_once $__dir . '/HTMLPurifier/ConfigSchema.php';
require_once $__dir . '/HTMLPurifier/ContentSets.php';
require_once $__dir . '/HTMLPurifier/Context.php';
require_once $__dir . '/HTMLPurifier/DefinitionCache.php';
require_once $__dir . '/HTMLPurifier/DefinitionCacheFactory.php';
require_once $__dir . '/HTMLPurifier/Doctype.php';
require_once $__dir . '/HTMLPurifier/DoctypeRegistry.php';
require_once $__dir . '/HTMLPurifier/ElementDef.php';
require_once $__dir . '/HTMLPurifier/Encoder.php';
require_once $__dir . '/HTMLPurifier/EntityLookup.php';
require_once $__dir . '/HTMLPurifier/EntityParser.php';
require_once $__dir . '/HTMLPurifier/ErrorCollector.php';
require_once $__dir . '/HTMLPurifier/ErrorStruct.php';
require_once $__dir . '/HTMLPurifier/Exception.php';
require_once $__dir . '/HTMLPurifier/Filter.php';
require_once $__dir . '/HTMLPurifier/Generator.php';
require_once $__dir . '/HTMLPurifier/HTMLDefinition.php';
require_once $__dir . '/HTMLPurifier/HTMLModule.php';
require_once $__dir . '/HTMLPurifier/HTMLModuleManager.php';
require_once $__dir . '/HTMLPurifier/IDAccumulator.php';
require_once $__dir . '/HTMLPurifier/Injector.php';
require_once $__dir . '/HTMLPurifier/Language.php';
require_once $__dir . '/HTMLPurifier/LanguageFactory.php';
require_once $__dir . '/HTMLPurifier/Length.php';
require_once $__dir . '/HTMLPurifier/Lexer.php';
require_once $__dir . '/HTMLPurifier/Node.php';
require_once $__dir . '/HTMLPurifier/PercentEncoder.php';
require_once $__dir . '/HTMLPurifier/PropertyList.php';
require_once $__dir . '/HTMLPurifier/PropertyListIterator.php';
require_once $__dir . '/HTMLPurifier/Queue.php';
require_once $__dir . '/HTMLPurifier/Strategy.php';
require_once $__dir . '/HTMLPurifier/StringHash.php';
require_once $__dir . '/HTMLPurifier/StringHashParser.php';
require_once $__dir . '/HTMLPurifier/TagTransform.php';
require_once $__dir . '/HTMLPurifier/Token.php';
require_once $__dir . '/HTMLPurifier/TokenFactory.php';
require_once $__dir . '/HTMLPurifier/URI.php';
require_once $__dir . '/HTMLPurifier/URIDefinition.php';
require_once $__dir . '/HTMLPurifier/URIFilter.php';
require_once $__dir . '/HTMLPurifier/URIParser.php';
require_once $__dir . '/HTMLPurifier/URIScheme.php';
require_once $__dir . '/HTMLPurifier/URISchemeRegistry.php';
require_once $__dir . '/HTMLPurifier/UnitConverter.php';
require_once $__dir . '/HTMLPurifier/VarParser.php';
require_once $__dir . '/HTMLPurifier/VarParserException.php';
require_once $__dir . '/HTMLPurifier/Zipper.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS.php';
require_once $__dir . '/HTMLPurifier/AttrDef/Clone.php';
require_once $__dir . '/HTMLPurifier/AttrDef/Enum.php';
require_once $__dir . '/HTMLPurifier/AttrDef/Integer.php';
require_once $__dir . '/HTMLPurifier/AttrDef/Lang.php';
require_once $__dir . '/HTMLPurifier/AttrDef/Switch.php';
require_once $__dir . '/HTMLPurifier/AttrDef/Text.php';
require_once $__dir . '/HTMLPurifier/AttrDef/URI.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Number.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/AlphaValue.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Background.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Border.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Color.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Composite.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Filter.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Font.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/FontFamily.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Ident.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Length.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/ListStyle.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Multiple.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Percentage.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/TextDecoration.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/URI.php';
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Bool.php';
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Nmtokens.php';
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Class.php';
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Color.php';
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/FrameTarget.php';
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/ID.php';
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Pixels.php';
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Length.php';
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/LinkTypes.php';
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/MultiLength.php';
require_once $__dir . '/HTMLPurifier/AttrDef/URI/Email.php';
require_once $__dir . '/HTMLPurifier/AttrDef/URI/Host.php';
require_once $__dir . '/HTMLPurifier/AttrDef/URI/IPv4.php';
require_once $__dir . '/HTMLPurifier/AttrDef/URI/IPv6.php';
require_once $__dir . '/HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/Background.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/BdoDir.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/BgColor.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/BoolToCSS.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/Border.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/EnumToCSS.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/ImgRequired.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/ImgSpace.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/Input.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/Lang.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/Length.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/Name.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/NameSync.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/Nofollow.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/SafeEmbed.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/SafeObject.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/SafeParam.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/ScriptRequired.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/TargetBlank.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/TargetNoopener.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/TargetNoreferrer.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/Textarea.php';
require_once $__dir . '/HTMLPurifier/ChildDef/Chameleon.php';
require_once $__dir . '/HTMLPurifier/ChildDef/Custom.php';
require_once $__dir . '/HTMLPurifier/ChildDef/Empty.php';
require_once $__dir . '/HTMLPurifier/ChildDef/List.php';
require_once $__dir . '/HTMLPurifier/ChildDef/Required.php';
require_once $__dir . '/HTMLPurifier/ChildDef/Optional.php';
require_once $__dir . '/HTMLPurifier/ChildDef/StrictBlockquote.php';
require_once $__dir . '/HTMLPurifier/ChildDef/Table.php';
require_once $__dir . '/HTMLPurifier/DefinitionCache/Decorator.php';
require_once $__dir . '/HTMLPurifier/DefinitionCache/Null.php';
require_once $__dir . '/HTMLPurifier/DefinitionCache/Serializer.php';
require_once $__dir . '/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php';
require_once $__dir . '/HTMLPurifier/DefinitionCache/Decorator/Memory.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Bdo.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/CommonAttributes.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Edit.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Forms.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Hypertext.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Iframe.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Image.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Legacy.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/List.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Name.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Nofollow.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Object.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Presentation.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Proprietary.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Ruby.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/SafeEmbed.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/SafeObject.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/SafeScripting.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Scripting.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/StyleAttribute.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Tables.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Target.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/TargetBlank.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/TargetNoopener.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/TargetNoreferrer.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Text.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/XMLCommonAttributes.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/Name.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/Proprietary.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/Strict.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/Transitional.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/XHTML.php';
require_once $__dir . '/HTMLPurifier/Injector/AutoParagraph.php';
require_once $__dir . '/HTMLPurifier/Injector/DisplayLinkURI.php';
require_once $__dir . '/HTMLPurifier/Injector/Linkify.php';
require_once $__dir . '/HTMLPurifier/Injector/PurifierLinkify.php';
require_once $__dir . '/HTMLPurifier/Injector/RemoveEmpty.php';
require_once $__dir . '/HTMLPurifier/Injector/RemoveSpansWithoutAttributes.php';
require_once $__dir . '/HTMLPurifier/Injector/SafeObject.php';
require_once $__dir . '/HTMLPurifier/Lexer/DOMLex.php';
require_once $__dir . '/HTMLPurifier/Lexer/DirectLex.php';
require_once $__dir . '/HTMLPurifier/Node/Comment.php';
require_once $__dir . '/HTMLPurifier/Node/Element.php';
require_once $__dir . '/HTMLPurifier/Node/Text.php';
require_once $__dir . '/HTMLPurifier/Strategy/Composite.php';
require_once $__dir . '/HTMLPurifier/Strategy/Core.php';
require_once $__dir . '/HTMLPurifier/Strategy/FixNesting.php';
require_once $__dir . '/HTMLPurifier/Strategy/MakeWellFormed.php';
require_once $__dir . '/HTMLPurifier/Strategy/RemoveForeignElements.php';
require_once $__dir . '/HTMLPurifier/Strategy/ValidateAttributes.php';
require_once $__dir . '/HTMLPurifier/TagTransform/Font.php';
require_once $__dir . '/HTMLPurifier/TagTransform/Simple.php';
require_once $__dir . '/HTMLPurifier/Token/Comment.php';
require_once $__dir . '/HTMLPurifier/Token/Tag.php';
require_once $__dir . '/HTMLPurifier/Token/Empty.php';
require_once $__dir . '/HTMLPurifier/Token/End.php';
require_once $__dir . '/HTMLPurifier/Token/Start.php';
require_once $__dir . '/HTMLPurifier/Token/Text.php';
require_once $__dir . '/HTMLPurifier/URIFilter/DisableExternal.php';
require_once $__dir . '/HTMLPurifier/URIFilter/DisableExternalResources.php';
require_once $__dir . '/HTMLPurifier/URIFilter/DisableResources.php';
require_once $__dir . '/HTMLPurifier/URIFilter/HostBlacklist.php';
require_once $__dir . '/HTMLPurifier/URIFilter/MakeAbsolute.php';
require_once $__dir . '/HTMLPurifier/URIFilter/Munge.php';
require_once $__dir . '/HTMLPurifier/URIFilter/SafeIframe.php';
require_once $__dir . '/HTMLPurifier/URIScheme/data.php';
require_once $__dir . '/HTMLPurifier/URIScheme/file.php';
require_once $__dir . '/HTMLPurifier/URIScheme/ftp.php';
require_once $__dir . '/HTMLPurifier/URIScheme/http.php';
require_once $__dir . '/HTMLPurifier/URIScheme/https.php';
require_once $__dir . '/HTMLPurifier/URIScheme/mailto.php';
require_once $__dir . '/HTMLPurifier/URIScheme/news.php';
require_once $__dir . '/HTMLPurifier/URIScheme/nntp.php';
require_once $__dir . '/HTMLPurifier/URIScheme/tel.php';
require_once $__dir . '/HTMLPurifier/VarParser/Flexible.php';
require_once $__dir . '/HTMLPurifier/VarParser/Native.php';


code:
<?php

/**
 * Converts a stream of HTMLPurifier_Token into an HTMLPurifier_Node,
 * and back again.
 *
 * @note This transformation is not an equivalence.  We mutate the input
 * token stream to make it so; see all [MUT] markers in code.
 */
class HTMLPurifier_Arborize
{
    public static function arborize($tokens, $config, $context) {
        $definition = $config->getHTMLDefinition();
        $parent = new HTMLPurifier_Token_Start($definition->info_parent);
        $stack = array($parent->toNode());
        foreach ($tokens as $token) {
            $token->skip = null; // [MUT]
            $token->carryover = null; // [MUT]
            if ($token instanceof HTMLPurifier_Token_End) {
                $token->start = null; // [MUT]
                $r = array_pop($stack);
                //assert($r->name === $token->name);
                //assert(empty($token->attr));
                $r->endCol = $token->col;
                $r->endLine = $token->line;
                $r->endArmor = $token->armor;
                continue;
            }
            $node = $token->toNode();
            $stack[count($stack)-1]->children[] = $node;
            if ($token instanceof HTMLPurifier_Token_Start) {
                $stack[] = $node;
            }
        }
        //assert(count($stack) == 1);
        return $stack[0];
    }

    public static function flatten($node, $config, $context) {
        $level = 0;
        $nodes = array($level => new HTMLPurifier_Queue(array($node)));
        $closingTokens = array();
        $tokens = array();
        do {
            while (!$nodes[$level]->isEmpty()) {
                $node = $nodes[$level]->shift(); // FIFO
                list($start, $end) = $node->toTokenPair();
                if ($level > 0) {
                    $tokens[] = $start;
                }
                if ($end !== NULL) {
                    $closingTokens[$level][] = $end;
                }
                if ($node instanceof HTMLPurifier_Node_Element) {
                    $level++;
                    $nodes[$level] = new HTMLPurifier_Queue();
                    foreach ($node->children as $childNode) {
                        $nodes[$level]->push($childNode);
                    }
                }
            }
            $level--;
            if ($level && isset($closingTokens[$level])) {
                while ($token = array_pop($closingTokens[$level])) {
                    $tokens[] = $token;
                }
            }
        } while ($level > 0);
        return $tokens;
    }
}